UNPKG

@carbon/react

Version:

React components for the Carbon Design System

111 lines (109 loc) 3.79 kB
/** * Copyright IBM Corp. 2016, 2026 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ import { useCallback, useEffect, useRef, useState } from "react"; import "react/jsx-runtime"; import isEqual from "react-fast-compare"; //#region src/internal/Selection.tsx /** * Copyright IBM Corp. 2016, 2025 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ const callOnChangeHandler = ({ isControlled, isMounted, onChangeHandlerControlled, onChangeHandlerUncontrolled, selectedItems }) => { if (isControlled) { if (isMounted && onChangeHandlerControlled) setTimeout(() => { onChangeHandlerControlled({ selectedItems }); }, 0); } else onChangeHandlerUncontrolled(selectedItems); }; const useSelection = ({ disabled, onChange, initialSelectedItems = [], selectedItems: controlledItems, selectAll = false, filteredItems = [] }) => { const isMounted = useRef(false); const savedOnChange = useRef(onChange); const [uncontrolledItems, setUncontrolledItems] = useState(initialSelectedItems); const isControlled = !!controlledItems; const selectedItems = isControlled ? controlledItems : uncontrolledItems; const onItemChange = useCallback((item) => { if (disabled) return; const allSelectableItems = filteredItems.filter((item) => !item?.disabled && !item?.isSelectAll); const disabledItemCount = filteredItems.filter((item) => item?.disabled).length; let newSelectedItems; if (item?.isSelectAll && selectedItems.length > 0) newSelectedItems = []; else if (item?.isSelectAll && selectedItems.length === 0) newSelectedItems = allSelectableItems; else { const selectedIndex = selectedItems.findLastIndex((selectedItem) => isEqual(selectedItem, item)); if (selectedIndex === -1) { newSelectedItems = selectedItems.concat(item); if (selectAll && filteredItems.length - 1 === newSelectedItems.length + disabledItemCount) newSelectedItems = allSelectableItems; } else { newSelectedItems = removeAtIndex(selectedItems, selectedIndex); newSelectedItems = newSelectedItems.filter((item) => !item?.isSelectAll); } } callOnChangeHandler({ isControlled, isMounted: isMounted.current, onChangeHandlerControlled: savedOnChange.current, onChangeHandlerUncontrolled: setUncontrolledItems, selectedItems: newSelectedItems }); }, [ disabled, selectedItems, filteredItems, selectAll, isControlled ]); const clearSelection = useCallback(() => { if (disabled) return; callOnChangeHandler({ isControlled, isMounted: isMounted.current, onChangeHandlerControlled: savedOnChange.current, onChangeHandlerUncontrolled: setUncontrolledItems, selectedItems: [] }); }, [disabled, isControlled]); const toggleAll = useCallback((items) => { callOnChangeHandler({ isControlled, isMounted: isMounted.current, onChangeHandlerControlled: savedOnChange.current, onChangeHandlerUncontrolled: setUncontrolledItems, selectedItems: items }); }, [isControlled]); useEffect(() => { savedOnChange.current = onChange; }, [onChange]); useEffect(() => { if (isMounted.current && savedOnChange.current && !isControlled) savedOnChange.current({ selectedItems }); }, [isControlled, selectedItems]); useEffect(() => { isMounted.current = true; return () => { isMounted.current = false; }; }, []); return { clearSelection, onItemChange, toggleAll, selectedItems }; }; /** * Generic utility for safely removing an element at a given index from an * array. */ const removeAtIndex = (array, index) => { const result = array.slice(); result.splice(index, 1); return result; }; //#endregion export { useSelection };