@mantine/core
Version:
React components library focused on usability, accessibility and developer experience
1 lines • 9.48 kB
Source Map (JSON)
{"version":3,"file":"OverflowList.cjs","names":["createVarsResolver","getSpacing","genericFactory","useProps","useStyles","useDimensions","getRowPositionsData","Box","Fragment","classes"],"sources":["../../../src/components/OverflowList/OverflowList.tsx"],"sourcesContent":["// Originally based on https://github.com/Eliav2/react-responsive-overflow-list (MIT License)\n// Contains the modified version adapted for Mantine\nimport { cloneElement, Ref, useRef, useState } from 'react';\nimport { Fragment } from 'react/jsx-runtime';\nimport { useIsomorphicEffect, useMergedRef } from '@mantine/hooks';\nimport {\n Box,\n BoxProps,\n createVarsResolver,\n ElementProps,\n Factory,\n genericFactory,\n getSpacing,\n MantineSpacing,\n StylesApiProps,\n useProps,\n useStyles,\n} from '../../core';\nimport { getRowPositionsData } from './get-row-position-data';\nimport { useDimensions } from './use-dimensions';\nimport classes from './OverflowList.module.css';\n\nexport type OverflowListStylesNames = 'root';\nexport type OverflowListCssVariables = {\n root: '--ol-gap';\n};\n\nexport interface OverflowListProps<T = any>\n extends BoxProps, StylesApiProps<OverflowListFactory>, ElementProps<'div', 'children'> {\n /** Array of items to display */\n data: T[];\n\n /** Function to render item */\n renderItem: (item: T, index: number) => React.ReactNode;\n\n /** Function to render hidden items */\n renderOverflow: (items: T[]) => React.ReactNode;\n\n /** Number of rows to display @default 1 */\n maxRows?: number;\n\n /** Maximum number of visible items @default Infinity */\n maxVisibleItems?: number;\n\n /** Key of `theme.spacing` or any valid CSS value for `gap`, numbers are converted to rem @default 'xs' */\n gap?: MantineSpacing;\n\n ref?: Ref<HTMLDivElement>;\n}\n\nexport type OverflowListFactory = Factory<{\n props: OverflowListProps<any>;\n ref: HTMLDivElement;\n stylesNames: OverflowListStylesNames;\n vars: OverflowListCssVariables;\n signature: <T = any>(props: OverflowListProps<T>) => React.JSX.Element;\n}>;\n\nconst defaultProps = {\n maxRows: 1,\n maxVisibleItems: Infinity,\n} satisfies Partial<OverflowListProps<any>>;\n\nconst varsResolver = createVarsResolver<OverflowListFactory>((_, { gap }) => ({\n root: {\n '--ol-gap': getSpacing(gap),\n },\n}));\n\nexport const OverflowList = genericFactory<OverflowListFactory>((_props) => {\n const props = useProps('OverflowList', defaultProps, _props);\n const {\n classNames,\n className,\n style,\n styles,\n unstyled,\n vars,\n attributes,\n data,\n renderOverflow,\n renderItem,\n maxRows,\n maxVisibleItems,\n ref,\n ...others\n } = props;\n\n const getStyles = useStyles<OverflowListFactory>({\n name: 'OverflowList',\n classes,\n props,\n className,\n style,\n classNames,\n styles,\n unstyled,\n attributes,\n vars,\n varsResolver,\n });\n\n const [visibleCount, setVisibleCount] = useState(data.length);\n const [subtractCount, setSubtractCount] = useState(0);\n const [phase, setPhase] = useState<'normal' | 'measuring' | 'measuring-overflow-indicator'>(\n 'normal'\n );\n\n const containerRef = useRef<HTMLElement>(null);\n const rootRef = useMergedRef(containerRef, ref);\n const finalVisibleCount = visibleCount - subtractCount;\n const overflowCount = data.length - finalVisibleCount;\n const showOverflow = overflowCount > 0 && phase !== 'measuring';\n const overflowElement = showOverflow ? renderOverflow?.(data.slice(finalVisibleCount)) : null;\n\n const _overflowRef = useRef<HTMLElement>(null);\n const overflowRef = useMergedRef(_overflowRef, (overflowElement as any)?.ref);\n const dimensions = useDimensions(containerRef);\n\n useIsomorphicEffect(() => {\n setPhase('measuring');\n setVisibleCount(data.length);\n setSubtractCount(0);\n }, [data.length, maxRows]);\n\n useIsomorphicEffect(() => {\n if (phase === 'measuring') {\n countVisibleItems();\n setPhase('measuring-overflow-indicator');\n }\n }, [phase]);\n\n useIsomorphicEffect(() => {\n if (phase === 'measuring-overflow-indicator') {\n const updateWasNeeded = updateOverflowIndicator();\n if (!updateWasNeeded) {\n setPhase('normal');\n }\n }\n }, [phase, subtractCount]);\n\n useIsomorphicEffect(() => {\n if (phase === 'normal') {\n setPhase('measuring');\n setSubtractCount(0);\n }\n }, [dimensions]);\n\n const countVisibleItems = () => {\n const rowData = getRowPositionsData(containerRef, _overflowRef);\n if (!rowData) {\n return;\n }\n\n if (data.length === 1) {\n const itemRef = rowData.itemsSizesMap[rowData.rowPositions[0]].elements.values().next().value;\n const containerWidth = containerRef.current?.getBoundingClientRect().width ?? 0;\n const itemWidth = itemRef?.getBoundingClientRect().width ?? 0;\n\n if (itemWidth > containerWidth) {\n setVisibleCount(0);\n } else {\n setVisibleCount(1);\n }\n\n return;\n }\n\n const visibleRowPositions = rowData.rowPositions.slice(0, maxRows);\n\n let fittingCount = visibleRowPositions.reduce((acc, position) => {\n return acc + rowData.itemsSizesMap[position].elements.size;\n }, 0);\n\n fittingCount = Math.min(fittingCount, maxVisibleItems);\n setVisibleCount(fittingCount);\n };\n\n const updateOverflowIndicator = () => {\n if (!_overflowRef.current) {\n return false;\n }\n const rowData = getRowPositionsData(containerRef, _overflowRef);\n if (!rowData) {\n return false;\n }\n\n const { rowPositions, itemsSizesMap } = rowData;\n\n const overflowRect = _overflowRef.current.getBoundingClientRect();\n const overflowMiddleY = overflowRect.top + overflowRect.height / 2;\n const lastRowTop = rowPositions[rowPositions.length - 1];\n const lastRow = itemsSizesMap[lastRowTop];\n\n if (overflowMiddleY > lastRow.bottom) {\n setSubtractCount((c) => c + 1);\n return true;\n }\n\n return false;\n };\n\n const clonedOverflowElement = overflowElement\n ? cloneElement(overflowElement as React.ReactElement<any>, { ref: overflowRef })\n : null;\n\n let finalItems = data;\n if (maxVisibleItems) {\n finalItems = finalItems.slice(0, maxVisibleItems);\n }\n\n return (\n <Box ref={rootRef} {...getStyles('root')} {...others}>\n {finalItems.map((item, index) => {\n const isVisible = phase === 'measuring' || index < finalVisibleCount;\n if (!isVisible) {\n return null;\n }\n const itemComponent = renderItem(item, index);\n\n return <Fragment key={index}>{itemComponent}</Fragment>;\n })}\n\n {clonedOverflowElement}\n </Box>\n );\n});\n\nOverflowList.displayName = '@mantine/core/OverflowList';\nOverflowList.classes = classes;\nOverflowList.varsResolver = varsResolver;\n"],"mappings":"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"}