import {MultiSelect, MultiSelectProps} from "@mantine/core"; import {useEffect, useMemo, useState} from "react"; import {groupBy} from "lodash"; interface ObjectWithIdAndName { id: number, name: string } export type MultiselectObjectType = T; type ControlledValueProps = { value: MultiselectObjectType[], onChange: (value: MultiselectObjectType[]) => void; } type CustomLabelAndKeyProps = { getLabelFn: (item: MultiselectObjectType) => string; getValueFn: (item: MultiselectObjectType) => string; } type RestProps = { defaultValue?: MultiselectObjectType[] onChange: (value: MultiselectObjectType[]) => void; data: MultiselectObjectType[]; groupBy?: (item: MultiselectObjectType) => string; filterBy?: (item: MultiselectObjectType) => boolean; } const defaultGetLabelFn = (item: T): string => { return item.name; } const defaultGetValueFn = (item: T): string => { return item.id.toString(); } export type ObjectMultiSelectProps = (RestProps & Partial>) & Omit & (T extends ObjectWithIdAndName ? Partial> : CustomLabelAndKeyProps); const ObjectMultiSelect = (props: ObjectMultiSelectProps) => { const isControlled = 'value' in props; const haveGetValueFn = 'getValueFn' in props; const haveGetLabelFn = 'getLabelFn' in props; const [internalValue, setInternalValue] = useState[] | undefined>(props.defaultValue); const value = (isControlled ? props.value : internalValue) || []; const getValueFn = (haveGetValueFn && props.getValueFn) || defaultGetValueFn; const getLabelFn = (haveGetLabelFn && props.getLabelFn) || defaultGetLabelFn; const data = useMemo(() => { const propsData = props.filterBy ? props.data.filter(props.filterBy) : props.data; if (props.groupBy) { const groupedData = groupBy(propsData, props.groupBy); return Object.entries(groupedData).map(([group, items]) => ({ group, items: items.map(item => ({ label: getLabelFn(item), value: getValueFn(item) })) })); } else { return propsData.map(item => ({ label: getLabelFn(item), value: getValueFn(item) })); } }, [props.data, props.groupBy]); const handleOnChange = (event: string[]) => { const objects = props.data.filter(item => event.includes(getValueFn(item))); if (isControlled) { props.onChange(objects); return; } setInternalValue(objects); } useEffect(() => { if (isControlled || !internalValue) return; props.onChange(internalValue); }, [internalValue]); return ( getValueFn(item))} onChange={handleOnChange} data={data} /> ) } export default ObjectMultiSelect;