@gravity-ui/uikit
Version:
Gravity UI base styling and components
88 lines (87 loc) • 3.79 kB
JavaScript
;
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