UNPKG

@mantine/core

Version:

React components library focused on usability, accessibility and developer experience

174 lines (173 loc) 7.16 kB
"use client"; import { getSpacing } from "../../core/utils/get-size/get-size.mjs"; import { createVarsResolver } from "../../core/styles-api/create-vars-resolver/create-vars-resolver.mjs"; import { useProps } from "../../core/MantineProvider/use-props/use-props.mjs"; import { useStyles } from "../../core/styles-api/use-styles/use-styles.mjs"; import { genericFactory } from "../../core/factory/factory.mjs"; import { Box } from "../../core/Box/Box.mjs"; import { getRowPositionsData } from "./get-row-position-data.mjs"; import { useDimensions } from "./use-dimensions.mjs"; import OverflowList_module_default from "./OverflowList.module.mjs"; import { cloneElement, useMemo, useRef, useState } from "react"; import { useIsomorphicEffect, useMergedRef } from "@mantine/hooks"; import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime"; //#region packages/@mantine/core/src/components/OverflowList/OverflowList.tsx const defaultProps = { maxRows: 1, maxVisibleItems: Infinity }; const varsResolver = createVarsResolver((_, { gap }) => ({ root: { "--ol-gap": getSpacing(gap) } })); function getDataSignature(data, getItemKey) { return data.map((item, index) => { if (getItemKey) return getItemKey(item, index); return item !== null && (typeof item === "object" || typeof item === "function") ? index : String(item); }).join("\0"); } const OverflowList = genericFactory((_props) => { const props = useProps("OverflowList", defaultProps, _props); const { classNames, className, style, styles, unstyled, vars, attributes, data, renderOverflow, renderItem, maxRows, maxVisibleItems, collapseFrom, getItemKey, ref, ...others } = props; const getStyles = useStyles({ name: "OverflowList", classes: OverflowList_module_default, props, className, style, classNames, styles, unstyled, attributes, vars, varsResolver }); const [visibleCount, setVisibleCount] = useState(data.length); const [subtractCount, setSubtractCount] = useState(0); const [phase, setPhase] = useState("normal"); const containerRef = useRef(null); const rootRef = useMergedRef(containerRef, ref); const finalVisibleCount = visibleCount - subtractCount; const showOverflow = data.length - finalVisibleCount > 0 && phase !== "measuring"; const isCollapseStart = collapseFrom === "start"; const overflowItems = isCollapseStart ? data.slice(0, data.length - finalVisibleCount) : data.slice(finalVisibleCount); const overflowElement = showOverflow ? renderOverflow?.(overflowItems) : null; const _overflowRef = useRef(null); const overflowRef = useMergedRef(_overflowRef, overflowElement?.ref); const dimensions = useDimensions(containerRef); useIsomorphicEffect(() => { setPhase("measuring"); setVisibleCount(data.length); setSubtractCount(0); }, [ useMemo(() => getDataSignature(data, getItemKey), [data, getItemKey]), maxRows, collapseFrom ]); useIsomorphicEffect(() => { if (phase === "measuring") { countVisibleItems(); setPhase("measuring-overflow-indicator"); } }, [phase]); useIsomorphicEffect(() => { if (phase === "measuring-overflow-indicator") { if (!updateOverflowIndicator()) setPhase("normal"); } }, [phase, subtractCount]); useIsomorphicEffect(() => { if (phase === "normal") { setPhase("measuring"); setSubtractCount(0); } }, [dimensions]); const fitsInRows = (itemWidths, containerWidth, columnGap, startIndex = 0) => { let rows = 1; let rowWidth = 0; for (let i = startIndex; i < itemWidths.length; i += 1) { const width = itemWidths[i]; const needed = rowWidth > 0 ? width + columnGap : width; if (rowWidth + needed > containerWidth && rowWidth > 0) { rows++; if (rows > maxRows) return false; rowWidth = width; } else rowWidth += needed; } return true; }; const countVisibleItems = () => { const rowData = getRowPositionsData(containerRef, _overflowRef); if (!rowData) return; const container = containerRef.current; if (!container) return; if (isCollapseStart) { const containerWidth = container.getBoundingClientRect().width; const columnGap = parseFloat(getComputedStyle(container).columnGap) || 0; const widths = rowData.children.map((child) => child.getBoundingClientRect().width); let count = 0; for (let i = widths.length - 1; i >= 0; i--) { if (!fitsInRows(widths, containerWidth, columnGap, i)) break; count = widths.length - i; } count = Math.min(count, maxVisibleItems); setVisibleCount(count); return; } if (data.length === 1) { const itemRef = rowData.itemsSizesMap[rowData.rowPositions[0]].elements.values().next().value; const containerWidth = container.getBoundingClientRect().width; if ((itemRef?.getBoundingClientRect().width ?? 0) > containerWidth) setVisibleCount(0); else setVisibleCount(1); return; } let fittingCount = rowData.rowPositions.slice(0, maxRows).reduce((acc, position) => { return acc + rowData.itemsSizesMap[position].elements.size; }, 0); fittingCount = Math.min(fittingCount, maxVisibleItems); setVisibleCount(fittingCount); }; const updateOverflowIndicator = () => { if (!_overflowRef.current) return false; const rowData = getRowPositionsData(containerRef, _overflowRef); if (!rowData) return false; const { rowPositions, itemsSizesMap } = rowData; if (isCollapseStart) { const container = containerRef.current; if (!container) return false; const containerWidth = container.getBoundingClientRect().width; const columnGap = parseFloat(getComputedStyle(container).columnGap) || 0; if (!fitsInRows([_overflowRef.current.getBoundingClientRect().width, ...rowData.children.map((child) => child.getBoundingClientRect().width)], containerWidth, columnGap)) { setSubtractCount((c) => c + 1); return true; } return false; } const overflowRect = _overflowRef.current.getBoundingClientRect(); if (overflowRect.top + overflowRect.height / 2 > itemsSizesMap[rowPositions[rowPositions.length - 1]].bottom) { setSubtractCount((c) => c + 1); return true; } return false; }; const clonedOverflowElement = overflowElement ? cloneElement(overflowElement, { ref: overflowRef }) : null; let finalItems = data; if (maxVisibleItems) finalItems = isCollapseStart ? finalItems.slice(-maxVisibleItems) : finalItems.slice(0, maxVisibleItems); const indexOffset = isCollapseStart ? data.length - finalItems.length : 0; return /* @__PURE__ */ jsxs(Box, { ref: rootRef, ...getStyles("root"), ...others, children: [ isCollapseStart && clonedOverflowElement, finalItems.map((item, index) => { if (!(phase === "measuring" || (isCollapseStart ? index >= finalItems.length - finalVisibleCount : index < finalVisibleCount))) return null; const dataIndex = indexOffset + index; return /* @__PURE__ */ jsx(Fragment$1, { children: renderItem(item, dataIndex) }, dataIndex); }), !isCollapseStart && clonedOverflowElement ] }); }); OverflowList.displayName = "@mantine/core/OverflowList"; OverflowList.classes = OverflowList_module_default; OverflowList.varsResolver = varsResolver; //#endregion export { OverflowList }; //# sourceMappingURL=OverflowList.mjs.map