18128: Adds Multi Panel View (MPV for short) component family.
authorLucas Di Pentima <lucas.dipentima@curii.com>
Fri, 8 Oct 2021 18:49:15 +0000 (15:49 -0300)
committerLucas Di Pentima <lucas.dipentima@curii.com>
Thu, 2 Dec 2021 23:01:56 +0000 (20:01 -0300)
MPVContainer aims to be a drop-in replacement for <Grid container...>
that will handle its children's visibility and pass to them additional
props so they can optionally offer UI elements to the user.

MPVPanelContent aims to be a drop-in replacement for <Grid item...>
in case there's a need for special layout inside a container. This
component will forward the additional props to its children.

Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas.dipentima@curii.com>

src/components/icon/icon.tsx
src/components/multi-panel-view/multi-panel-view.tsx [new file with mode: 0644]

index 26ce4feae7c117c589ac336a211a4329365a65c7..05b94f7eeea2315635d8db0cdd657413da26a32f 100644 (file)
@@ -112,6 +112,7 @@ export const FileIcon: IconType = (props) => <DescriptionIcon {...props} />;
 export const HelpIcon: IconType = (props) => <Help {...props} />;
 export const HelpOutlineIcon: IconType = (props) => <HelpOutline {...props} />;
 export const ImportContactsIcon: IconType = (props) => <ImportContacts {...props} />;
+export const InfoIcon: IconType = (props) => <Info {...props} />;
 export const InputIcon: IconType = (props) => <InsertDriveFile {...props} />;
 export const KeyIcon: IconType = (props) => <VpnKey {...props} />;
 export const LogIcon: IconType = (props) => <SettingsEthernet {...props} />;
diff --git a/src/components/multi-panel-view/multi-panel-view.tsx b/src/components/multi-panel-view/multi-panel-view.tsx
new file mode 100644 (file)
index 0000000..54bea41
--- /dev/null
@@ -0,0 +1,104 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import React, { ReactElement, ReactNode, useState } from 'react';
+import { Button, Grid } from "@material-ui/core";
+import { GridProps } from '@material-ui/core/Grid';
+import { isArray } from 'lodash';
+import { DefaultView } from 'components/default-view/default-view';
+import { InfoIcon } from 'components/icon/icon';
+import { ReactNodeArray } from 'prop-types';
+
+interface MPVHideablePanelDataProps {
+    name: string;
+    visible: boolean;
+    children: ReactNode;
+}
+
+interface MPVHideablePanelActionProps {
+    doHidePanel: () => void;
+}
+
+type MPVPanelProps = MPVHideablePanelDataProps & MPVHideablePanelActionProps;
+
+const MPVHideablePanel = ({doHidePanel, name, visible, ...props}: MPVPanelProps) =>
+    visible
+    ? <>
+        {React.cloneElement((props.children as ReactElement), { doHidePanel, panelName: name })}
+    </>
+    : null;
+
+interface MPVPanelContentDataProps {
+    panelName?: string;
+    children: ReactElement;
+}
+
+interface MPVPanelContentActionProps {
+    doHidePanel?: () => void;
+}
+
+type MPVPanelContentProps = MPVPanelContentDataProps & MPVPanelContentActionProps & GridProps;
+
+// Grid item compatible component for layout and MPV props passing
+export const MPVPanelContent = ({doHidePanel, panelName, ...props}: MPVPanelContentProps) =>
+    <Grid item {...props}>
+        {React.cloneElement(props.children, { doHidePanel, panelName })}
+    </Grid>;
+
+export interface MPVContainerDataProps {
+    panelNames?: string[];
+}
+
+type MPVContainerProps = MPVContainerDataProps & GridProps;
+
+// Grid container compatible component that also handles panel toggling.
+export const MPVContainer = ({children, panelNames, ...props}: MPVContainerProps) => {
+    if (children === undefined || children === null || children === {}) {
+        children = [];
+    } else if (!isArray(children)) {
+        children = [children];
+    }
+    const visibility = (children as ReactNodeArray).map(() => true);
+    const [panelVisibility, setPanelVisibility] = useState<boolean[]>(visibility);
+
+    let panels: JSX.Element[] = [];
+    let toggles: JSX.Element[] = [];
+
+    if (isArray(children)) {
+        for (let idx = 0; idx < children.length; idx++) {
+            const toggleFn = (idx: number) => () => {
+                setPanelVisibility([
+                    ...panelVisibility.slice(0, idx),
+                    !panelVisibility[idx],
+                    ...panelVisibility.slice(idx+1)
+                ])
+            };
+            const toggleLabel = panelVisibility[idx] ? 'Hide' : 'Show'
+            const panelName = panelNames === undefined
+                ? `Panel ${idx+1}`
+                : panelNames[idx] || `Panel ${idx+1}`;
+
+
+            toggles = [
+                ...toggles,
+                <Button onClick={toggleFn(idx)}>{toggleLabel} {panelName}</Button>
+            ];
+
+            const aPanel =
+                <MPVHideablePanel visible={panelVisibility[idx]} name={panelName} doHidePanel={toggleFn(idx)}>
+                    {children[idx]}
+                </MPVHideablePanel>;
+            panels = [...panels, aPanel];
+        };
+    };
+
+    return <Grid container {...props}>
+        { toggles }
+        { panelVisibility.includes(true)
+            ? panels
+            : <Grid container alignItems='center' justify='center'>
+                <DefaultView messages={["All panels are hidden.", "Click on the buttons above to show them."]} icon={InfoIcon} />
+            </Grid> }
+    </Grid>;
+};