UNPKG

@gravity-ui/uikit

Version:

Gravity UI base styling and components

88 lines (87 loc) 3.79 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.useCollapseChildren = useCollapseChildren; const tslib_1 = require("tslib"); const React = tslib_1.__importStar(require("react")); const isEqual_1 = tslib_1.__importDefault(require("lodash/isEqual.js")); const useLayoutEffect_1 = require("../useLayoutEffect/index.js"); const useResizeObserver_1 = require("../useResizeObserver/index.js"); function useCollapseChildren({ enabled = true, containerRef, preservedRefs = [], minCount = 0, maxCount = Infinity, direction = 'end', gap = 0, childSelector = '*', getChildWidth = (child) => child.getBoundingClientRect().width, }) { const exertedProps = [minCount, maxCount, direction, gap, childSelector]; const [exertedPropsState, setExertedPropsState] = React.useState(exertedProps); const [calculated, setCalculated] = React.useState(false); const isCalculatingRef = React.useRef(true); const [visibleCount, setVisibleCount] = React.useState(maxCount); const calculate = (desiredVisibleCount) => { const container = containerRef.current; if (!container) return; // Batch elements measurement to optimize performance const containerChildren = Array.from(container.querySelectorAll(`:scope > :where(${childSelector})`)); const containerWidth = container.getBoundingClientRect().width; const childWidthMap = new WeakMap(containerChildren.map((child) => [child, getChildWidth(child)])); if (direction === 'start') { containerChildren.reverse(); } // Place preserved items at the beginning to calculate them first const preservedItems = preservedRefs.flatMap((ref) => ref.current ?? []); containerChildren.sort((itemA, itemB) => { const valueA = preservedItems.includes(itemA) ? 1 : 0; const valueB = preservedItems.includes(itemB) ? 1 : 0; return valueB - valueA; }); let availableWidth = containerWidth; let newVisibleCount = 0; for (const item of containerChildren) { if (newVisibleCount >= maxCount) { break; } availableWidth -= childWidthMap.get(item) ?? 0; if (availableWidth < 0) break; availableWidth -= gap; if (!preservedItems.includes(item)) { newVisibleCount++; } } const minNewVisibleCount = Math.max(newVisibleCount, minCount); if (minNewVisibleCount === desiredVisibleCount) { setCalculated(true); } else { setVisibleCount(minNewVisibleCount); } }; const recalculate = React.useCallback(() => { if (enabled && !isCalculatingRef.current) { isCalculatingRef.current = true; setVisibleCount(maxCount); setCalculated(false); } }, [enabled, maxCount]); // Recalculate on changing props if (!(0, isEqual_1.default)(exertedProps, exertedPropsState)) { recalculate(); setExertedPropsState(exertedProps); } (0, useResizeObserver_1.useResizeObserver)({ ref: containerRef, onResize: recalculate, }); (0, useLayoutEffect_1.useLayoutEffect)(() => { if (calculated) { isCalculatingRef.current = false; return; } if (enabled) { calculate(visibleCount); } else { setVisibleCount(Infinity); setCalculated(true); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [enabled, calculated, visibleCount]); return { calculated, recalculate, visibleCount }; } //# sourceMappingURL=useCollapseChildren.js.map