// Copyright (C) The Arvados Authors. All rights reserved. // // SPDX-License-Identifier: AGPL-3.0 import React, { useState, useRef, useEffect } from 'react'; import { StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core'; import classnames from 'classnames'; import { ArvadosTheme } from 'common/custom-theme'; import { OverflowMenu, OverflowChild } from './ms-toolbar-overflow-menu'; type CssRules = 'visible' | 'inVisible' | 'toolbarWrapper' | 'overflowStyle'; const styles: StyleRulesCallback = (theme: ArvadosTheme) => ({ visible: { order: 0, visibility: 'visible', opacity: 1, }, inVisible: { order: 100, visibility: 'hidden', pointerEvents: 'none', }, toolbarWrapper: { display: 'flex', overflow: 'hidden', padding: '0 0px 0 20px', width: '100%', }, overflowStyle: { order: 99, position: 'sticky', right: '-2rem', width: 0, }, }); type WrapperProps = { children: OverflowChild[]; menuLength: number; }; export const IntersectionObserverWrapper = withStyles(styles)((props: WrapperProps & WithStyles) => { const { classes, children, menuLength } = props; const lastEntryId = (children[menuLength - 1] as any).props['data-targetid']; const navRef = useRef(null); const [visibilityMap, setVisibilityMap] = useState>({}); const [numHidden, setNumHidden] = useState(() => findNumHidden(visibilityMap)); const prevNumHidden = useRef(numHidden); const handleIntersection = (entries) => { const updatedEntries: Record = {}; entries.forEach((entry) => { const targetid = entry.target.dataset.targetid as string; if (entry.isIntersecting) { updatedEntries[targetid] = true; } else { updatedEntries[targetid] = false; } }); setVisibilityMap((prev) => ({ ...prev, ...updatedEntries, [lastEntryId]: Object.keys(updatedEntries)[0] === lastEntryId, })); }; useEffect(() => { if ((prevNumHidden.current === 2 || prevNumHidden.current === 0) && numHidden === 1) { setVisibilityMap((prev) => ({ ...prev, [lastEntryId]: true, })); } prevNumHidden.current = numHidden; }, [numHidden, lastEntryId]); useEffect(() => { setNumHidden(findNumHidden(visibilityMap)); }, [visibilityMap]); useEffect((): any => { setVisibilityMap({}); const observer = new IntersectionObserver(handleIntersection, { root: navRef.current, rootMargin: '0px -30px 0px 0px', threshold: 1, }); // We are adding observers to child elements of the container div // with ref as navRef. Notice that we are adding observers // only if we have the data attribute targetid on the child element if (navRef.current) Array.from(navRef.current.children).forEach((item: any) => { if (item.dataset.targetid) { observer.observe(item); } }); return () => { observer.disconnect(); }; // eslint-disable-next-line }, [menuLength]); function findNumHidden(visMap: {}) { return Object.values(visMap).filter((x) => x === false).length; } return (
{React.Children.map(children, (child) => { return React.cloneElement(child, { className: classnames(child.props.className, { [classes.visible]: !!visibilityMap[child.props['data-targetid']], [classes.inVisible]: !visibilityMap[child.props['data-targetid']], }), }); })} {numHidden >= 2 && ( {children} )}
); });