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}
+
+
+ );
+};
+
+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 && (
-
- )}
-
- );
- });
- };
- 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 && (
-
- )}
-
- );
- });
- };
return (
@@ -107,21 +89,11 @@ const Header = () => {
-
- {renderToolbar()}
-
- {
- alert('Open menu');
- }}
- >
-
-
-
+