UNPKG

@carbon/ibm-products

Version:

Carbon for IBM Products

114 lines (109 loc) 3.9 kB
/** * Copyright IBM Corp. 2020, 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. */ 'use strict'; var React = require('react'); var useResizeObserver = require('../useResizeObserver.js'); function useOverflowItems() { let items = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; let containerRef = arguments.length > 1 ? arguments[1] : undefined; let offsetRef = arguments.length > 2 ? arguments[2] : undefined; let maxItems = arguments.length > 3 ? arguments[3] : undefined; let onChange = arguments.length > 4 ? arguments[4] : undefined; const [remainingWidth, setRemainingWidth] = React.useState(0); const offsetWidthRef = React.useRef(0); const itemsRef = React.useRef(null); const visibleItemCount = React.useRef(0); const minWidthRef = React.useRef(0); const requiredWidthRef = React.useRef(0); const handleResize = () => { if (containerRef?.current) { offsetWidthRef.current = offsetRef?.current?.offsetWidth || 0; const usableWidth = containerRef.current.offsetWidth - offsetWidthRef.current; setRemainingWidth(usableWidth); } }; const offsetRefHandler = node => { if (node && containerRef?.current) { offsetWidthRef.current = node.offsetWidth; const usableWidth = containerRef.current.offsetWidth - offsetWidthRef.current; setRemainingWidth(usableWidth); } return node; }; useResizeObserver.useResizeObserver(containerRef, handleResize); const getMap = () => { if (!itemsRef.current) { itemsRef.current = new Map(); } return itemsRef.current; }; const requiredWidth = items?.slice(0, maxItems)?.reduce((acc, item) => acc + (getMap().get(item.id) || 0), 0); 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 items; } const map = getMap(); let maxReached = false; let accumulatedWidth = 0; let includeOffset = false; if (maxItems) { includeOffset = requiredWidth + offsetWidthRef?.current > requiredWidth; } else { includeOffset = requiredWidth > remainingWidth + offsetWidthRef?.current; } return items.slice(0, maxItems).reduce((prev, cur) => { if (maxReached) { return prev; } const itemWidth = map.get(cur.id) || 0; let willFit = accumulatedWidth + itemWidth <= remainingWidth; if (!includeOffset) { willFit = accumulatedWidth + itemWidth <= remainingWidth + offsetWidthRef?.current; } if (willFit) { accumulatedWidth += itemWidth; prev.push(cur); } else { maxReached = true; } return prev; }, []); }; const visibleItems = getVisibleItems(); const hiddenItems = items.slice(visibleItems?.length); // only call the change handler when the number of visible items has changed if (visibleItems?.length !== visibleItemCount.current || remainingWidth !== minWidthRef.current || requiredWidth !== requiredWidthRef.current) { visibleItemCount.current = visibleItems?.length; minWidthRef.current = remainingWidth; requiredWidthRef.current = requiredWidth; const firstItemKey = getMap()?.keys()?.next()?.value || ''; const firstItemWidth = getMap()?.get(firstItemKey) || 0; onChange?.({ hiddenItems, minWidth: remainingWidth, maxWidth: requiredWidth + firstItemWidth }); } return { visibleItems, itemRefHandler, hiddenItems, offsetRefHandler }; } exports.useOverflowItems = useOverflowItems;