add select input with multiple values

This commit is contained in:
Alexander Navarro 2023-10-07 18:52:43 -03:00
parent c8b808bb14
commit e6df894c85
8 changed files with 1265 additions and 1057 deletions

View file

@ -0,0 +1,130 @@
import React, {
useState,
type ChangeEventHandler,
useRef,
useEffect,
} from 'react';
import styles from './SelectInput.module.css';
interface Props {
onChange: (value: string | string[] | null) => void;
options: [{ label: string; value: any }];
keyData: string;
isMultiple?: boolean;
}
export default function SelectInput({
options,
isMultiple = false,
onChange,
}: Props): JSX.Element {
const [selected, setSelected] = useState<string[]>([]);
const [filteredOptions, setFilteredOptions] = useState<Props['options']>([]);
const [isOptionsOpen, setIsOptionsOpen] = useState<boolean>(false);
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
setFilteredOptions(options);
}, [options]);
useEffect(() => {
if (selected.length === 0) {
onChange(null);
return;
}
onChange(isMultiple ? selected : selected.at(0));
}, [selected]);
const handleFocusInput = (): void => {
setIsOptionsOpen(true);
inputRef.current.focus();
};
const handleAddElement = (item: any): void => {
setSelected((prev) => {
if (isMultiple) {
return [...prev, item];
}
return [item];
});
setIsOptionsOpen(false);
inputRef.current.value = '';
};
const handleRemoveElement = (idx: number): void => {
setIsOptionsOpen(false);
setSelected((prev) => {
prev.splice(idx, 1);
return [...prev];
});
};
const handleLooseFocus = (e: React.FocusEvent<HTMLDivElement>): void => {
if (!e.currentTarget.contains(e.relatedTarget)) {
// Not triggered when swapping focus between children
setIsOptionsOpen(false);
}
};
const handleFilterOptions = ({
target,
}: React.ChangeEvent<HTMLInputElement>): void => {
if (target.value === '') {
setFilteredOptions(options);
return;
}
const newOptions = options.filter(
(item) =>
item.label.toLowerCase().search(target.value.toLowerCase()) !== -1,
);
setFilteredOptions(newOptions);
};
return (
<div className={styles.wrapper} onBlur={handleLooseFocus}>
<div
className={styles.input}
onClick={() => {
handleFocusInput();
}}
>
{selected.map((item, idx) => (
<div className={styles.selectedItem + ' hstack'} key={idx}>
<div>{item}</div>
<div
className={styles.deleteItem}
onClick={() => {
handleRemoveElement(idx);
}}
>
{'X'}
</div>
</div>
))}
<input
ref={inputRef}
className={styles.realInput}
type="text"
onChange={handleFilterOptions}
/>
</div>
<div className={styles.optionList} hidden={!isOptionsOpen}>
{filteredOptions.map((item, idx) => (
<button
className={styles.optionItem}
key={idx}
disabled={selected.includes(item.value)}
onClick={() => {
handleAddElement(item.value);
}}
>
{item.label}
</button>
))}
</div>
</div>
);
}