@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
JavaScript
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));
};