UNPKG

@carbon/react

Version:

React components for the Carbon Design System

100 lines (98 loc) 3.6 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. */ require("../_virtual/_rolldown/runtime.js"); const require_useResizeObserver = require("./useResizeObserver.js"); const require_usePreviousValue = require("./usePreviousValue.js"); let react = require("react"); //#region src/internal/useOverflowItems.ts /** * Copyright IBM Corp. 2025, 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. */ /** * Manages overflow items in a container by automatically hiding items that don't fit. * @param items - Array of items to manage for overflow, each must have an `id` property. * @param containerRef - React ref to the container element that holds the items. * @param offsetRef - Optional ref to an offset element (like a "more" button) whose width is reserved when calculating available space. * @param maxItems - Optional maximum number of visible items. If undefined, only container space constrains visibility. * @param onChange - Optional callback called when hidden items change. Receives array of currently hidden items. * @returns Object with `visibleItems` (items to display), `hiddenItems` (items that don't fit), and `itemRefHandler` (function to attach refs to items for width measurement). */ const useOverflowItems = (items, containerRef, offsetRef, maxItems, onChange) => { const itemsRef = (0, react.useRef)(null); const [maxWidth, setMaxWidth] = (0, react.useState)(0); const overflowItems = (0, react.useMemo)(() => Array.isArray(items) ? items : [], [items]); const handleResize = () => { if (containerRef.current) { const offset = offsetRef?.current?.offsetWidth || 0; setMaxWidth(containerRef.current.offsetWidth - offset); } }; require_useResizeObserver.useResizeObserver({ ref: containerRef, onResize: handleResize }); const getMap = () => { if (!itemsRef.current) itemsRef.current = /* @__PURE__ */ new Map(); return itemsRef.current; }; const itemRefHandler = (id, node) => { const map = getMap(); if (node) { const style = getComputedStyle?.(node); const totalWidth = node.offsetWidth + parseInt(style.marginLeft) + parseInt(style.marginRight); map.set(id, totalWidth); } return () => { map.delete(id); }; }; const getVisibleItems = () => { if (!containerRef) return overflowItems; const map = getMap(); let maxReached = false; let accumulatedWidth = 0; return overflowItems.slice(0, maxItems).reduce((prev, cur) => { if (maxReached) return prev; const itemWidth = map.get(cur.id) || 0; if (accumulatedWidth + itemWidth <= maxWidth) { accumulatedWidth += itemWidth; prev.push(cur); } else maxReached = true; return prev; }, []); }; const visibleItems = (0, react.useMemo)(() => { return getVisibleItems(); }, [ overflowItems, maxWidth, maxItems ]); const hiddenItems = (0, react.useMemo)(() => { return overflowItems.slice(visibleItems.length); }, [overflowItems, visibleItems]); const previousHiddenItems = require_usePreviousValue.usePreviousValue(hiddenItems); (0, react.useEffect)(() => { if (previousHiddenItems && onChange) { if (hiddenItems.length !== previousHiddenItems.length || hiddenItems.some((item, index) => item !== previousHiddenItems[index])) onChange(hiddenItems); } }, [ hiddenItems, previousHiddenItems, onChange ]); return { visibleItems, itemRefHandler, hiddenItems }; }; //#endregion exports.default = useOverflowItems;