Files
Fulfillment-Frontend/src/components/ObjectMultiSelect/ObjectMultiSelect.tsx
2024-08-06 04:55:24 +03:00

96 lines
3.1 KiB
TypeScript

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> = T;
type ControlledValueProps<T> = {
value: MultiselectObjectType<T>[],
onChange: (value: MultiselectObjectType<T>[]) => void;
}
type CustomLabelAndKeyProps<T> = {
getLabelFn: (item: MultiselectObjectType<T>) => string;
getValueFn: (item: MultiselectObjectType<T>) => string;
}
type RestProps<T> = {
defaultValue?: MultiselectObjectType<T>[]
onChange: (value: MultiselectObjectType<T>[]) => void;
data: MultiselectObjectType<T>[];
groupBy?: (item: MultiselectObjectType<T>) => string;
filterBy?: (item: MultiselectObjectType<T>) => boolean;
}
const defaultGetLabelFn = <T extends { name: string }>(item: T): string => {
return item.name;
}
const defaultGetValueFn = <T extends { id: number }>(item: T): string => {
return item.id.toString();
}
export type ObjectMultiSelectProps<T> =
(RestProps<T> & Partial<ControlledValueProps<T>>)
& Omit<MultiSelectProps, 'value' | 'onChange' | 'data'>
& (T extends ObjectWithIdAndName ? Partial<CustomLabelAndKeyProps<T>> : CustomLabelAndKeyProps<T>);
const ObjectMultiSelect = <T, >(props: ObjectMultiSelectProps<T>) => {
const isControlled = 'value' in props;
const haveGetValueFn = 'getValueFn' in props;
const haveGetLabelFn = 'getLabelFn' in props;
const [internalValue, setInternalValue] = useState<MultiselectObjectType<T>[] | 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 (
<MultiSelect
{...props}
value={value.map(item => getValueFn(item))}
onChange={handleOnChange}
data={data}
/>
)
}
export default ObjectMultiSelect;