UNPKG

@adaptabletools/adaptable

Version:

Powerful data-agnostic HTML5 AG Grid extension which provides advanced, cutting-edge functionality to meet all DataGrid requirements

101 lines (100 loc) 4.12 kB
import * as React from 'react'; import { useRef, useState } from 'react'; import { Flex } from 'rebass'; import { useLatest } from '../utils/useLatest'; import usePrevious from '../utils/usePrevious'; const Container = ({ children, index, hidden, observerRef, }) => { const [mounted, setMounted] = useState(false); React.useEffect(() => { const timeoutId = setTimeout(() => { // we wait for a bit, so intersection observer // has time to kick in before we trigger a rerender // to make the element visible if needed setMounted(true); }, 50); return () => clearTimeout(timeoutId); }, []); const domRef = useRef(null); const onMount = (node) => { if (node) { if (!node.getAttribute('data-index')) { throw 'The element you render from "renderItem" should accept a "data-index" prop.'; } observerRef.current?.observe(node); } else { observerRef.current?.unobserve(domRef.current); } domRef.current = node; }; let child = React.Children.toArray(children)[0]; const cloneProps = { ref: onMount, 'data-index': index, }; if (hidden || !mounted) { cloneProps.style = { ...child.props?.style, visibility: 'hidden' }; } child = React.cloneElement(child, cloneProps); return React.createElement(React.Fragment, null, child); }; export const EllipsisContainer = (props) => { const { allowWrap, count, direction, renderItem, renderEllipsis, ...boxProps } = props; const domRef = useRef(null); const observerRef = useRef(null); const [minNotVisibleIndex, setMinNotVisibleIndex] = React.useState(props.count); const [visibleSet] = useState(() => new Set()); const [invisibleSet] = useState(() => new Set()); const getCount = useLatest(props.count); React.useEffect(() => { const callback = (entries) => { const count = getCount(); const isVisible = (entry) => entry.intersectionRatio === 1; entries.forEach((entry) => { const { target } = entry; const index = Number(target.getAttribute('data-index')); const visible = isVisible(entry); if (visible) { visibleSet.add(index); invisibleSet.delete(index); } else { visibleSet.delete(index); invisibleSet.add(index); } }); const minNotVisibleIndex = Math.min(count, ...invisibleSet.values()); setMinNotVisibleIndex(minNotVisibleIndex); }; if (allowWrap) { return; } const observer = new IntersectionObserver(callback, { root: domRef.current, threshold: 0.01, }); observerRef.current = observer; return () => { observer.disconnect(); }; }, []); const prevCount = usePrevious(count, count); React.useLayoutEffect(() => { if (allowWrap) { return; } if (minNotVisibleIndex === prevCount) { setMinNotVisibleIndex(count); } }, [count, minNotVisibleIndex, allowWrap]); const children = [...new Array(count)].map((_, index) => { const remaining = count - 1 - index; const hidden = allowWrap ? false : index >= minNotVisibleIndex; return (React.createElement(React.Fragment, { key: index }, React.createElement(Container, { hidden: hidden, index: index, observerRef: observerRef }, renderItem(index, count)), !allowWrap && index === minNotVisibleIndex - 1 && remaining ? renderEllipsis?.({ remaining }) ?? '...' : null)); }); return (React.createElement(Flex, { ref: domRef, flexWrap: allowWrap ? 'wrap' : 'nowrap', alignItems: "center", "data-name": "options-container", ...boxProps, flexDirection: direction === 'horizontal' ? 'row' : 'column' }, children)); };