21317: toolbar css good Arvados-DCO-1.1-Signed-off-by: Lisa Knox <lisa.knox@curii...
[arvados.git] / services / workbench2 / src / components / multiselect-toolbar / ms-toolbar-overflow-wrapper.tsx
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import React, { useState, useRef, useEffect } from 'react';
6 import { StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core';
7 import classnames from 'classnames'
8 import { ArvadosTheme } from 'common/custom-theme';
9 import { OverflowMenu } from './ms-toolbar-overflow-menu';
10
11 type CssRules = 'visible' | 'inVisible' | 'toolbarWrapper' | 'overflowStyle';
12
13 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
14     visible: {
15         order: 0,
16         visibility: 'visible',
17         opacity: 1,
18     },
19     inVisible: {
20         order: 100,
21         visibility: 'hidden',
22         pointerEvents: 'none',
23     },
24     toolbarWrapper: {
25         display: 'flex',
26         overflow: 'hidden',
27         padding: '0 20px',
28         width: '100%',
29     },
30     overflowStyle: {
31         order: 99,
32         position: 'sticky',
33         right: '-2rem',
34         backgroundColor: 'white',
35     },
36 });
37
38 export const IntersectionObserverWrapper = withStyles(styles)((props: any & WithStyles<CssRules>) => {
39   const { classes, children} = props
40
41     const navRef = useRef<any>(null);
42     const [visibilityMap, setVisibilityMap] = useState({});
43
44     const handleIntersection = (entries) => {
45         const updatedEntries = {};
46         entries.forEach((entry) => {
47             const targetid = entry.target.dataset.targetid;
48             if (entry.isIntersecting) {
49                 updatedEntries[targetid] = true;
50             } else {
51                 updatedEntries[targetid] = false;
52             }
53         });
54
55         setVisibilityMap((prev) => ({
56             ...prev,
57             ...updatedEntries,
58         }));
59     };
60     useEffect((): any => {
61         const observer = new IntersectionObserver(handleIntersection, {
62             root: navRef.current,
63             rootMargin: '0px -20px 0px 0px',
64             threshold: 1,
65         });
66         // We are adding observers to child elements of the container div
67         // with ref as navRef. Notice that we are adding observers
68         // only if we have the data attribute targetid on the child element
69         if (navRef.current)
70             Array.from(navRef.current.children).forEach((item: any) => {
71                 if (item.dataset.targetid) {
72                     observer.observe(item);
73                 }
74             });
75         return () => {
76             observer.disconnect();
77         };
78     }, []);
79
80     return (
81       <div className={classes.toolbarWrapper} ref={navRef}>
82       {React.Children.map(children, (child) => {
83         return React.cloneElement(child, {
84           className: classnames(child.props.className, {
85             [classes.visible]: !!visibilityMap[child.props["data-targetid"]],
86             [classes.inVisible]: !visibilityMap[child.props["data-targetid"]]
87           })
88         });
89       })}
90       <OverflowMenu
91         visibilityMap={visibilityMap}
92         className={classes.overflowStyle}
93       >
94         {children}
95       </OverflowMenu>
96     </div>
97     );
98 });