"use client"; import * as React from "react"; import List from "@mui/material/List"; import ListItem from "@mui/material/ListItem"; import ListItemText from "@mui/material/ListItemText"; import ListItemIcon from "@mui/material/ListItemIcon"; import Checkbox from "@mui/material/Checkbox"; import IconButton from "@mui/material/Fab"; import Divider from "@mui/material/Divider"; import ChevronLeft from "@mui/icons-material/ChevronLeft"; import ChevronRight from "@mui/icons-material/ChevronRight"; import intersection from "lodash/intersection"; import differenceBy from "lodash/differenceBy"; import Stack from "@mui/material/Stack"; import Paper from "@mui/material/Paper"; import Typography from "@mui/material/Typography"; import ListSubheader from "@mui/material/ListSubheader"; import groupBy from "lodash/groupBy"; import uniqBy from "lodash/uniqBy"; import { useTranslation } from "react-i18next"; import union from "lodash/union"; import intersectionBy from "lodash/intersectionBy"; export interface LabelGroup { id: number; name: string; } export interface LabelWithId { id: number; label: string; group?: LabelGroup; } export interface TransferListProps { allItems: LabelWithId[]; selectedItems: LabelWithId[]; onChange: (selectedItems: LabelWithId[]) => void; allItemsLabel: string; selectedItemsLabel: string; } interface ItemListProps { items: LabelWithId[]; checkedItems: LabelWithId[]; label: string; handleToggleAll: ( items: LabelWithId[], checkedItems: LabelWithId[], ) => React.MouseEventHandler; handleToggleAllInGroup: ( groupItems: LabelWithId[], checkedItems: LabelWithId[], ) => React.MouseEventHandler; handleToggle: (item: LabelWithId) => React.MouseEventHandler; } const ItemList: React.FC = ({ items, checkedItems, label, handleToggle, handleToggleAll, handleToggleAllInGroup, }) => { const { t } = useTranslation(); const groups: LabelGroup[] = uniqBy( [ ...items.reduce((acc, item) => { return item.group ? [...acc, item.group] : acc; }, []), // Items with no group { id: 0, name: t("Ungrouped") }, ], "id", ); const groupedItems = groupBy(items, (item) => item.group?.id ?? 0); return ( {label} {`${checkedItems.length}/${ items.length } ${t("selected")}`} } > {groups.map((group) => { const groupItems = groupedItems[group.id]; const selectedGroupItems = intersectionBy( checkedItems, groupItems, "id", ); if (!groupItems) return null; return ( {group.name} {groupItems.map((item) => { return ( ); })} ); })} ); }; const TransferList: React.FC = ({ allItems, selectedItems, allItemsLabel, selectedItemsLabel, onChange, }) => { // Keep a map for the original order of items const sortMap = React.useMemo(() => { return allItems.reduce<{ [id: string]: number }>( (acc, item, index) => ({ ...acc, [item.id]: index }), {}, ); }, [allItems]); const compareFn = React.useCallback( (a: LabelWithId, b: LabelWithId) => sortMap[a.id] - sortMap[b.id], [sortMap], ); const [checkedList, setCheckedList] = React.useState([]); const [leftList, setLeftList] = React.useState( differenceBy(allItems, selectedItems, "id"), ); React.useEffect(() => { setLeftList(differenceBy(allItems, selectedItems, "id")); }, [allItems, selectedItems]); const rightList = selectedItems; const leftListChecked = intersection(checkedList, leftList); const rightListChecked = intersection(checkedList, rightList); const handleToggle = React.useCallback( (value: LabelWithId) => () => { const isChecked = checkedList.includes(value); const newCheckedList = isChecked ? differenceBy(checkedList, [value], "id") : [...checkedList, value]; setCheckedList(newCheckedList); }, [checkedList], ); const handleToggleAll = React.useCallback( (items: LabelWithId[], checkedItems: LabelWithId[]) => () => { if (checkedItems.length === items.length) { setCheckedList(differenceBy(checkedList, checkedItems, "id")); } else { setCheckedList([...checkedList, ...items]); } }, [checkedList], ); const handleCheckedRight = () => { onChange([...selectedItems, ...leftListChecked].sort(compareFn)); setLeftList(differenceBy(leftList, leftListChecked, "id").sort(compareFn)); setCheckedList(differenceBy(checkedList, leftListChecked, "id")); }; const handleCheckedLeft = () => { setLeftList([...leftList, ...rightListChecked].sort(compareFn)); onChange(differenceBy(rightList, rightListChecked, "id").sort(compareFn)); setCheckedList(differenceBy(checkedList, rightListChecked, "id")); }; const handleToggleAllInGroup = React.useCallback( (groupItems: LabelWithId[], checkedItems: LabelWithId[]) => () => { const selectedGroupItems = intersectionBy(checkedItems, groupItems, "id"); if (selectedGroupItems.length !== groupItems.length) { setCheckedList(union(checkedList, groupItems)); } else { setCheckedList(differenceBy(checkedList, groupItems, "id")); } }, [checkedList], ); return ( ); }; export default TransferList;