import React, { useEffect, useState, useCallback, useRef } from 'react'; import PropTypes from 'prop-types'; import { InvestigationalUseDialog } from '@ohif/ui'; import { HangingProtocolService, CommandsManager } from '@ohif/core'; import { useAppConfig } from '@state'; import ViewerHeader from './ViewerHeader'; import SidePanelWithServices from '../Components/SidePanelWithServices'; import { Onboarding, ResizablePanelGroup, ResizablePanel, ResizableHandle } from '@ohif/ui-next'; import useResizablePanels from './ResizablePanelsHook'; const resizableHandleClassName = 'mt-[1px] bg-black'; function ViewerLayout({ // From Extension Module Params extensionManager, servicesManager, hotkeysManager, commandsManager, // From Modes viewports, ViewportGridComp, leftPanelClosed = false, rightPanelClosed = false, leftPanelResizable = false, rightPanelResizable = false, }: withAppTypes): React.FunctionComponent { const [appConfig] = useAppConfig(); const { panelService, hangingProtocolService, customizationService } = servicesManager.services; const [showLoadingIndicator, setShowLoadingIndicator] = useState(appConfig.showLoadingIndicator); const hasPanels = useCallback( (side): boolean => !!panelService.getPanels(side).length, [panelService] ); const [hasRightPanels, setHasRightPanels] = useState(hasPanels('right')); const [hasLeftPanels, setHasLeftPanels] = useState(hasPanels('left')); const [leftPanelClosedState, setLeftPanelClosed] = useState(leftPanelClosed); const [rightPanelClosedState, setRightPanelClosed] = useState(rightPanelClosed); const [ leftPanelProps, rightPanelProps, resizablePanelGroupProps, resizableLeftPanelProps, resizableViewportGridPanelProps, resizableRightPanelProps, onHandleDragging, ] = useResizablePanels( leftPanelClosed, setLeftPanelClosed, rightPanelClosed, setRightPanelClosed ); const handleMouseEnter = () => { (document.activeElement as HTMLElement)?.blur(); }; const LoadingIndicatorProgress = customizationService.getCustomization( 'ui.loadingIndicatorProgress' ); /** * 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 getComponent = id => { const entry = extensionManager.getModuleEntry(id); if (!entry || !entry.component) { throw new Error( `${id} is not valid for an extension module or no component found from extension ${id}. Please verify your configuration or ensure that the extension is properly registered. It's also possible that your mode is utilizing a module from an extension that hasn't been included in its dependencies (add the extension to the "extensionDependencies" array in your mode's index.js file). Check the reference string to the extension in your Mode configuration` ); } return { entry }; }; useEffect(() => { const { unsubscribe } = hangingProtocolService.subscribe( HangingProtocolService.EVENTS.PROTOCOL_CHANGED, // Todo: right now to set the loading indicator to false, we need to wait for the // hangingProtocolService to finish applying the viewport matching to each viewport, // however, this might not be the only approach to set the loading indicator to false. we need to explore this further. () => { setShowLoadingIndicator(false); } ); return () => { unsubscribe(); }; }, [hangingProtocolService]); const getViewportComponentData = viewportComponent => { const { entry } = getComponent(viewportComponent.namespace); return { component: entry.component, isReferenceViewable: entry.isReferenceViewable, displaySetsToDisplay: viewportComponent.displaySetsToDisplay, }; }; useEffect(() => { const { unsubscribe } = panelService.subscribe( panelService.EVENTS.PANELS_CHANGED, ({ options }) => { setHasLeftPanels(hasPanels('left')); setHasRightPanels(hasPanels('right')); if (options?.leftPanelClosed !== undefined) { setLeftPanelClosed(options.leftPanelClosed); } if (options?.rightPanelClosed !== undefined) { setRightPanelClosed(options.rightPanelClosed); } } ); return () => { unsubscribe(); }; }, [panelService, hasPanels]); const viewportComponents = viewports.map(getViewportComponentData); return (
{showLoadingIndicator && } {/* LEFT SIDEPANELS */} {hasLeftPanels ? ( <> ) : null} {/* TOOLBAR + GRID */}
{hasRightPanels ? ( <> ) : null}
); } ViewerLayout.propTypes = { // From extension module params extensionManager: PropTypes.shape({ getModuleEntry: PropTypes.func.isRequired, }).isRequired, commandsManager: PropTypes.instanceOf(CommandsManager), servicesManager: PropTypes.object.isRequired, // From modes leftPanels: PropTypes.array, rightPanels: PropTypes.array, leftPanelClosed: PropTypes.bool.isRequired, rightPanelClosed: PropTypes.bool.isRequired, /** Responsible for rendering our grid of viewports; provided by consuming application */ children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]).isRequired, viewports: PropTypes.array, }; export default ViewerLayout;