Kaballas's picture
initialize project structure with essential configurations and components
56b6519
import {
Field,
Label,
Listbox,
ListboxButton,
ListboxOption,
ListboxOptions,
} from '@headlessui/react';
import { CheckIcon, ChevronDownIcon } from '@heroicons/react/20/solid';
import { Chip } from '@mui/material';
import clsx from 'clsx';
import React, { useEffect, useState } from 'react';
import { GetCustomFieldType } from '../../../../../services/data';
type ListItem = {
id: number;
value: string;
label?: string;
};
type MultiSelectDropdownProps = {
items: ListItem[];
title: string;
placeholder?: string;
setCurrentCustomFields: React.Dispatch<
React.SetStateAction<GetCustomFieldType[]>
>;
id: string;
text: string[];
currentLanguage: string;
};
const MultiSelectDropdownCustom: React.FC<MultiSelectDropdownProps> = ({
items,
title,
placeholder,
setCurrentCustomFields,
id,
text,
currentLanguage,
}) => {
const [selected, setSelected] = useState<ListItem[]>(
items.filter(item => text.includes(item.value)),
);
useEffect(() => {
setSelected(items.filter(item => text.includes(item.value)));
}, [items, text]);
const onChange = (items: ListItem[]) => {
const values = items.map(item => item.value);
setCurrentCustomFields((prevFields: GetCustomFieldType[]) => {
return prevFields.map((field: GetCustomFieldType) =>
field._id === id
? {
...field,
text: field.text.map(itemIter =>
itemIter.locale === currentLanguage
? {
locale: itemIter.locale,
value: values,
}
: itemIter,
),
}
: field,
);
});
};
const ids = items.map(item => item.id);
const hasDuplicateIds = new Set(ids).size !== ids.length;
if (hasDuplicateIds) {
throw new Error('the item IDs must be unique.');
}
const handleChange = (items: ListItem[]) => {
onChange(items);
};
const handleDeleteChip = (deletedItem: ListItem) => {
handleChange(selected.filter(item => item !== deletedItem));
};
return (
<Field>
<Label className="block text-sm font-medium leading-6 text-gray-300">
{title}
</Label>
<Listbox multiple onChange={onChange} value={selected}>
<ListboxButton
className={clsx(
'inline-flex items-center justify-between w-full text-left rounded-lg bg-white/5 py-1.5 pl-3 text-left text-sm/6 text-white focus:outline-none data-[focus]:outline-2 data-[focus]:-outline-offset-2 data-[focus]:outline-white/25 min-h-[2.3rem]',
)}
>
<span className="overflow-x-auto">
{selected.length > 0
? selected.map(s => (
<Chip
color="primary"
key={s.id}
label={s.label ? s.label : s.value}
onDelete={() => handleDeleteChip(s)}
variant="outlined"
/>
))
: placeholder}
</span>
<ChevronDownIcon
aria-hidden="true"
className="size-4 fill-white/60 mr-1"
/>
</ListboxButton>
<ListboxOptions
anchor="bottom"
className={clsx(
'w-[var(--button-width)] rounded-xl border border-white/5 bg-stone-800 p-1 [--anchor-gap:var(--spacing-1)] focus:outline-none transition duration-100 ease-in data-[leave]:data-[closed]:opacity-0 z-50',
)}
transition
>
{items.map(item => (
<ListboxOption
className="group flex cursor-default items-center gap-2 rounded-lg py-1.5 px-3 select-none data-[focus]:bg-white/10"
key={item.id}
value={item}
>
<CheckIcon
className={clsx('size-4 fill-white', {
visible: selected.some(s => s.id === item.id),
invisible: !selected.some(s => s.id === item.id),
})}
/>
<div className="text-sm/6 text-white">
{item.label ? item.label : item.value}
</div>
</ListboxOption>
))}
</ListboxOptions>
</Listbox>
</Field>
);
};
export default MultiSelectDropdownCustom;