Update sidePanel to support multiple components
This commit is contained in:
parent
802a8d5f1b
commit
08ebfd5865
@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useCallback } from 'react';
|
import React, { useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
|
||||||
@ -53,30 +53,78 @@ const position = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getChildComponent = (childComponents, componentOpen) => {
|
||||||
|
if (Array.isArray(childComponents)) {
|
||||||
|
return childComponents.find(
|
||||||
|
(_childComponent) => _childComponent.name === componentOpen
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return childComponents;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const SidePanel = ({
|
const SidePanel = ({
|
||||||
side,
|
side,
|
||||||
className,
|
className,
|
||||||
children,
|
defaultComponentOpen,
|
||||||
defaultIsOpen,
|
childComponents,
|
||||||
componentLabel,
|
|
||||||
iconLabel,
|
|
||||||
iconName,
|
|
||||||
}) => {
|
}) => {
|
||||||
const [isOpen, setIsOpen] = useState(defaultIsOpen);
|
const [componentOpen, setComponentOpen] = useState(defaultComponentOpen);
|
||||||
const openStatus = isOpen ? 'open' : 'closed';
|
|
||||||
|
const openStatus = componentOpen ? 'open' : 'closed';
|
||||||
const style = Object.assign({}, styleMap[openStatus][side], baseStyle);
|
const style = Object.assign({}, styleMap[openStatus][side], baseStyle);
|
||||||
|
|
||||||
const getSidePanelHeader = useCallback(() => {
|
const childComponent = getChildComponent(childComponents, componentOpen);
|
||||||
return (
|
|
||||||
<React.Fragment>
|
const getPanelButtons = () => {
|
||||||
{isOpen ? (
|
const _childComponents = Array.isArray(childComponents)
|
||||||
|
? childComponents
|
||||||
|
: [childComponents];
|
||||||
|
return _childComponents.map((childComponent) => {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
key={childComponent.name}
|
||||||
|
variant="text"
|
||||||
|
color="inherit"
|
||||||
|
onClick={() => {
|
||||||
|
setComponentOpen(childComponent.name);
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
minWidth: `${collapsedWidth}px`,
|
||||||
|
width: `${collapsedWidth}px`,
|
||||||
|
}}
|
||||||
|
className="flex flex-col text-xs px-1 py-1 text-white border-transparent border-b"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
name={childComponent.iconName}
|
||||||
|
className="text-primary-active"
|
||||||
|
/>
|
||||||
|
<span className="mt-2 text-white text-xs">
|
||||||
|
{childComponent.iconLabel}
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={classnames(
|
||||||
|
className,
|
||||||
|
baseClasses,
|
||||||
|
classesMap[openStatus][side]
|
||||||
|
)}
|
||||||
|
style={style}
|
||||||
|
>
|
||||||
|
{componentOpen ? (
|
||||||
|
<React.Fragment>
|
||||||
<div className="px-3 border-b border-secondary-light">
|
<div className="px-3 border-b border-secondary-light">
|
||||||
<Button
|
<Button
|
||||||
variant="text"
|
variant="text"
|
||||||
color="inherit"
|
color="inherit"
|
||||||
rounded="none"
|
rounded="none"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setIsOpen(false);
|
setComponentOpen(null);
|
||||||
}}
|
}}
|
||||||
className="flex flex-row items-center px-3 h-12 relative w-full"
|
className="flex flex-row items-center px-3 h-12 relative w-full"
|
||||||
>
|
>
|
||||||
@ -89,58 +137,45 @@ const SidePanel = ({
|
|||||||
style={{ ...position[side] }}
|
style={{ ...position[side] }}
|
||||||
/>
|
/>
|
||||||
<span className="flex-1 text-primary-active">
|
<span className="flex-1 text-primary-active">
|
||||||
{componentLabel}
|
{childComponent.label}
|
||||||
</span>
|
</span>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
{childComponent.content}
|
||||||
<Button
|
</React.Fragment>
|
||||||
variant="text"
|
) : (
|
||||||
color="inherit"
|
<React.Fragment>{getPanelButtons()}</React.Fragment>
|
||||||
onClick={() => {
|
|
||||||
setIsOpen(true);
|
|
||||||
}}
|
|
||||||
style={{
|
|
||||||
minWidth: `${collapsedWidth}px`,
|
|
||||||
width: `${collapsedWidth}px`,
|
|
||||||
}}
|
|
||||||
className="flex flex-col text-xs px-1 py-1 text-white border-transparent border-b"
|
|
||||||
>
|
|
||||||
<Icon name={iconName} className="text-primary-active" />
|
|
||||||
<span className="mt-2 text-white text-xs">{iconLabel}</span>
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
}, [componentLabel, iconLabel, iconName, isOpen, side]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={classnames(
|
|
||||||
className,
|
|
||||||
baseClasses,
|
|
||||||
classesMap[openStatus][side]
|
|
||||||
)}
|
)}
|
||||||
style={style}
|
|
||||||
>
|
|
||||||
{getSidePanelHeader()}
|
|
||||||
{isOpen && children}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
SidePanel.defaultProps = {
|
SidePanel.defaultProps = {
|
||||||
defaultIsOpen: false,
|
defaultComponentOpen: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
SidePanel.propTypes = {
|
SidePanel.propTypes = {
|
||||||
side: PropTypes.oneOf(['left', 'right']).isRequired,
|
side: PropTypes.oneOf(['left', 'right']).isRequired,
|
||||||
iconLabel: PropTypes.string.isRequired,
|
|
||||||
componentLabel: PropTypes.string.isRequired,
|
|
||||||
iconName: PropTypes.string.isRequired,
|
|
||||||
defaultIsOpen: PropTypes.bool,
|
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
children: PropTypes.node,
|
defaultComponentOpen: PropTypes.string,
|
||||||
|
childComponents: PropTypes.oneOfType([
|
||||||
|
PropTypes.shape({
|
||||||
|
iconName: PropTypes.string.isRequired,
|
||||||
|
iconLabel: PropTypes.string.isRequired,
|
||||||
|
name: PropTypes.string.isRequired,
|
||||||
|
label: PropTypes.string.isRequired,
|
||||||
|
content: PropTypes.node,
|
||||||
|
}),
|
||||||
|
PropTypes.arrayOf(
|
||||||
|
PropTypes.shape({
|
||||||
|
iconName: PropTypes.string.isRequired,
|
||||||
|
iconLabel: PropTypes.string.isRequired,
|
||||||
|
name: PropTypes.string.isRequired,
|
||||||
|
label: PropTypes.string.isRequired,
|
||||||
|
content: PropTypes.node,
|
||||||
|
})
|
||||||
|
),
|
||||||
|
]),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SidePanel;
|
export default SidePanel;
|
||||||
|
|||||||
@ -24,19 +24,52 @@ import { SidePanel } from '@ohif/ui';
|
|||||||
>
|
>
|
||||||
<SidePanel
|
<SidePanel
|
||||||
side="left"
|
side="left"
|
||||||
iconName="group-layers"
|
childComponents={{
|
||||||
iconLabel="Studies"
|
iconName: "group-layers",
|
||||||
componentLabel="Studies"
|
iconLabel: "Studies",
|
||||||
defaultIsOpen={false}
|
name: "studies",
|
||||||
>
|
label: "Studies",
|
||||||
<div className="flex justify-center text-white p-2">panel content</div>
|
content: <div className="flex justify-center text-white p-2">panel content</div>,
|
||||||
</SidePanel>
|
}}
|
||||||
|
/>
|
||||||
<div className="flex flex-1 h-100 overflow-hidden bg-primary-main items-center justify-center text-white">
|
<div className="flex flex-1 h-100 overflow-hidden bg-primary-main items-center justify-center text-white">
|
||||||
CONTENT
|
CONTENT
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Playground>
|
</Playground>
|
||||||
|
|
||||||
|
|
||||||
|
## Multiple panels
|
||||||
|
|
||||||
|
<Playground>
|
||||||
|
<div
|
||||||
|
className="flex flex-row flex-no-wrap flex-1 items-stretch overflow-hidden w-full"
|
||||||
|
style={{ height: '500px' }}
|
||||||
|
>
|
||||||
|
<SidePanel
|
||||||
|
side="left"
|
||||||
|
childComponents={[{
|
||||||
|
iconName: "group-layers",
|
||||||
|
iconLabel: "Studies",
|
||||||
|
name: "studies",
|
||||||
|
label: "Studies",
|
||||||
|
content: <div className="flex justify-center text-white p-2">studies panel content</div>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
iconName: "list-bullets",
|
||||||
|
iconLabel: "Measure",
|
||||||
|
name: "measurements",
|
||||||
|
label: "Measurements",
|
||||||
|
content: <div className="flex justify-center text-white p-2">measure panel content</div>,
|
||||||
|
}]}
|
||||||
|
/>
|
||||||
|
<div className="flex flex-1 h-100 overflow-hidden bg-primary-main items-center justify-center text-white">
|
||||||
|
CONTENT
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Playground>
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
## Properties
|
||||||
|
|
||||||
<Props of={SidePanel} />
|
<Props of={SidePanel} />
|
||||||
|
|||||||
@ -57,14 +57,18 @@ import { tabs } from './studyBrowserMockData';
|
|||||||
>
|
>
|
||||||
<SidePanel
|
<SidePanel
|
||||||
side="left"
|
side="left"
|
||||||
iconName="group-layers"
|
defaultComponentOpen='studies'
|
||||||
iconLabel="Studies"
|
childComponents={{
|
||||||
componentLabel="Studies"
|
iconName: 'group-layers',
|
||||||
defaultIsOpen={true}
|
iconLabel: 'Studies',
|
||||||
>
|
label: 'Studies',
|
||||||
<StudyBrowser tabs={tabs} />
|
name: 'studies',
|
||||||
</SidePanel>
|
content: (
|
||||||
<div className="flex flex-col flex-1 h-100">
|
<StudyBrowser tabs={tabs} />
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div className="flex flex-col flex-1 h-full pb-2">
|
||||||
<div className="flex flex-2 w-100 border-b border-transparent h-12">
|
<div className="flex flex-2 w-100 border-b border-transparent h-12">
|
||||||
<ViewportToolbar />
|
<ViewportToolbar />
|
||||||
</div>
|
</div>
|
||||||
@ -100,45 +104,49 @@ import { tabs } from './studyBrowserMockData';
|
|||||||
</div>
|
</div>
|
||||||
<SidePanel
|
<SidePanel
|
||||||
side="right"
|
side="right"
|
||||||
iconName="list-bullets"
|
defaultComponentOpen="measurements"
|
||||||
iconLabel="Measure"
|
childComponents={{
|
||||||
componentLabel="Measurements"
|
iconName: 'list-bullets',
|
||||||
defaultIsOpen={true}
|
iconLabel: 'Measure',
|
||||||
>
|
label: 'Measurements',
|
||||||
<MeasurementsPanel
|
name: 'measurements',
|
||||||
descriptionData={descriptionData}
|
content: (
|
||||||
measurementTableData={measurementTableData}
|
<MeasurementsPanel
|
||||||
actionButtons={
|
descriptionData={descriptionData}
|
||||||
<>
|
measurementTableData={measurementTableData}
|
||||||
<ButtonGroup onClick={() => alert('Export')}>
|
actionButtons={
|
||||||
<Button
|
<React.Fragment>
|
||||||
className="text-white border-primary-main bg-black text-base py-2 px-2"
|
<ButtonGroup onClick={() => alert('Export')}>
|
||||||
size="initial"
|
<Button
|
||||||
color="inherit"
|
className="text-white border-primary-main bg-black text-base py-2 px-2"
|
||||||
>
|
size="initial"
|
||||||
Export
|
color="inherit"
|
||||||
</Button>
|
>
|
||||||
<IconButton
|
Export
|
||||||
className="bg-black border-primary-main px-2 text-white px-2"
|
</Button>
|
||||||
color="inherit"
|
<IconButton
|
||||||
size="initial"
|
className="bg-black border-primary-main px-2 text-white px-2"
|
||||||
>
|
color="inherit"
|
||||||
<Icon name="arrow-down" />
|
size="initial"
|
||||||
</IconButton>
|
>
|
||||||
</ButtonGroup>
|
<Icon name="arrow-down" />
|
||||||
<Button
|
</IconButton>
|
||||||
className="text-white border border-primary-main bg-black text-base py-2 px-2 ml-2"
|
</ButtonGroup>
|
||||||
variant="outlined"
|
<Button
|
||||||
size="initial"
|
className="text-white border border-primary-main bg-black text-base py-2 px-2 ml-2"
|
||||||
color="inherit"
|
variant="outlined"
|
||||||
onClick={() => alert('Create Report')}
|
size="initial"
|
||||||
>
|
color="inherit"
|
||||||
Create Report
|
onClick={() => alert('Create Report')}
|
||||||
</Button>
|
>
|
||||||
</>
|
Create Report
|
||||||
}
|
</Button>
|
||||||
/>
|
</React.Fragment>
|
||||||
</SidePanel>
|
}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user