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:
Joe Boccanfuso authored and GitHub committed 2023-03-09 22:30:09 -05:00
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`}
>