diff --git a/platform/ui/index.js b/platform/ui/index.js index 70c105683..4bd6b94f9 100644 --- a/platform/ui/index.js +++ b/platform/ui/index.js @@ -28,6 +28,7 @@ export { MeasurementTable, NavBar, Notification, + PrimaryToolbar, Select, SegmentationTable, SidePanel, @@ -46,6 +47,8 @@ export { Thumbnail, ThumbnailSR, ThumbnailList, + ToolbarButton, + Tooltip, Typography, ViewportActionBar, ViewportGrid, diff --git a/platform/ui/src/assets/styles/styles.css b/platform/ui/src/assets/styles/styles.css index 38b720177..494887a42 100644 --- a/platform/ui/src/assets/styles/styles.css +++ b/platform/ui/src/assets/styles/styles.css @@ -41,62 +41,6 @@ @apply hidden; } -.tooltip { - @apply z-10 absolute; -} - -/* TOOLTIP WORKAROUND FOR ARROW UP */ -.tooltip.tooltip-up::before { - @apply bg-primary-dark absolute z-10; - content: ''; - width: 14px; - height: 1px; - top: -1px; - left: 50%; - transform: translateX(-50%); -} - -.tooltip.tooltip-right { - left: calc(100% + 15px); -} - -.tooltip.tooltip-right::before { - @apply bg-black absolute z-10; - content: ''; - width: 2px; - height: 15px; - left: -1px; - top: 50%; - transform: translateY(-50%); -} - -.tooltip.tooltip-top-right::before { - @apply bg-primary-dark absolute z-10; - content: ''; - width: 15px; - height: 2px; - right: 5px; - top: -1px; -} - -.tooltip.tooltip-top-left::before { - @apply bg-primary-dark absolute z-10; - content: ''; - width: 15px; - height: 2px; - left: 5px; - top: -1px; -} - -/* TOOLTIP WORKAROUND FOR ARROW */ -.showTooltipOnHover .tooltip { - display: none; -} - -.showTooltipOnHover:hover .tooltip { - display: block; -} - .showExcludeButtonOnHover .excludeButton { display: none; } diff --git a/platform/ui/src/components/PrimaryToolbar/PrimaryToolbar.jsx b/platform/ui/src/components/PrimaryToolbar/PrimaryToolbar.jsx new file mode 100644 index 000000000..13dd59b23 --- /dev/null +++ b/platform/ui/src/components/PrimaryToolbar/PrimaryToolbar.jsx @@ -0,0 +1,79 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import classnames from 'classnames'; + +import { ToolbarButton, IconButton, Icon, Tooltip } from '@ohif/ui'; + +const PrimaryToolbar = ({ activeTool, tools, moreTools }) => { + return ( +
+ {tools.map((tool) => { + const { id, onClick, icon, label, dropdownContent } = tool; + const isActive = activeTool === tool.id; + return ( +
+ +
+ ); + })} + {!!moreTools.length && ( + <> + + + + + + + + )} +
+ ); +}; + +PrimaryToolbar.defaultProps = { + activeTool: '', + moreTools: [], +}; + +PrimaryToolbar.propTypes = { + activeTool: PropTypes.string, + tools: PropTypes.arrayOf( + PropTypes.shape({ + id: PropTypes.string, + label: PropTypes.string, + icon: PropTypes.string, + commandName: PropTypes.string, + commandOptions: PropTypes.shape({ + toolName: PropTypes.string, + }), + onClick: PropTypes.func, + dropdownContent: PropTypes.node, + }) + ).isRequired, + moreTools: PropTypes.arrayOf( + PropTypes.shape({ + id: PropTypes.string, + label: PropTypes.string, + icon: PropTypes.string, + commandName: PropTypes.string, + commandOptions: PropTypes.shape({ + toolName: PropTypes.string, + }), + onClick: PropTypes.func, + }) + ), +}; + +export default PrimaryToolbar; diff --git a/platform/ui/src/components/PrimaryToolbar/PrimaryToolbar.mdx b/platform/ui/src/components/PrimaryToolbar/PrimaryToolbar.mdx new file mode 100644 index 000000000..50bada881 --- /dev/null +++ b/platform/ui/src/components/PrimaryToolbar/PrimaryToolbar.mdx @@ -0,0 +1,127 @@ +--- +name: Primary Toolbar +menu: Data Display +route: components/primaryToolbar +--- + +import { useState } from 'react'; +import { Playground } from 'docz'; +import classnames from 'classnames'; +import { PrimaryToolbar } from '@ohif/ui'; + +# Primary Toolbar + +Primary Toolbar is used to create a list of tools. + +## Import + +```javascript +import { PrimaryToolbar } from '@ohif/ui'; +``` + + + {() => { + const [activeTool, setActiveTool] = useState('Zoom'); + const dropdownContent = [ + { + name: 'Soft tissue', + value: '400/40', + }, + { name: 'Lung', value: '1500 / -600' }, + { name: 'Liver', value: '150 / 90' }, + { name: 'Bone', value: '2500 / 480' }, + { name: 'Brain', value: '80 / 40' }, + ]; + const tools = [ + { + id: 'Zoom', + label: 'Zoom', + icon: 'tool-zoom', + commandName: 'setToolActive', + commandOptions: { toolName: 'Zoom' }, + onClick: () => setActiveTool('Zoom'), + }, + { + id: 'Wwwc', + label: 'Levels', + icon: 'tool-window-level', + commandName: 'setToolActive', + commandOptions: { toolName: 'Wwwc' }, + onClick: () => setActiveTool('Wwwc'), + dropdownContent: ( +
+ {dropdownContent.map((row, i) => ( +
+
+ {row.name} + + {row.value} + +
+ {i} +
+ ))} +
+ ), + }, + { + id: 'Pan', + label: 'Pan', + icon: 'tool-move', + commandName: 'setToolActive', + commandOptions: { toolName: 'Pan' }, + onClick: () => setActiveTool('Pan'), + }, + { + id: 'Capture', + label: 'Capture', + icon: 'tool-capture', + commandName: 'setToolActive', + commandOptions: { toolName: 'Capture' }, + onClick: () => setActiveTool('Capture'), + }, + { + id: 'Layout', + label: 'Layout', + icon: 'tool-layout', + commandName: 'setToolActive', + commandOptions: { toolName: 'Layout' }, + onClick: () => setActiveTool('Layout'), + }, + ]; + const moreTools = [ + { + id: 'Zoom 2', + label: 'Zoom', + icon: 'tool-zoom', + commandName: 'setToolActive', + commandOptions: { toolName: 'Zoom' }, + onClick: () => setActiveTool('Zoom 2'), + }, + { + id: 'Layout 2', + label: 'Layout', + icon: 'tool-layout', + commandName: 'setToolActive', + commandOptions: { toolName: 'Layout' }, + onClick: () => setActiveTool('Layout 2'), + }, + ]; + return ( +
+ +
+ ); + }} +
+ +## Properties + + diff --git a/platform/ui/src/components/PrimaryToolbar/index.js b/platform/ui/src/components/PrimaryToolbar/index.js new file mode 100644 index 000000000..62ef7d92f --- /dev/null +++ b/platform/ui/src/components/PrimaryToolbar/index.js @@ -0,0 +1,2 @@ +import PrimaryToolbar from './PrimaryToolbar'; +export default PrimaryToolbar; diff --git a/platform/ui/src/components/ToolbarButton/ToolbarButton.jsx b/platform/ui/src/components/ToolbarButton/ToolbarButton.jsx new file mode 100644 index 000000000..51aa861ad --- /dev/null +++ b/platform/ui/src/components/ToolbarButton/ToolbarButton.jsx @@ -0,0 +1,53 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import classnames from 'classnames'; + +import { IconButton, Icon, Tooltip } from '@ohif/ui'; + +const ToolbarButton = ({ + id, + isActive, + onClick, + icon, + label, + dropdownContent, +}) => { + const shouldShowDropdown = !!isActive && !!dropdownContent; + return ( +
+ + + + + +
+ ); +}; + +ToolbarButton.defaultProps = { + dropdownContent: null, + isActive: false, +}; + +ToolbarButton.propTypes = { + id: PropTypes.string.isRequired, + isActive: PropTypes.bool, + onClick: PropTypes.func.isRequired, + icon: PropTypes.string.isRequired, + label: PropTypes.string.isRequired, + /** Tooltip content can be replaced for a customized content by passing a node to this value. */ + dropdownContent: PropTypes.node, +}; + +export default ToolbarButton; diff --git a/platform/ui/src/components/ToolbarButton/ToolbarButton.mdx b/platform/ui/src/components/ToolbarButton/ToolbarButton.mdx new file mode 100644 index 000000000..4cdb0c221 --- /dev/null +++ b/platform/ui/src/components/ToolbarButton/ToolbarButton.mdx @@ -0,0 +1,44 @@ +--- +name: Toolbar Button +menu: General +route: components/toolbarButton +--- + +import { useState } from 'react'; +import { Playground, Props } from 'docz'; +import { ToolbarButton } from '@ohif/ui'; + +# Toolbar Button + +Toolbar Buttons are used to populate the Toolbar. + +## Import + +```javascript +import { ToolbarButton } from '@ohif/ui'; +``` + + + {() => { + const [isActive, setIsActive] = useState(false); + const tool = { + id: 'Zoom', + label: 'Zoom', + icon: 'tool-zoom', + type: null, + commandName: 'setToolActive', + commandOptions: { toolName: 'Zoom' }, + onClick: () => setIsActive(true), + isActive, + }; + return ( +
+ +
+ ); + }} +
+ +## Properties + + diff --git a/platform/ui/src/components/ToolbarButton/index.js b/platform/ui/src/components/ToolbarButton/index.js new file mode 100644 index 000000000..45f19b9df --- /dev/null +++ b/platform/ui/src/components/ToolbarButton/index.js @@ -0,0 +1,2 @@ +import ToolbarButton from './ToolbarButton'; +export default ToolbarButton; diff --git a/platform/ui/src/components/Tooltip/Tooltip.jsx b/platform/ui/src/components/Tooltip/Tooltip.jsx new file mode 100644 index 000000000..ec5752668 --- /dev/null +++ b/platform/ui/src/components/Tooltip/Tooltip.jsx @@ -0,0 +1,91 @@ +import React, { useState } from 'react'; +import PropTypes from 'prop-types'; +import classnames from 'classnames'; + +import './tooltip.css'; + +const arrowPositionStyle = { + bottom: { + top: -15, + left: '50%', + transform: 'translateX(-50%)', + }, + 'bottom-left': { top: -15, left: 5 }, + 'bottom-right': { top: -15, right: 5 }, + right: { + top: 'calc(50% - 8px)', + left: -15, + transform: 'rotate(270deg)', + }, + left: { + top: 'calc(50% - 8px)', + right: -15, + transform: 'rotate(-270deg)', + }, +}; + +const Tooltip = ({ position, content, tight, children }) => { + const [isActive, setIsActive] = useState(false); + return ( +
{ + if (!isActive) { + setIsActive(true); + } + }} + onMouseOut={() => { + if (isActive) { + setIsActive(false); + } + }} + > + {children} +
+
+ {content} + + + +
+
+
+ ); +}; + +Tooltip.defaultProps = { + tight: false, + position: 'bottom', +}; + +Tooltip.propTypes = { + tight: PropTypes.bool, + position: PropTypes.oneOf([ + 'bottom', + 'bottom-left', + 'bottom-right', + 'left', + 'right', + ]), + children: PropTypes.node.isRequired, + content: PropTypes.node.isRequired, +}; + +export default Tooltip; diff --git a/platform/ui/src/components/Tooltip/Tooltip.mdx b/platform/ui/src/components/Tooltip/Tooltip.mdx new file mode 100644 index 000000000..fd638cb6d --- /dev/null +++ b/platform/ui/src/components/Tooltip/Tooltip.mdx @@ -0,0 +1,40 @@ +--- +name: Tooltip +menu: Data Display +route: components/tooltip +--- + +import { Playground, Props } from 'docz'; +import { Tooltip, Typography } from '@ohif/ui'; + +# Tooltip + +Tooltips display informative content when users hover over, focus on, or tap an +element. + +## Import + +```javascript +import { Tooltip } from '@ohif/ui'; +``` + + + {() => { + const tooltips = ['bottom', 'bottom-left', 'bottom-right', 'left', 'right']; + return ( +
+ {tooltips.map((tooltip) => ( +
+ + Tooltip {tooltip} + +
+ ))} +
+ ); + }} +
+ +## Properties + + diff --git a/platform/ui/src/components/Tooltip/index.js b/platform/ui/src/components/Tooltip/index.js new file mode 100644 index 000000000..2f9a4f833 --- /dev/null +++ b/platform/ui/src/components/Tooltip/index.js @@ -0,0 +1,2 @@ +import Tooltip from './Tooltip'; +export default Tooltip; diff --git a/platform/ui/src/components/Tooltip/tooltip.css b/platform/ui/src/components/Tooltip/tooltip.css new file mode 100644 index 000000000..1c388a835 --- /dev/null +++ b/platform/ui/src/components/Tooltip/tooltip.css @@ -0,0 +1,91 @@ +.tooltip { + @apply z-10 absolute; +} + +/* TOOLTIP WORKAROUND FOR ARROW UP */ +.tooltip.tooltip-bottom .tooltip-box::before { + @apply bg-primary-dark absolute z-10; + content: ''; + width: 14px; + height: 1px; + top: -1px; + left: 50%; + transform: translateX(-50%); +} + +.tooltip.tooltip-bottom { + @apply pt-2 mt-1; + left: 50%; + transform: translateX(-50%); +} + +.tooltip.tooltip-bottom-left { + @apply pt-2 mt-1; + left: 0; +} + +.tooltip.tooltip-bottom-right { + @apply pt-2 mt-1; + right: 0; +} + +.tooltip.tooltip-left { + @apply mr-4; + top: 50%; + right: calc(100%); + transform: translateY(-50%); +} + +.tooltip.tooltip-right { + @apply ml-4; + top: 50%; + left: calc(100%); + transform: translateY(-50%); +} + +.tooltip.tooltip-right .tooltip-box::before { + @apply bg-primary-dark absolute z-10; + content: ''; + width: 2px; + height: 15px; + left: -1px; + top: 50%; + transform: translateY(-50%); +} + +.tooltip.tooltip-left .tooltip-box::before { + @apply bg-primary-dark absolute z-10; + content: ''; + width: 2px; + height: 15px; + right: -1px; + top: 50%; + transform: translateY(-50%); +} + +.tooltip.tooltip-bottom-right .tooltip-box::before { + @apply bg-primary-dark absolute z-10; + content: ''; + width: 15px; + height: 2px; + right: 5px; + top: -1px; +} + +.tooltip.tooltip-bottom-left .tooltip-box::before { + @apply bg-primary-dark absolute z-10; + content: ''; + width: 15px; + height: 2px; + left: 5px; + top: -1px; +} + +/* TODO: REMOVE BELOW CLASS "showTooltipOnHover" AFTER TOOLTIP REPLACEMENT */ +.showTooltipOnHover .tooltip { + display: none; +} + +.showTooltipOnHover:hover .tooltip { + display: block; +} diff --git a/platform/ui/src/components/index.js b/platform/ui/src/components/index.js index acce04bfe..05916bec2 100644 --- a/platform/ui/src/components/index.js +++ b/platform/ui/src/components/index.js @@ -15,6 +15,7 @@ import MeasurementTable from './MeasurementTable'; import NavBar from './NavBar'; import Notification from './Notification'; import Select from './Select'; +import PrimaryToolbar from './PrimaryToolbar'; import SegmentationTable from './SegmentationTable'; import SidePanel from './SidePanel'; import StudyBrowser from './StudyBrowser'; @@ -32,6 +33,8 @@ import ThemeWrapper from './ThemeWrapper/'; import Thumbnail from './Thumbnail'; import ThumbnailSR from './ThumbnailSR'; import ThumbnailList from './ThumbnailList'; +import ToolbarButton from './ToolbarButton'; +import Tooltip from './Tooltip'; import Typography from './Typography'; import ViewportActionBar from './ViewportActionBar'; import ViewportGrid from './ViewportGrid'; @@ -53,6 +56,7 @@ export { MeasurementTable, NavBar, Notification, + PrimaryToolbar, Select, SegmentationTable, SidePanel, @@ -71,6 +75,8 @@ export { Thumbnail, ThumbnailSR, ThumbnailList, + ToolbarButton, + Tooltip, Typography, ViewportActionBar, ViewportGrid, diff --git a/platform/ui/src/views/ToolbarHeader/ToolbarHeader.mdx b/platform/ui/src/views/ToolbarHeader/ToolbarHeader.mdx deleted file mode 100644 index b9d7be493..000000000 --- a/platform/ui/src/views/ToolbarHeader/ToolbarHeader.mdx +++ /dev/null @@ -1,166 +0,0 @@ ---- -name: Toolbar Header -menu: Views -route: examples/toolbarHeader ---- - -import { useState } from 'react'; -import { Playground } from 'docz'; -import classnames from 'classnames'; -import { NavBar, Typography, Svg, Icon, IconButton } from '@ohif/ui'; - -# Toolbar Header - -This example shows you how you can build a Toolbar using the available -components. - -## Basic usage - - - {() => { - const [activeTool, setActiveTool] = useState('Zoom'); - const [showTooltip, setShowTooltip] = useState(null); - const tools = [ - { - id: 'Zoom', - label: 'Zoom', - icon: 'tool-zoom', - type: null, - commandName: 'setToolActive', - commandOptions: { toolName: 'Zoom' }, - }, - { - id: 'Wwwc', - label: 'Levels', - icon: 'tool-window-level', - type: null, - commandName: 'setToolActive', - commandOptions: { toolName: 'Wwwc' }, - }, - { - id: 'Pan', - label: 'Pan', - icon: 'tool-move', - type: null, - commandName: 'setToolActive', - commandOptions: { toolName: 'Pan' }, - }, - { - id: 'Capture', - label: 'Capture', - icon: 'tool-capture', - type: null, - commandName: 'setToolActive', - commandOptions: { toolName: 'Capture' }, - }, - { - id: 'Layout', - label: 'Layout', - icon: 'tool-layout', - type: null, - commandName: 'setToolActive', - commandOptions: { toolName: 'Layout' }, - }, - ]; - const renderToolbar = () => { - return tools.map((tool, i) => { - const isActive = activeTool === tool.id; - const shouldShowTooltip = showTooltip === tool.id; - return ( -
- { - setActiveTool(tool.id); - }} - onMouseOver={() => setShowTooltip(tool.id)} - onMouseOut={() => setShowTooltip(null)} - key={tool.id} - > - - - {shouldShowTooltip && ( -
- {tool.label} - - - -
- )} -
- ); - }); - }; - return ( -
- -
-
-
- alert('Navigate to previous page')} - /> - - - -
-
-
-
- {renderToolbar()} - - { - alert('Open menu'); - }} - > - - -
-
-
- - FOR INVESTIGATIONAL USE ONLY - - {}} - > - - - - -
-
-
-
- ); - }} -
- -## Properties - - diff --git a/platform/ui/src/views/Viewer/components/Header.js b/platform/ui/src/views/Viewer/components/Header.js index efbabaa8c..3c6b2a228 100644 --- a/platform/ui/src/views/Viewer/components/Header.js +++ b/platform/ui/src/views/Viewer/components/Header.js @@ -1,96 +1,78 @@ import React, { useState } from 'react'; -import classnames from 'classnames'; -import { NavBar, Svg, Icon, IconButton } from '@ohif/ui'; +import { NavBar, Svg, Icon, IconButton, PrimaryToolbar } from '@ohif/ui'; const Header = () => { - const [activeTool, setActiveTool] = useState(null); - const [showTooltip, setShowTooltip] = useState(null); + const [activeTool, setActiveTool] = useState('Zoom'); + const dropdownContent = [ + { + name: 'Soft tissue', + value: '400/40', + }, + { name: 'Lung', value: '1500 / -600' }, + { name: 'Liver', value: '150 / 90' }, + { name: 'Bone', value: '2500 / 480' }, + { name: 'Brain', value: '80 / 40' }, + ]; const tools = [ { id: 'Zoom', label: 'Zoom', icon: 'tool-zoom', - type: null, commandName: 'setToolActive', commandOptions: { toolName: 'Zoom' }, + onClick: () => setActiveTool('Zoom'), }, { id: 'Wwwc', label: 'Levels', icon: 'tool-window-level', - type: null, commandName: 'setToolActive', commandOptions: { toolName: 'Wwwc' }, + onClick: () => setActiveTool('Wwwc'), + dropdownContent: ( +
+ {dropdownContent.map((row, i) => ( +
+
+ {row.name} + + {row.value} + +
+ {i} +
+ ))} +
+ ), }, { id: 'Pan', label: 'Pan', icon: 'tool-move', - type: null, commandName: 'setToolActive', commandOptions: { toolName: 'Pan' }, + onClick: () => setActiveTool('Pan'), }, { id: 'Capture', label: 'Capture', icon: 'tool-capture', - type: null, commandName: 'setToolActive', commandOptions: { toolName: 'Capture' }, + onClick: () => setActiveTool('Capture'), }, { id: 'Layout', label: 'Layout', icon: 'tool-layout', - type: null, commandName: 'setToolActive', commandOptions: { toolName: 'Layout' }, + onClick: () => setActiveTool('Layout'), }, ]; - const renderToolbar = () => { - return tools.map((tool, i) => { - const isActive = activeTool === tool.id; - const shouldShowTooltip = showTooltip === tool.id; - return ( -
- { - setActiveTool(tool.id); - }} - onMouseOver={() => setShowTooltip(tool.id)} - onMouseOut={() => setShowTooltip(null)} - key={tool.id} - > - - - {shouldShowTooltip && ( -
- {tool.label} - - - -
- )} -
- ); - }); - }; return (
@@ -107,21 +89,11 @@ const Header = () => {
-
- {renderToolbar()} - - { - alert('Open menu'); - }} - > - - -
+