feat(PanelService): Left or right side panel to auto start as closed but auto open when needed (#3212)
* feat(SidePanel) OHIF issue #3135 - Added a method to set a callback for a Panel to invoke when it is ready to be shown (e.g. its data is loaded). - Implemented such methods for both the segmentation and measurement panels. - The SidePanel component now adds a callback to Panel components so that it will automatically open a Panel that was initially closed and yet to be opened. - Updated the OHIF documentation accordingly. * PR feedback - added a PanelService that centralized much of the logic that existed in panel PanelModule - the SidePanel subscribes to PanelService.EVENTS.ACTIVATE_PANEL for each of its child panels * Removed the PanelMeasurementTableTracking setMeasurementPanelContentReadyCallback method. * Made the forceActive flag in the PanelService optional and defaulted it to false. * Fixed failing top level exports unit test. * - PanelService subscriptions are now per panel (id) so subscribers do not necessarily need to check the panel id in the event when it is fired - PanelService activate panel trigger subscriptions are now returned so that they can be (better) managed outside of the service - updated/created the various documentation for panels and PanelService * Clarified various documentation. Moved the code to add the activate panel triggers out of the extensions and into the longitudinal mode. * Removed the openWhenPanelActivated flag. PanelService now conforms to extending PubSubService like the other services. Updated various documentation. * Fixed failing e2e, mpr test. * Renamed the ActivatePanelTriggers type properties. The ExtensionManager now sets the id of various modules as a property on each of those modules.
This commit is contained in:
1 parent
e4e62e9e14
commit
5670a4d8d1
21 files changed
+294
-23
No files matched your search
@@ -1,6 +1,6 @@
|
||||
import classnames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import SwiperCore, {
|
||||
A11y,
|
||||
@@ -11,6 +11,8 @@ import SwiperCore, {
|
||||
} from 'swiper';
|
||||
import { Swiper, SwiperSlide } from 'swiper/react';
|
||||
|
||||
import { PanelService, ServicesManager, Types } from '@ohif/core';
|
||||
|
||||
import { Button, Icon, IconButton, Tooltip } from '../';
|
||||
|
||||
import 'swiper/css';
|
||||
@@ -67,15 +69,23 @@ const position = {
|
||||
};
|
||||
|
||||
const SidePanel = ({
|
||||
servicesManager,
|
||||
side,
|
||||
className,
|
||||
activeTabIndex: activeTabIndexProp,
|
||||
tabs,
|
||||
}) => {
|
||||
const panelService: PanelService = servicesManager.services.panelService;
|
||||
|
||||
const { t } = useTranslation('SidePanel');
|
||||
|
||||
// Tracks whether this SidePanel has been opened at least once since this SidePanel was inserted into the DOM.
|
||||
// Thus going to the Study List page and back to the viewer resets this flag for a SidePanel.
|
||||
const [hasBeenOpened, setHasBeenOpened] = useState(
|
||||
activeTabIndexProp !== null
|
||||
);
|
||||
const [panelOpen, setPanelOpen] = useState(activeTabIndexProp !== null);
|
||||
const [activeTabIndex, setActiveTabIndex] = useState(activeTabIndexProp || 0);
|
||||
const [activeTabIndex, setActiveTabIndex] = useState(activeTabIndexProp ?? 0);
|
||||
const swiperRef = useRef() as any;
|
||||
const [swiper, setSwiper] = useState<any>();
|
||||
|
||||
@@ -102,6 +112,41 @@ const SidePanel = ({
|
||||
}
|
||||
}, [swiper]);
|
||||
|
||||
const updatePanelOpen = useCallback((panelOpen: boolean) => {
|
||||
setPanelOpen(panelOpen);
|
||||
if (panelOpen) {
|
||||
setHasBeenOpened(true);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const updateActiveTabIndex = useCallback(
|
||||
(activeTabIndex: number) => {
|
||||
setActiveTabIndex(activeTabIndex);
|
||||
updatePanelOpen(true);
|
||||
},
|
||||
[updatePanelOpen]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const activatePanelSubscription = panelService.subscribe(
|
||||
panelService.EVENTS.ACTIVATE_PANEL,
|
||||
(activatePanelEvent: Types.ActivatePanelEvent) => {
|
||||
if (!hasBeenOpened || activatePanelEvent.forceActive) {
|
||||
const tabIndex = tabs.findIndex(
|
||||
tab => tab.id === activatePanelEvent.panelId
|
||||
);
|
||||
if (tabIndex !== -1) {
|
||||
updateActiveTabIndex(tabIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return () => {
|
||||
activatePanelSubscription.unsubscribe();
|
||||
};
|
||||
}, [tabs, hasBeenOpened, panelService, updateActiveTabIndex]);
|
||||
|
||||
const getCloseStateComponent = () => {
|
||||
const _childComponents = Array.isArray(tabs) ? tabs : [tabs];
|
||||
return (
|
||||
@@ -112,7 +157,7 @@ const SidePanel = ({
|
||||
side === 'left' ? 'pr-2 justify-end' : 'pl-2 justify-start'
|
||||
)}
|
||||
onClick={() => {
|
||||
setPanelOpen(prev => !prev);
|
||||
updatePanelOpen(prev => !prev);
|
||||
}}
|
||||
data-cy={`side-panel-header-${side}`}
|
||||
>
|
||||
@@ -142,8 +187,7 @@ const SidePanel = ({
|
||||
size="initial"
|
||||
className="text-primary-active"
|
||||
onClick={() => {
|
||||
setActiveTabIndex(index);
|
||||
setPanelOpen(true);
|
||||
updateActiveTabIndex(index);
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
@@ -180,7 +224,7 @@ const SidePanel = ({
|
||||
tabs.length === 1 && 'mb-1'
|
||||
)}
|
||||
onClick={() => {
|
||||
setPanelOpen(prev => !prev);
|
||||
updatePanelOpen(prev => !prev);
|
||||
// slideToActivePanel();
|
||||
}}
|
||||
data-cy={`side-panel-header-${side}`}
|
||||
@@ -215,8 +259,7 @@ const SidePanel = ({
|
||||
nextRef,
|
||||
tabs,
|
||||
activeTabIndex,
|
||||
setActiveTabIndex,
|
||||
setPanelOpen
|
||||
updateActiveTabIndex
|
||||
)}
|
||||
{/** carousel navigation with the arrows */}
|
||||
{/** only show carousel nav if tabs are more than 3 tabs */}
|
||||
@@ -250,6 +293,7 @@ SidePanel.defaultProps = {
|
||||
};
|
||||
|
||||
SidePanel.propTypes = {
|
||||
servicesManager: PropTypes.instanceOf(ServicesManager),
|
||||
side: PropTypes.oneOf(['left', 'right']).isRequired,
|
||||
className: PropTypes.string,
|
||||
activeTabIndex: PropTypes.number,
|
||||
@@ -273,8 +317,7 @@ function _getMoreThanOneTabLayout(
|
||||
nextRef: React.MutableRefObject<undefined>,
|
||||
tabs: any,
|
||||
activeTabIndex: any,
|
||||
setActiveTabIndex: React.Dispatch<any>,
|
||||
setPanelOpen: React.Dispatch<React.SetStateAction<boolean>>
|
||||
updateActiveTabIndex
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
@@ -309,8 +352,7 @@ function _getMoreThanOneTabLayout(
|
||||
)}
|
||||
key={index}
|
||||
onClick={() => {
|
||||
setActiveTabIndex(index);
|
||||
setPanelOpen(true);
|
||||
updateActiveTabIndex(index);
|
||||
}}
|
||||
data-cy={`${obj.name}-btn`}
|
||||
>
|
||||
|
||||
Reference in new issue
Block a user