[arvados] created: 2.7.0-5794-g08fb678c7f

git repository hosting git at public.arvados.org
Tue Jan 9 16:10:29 UTC 2024


        at  08fb678c7ff12d3f420477f34610383960b65482 (commit)


commit 08fb678c7ff12d3f420477f34610383960b65482
Author: Lisa Knox <lisaknox83 at gmail.com>
Date:   Tue Jan 9 11:10:05 2024 -0500

    21317: initial overflow components up Arvados-DCO-1.1-Signed-off-by: Lisa Knox <lisa.knox at curii.com>

diff --git a/services/workbench2/src/components/multiselect-toolbar/MultiselectToolbar.tsx b/services/workbench2/src/components/multiselect-toolbar/MultiselectToolbar.tsx
index f92c0dcf4e..5017e99dd8 100644
--- a/services/workbench2/src/components/multiselect-toolbar/MultiselectToolbar.tsx
+++ b/services/workbench2/src/components/multiselect-toolbar/MultiselectToolbar.tsx
@@ -34,6 +34,7 @@ import { getProcess } from "store/processes/process";
 import { Process } from "store/processes/process";
 import { PublicFavoritesState } from "store/public-favorites/public-favorites-reducer";
 import { isExactlyOneSelected } from "store/multiselect/multiselect-actions";
+import { IntersectionObserverWrapper } from "./ms-toolbar-overflow-wrapper";
 
 const WIDTH_TRANSITION = 150
 
@@ -134,12 +135,14 @@ export const MultiselectToolbar = connect(
                     data-cy='multiselect-toolbar'
                     >
                     {actions.length ? (
-                        actions.map((action, i) =>{
+                        <IntersectionObserverWrapper>
+                        {actions.map((action, i) =>{
                             const { hasAlts, useAlts, name, altName, icon, altIcon } = action;
                         return hasAlts ? (
                             <Tooltip
                                 className={classes.button}
                                 title={currentPathIsTrash || (useAlts && useAlts(singleSelectedUuid, iconProps)) ? altName : name}
+                                data-targetid={name}
                                 key={i}
                                 disableFocusListener
                             >
@@ -170,7 +173,8 @@ export const MultiselectToolbar = connect(
                                 </span>
                             </Tooltip>
                         );
-                        })
+                        })}
+                        </IntersectionObserverWrapper>
                     ) : (
                         <></>
                     )}
diff --git a/services/workbench2/src/components/multiselect-toolbar/ms-toolbar-overflow-menu.tsx b/services/workbench2/src/components/multiselect-toolbar/ms-toolbar-overflow-menu.tsx
new file mode 100644
index 0000000000..1f5be3b85d
--- /dev/null
+++ b/services/workbench2/src/components/multiselect-toolbar/ms-toolbar-overflow-menu.tsx
@@ -0,0 +1,72 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import React, { useState, useMemo } from "react";
+import MoreVertIcon from "@material-ui/icons/MoreVert";
+import classnames from "classnames";
+import { IconButton, Menu, MenuItem, StyleRulesCallback, WithStyles, withStyles } from "@material-ui/core";
+import { ArvadosTheme } from "common/custom-theme";
+
+type CssRules = 'inOverflowMenu'
+
+const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
+  inOverflowMenu: {
+    "&:hover": {
+      backgroundColor: "transparent"
+    }
+  }
+});
+
+export const OverflowMenu = withStyles(styles)((props: any & WithStyles<CssRules>) => {
+  const { children, className, visibilityMap, classes } = props
+  const [anchorEl, setAnchorEl] = useState(null);
+  const open = Boolean(anchorEl);
+  const handleClick = (event) => {
+    setAnchorEl(event.currentTarget);
+  };
+
+  const handleClose = () => {
+    setAnchorEl(null);
+  };
+
+  const shouldShowMenu = useMemo(
+    () => Object.values(visibilityMap).some((v) => v === false),
+    [visibilityMap]
+  );
+  if (!shouldShowMenu) {
+    return null;
+  }
+  return (
+    <div className={className}>
+      <IconButton
+        aria-label="more"
+        aria-controls="long-menu"
+        aria-haspopup="true"
+        onClick={handleClick}
+      >
+        <MoreVertIcon />
+      </IconButton>
+      <Menu
+        id="long-menu"
+        anchorEl={anchorEl}
+        keepMounted
+        open={open}
+        onClose={handleClose}
+      >
+        {React.Children.map(children, (child:any) => {
+          if (!visibilityMap[child.props["data-targetid"]]) {
+            return (
+              <MenuItem key={child} onClick={handleClose}>
+                {React.cloneElement(child, {
+                  className: classnames(child.className, classes.inOverflowMenu)
+                })}
+              </MenuItem>
+            );
+          }
+          return null;
+        })}
+      </Menu>
+    </div>
+  );
+})
\ No newline at end of file
diff --git a/services/workbench2/src/components/multiselect-toolbar/ms-toolbar-overflow-wrapper.tsx b/services/workbench2/src/components/multiselect-toolbar/ms-toolbar-overflow-wrapper.tsx
new file mode 100644
index 0000000000..d97c2f0fc8
--- /dev/null
+++ b/services/workbench2/src/components/multiselect-toolbar/ms-toolbar-overflow-wrapper.tsx
@@ -0,0 +1,108 @@
+// 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 { ArvadosTheme } from 'common/custom-theme';
+import { OverflowMenu } from './ms-toolbar-overflow-menu';
+
+type CssRules = 'visible' | 'inVisible' | 'toolbarWrapper' | 'overflowStyle';
+
+const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
+    visible: {
+        order: 0,
+        visibility: 'visible',
+        opacity: 1,
+    },
+    inVisible: {
+        order: 100,
+        visibility: 'hidden',
+        pointerEvents: 'none',
+    },
+    toolbarWrapper: {
+        display: 'flex',
+        overflow: 'hidden',
+        padding: '0 20px',
+        width: '75%',
+    },
+    overflowStyle: {
+        order: 99,
+        position: 'sticky',
+        right: '0',
+        backgroundColor: 'white',
+    },
+});
+
+export const IntersectionObserverWrapper = withStyles(styles)((props: any & WithStyles<CssRules>) => {
+  const { classes, children} = props
+
+    const navRef = useRef<any>(null);
+    const [visibilityMap, setVisibilityMap] = useState({});
+
+    const handleIntersection = (entries) => {
+        const updatedEntries = {};
+        entries.forEach((entry) => {
+            const targetid = entry.target.dataset.targetid;
+            console.log(entry, targetid);
+            if (entry.isIntersecting) {
+                updatedEntries[targetid] = true;
+            } else {
+                updatedEntries[targetid] = false;
+            }
+        });
+
+        setVisibilityMap((prev) => ({
+            ...prev,
+            ...updatedEntries,
+        }));
+    };
+    useEffect((): any => {
+        const observer = new IntersectionObserver(handleIntersection, {
+            root: navRef.current,
+            threshold: 1,
+        });
+        // We are addting 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();
+        };
+    }, []);
+
+    return (
+      <div className={classes.toolbarWrapper} ref={navRef}>
+      {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"]]
+          })
+        });
+      })}
+      <OverflowMenu
+        visibilityMap={visibilityMap}
+        className={classes.overflowStyle}
+      >
+        {children}
+      </OverflowMenu>
+    </div>
+    );
+});
+
+const classnames = (...args: Array<string | Record<string, boolean>>) => {
+    return args.reduce((output: string, currentArg: any) => {
+        if (typeof currentArg === 'string') output += currentArg + ' ';
+        else
+            for (const entry in currentArg) {
+                if (currentArg[entry] === true) output += entry + ' ';
+            }
+        return output;
+    }, '');
+};
\ No newline at end of file

-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list