@craftercms/studio-ui
Version:
Services, components, models & utils to build CrafterCMS authoring extensions.
149 lines (147 loc) • 5.6 kB
JavaScript
/*
* Copyright (C) 2007-2022 Crafter Software Corporation. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Copyright (C) 2007-2023 Crafter Software Corporation. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { createLookupTable } from '../../utils/object';
function intersection(listItemsA, listItemsB) {
return listItemsA.filter((itemA) => listItemsB.find((itemB) => itemA.id === itemB.id));
}
export function excludeCommonItems(listItemsA, listItemsB) {
return listItemsA.filter((itemA) => !listItemsB.find((itemB) => itemA.id === itemB.id));
}
export const filterTransferListItemsByKeyword = (items, keyword) =>
items?.filter((item) => `${item.title}${item.subtitle}`.toLowerCase().includes(keyword.toLowerCase())) ?? null;
export const useTransferListState = () => {
const [sourceItems, setSourceItems] = useState([]);
const [targetItems, setTargetItems] = useState([]);
const [checkedList, setCheckedList] = useState({});
const [sourceFilterKeyword, setSourceFilterKeyword] = useState('');
const [targetFilterKeyword, setTargetFilterKeyword] = useState('');
// Client-side filtered items
const filteredSourceItems = filterTransferListItemsByKeyword(sourceItems, sourceFilterKeyword);
const filteredTargetItems = filterTransferListItemsByKeyword(targetItems, targetFilterKeyword);
const [itemsLookup, setItemsLookup] = useState({});
const itemsLookupRef = useRef({});
itemsLookupRef.current = itemsLookup;
useEffect(() => {
setItemsLookup({
...itemsLookupRef.current,
...createLookupTable(sourceItems),
...createLookupTable(targetItems)
});
}, [sourceItems, targetItems]);
const isAllChecked = useCallback(
(items) => {
return items?.length
? !items.some(
(item) =>
!Object.keys(checkedList).find(function (checked) {
return checked === item.id && checkedList[checked];
})
)
: false;
},
[checkedList]
);
const getChecked = (items) => {
return intersection(
Object.keys(checkedList)
.filter((key) => checkedList[key])
.map((id) => itemsLookup[id]),
items
);
};
const disableAdd = getChecked(sourceItems).length === 0;
const disableRemove = getChecked(targetItems).length === 0;
const onItemClicked = (item) => {
if (checkedList[item.id]) {
setCheckedList({ ...checkedList, [item.id]: false });
} else {
setCheckedList({ ...checkedList, [item.id]: true });
}
};
const onCheckAllClicked = (items, checked) => {
const nextCheckedList = {};
items.forEach((item) => {
nextCheckedList[item.id] = checked;
});
setCheckedList({ ...checkedList, ...nextCheckedList });
};
const addToTarget = () => {
const nextCheckedList = {};
const leftCheckedItems = getChecked(sourceItems);
if (leftCheckedItems.length) {
leftCheckedItems.forEach((item) => (nextCheckedList[item.id] = false));
setCheckedList({ ...checkedList, ...nextCheckedList });
setSourceItems(excludeCommonItems(sourceItems, leftCheckedItems));
setTargetItems([...targetItems, ...leftCheckedItems]);
}
};
const removeFromTarget = () => {
const nextCheckedList = {};
const rightCheckedItems = getChecked(targetItems);
if (rightCheckedItems.length) {
rightCheckedItems.forEach((item) => (nextCheckedList[item.id] = false));
setCheckedList({ ...checkedList, ...nextCheckedList });
setTargetItems(excludeCommonItems(targetItems, rightCheckedItems));
setSourceItems([...sourceItems, ...rightCheckedItems]);
}
};
const sourceItemsAllChecked = useMemo(() => {
return isAllChecked(filteredSourceItems);
}, [isAllChecked, filteredSourceItems]);
const targetItemsAllChecked = useMemo(() => {
return isAllChecked(filteredTargetItems);
}, [isAllChecked, filteredTargetItems]);
return {
sourceItems,
setSourceItems,
sourceFilterKeyword,
setSourceFilterKeyword,
filteredSourceItems,
targetItems,
setTargetItems,
targetFilterKeyword,
setTargetFilterKeyword,
filteredTargetItems,
checkedList,
setCheckedList,
onItemClicked,
isAllChecked,
onCheckAllClicked,
getChecked,
disableAdd,
disableRemove,
sourceItemsAllChecked,
targetItemsAllChecked,
addToTarget,
removeFromTarget
};
};