karusic/front/src/components/select/SelectMultiple.tsx

165 lines
4.6 KiB
TypeScript

import { RefObject, useEffect, useMemo, useRef, useState } from 'react';
import { MdEdit, MdKeyboardArrowDown, MdKeyboardArrowUp } from 'react-icons/md';
import { SelectList, SelectListModel } from '@/components/select/SelectList';
import { isNullOrUndefined } from '@/utils/validator';
import { Button, Flex, HStack, Input } from '@/ui';
export type SelectMultipleProps = {
options?: object[];
values?: (number | string)[];
onChange?: (value: (number | string)[] | undefined) => void;
keyKey?: string;
keyValue?: string;
ref?: RefObject<any>;
// if set add capability to add the search item
onCreate?: (data: string) => void;
// if a suggestion exist at the auto compleat
suggestion?: string;
};
export const SelectMultiple = ({
options,
onChange,
values,
ref,
keyKey = 'id',
keyValue = keyKey,
suggestion,
onCreate,
}: SelectMultipleProps) => {
const [showList, setShowList] = useState(false);
const transformedOption = useMemo(() => {
return options?.map((element) => {
return {
id: element[keyKey],
name: element[keyValue],
} as SelectListModel;
});
}, [options, keyKey, keyValue]);
const [hasSuggestion, setHasSuggestion] = useState<boolean>(
onCreate && suggestion ? true : false
);
const [currentSearch, setCurrentSearch] = useState<string | undefined>(
onCreate ? suggestion : undefined
);
useEffect(() => {
if (suggestion) {
setCurrentSearch(suggestion);
setHasSuggestion(true);
}
}, [suggestion]);
const refFocus = ref ?? useRef<HTMLInputElement | null>(null);
const selectedOptions = useMemo(() => {
if (isNullOrUndefined(values) || !transformedOption) {
return [];
}
return transformedOption.filter((element) => {
return values.includes(element[keyKey]);
});
}, [values, transformedOption]);
const selectValue = (data: SelectListModel) => {
const newValues = values?.includes(data.id)
? values.filter((elem) => data.id !== elem)
: [...(values ?? []), data.id];
setShowList(false);
if (onChange) {
if (newValues.length == 0) {
onChange(undefined);
} else {
onChange(newValues);
}
}
};
if (!options) {
return <></>;// TODO: <Spinner />;
}
const onChangeInput = (value: string): void => {
setHasSuggestion(false);
if (value === '') {
setCurrentSearch(undefined);
} else {
setCurrentSearch(value);
}
};
const onOpenClose = () => {
if (!showList) {
refFocus?.current?.focus();
return;
}
};
const createNewItem = !onCreate
? undefined
: (data: string) => {
onCreate(data);
setCurrentSearch(undefined);
};
return (
<Flex direction="column" width="100%" gap="0px">
{selectedOptions && (
<HStack style={{ flexWrap: "wrap", /*spacing="5px"*/ justifyContent: "left", width: "100%", marginBottom: "2px" }}>
{selectedOptions.map((data) => (
<Flex align="flex-start" key={data[keyKey]}>
{/* <Tag.Root
size="md"
key="md"
borderRadius="5px"
variant="solid"
background="green.500"
>
<Tag.Label>{data[keyValue] ?? `id=${data[keyKey]}`}</Tag.Label>
<Tag.CloseTrigger onClick={() => selectValue(data)} />
</Tag.Root> */}
</Flex>
))}
</HStack>
)}
<Flex>
<Input
ref={refFocus}
style={{
width: "100%",
borderRadius: "5px 0 0 5px",
}}
onChange={(e) => onChangeInput(e.target.value)}
//onSubmit={onSubmit}
// TODO: onFocus={() => setShowList(true)}
// TODO: onBlur={() => setTimeout(() => setShowList(false), 200)}
value={showList ? (currentSearch ?? '') : hasSuggestion ? `suggest: ${currentSearch}` : ''}
/>
<Button
onClick={onOpenClose}
//variant="outline"
style={{
borderRadius: "0 5px 5px 0",
borderWidth: "1px 1px 1px 0",
}}
>
{showList ? (
<MdKeyboardArrowUp color="gray.300" />
) : hasSuggestion ? (
<MdEdit color="gray.300" />
) : (
<MdKeyboardArrowDown color="gray.300" />
)}
</Button>
</Flex>
{showList && (
<SelectList
options={transformedOption}
selected={selectedOptions}
search={currentSearch}
onSelectValue={selectValue}
onCreate={createNewItem}
/>
)}
</Flex>
);
};