X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/df1ebc0e3184afd3fb66414651fc1aec713928bf..3fd36192430554d3e28fcee28d23d534881dae7a:/services/workbench2/src/components/multi-panel-view/multi-panel-view.tsx diff --git a/services/workbench2/src/components/multi-panel-view/multi-panel-view.tsx b/services/workbench2/src/components/multi-panel-view/multi-panel-view.tsx index 203748d5e0..6146151996 100644 --- a/services/workbench2/src/components/multi-panel-view/multi-panel-view.tsx +++ b/services/workbench2/src/components/multi-panel-view/multi-panel-view.tsx @@ -8,6 +8,8 @@ import { Grid, Paper, StyleRulesCallback, + Tab, + Tabs, Tooltip, withStyles, WithStyles @@ -19,12 +21,32 @@ import { InfoIcon } from 'components/icon/icon'; import { ReactNodeArray } from 'prop-types'; import classNames from 'classnames'; -type CssRules = 'root' | 'button' | 'buttonIcon' | 'content'; +type CssRules = + | 'gridContainerRoot' + | 'exclusiveGridContainerRoot' + | 'gridItemRoot' + | 'paperRoot' + | 'button' + | 'buttonIcon' + | 'content' + | 'exclusiveContentPaper' + | 'tabs'; const styles: StyleRulesCallback = theme => ({ - root: { + gridContainerRoot: { marginTop: '10px', }, + exclusiveGridContainerRoot: { + marginTop: 0, + }, + gridItemRoot: { + paddingTop: '0 !important', + }, + paperRoot: { + height: '100%', + display: 'flex', + flexDirection: 'column', + }, button: { padding: '2px 5px', marginRight: '5px', @@ -36,6 +58,15 @@ const styles: StyleRulesCallback = theme => ({ }, content: { overflow: 'auto', + maxWidth: 'initial', + }, + exclusiveContentPaper: { + boxShadow: 'none', + }, + tabs: { + flexGrow: 1, + flexShrink: 1, + maxWidth: 'initial', }, }); @@ -46,6 +77,7 @@ interface MPVHideablePanelDataProps { illuminated: boolean; children: ReactNode; panelRef?: MutableRefObject; + paperClassName?: string; } interface MPVHideablePanelActionProps { @@ -56,12 +88,21 @@ interface MPVHideablePanelActionProps { type MPVHideablePanelProps = MPVHideablePanelDataProps & MPVHideablePanelActionProps; -const MPVHideablePanel = ({doHidePanel, doMaximizePanel, doUnMaximizePanel, name, visible, maximized, illuminated, ...props}: MPVHideablePanelProps) => +const MPVHideablePanel = ({ doHidePanel, doMaximizePanel, doUnMaximizePanel, name, visible, maximized, illuminated, paperClassName, ...props }: MPVHideablePanelProps) => visible - ? <> - {React.cloneElement((props.children as ReactElement), { doHidePanel, doMaximizePanel, doUnMaximizePanel, panelName: name, panelMaximized: maximized, panelIlluminated: illuminated, panelRef: props.panelRef })} - - : null; + ? <> + {React.cloneElement((props.children as ReactElement), { + doHidePanel, + doMaximizePanel, + doUnMaximizePanel, + panelName: name, + panelMaximized: maximized, + panelIlluminated: illuminated, + panelRef: props.panelRef, + paperClassName, + })} + + : null; interface MPVPanelDataProps { panelName?: string; @@ -71,6 +112,7 @@ interface MPVPanelDataProps { forwardProps?: boolean; maxHeight?: string; minHeight?: string; + paperClassName?: string; } interface MPVPanelActionProps { @@ -82,15 +124,15 @@ interface MPVPanelActionProps { // Props received by panel implementors export type MPVPanelProps = MPVPanelDataProps & MPVPanelActionProps; -type MPVPanelContentProps = {children: ReactElement} & MPVPanelProps & GridProps; +type MPVPanelContentProps = { children: ReactElement } & MPVPanelProps & GridProps; // Grid item compatible component for layout and MPV props passing -export const MPVPanelContent = ({doHidePanel, doMaximizePanel, doUnMaximizePanel, panelName, - panelMaximized, panelIlluminated, panelRef, forwardProps, maxHeight, minHeight, - ...props}: MPVPanelContentProps) => { +export const MPVPanelContent = ({ doHidePanel, doMaximizePanel, doUnMaximizePanel, panelName, + panelMaximized, panelIlluminated, panelRef, forwardProps, maxHeight, minHeight, paperClassName, + ...props }: MPVPanelContentProps) => { useEffect(() => { if (panelRef && panelRef.current) { - panelRef.current.scrollIntoView({alignToTop: true}); + panelRef.current.scrollIntoView({ alignToTop: true }); } }, [panelRef]); @@ -98,12 +140,12 @@ export const MPVPanelContent = ({doHidePanel, doMaximizePanel, doUnMaximizePanel ? '100%' : maxHeight; - return + return {/* Element to scroll to when the panel is selected */} - - { forwardProps - ? React.cloneElement(props.children, { doHidePanel, doMaximizePanel, doUnMaximizePanel, panelName, panelMaximized }) - : props.children } + + {forwardProps + ? React.cloneElement(props.children, { doHidePanel, doMaximizePanel, doUnMaximizePanel, panelName, panelMaximized, paperClassName }) + : React.cloneElement(props.children, { paperClassName })} ; } @@ -114,70 +156,83 @@ export interface MPVPanelState { } interface MPVContainerDataProps { panelStates?: MPVPanelState[]; + mutuallyExclusive?: boolean; } type MPVContainerProps = MPVContainerDataProps & GridProps; // Grid container compatible component that also handles panel toggling. -const MPVContainerComponent = ({children, panelStates, classes, ...props}: MPVContainerProps & WithStyles) => { - if (children === undefined || children === null || children === {}) { +const MPVContainerComponent = ({ children, panelStates, classes, ...props }: MPVContainerProps & WithStyles) => { + if (children === undefined || children === null || Object.keys(children).length === 0) { children = []; } else if (!isArray(children)) { children = [children]; } const initialVisibility = (children as ReactNodeArray).map((_, idx) => !panelStates || // if panelStates wasn't passed, default to all visible panels - (panelStates[idx] && - (panelStates[idx].visible || panelStates[idx].visible === undefined))); + (panelStates[idx] && + (panelStates[idx].visible || panelStates[idx].visible === undefined))); const [panelVisibility, setPanelVisibility] = useState(initialVisibility); const [previousPanelVisibility, setPreviousPanelVisibility] = useState(initialVisibility); const [highlightedPanel, setHighlightedPanel] = useState(-1); + const currentSelectedPanel = panelVisibility.findIndex(Boolean); const [selectedPanel, setSelectedPanel] = useState(-1); const panelRef = useRef(null); let panels: JSX.Element[] = []; let buttons: JSX.Element[] = []; + let tabs: JSX.Element[] = []; + let buttonBar: JSX.Element = <>; if (isArray(children)) { - for (let idx = 0; idx < children.length; idx++) { - const showFn = (idx: number) => () => { - setPreviousPanelVisibility(initialVisibility); + const showFn = (idx: number) => () => { + setPreviousPanelVisibility(initialVisibility); + if (props.mutuallyExclusive) { + // Hide all other panels setPanelVisibility([ - ...panelVisibility.slice(0, idx), + ...(new Array(idx).fill(false)), true, - ...panelVisibility.slice(idx+1) + ...(new Array(panelVisibility.length-(idx+1)).fill(false)), ]); - setSelectedPanel(idx); - }; - const hideFn = (idx: number) => () => { - setPreviousPanelVisibility(initialVisibility); + } else { setPanelVisibility([ ...panelVisibility.slice(0, idx), - false, - ...panelVisibility.slice(idx+1) - ]) - }; - const maximizeFn = (idx: number) => () => { - setPreviousPanelVisibility(panelVisibility); - // Maximize X == hide all but X - setPanelVisibility([ - ...panelVisibility.slice(0, idx).map(() => false), true, - ...panelVisibility.slice(idx+1).map(() => false), + ...panelVisibility.slice(idx + 1) ]); - }; - const unMaximizeFn = (idx: number) => () => { - setPanelVisibility(previousPanelVisibility); - setSelectedPanel(idx); } + setSelectedPanel(idx); + }; + const hideFn = (idx: number) => () => { + setPreviousPanelVisibility(initialVisibility); + setPanelVisibility([ + ...panelVisibility.slice(0, idx), + false, + ...panelVisibility.slice(idx+1) + ]) + }; + const maximizeFn = (idx: number) => () => { + setPreviousPanelVisibility(panelVisibility); + // Maximize X == hide all but X + setPanelVisibility([ + ...panelVisibility.slice(0, idx).map(() => false), + true, + ...panelVisibility.slice(idx+1).map(() => false), + ]); + }; + const unMaximizeFn = (idx: number) => () => { + setPanelVisibility(previousPanelVisibility); + setSelectedPanel(idx); + } + for (let idx = 0; idx < children.length; idx++) { const panelName = panelStates === undefined - ? `Panel ${idx+1}` - : (panelStates[idx] && panelStates[idx].name) || `Panel ${idx+1}`; + ? `Panel ${idx + 1}` + : (panelStates[idx] && panelStates[idx].name) || `Panel ${idx + 1}`; const btnVariant = panelVisibility[idx] ? "contained" : "outlined"; const btnTooltip = panelVisibility[idx] ? `` - :`Open ${panelName} panel`; + : `Open ${panelName} panel`; const panelIsMaximized = panelVisibility[idx] && panelVisibility.filter(e => e).length === 1; @@ -193,13 +248,22 @@ const MPVContainerComponent = ({children, panelStates, classes, ...props}: MPVCo setHighlightedPanel(-1); }} onClick={showFn(idx)}> - {panelName} + {panelName} ]; + tabs = [ + ...tabs, + <>{panelName} + ]; + const aPanel = - null}> @@ -207,21 +271,40 @@ const MPVContainerComponent = ({children, panelStates, classes, ...props}: MPVCo ; panels = [...panels, aPanel]; }; + + buttonBar = props.mutuallyExclusive ? + showFn(val)()} data-cy={"mpv-tabs"}> + {tabs.map((tgl, idx) => )} + : + + {buttons.map((tgl, idx) => {tgl})} + ; }; - return - - { buttons.map((tgl, idx) => {tgl}) } - - setSelectedPanel(-1)}> - { panelVisibility.includes(true) - ? panels - : - - } - + const content = setSelectedPanel(-1)}> + {panelVisibility.includes(true) + ? panels + : + + } ; + + if (props.mutuallyExclusive) { + return + + + {buttonBar} + {content} + + + ; + } else { + return + {buttonBar} + {content} + ; + } }; export const MPVContainer = withStyles(styles)(MPVContainerComponent);