import React, { useEffect, useState } from 'react'; import PropTypes from 'prop-types'; import { useTranslation } from 'react-i18next'; import { SidePanel, ErrorBoundary, UserPreferences, AboutModal, Header, useModal } from '@ohif/ui'; import NestedMenu from './ToolbarButtonNestedMenu.jsx'; // TODO: Having ToolbarPrimary and ToolbarSecondary is ugly, but // these are going to be unified shortly so this is good enough for now. function ToolbarPrimary({ servicesManager }) { const { ToolBarService } = servicesManager.services; const defaultTool = { icon: 'tool-more-menu', label: 'More', isActive: false, }; const [toolbars, setToolbars] = useState({ primary: [], secondary: [] }); const [activeTool, setActiveTool] = useState(defaultTool); const setActiveToolHandler = (tool, isNested) => { setActiveTool(isNested ? tool : defaultTool); }; const onPrimaryClickHandler = (evt, btn) => { if ( btn.props && btn.props.commands && evt.value && btn.props.commands[evt.value] ) { const { commandName, commandOptions } = btn.props.commands[evt.value]; commandsManager.runCommand(commandName, commandOptions); } }; useEffect(() => { const { unsubscribe } = ToolBarService.subscribe( ToolBarService.EVENTS.TOOL_BAR_MODIFIED, () => { console.warn('~~~ TOOL BAR MODIFIED EVENT CAUGHT'); const updatedToolbars = { primary: ToolBarService.getButtonSection('primary', { onClick: onPrimaryClickHandler, setActiveTool: setActiveToolHandler, }), secondary: ToolBarService.getButtonSection('secondary', { setActiveTool: setActiveToolHandler, }), }; setToolbars(updatedToolbars); } ); return unsubscribe; }, [ToolBarService]); return <> {toolbars.primary.map((toolDef, index) => { const isNested = Array.isArray(toolDef); if (!isNested) { const { id, Component, componentProps } = toolDef; return ; } else { return (
{toolDef.map(x => { const { id, Component, componentProps } = x; return ( ); })}
); } })} } function ToolbarSecondary({ servicesManager }) { const { ToolBarService } = servicesManager.services; const defaultTool = { icon: 'tool-more-menu', label: 'More', isActive: false, }; const [toolbars, setToolbars] = useState({ primary: [], secondary: [] }); const [nestedActiveTool, setNestedActiveTool] = useState(defaultTool); const setActiveToolHandler = (tool, isNested) => { setNestedActiveTool(isNested ? tool : defaultTool); }; const onPrimaryClickHandler = (evt, btn) => { if ( btn.props && btn.props.commands && evt.item && evt.item.value && btn.props.commands[evt.item.value] ) { const { commandName, commandOptions } = btn.props.commands[evt.item.value]; commandsManager.runCommand(commandName, commandOptions); } }; useEffect(() => { const { unsubscribe } = ToolBarService.subscribe( ToolBarService.EVENTS.TOOL_BAR_MODIFIED, () => { console.warn('~~~ TOOL BAR MODIFIED EVENT CAUGHT'); const updatedToolbars = { primary: ToolBarService.getButtonSection('primary', { onClick: onPrimaryClickHandler, setActiveTool: setActiveToolHandler, }), secondary: ToolBarService.getButtonSection('secondary', { setActiveTool: setActiveToolHandler, }), }; setToolbars(updatedToolbars); } ); return unsubscribe; }, [ToolBarService]); return <> {toolbars.secondary.map(toolDef => { const { id, Component, componentProps } = toolDef; return ; })} } function ViewerLayout({ // From Extension Module Params extensionManager, servicesManager, hotkeysManager, commandsManager, // From Modes leftPanels, rightPanels, viewports, ViewportGridComp, }) { const { t } = useTranslation(); const { show, hide } = useModal(); const { hotkeyDefinitions, hotkeyDefaults } = hotkeysManager; const menuOptions = [ { title: t('Header:About'), icon: 'info', onClick: () => show({ content: AboutModal, title: 'About OHIF Viewer' }) }, { title: t('Header:Preferences'), icon: 'settings', onClick: () => show({ title: t('UserPreferencesModal:User Preferences'), content: UserPreferences, contentProps: { hotkeyDefaults: hotkeysManager.getValidHotkeyDefinitions(hotkeyDefaults), hotkeyDefinitions, onCancel: hide, onSubmit: ({ hotkeyDefinitions }) => { hotkeysManager.setHotkeys(hotkeyDefinitions); hide(); }, onReset: () => hotkeysManager.restoreDefaultBindings() } }) }, ]; /** * Set body classes (tailwindcss) that don't allow vertical * or horizontal overflow (no scrolling). Also guarantee window * is sized to our viewport. */ useEffect(() => { document.body.classList.add('bg-black'); document.body.classList.add('overflow-hidden'); return () => { document.body.classList.remove('bg-black'); document.body.classList.remove('overflow-hidden'); }; }, []); const getPanelData = id => { const entry = extensionManager.getModuleEntry(id); // TODO, not sure why sidepanel content has to be JSX, and not a children prop? const content = entry.component; return { iconName: entry.iconName, iconLabel: entry.iconLabel, label: entry.label, name: entry.name, content, }; }; const getViewportComponentData = viewportComponent => { const entry = extensionManager.getModuleEntry(viewportComponent.namespace); return { component: entry.component, displaySetsToDisplay: viewportComponent.displaySetsToDisplay, }; }; const leftPanelComponents = leftPanels.map(getPanelData); const rightPanelComponents = rightPanels.map(getPanelData); const viewportComponents = viewports.map(getViewportComponentData); return (
{/* LEFT SIDEPANELS */} {leftPanelComponents.length && ( )} {/* TOOLBAR + GRID */}
{rightPanelComponents.length && ( )}
); } ViewerLayout.propTypes = { // From extension module params extensionManager: PropTypes.shape({ getModuleEntry: PropTypes.func.isRequired, }).isRequired, commandsManager: PropTypes.object, // From modes leftPanels: PropTypes.array, rightPanels: PropTypes.array, /** Responsible for rendering our grid of viewports; provided by consuming application */ children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]).isRequired, }; ViewerLayout.defaultProps = { leftPanels: [], rightPanels: [], }; export default ViewerLayout;