feat(actions): simplify action corner api through the toolbarService (#5033)
This commit is contained in:
1 parent
f71b88e639
commit
7c15bb8901
246 files changed
+7930
-4270
No files matched your search
@@ -0,0 +1,72 @@
|
||||
---
|
||||
sidebar_position: 4
|
||||
sidebar_label: Commands
|
||||
summary: Migration guide for OHIF 3.11's different commands
|
||||
---
|
||||
|
||||
|
||||
|
||||
## updateStoredPositionPresentation
|
||||
|
||||
now uses displaySetInstanceUIDs instead of displaySetInstanceUID as a parameter.
|
||||
|
||||
|
||||
## `loadSRMeasurements` Command
|
||||
|
||||
**Key Changes:**
|
||||
|
||||
* The `loadSRMeasurements` command, previously part of the `@ohif/extension-cornerstone-dicom-sr` extension, has been **removed**.
|
||||
* Its functionality of hydrating a Structured Report (SR) and displaying its referenced series in a viewport is now primarily handled by the new `hydrateSecondaryDisplaySet` command available in the `@ohif/extension-cornerstone` extension.
|
||||
* The `hydrateStructuredReport` command (from `@ohif/extension-cornerstone-dicom-sr`) now solely focuses on hydrating the SR and returning its data, without directly manipulating viewports.
|
||||
|
||||
**Migration Steps:**
|
||||
|
||||
If you were previously using the `loadSRMeasurements` command to load and display SR measurements, you should update your code to use the `hydrateSecondaryDisplaySet` command.
|
||||
|
||||
1. **Identify `loadSRMeasurements` Usage:**
|
||||
Locate where your code calls `commandsManager.runCommand('loadSRMeasurements', ...)`.
|
||||
|
||||
2. **Update to `hydrateSecondaryDisplaySet`:**
|
||||
Replace the call to `loadSRMeasurements` with `hydrateSecondaryDisplaySet`. You will need to pass the full `displaySet` object for the SR and the target `viewportId`.
|
||||
|
||||
```diff
|
||||
- // Old way: using loadSRMeasurements
|
||||
- commandsManager.runCommand('loadSRMeasurements', {
|
||||
- displaySetInstanceUID: srDisplaySetInstanceUID,
|
||||
- // viewportId was implicitly the active one or not directly specifiable here
|
||||
- });
|
||||
-
|
||||
+ // New way: using hydrateSecondaryDisplaySet
|
||||
+ const { displaySetService, viewportGridService } = servicesManager.services;
|
||||
+
|
||||
+ // 1. Get the SR displaySet object
|
||||
+ const srDisplaySet = displaySetService.getDisplaySetByUID(srDisplaySetInstanceUID);
|
||||
+
|
||||
+ // 2. Determine the target viewportId (e.g., active viewport)
|
||||
+ const viewportId = viewportGridService.getActiveViewportId(); // Or your specific viewportId
|
||||
+
|
||||
+ if (srDisplaySet && viewportId) {
|
||||
+ commandsManager.runCommand('hydrateSecondaryDisplaySet', {
|
||||
+ displaySet: srDisplaySet,
|
||||
+ viewportId: viewportId,
|
||||
+ });
|
||||
+ } else {
|
||||
+ console.warn('SR DisplaySet or ViewportId not found, cannot hydrate.');
|
||||
+ }
|
||||
```
|
||||
|
||||
**Explanation:**
|
||||
|
||||
* The `loadSRMeasurements` command was responsible for both hydrating the SR (getting its measurement data and referenced series UIDs) and then updating the viewport to show the referenced series.
|
||||
* The new `hydrateSecondaryDisplaySet` command, when given an SR `displaySet` (`displaySet.Modality === 'SR'`), will:
|
||||
1. Internally call the `hydrateStructuredReport` command to parse the SR and get its details (including `SeriesInstanceUIDs` of referenced images).
|
||||
2. Then, it will automatically find the corresponding image display sets for those `SeriesInstanceUIDs`.
|
||||
3. Finally, it will update the specified `viewportId` to display the primary referenced image series.
|
||||
* This change centralizes the logic for hydrating secondary display sets (like SR, SEG, RTSTRUCT) and updating viewports into the `hydrateSecondaryDisplaySet` command within the core Cornerstone extension.
|
||||
|
||||
**Note on UI/Button Changes:**
|
||||
The UI button typically associated with "Load SR" (often seen in viewport corners or specific contexts) has also been refactored. The hydration of SRs is now often triggered by:
|
||||
* The `TrackedMeasurementsContext` if the `@ohif/extension-measurement-tracking` is in use.
|
||||
* The new `ModalityLoadBadge` component, which can appear in viewports containing SR, SEG, or RTSTRUCT display sets, offering a "LOAD" action that calls `hydrateSecondaryDisplaySet`.
|
||||
|
||||
If you had custom UI invoking `loadSRMeasurements`, you'll need to adapt it to call `hydrateSecondaryDisplaySet` as described above.
|
||||
@@ -0,0 +1,85 @@
|
||||
---
|
||||
title: Hydration
|
||||
sidebar_position: 3
|
||||
sidebar_label: Hydration
|
||||
summary: Migration guide for OHIF 3.11's hydration system changes, including the transition to a centralized hydration dialog and command-based hydration for secondary display sets.
|
||||
---
|
||||
|
||||
## Update Hydration Logic:
|
||||
* The `promptHydrateSEG` and `promptHydrateRT` functions have been updated to use the generic `utils.promptHydrationDialog` from `@ohif/extension-cornerstone`.
|
||||
* The actual hydration is now often triggered by the `hydrateSecondaryDisplaySet` command.
|
||||
|
||||
*Example: `promptHydrateRT` change*
|
||||
|
||||
```diff
|
||||
// extensions/cornerstone-dicom-rt/src/utils/promptHydrateRT.ts
|
||||
- const RESPONSE = {
|
||||
- NO_NEVER: -1,
|
||||
- CANCEL: 0,
|
||||
- HYDRATE_SEG: 5,
|
||||
- };
|
||||
+ import { utils, Types } from '@ohif/extension-cornerstone';
|
||||
|
||||
function promptHydrateRT({
|
||||
servicesManager,
|
||||
rtDisplaySet,
|
||||
viewportId,
|
||||
preHydrateCallbacks,
|
||||
hydrateRTDisplaySet,
|
||||
-}: withAppTypes) {
|
||||
- const { uiViewportDialogService, customizationService } = servicesManager.services;
|
||||
- // ... lots of old promise and dialog logic
|
||||
- return new Promise(async function (resolve, reject) {
|
||||
- // ...
|
||||
- });
|
||||
-}
|
||||
-
|
||||
-function _askHydrate(
|
||||
- // ...
|
||||
-) {
|
||||
- // ...
|
||||
-}
|
||||
+}: {
|
||||
+ servicesManager: AppTypes.ServicesManager;
|
||||
+ rtDisplaySet: AppTypes.DisplaySet;
|
||||
+ viewportId: string;
|
||||
+ preHydrateCallbacks?: Types.HydrationCallback[];
|
||||
+ hydrateRTDisplaySet: Types.HydrationCallback;
|
||||
+}) {
|
||||
+ return utils.promptHydrationDialog({
|
||||
+ servicesManager,
|
||||
+ viewportId,
|
||||
+ displaySet: rtDisplaySet,
|
||||
+ preHydrateCallbacks,
|
||||
+ hydrateCallback: hydrateRTDisplaySet,
|
||||
+ type: 'RTSTRUCT',
|
||||
+ });
|
||||
}
|
||||
```
|
||||
The `hydrateRTDisplaySet` callback passed to this function would now typically involve the `hydrateSecondaryDisplaySet` command.
|
||||
```diff
|
||||
// extensions/cornerstone-dicom-rt/src/viewports/OHIFCornerstoneRTViewport.tsx
|
||||
useEffect(() => {
|
||||
if (rtIsLoading) {
|
||||
return;
|
||||
}
|
||||
promptHydrateRT({
|
||||
servicesManager,
|
||||
viewportId,
|
||||
rtDisplaySet,
|
||||
- preHydrateCallbacks: [storePresentationState],
|
||||
- hydrateRTDisplaySet,
|
||||
- }).then(isHydrated => {
|
||||
- if (isHydrated) {
|
||||
- setIsHydrated(true);
|
||||
- }
|
||||
+ hydrateRTDisplaySet: async () => {
|
||||
+ return commandsManager.runCommand('hydrateSecondaryDisplaySet', {
|
||||
+ displaySet: rtDisplaySet,
|
||||
+ viewportId,
|
||||
+ });
|
||||
+ },
|
||||
});
|
||||
- }, [servicesManager, viewportId, rtDisplaySet, rtIsLoading]);
|
||||
+ }, [servicesManager, viewportId, rtDisplaySet, rtIsLoading, commandsManager]);
|
||||
```
|
||||
@@ -6,17 +6,3 @@ sidebar_label: 3.10 -> 3.11 beta
|
||||
# Migration Guide
|
||||
|
||||
This guide provides information about migrating from OHIF version 3.10 to version 3.11.
|
||||
|
||||
## General
|
||||
|
||||
`viewportActionMenu.segmentationOverlay` is renamed to `viewportActionMenu.dataOverlay`
|
||||
as it handles now both segmentation and data overlay.
|
||||
|
||||
## Viewport Action Menu Customization
|
||||
|
||||
The structure for defining viewport action menu customizations has changed. See the [Viewport Action Menu](./viewport-action-menu.md) migration guide for details.
|
||||
|
||||
|
||||
## updateStoredPositionPresentation
|
||||
|
||||
now uses displaySetInstanceUIDs instead of displaySetInstanceUID.
|
||||
@@ -0,0 +1,153 @@
|
||||
---
|
||||
sidebar_position: 2
|
||||
sidebar_label: Toolbar Service
|
||||
summary: Migration guide for OHIF 3.11's toolbar service changes, including the transition from `ViewportActionCornersService` to `ToolbarService`
|
||||
---
|
||||
|
||||
|
||||
**Key Changes:**
|
||||
|
||||
* **`ViewportActionCornersService` Removed:** The `ViewportActionCornersService` and its associated provider (`ViewportActionCornersProvider`) and hook (`useViewportActionCorners`) have been removed. Functionality for viewport corner items is now integrated into the `ToolbarService` and standard toolbar components.
|
||||
* **Viewport Corner Items as Toolbar Buttons:** Items previously managed by `ViewportActionCornersService` are now configured as regular toolbar buttons. They are assigned to specific toolbar sections (e.g., `viewportActionMenu.topLeft`) and rendered using `Toolbar` components within the viewport corners.
|
||||
* **`ToolbarService` API Updates:**
|
||||
* `ToolbarService.addButtons()` has been renamed to `ToolbarService.register()` to better reflect its purpose of registering button definitions rather than just adding them.
|
||||
* `ToolbarService.createButtonSection()` has been renamed to `ToolbarService.updateSection()` to better reflect that it is not about creating new sections but updating existing ones.
|
||||
* `ToolbarService` now has a `sections` property (e.g., `toolbarService.sections.viewportActionMenu.topLeft`) providing predefined section names.
|
||||
* **Enhanced `useToolbar` Hook:**
|
||||
* The `useToolbar` hook now returns additional state management functions for toolbar items:
|
||||
* `openItem`, `closeItem`, `isItemOpen` (for managing dropdown/popover states).
|
||||
* `lockItem`, `unlockItem`, `toggleLock`, `isItemLocked`.
|
||||
* `showItem`, `hideItem`, `toggleVisibility`, `isItemVisible`.
|
||||
* The `onInteraction` callback now receives `itemId` and `viewportId`.
|
||||
* **Toolbar Button Configuration:**
|
||||
* The `groupId` prop in button configurations (e.g., for `ohif.toolButtonList`, `ohif.toolBoxButtonGroup`) is generally replaced by directly using `buttonSection` to define the set of buttons.
|
||||
* Button `evaluate` functions can now leverage `evaluateProps.hideWhenDisabled` to automatically hide a button when it's disabled.
|
||||
* **New UI Components & Hooks for Viewport Corners:**
|
||||
* Specialized components like `ModalityLoadBadge`, `NavigationComponent`, `TrackingStatus`, `ViewportDataOverlayMenuWrapper`, `ViewportOrientationMenuWrapper`, `WindowLevelActionMenuWrapper` are now used as toolbar buttons, typically in viewport action menu sections.
|
||||
* `useViewportHover` hook can be used to determine if a viewport is hovered or active, controlling the visibility of corner toolbars.
|
||||
* **`IconPresentationProvider`:** A new `IconPresentationProvider` and `useIconPresentation` hook have been introduced to standardize icon sizing and styling within toolbars and related components.
|
||||
* **Legacy Toolbar Components Removed:** `ToolbarSplitButtonWithServicesLegacy` and `ToolbarButtonGroupWithServicesLegacy` have been removed.
|
||||
|
||||
**Migration Steps:**
|
||||
|
||||
1. **Update `ToolbarService` Method Calls:**
|
||||
* Replace all instances of `toolbarService.addButtons(...)` with `toolbarService.register(...)`.
|
||||
* Replace all instances of `toolbarService.createButtonSection(...)` with `toolbarService.updateSection(...)`.
|
||||
|
||||
```diff
|
||||
// Before
|
||||
- toolbarService.addButtons(toolbarButtons);
|
||||
- toolbarService.createButtonSection('primary', ['Zoom', 'Pan']);
|
||||
|
||||
// After
|
||||
+ toolbarService.register(toolbarButtons);
|
||||
+ toolbarService.updateSection('primary', ['Zoom', 'Pan']);
|
||||
```
|
||||
|
||||
2. **Migrate Viewport Action Corner Items:**
|
||||
* Remove any direct usage of the old `ViewportActionCornersService`, `useViewportActionCorners`, or `ViewportActionCornersProvider`.
|
||||
* Define your viewport corner items (like orientation menu, W/L menu, data overlay menu) as standard toolbar buttons using `toolbarService.register()`.
|
||||
* Assign these buttons to the new dedicated viewport action menu sections. You can access these section names via `toolbarService.sections.viewportActionMenu.<location>`, e.g., `toolbarService.sections.viewportActionMenu.topLeft`.
|
||||
|
||||
```diff
|
||||
// Before: Customization in viewportActionMenuCustomizations.ts (now deleted)
|
||||
// or direct use of ViewportActionCornersService.addComponent
|
||||
- // Example: viewportActionCornersService.addComponent({ viewportId, id: 'orientationMenu', component: MyOrientationMenu, location: 'topLeft' });
|
||||
|
||||
// After: In your mode's onModeEnter or similar setup
|
||||
+ const myViewportCornerButtons = [
|
||||
+ {
|
||||
+ id: 'orientationMenu',
|
||||
+ uiType: 'ohif.orientationMenu', // Or your custom component registered as a UI type
|
||||
+ props: { /* ... props for your component ... */ }
|
||||
+ },
|
||||
+ // ... other corner buttons
|
||||
+ ];
|
||||
+ toolbarService.register(myViewportCornerButtons);
|
||||
+ toolbarService.updateSection(
|
||||
+ toolbarService.sections.viewportActionMenu.topLeft,
|
||||
+ ['orientationMenu', /* other button IDs */]
|
||||
+ );
|
||||
```
|
||||
* The `OHIFViewportActionCorners.tsx` component now internally uses `Toolbar` components for each corner, which are populated by these sections.
|
||||
* For custom components that act as menus (e.g., popovers), use the `onOpen`, `onClose`, `isOpen` props passed down by the `Toolbar` component (which get these from `useToolbar`).
|
||||
|
||||
```diff
|
||||
// Before: Custom component might have managed its own open state
|
||||
- // const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
- // const handleOpenChange = (open) => setIsMenuOpen(open);
|
||||
|
||||
// After: Custom component receives isOpen, onOpen, onClose from Toolbar
|
||||
+ function MyCustomMenuButton({ isOpen, onOpen, onClose, ...rest }) {
|
||||
+ const handleOpenChange = (openState: boolean) => {
|
||||
+ if (openState) {
|
||||
+ onOpen?.();
|
||||
+ } else {
|
||||
+ onClose?.();
|
||||
+ }
|
||||
+ };
|
||||
+
|
||||
+ return (
|
||||
+ <Popover open={isOpen} onOpenChange={handleOpenChange}>
|
||||
+ {/* ... PopoverTrigger and PopoverContent ... */}
|
||||
+ </Popover>
|
||||
+ );
|
||||
+ }
|
||||
```
|
||||
|
||||
3. **Adapt Toolbar Button and Component Configurations:**
|
||||
* For `ohif.toolButtonList` or `ohif.toolBoxButtonGroup` (and their wrappers), the `groupId` prop is no longer the primary way to define the set of buttons. Instead, ensure the `buttonSection` prop correctly points to the section name containing the desired buttons. The `id` prop on these wrapper components should be unique for the component instance.
|
||||
|
||||
```diff
|
||||
// Before
|
||||
- {
|
||||
- id: 'MeasurementTools',
|
||||
- uiType: 'ohif.toolButtonList',
|
||||
- props: {
|
||||
- buttonSection: 'measurementSection',
|
||||
- groupId: 'MeasurementTools', // groupId often matched buttonSection
|
||||
- },
|
||||
- },
|
||||
|
||||
// After
|
||||
+ {
|
||||
+ id: 'MeasurementTools', // This is the ID of the ToolButtonList/ToolBox component itself
|
||||
+ uiType: 'ohif.toolButtonList',
|
||||
+ props: {
|
||||
+ // This section contains the actual tool buttons (e.g., Length, Bidirectional)
|
||||
+ buttonSection: 'measurementSection',
|
||||
+ },
|
||||
+ },
|
||||
```
|
||||
* Update wrappers like `ToolBoxButtonGroupWrapper` and `ToolButtonListWrapper`:
|
||||
* The `groupId` prop is replaced by `id` (which is the ID of the wrapper button itself).
|
||||
* The `onInteraction` callback in these wrappers now provides `id` (the wrapper's ID) instead of `groupId`.
|
||||
* If you have custom `evaluate` functions, you can now use `evaluateProps: { hideWhenDisabled: true }` in your button definition to automatically hide the button if it evaluates to disabled.
|
||||
|
||||
|
||||
5. **Adopt `IconPresentationProvider` (Optional but Recommended):**
|
||||
* For consistent icon styling across your application's toolbars, wrap a high-level component (like your main `Header` or layout component) with `<IconPresentationProvider size="yourDefaultSize">`.
|
||||
* Custom tool button components can then use the `useIconPresentation` hook to get appropriate class names for icons or a pre-styled `IconContainer`.
|
||||
|
||||
```diff
|
||||
// In your main App.tsx or Header.tsx
|
||||
+ import { IconPresentationProvider, ToolButton } from '@ohif/ui-next';
|
||||
// ...
|
||||
+ <IconPresentationProvider
|
||||
+ size="large" // Or "medium", "small", "tiny", or a number
|
||||
+ IconContainer={ToolButton} // Optional: default is Button
|
||||
+ containerProps={{ variant: 'primary', className: 'custom-container-class' }} // Optional
|
||||
+ >
|
||||
{/* Your Header content including Toolbars */}
|
||||
+ </IconPresentationProvider>
|
||||
|
||||
// In a custom tool button using icons
|
||||
+ import { useIconPresentation, Icons } from '@ohif/ui-next';
|
||||
+ function MyCustomToolButton({ iconName }) {
|
||||
+ const { className: iconClassName } = useIconPresentation();
|
||||
+ return <button><Icons.ByName name={iconName} className={iconClassName} /></button>;
|
||||
+ }
|
||||
```
|
||||
|
||||
6. **Remove Legacy Component Usage:**
|
||||
* Replace any usage of `ToolbarSplitButtonWithServicesLegacy` and `ToolbarButtonGroupWithServicesLegacy` with the newer patterns, typically by configuring individual buttons and using `ToolButtonList` or `ButtonGroup` from `@ohif/ui-next` directly, driven by `useToolbar`.
|
||||
@@ -0,0 +1,60 @@
|
||||
---
|
||||
sidebar_position: 5
|
||||
sidebar_label: UI
|
||||
summary: Migration guide for OHIF 3.11's UI changes, including the transition from `ViewportActionCornersService` to `ToolbarService` and the introduction of `useToolbar` hook.
|
||||
---
|
||||
|
||||
|
||||
## New useIconPresentation hook
|
||||
|
||||
This section details the introduction of the `IconPresentationProvider` and `useIconPresentation` hook, offering an optional way to manage icon size and container styling within the UI.
|
||||
|
||||
### Key Changes:
|
||||
|
||||
* **New Context and Hook:** Introduction of `IconPresentationProvider` and `useIconPresentation` to provide a standardized way to control the size and potentially the container component/props for icons and related interactive elements (like `ToolButton`).
|
||||
|
||||
### Migration Steps:
|
||||
|
||||
Using the `IconPresentationProvider` is **entirely optional**. If you do not wrap your components with this provider, components like `ToolButton` will continue to use their explicit `size` prop and default styling.
|
||||
|
||||
However, if you wish to centrally manage the presentation of icons within a specific part of your application's component tree, follow these steps:
|
||||
|
||||
1. **Identify the component subtree:** Determine which section of your UI you want to apply consistent icon styling to.
|
||||
|
||||
2. **Wrap with `IconPresentationProvider`:** Wrap the root of that component subtree with the `IconPresentationProvider`. Pass the desired `size` prop. You can also optionally provide a custom `IconContainer` component and `containerProps` if you want to change the wrapper around the icon itself (e.g., switching from a `Button` to a `ToolButton` or applying specific styling).
|
||||
|
||||
```jsx
|
||||
import { IconPresentationProvider, ToolButton } from '@ohif/ui-next';
|
||||
|
||||
function MyComponentTree() {
|
||||
return (
|
||||
// Icons and ToolButtons within this provider will inherit 'large' size
|
||||
<IconPresentationProvider size="large">
|
||||
{/* Any components inside that consume the context */}
|
||||
<ToolButton id="myTool" icon="Circle" tooltip="Draw Circle" />
|
||||
{/* ... other components ... */}
|
||||
</IconPresentationProvider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
3. **Consume the context in components (if building custom components):** If you are building a custom component that renders an icon and want it to respect the provider's settings, use the `useIconPresentation` hook within that component. This hook provides the configured size, a calculated CSS class name (`className`), the specified `IconContainer` component, and its `containerProps`.
|
||||
|
||||
```jsx
|
||||
import React from 'react';
|
||||
import { useIconPresentation, Icons } from '@ohif/ui-next';
|
||||
|
||||
function MyCustomIconButton({ iconName, ...rest }) {
|
||||
// This hook reads the nearest IconPresentationProvider context
|
||||
const { className, IconContainer, containerProps } = useIconPresentation();
|
||||
|
||||
// Use the provided IconContainer and its props
|
||||
return (
|
||||
<IconContainer {...rest} {...containerProps}>
|
||||
{/* Use the calculated className for the icon */}
|
||||
<Icons.ByName name={iconName} className={className} />
|
||||
</IconContainer>
|
||||
);
|
||||
}
|
||||
```
|
||||
*Note: Built-in components like `ToolButton` in `@ohif/ui-next` have been updated internally to consume this context automatically if a provider is available.*
|
||||
@@ -1,102 +1,124 @@
|
||||
---
|
||||
sidebar_position: 2
|
||||
sidebar_label: Viewport Action Menu
|
||||
summary: Migration guide for OHIF 3.11's viewport action menu customization changes, including the transition from individual item configurations to location-based arrays and the removal of index priorities.
|
||||
sidebar_position: 1
|
||||
sidebar_label: Viewport Corners
|
||||
summary: Migration guide for OHIF 3.11's viewport corners customization changes, including the transition from individual item configurations to location-based arrays and the removal of index priorities.
|
||||
---
|
||||
|
||||
# Viewport Action Menu Customization
|
||||
|
||||
In OHIF 3.11, we've redesigned how viewport action menu customizations are defined to make them more intuitive and organized by location.
|
||||
Okay, here's a migration guide based on the provided diff, focusing on the introduction of `TrackingStatus`, `ModalityLoadBadge`, and `NavigationComponent`.
|
||||
|
||||
## Changes
|
||||
**Key Changes:**
|
||||
|
||||
Previously, viewport action menu customizations were defined with individual item configurations that specified location and priority:
|
||||
* **Deprecated `ViewportActionCornersService`**: The `ViewportActionCornersService` and its associated provider (`ViewportActionCornersProvider`) have been removed. UI elements previously managed by this service are now typically handled by dedicated components integrated via the `ToolbarService`.
|
||||
* **New Centralized UI Components**:
|
||||
* `ModalityLoadBadge`: A new component in `@ohif/extension-cornerstone` that displays the status (e.g., SEG/RT/SR loaded or requiring hydration) and a "LOAD" button for secondary display sets (SEG, RTSTRUCT, SR). This replaces the inline status and load logic within individual viewport components like `OHIFCornerstoneSEGViewport` and `OHIFCornerstoneRTViewport`.
|
||||
* `TrackingStatus`: A new component in `@ohif/extension-cornerstone` to indicate if measurements in a viewport are being tracked. This replaces inline tracking status indicators previously in `OHIFCornerstoneSRMeasurementViewport` and `TrackedCornerstoneViewport`.
|
||||
* `NavigationComponent`: A new component in `@ohif/extension-cornerstone` that provides navigation arrows (e.g., for segments in SEG/RT or measurements in SR/tracked series). This replaces the `ViewportActionArrows` previously instantiated directly within viewport components.
|
||||
* **Viewport Simplification**: Viewport components like `OHIFCornerstoneSEGViewport`, `OHIFCornerstoneRTViewport`, and `OHIFCornerstoneSRMeasurementViewport` have been simplified. They no longer manage their own status indicators, load buttons, or navigation arrows. They now primarily delegate rendering to `OHIFCornerstoneViewport`.
|
||||
* **Refactored Hydration Prompts**: Utility functions like `promptHydrateSEG` and `promptHydrateRT` now use a centralized `utils.promptHydrationDialog` from `@ohif/extension-cornerstone`.
|
||||
* **Centralized Hydration Command**: A new command `hydrateSecondaryDisplaySet` has been added to `@ohif/extension-cornerstone` to handle the hydration logic for SEG, RTSTRUCT, and SR display sets.
|
||||
* **New Hooks**: Several new hooks have been introduced in `@ohif/extension-cornerstone` (e.g., `useViewportDisplaySets`, `useMeasurementTracking`, `useViewportSegmentations`, `useViewportHover`) to provide data and state for these new UI components.
|
||||
|
||||
```ts
|
||||
export default {
|
||||
'viewportActionMenu.orientationMenu': {
|
||||
enabled: true,
|
||||
location: viewportActionCornersService.LOCATIONS.topLeft,
|
||||
indexPriority: 1,
|
||||
},
|
||||
'viewportActionMenu.dataOverlay': {
|
||||
enabled: true,
|
||||
location: viewportActionCornersService.LOCATIONS.topLeft,
|
||||
indexPriority: 2,
|
||||
},
|
||||
// ...
|
||||
};
|
||||
```
|
||||
**Migration Steps:**
|
||||
|
||||
Now, viewport action menu customizations are organized by location, with each location having its own customization ID:
|
||||
1. **Remove `ViewportActionCornersService` Usage**:
|
||||
* If you were using `ViewportActionCornersService` to add custom components to viewport corners, you will need to refactor this. The recommended approach is to define these components as toolbar buttons and place them in designated viewport action menu sections (e.g., `viewportActionMenu.topLeft`) using the `ToolbarService`.
|
||||
* The internal status components (`_getStatusComponent`) and `ViewportActionArrows` within specific viewports (SEG, RT, SR) have been removed. Their functionality is now provided by `ModalityLoadBadge`, `TrackingStatus`, and `NavigationComponent`.
|
||||
|
||||
```ts
|
||||
export default {
|
||||
'viewportActionMenu.topLeft': [
|
||||
{
|
||||
id: 'orientationMenu',
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
id: 'dataOverlay',
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
id: 'windowLevelActionMenu',
|
||||
enabled: true,
|
||||
},
|
||||
],
|
||||
'viewportActionMenu.topRight': [],
|
||||
'viewportActionMenu.bottomLeft': [],
|
||||
'viewportActionMenu.bottomRight': [],
|
||||
};
|
||||
```
|
||||
|
||||
## Migration Steps
|
||||
3. **Integrate New UI Components via `ToolbarService`**:
|
||||
* The `ModalityLoadBadge`, `TrackingStatus`, and `NavigationComponent` are now registered with the `ToolbarService` within the `@ohif/extension-cornerstone`'s `getToolbarModule`.
|
||||
* Modes (e.g., `longitudinal`) should define toolbar sections for viewport corners and add these components to those sections.
|
||||
|
||||
1. Reorganize your viewport action menu customizations by using location-based customization IDs (`viewportActionMenu.topLeft`, `viewportActionMenu.topRight`, etc.).
|
||||
2. For each component, move it into the appropriate location array.
|
||||
3. Replace the component key with an `id` property that doesn't include the prefix (just use `orientationMenu` instead of `viewportActionMenu.orientationMenu`).
|
||||
4. Remove the `location` property (since it's now implied by the customization ID).
|
||||
5. Remove the `indexPriority` property (order in the array now determines display order).
|
||||
6. For each component, provide a `component` function that returns the component instance.
|
||||
*Example: Adding components to viewport corners in `longitudinal` mode*
|
||||
```diff
|
||||
// modes/longitudinal/src/index.ts
|
||||
function modeFactory({ modeConfiguration }) {
|
||||
return {
|
||||
// ...
|
||||
onModeEnter: ({ servicesManager, extensionManager, commandsManager }: withAppTypes) => {
|
||||
// ...
|
||||
toolbarService.addButtons(toolbarButtons);
|
||||
toolbarService.createButtonSection('primary', [
|
||||
// ... primary tools
|
||||
]);
|
||||
|
||||
## Component Rendering
|
||||
+ toolbarService.updateSection(toolbarService.sections.viewportActionMenu.topLeft, [
|
||||
+ 'orientationMenu',
|
||||
+ 'dataOverlayMenu',
|
||||
+ 'windowLevelMenu',
|
||||
+ ]);
|
||||
+ toolbarService.updateSection(toolbarService.sections.viewportActionMenu.topRight, [
|
||||
+ 'modalityLoadBadge',
|
||||
+ 'trackingStatus',
|
||||
+ 'navigationComponent',
|
||||
+ ]);
|
||||
// ...
|
||||
},
|
||||
```
|
||||
And ensure these buttons are defined in your mode's `toolbarButtons.ts`:
|
||||
```diff
|
||||
// modes/longitudinal/src/toolbarButtons.ts
|
||||
+ {
|
||||
+ id: 'modalityLoadBadge',
|
||||
+ uiType: 'ohif.modalityLoadBadge',
|
||||
+ props: {
|
||||
+ // ... props like icon, label, tooltip, evaluate
|
||||
+ evaluate: {
|
||||
+ name: 'evaluate.modalityLoadBadge',
|
||||
+ hideWhenDisabled: true,
|
||||
+ },
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ id: 'navigationComponent',
|
||||
+ uiType: 'ohif.navigationComponent',
|
||||
+ props: {
|
||||
+ // ... props
|
||||
+ evaluate: {
|
||||
+ name: 'evaluate.navigationComponent',
|
||||
+ hideWhenDisabled: true,
|
||||
+ },
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ id: 'trackingStatus',
|
||||
+ uiType: 'ohif.trackingStatus',
|
||||
+ props: {
|
||||
+ // ... props
|
||||
+ evaluate: {
|
||||
+ name: 'evaluate.trackingStatus',
|
||||
+ hideWhenDisabled: true,
|
||||
+ },
|
||||
+ },
|
||||
+ },
|
||||
```
|
||||
|
||||
Component rendering logic is now included directly in the item configuration via a `component` function:
|
||||
|
||||
```ts
|
||||
const createOrientationMenu = ({ viewportId, element, location }) => {
|
||||
return getViewportOrientationMenu({
|
||||
viewportId,
|
||||
element,
|
||||
location,
|
||||
});
|
||||
};
|
||||
5. **Direct Import of `OHIFCornerstoneViewport`**:
|
||||
* Extensions that were previously getting the cornerstone viewport component dynamically via `extensionManager.getModuleEntry('@ohif/extension-cornerstone.viewportModule.cornerstone')` should now import `OHIFCornerstoneViewport` directly from `@ohif/extension-cornerstone`.
|
||||
|
||||
const createDataOverlay = ({ viewportId, element, displaySets, location }) => {
|
||||
return getViewportDataOverlaySettingsMenu({
|
||||
viewportId,
|
||||
element,
|
||||
displaySets,
|
||||
location,
|
||||
});
|
||||
};
|
||||
```diff
|
||||
// extensions/cornerstone-dicom-pmap/src/viewports/OHIFCornerstonePMAPViewport.tsx
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useViewportGrid } from '@ohif/ui-next';
|
||||
+import { OHIFCornerstoneViewport } from '@ohif/extension-cornerstone';
|
||||
|
||||
export default {
|
||||
'viewportActionMenu.topLeft': [
|
||||
{
|
||||
id: 'orientationMenu',
|
||||
enabled: true,
|
||||
component: createOrientationMenu,
|
||||
},
|
||||
{
|
||||
id: 'dataOverlay',
|
||||
enabled: true,
|
||||
component: createDataOverlay,
|
||||
},
|
||||
// other components...
|
||||
],
|
||||
// other locations...
|
||||
};
|
||||
```
|
||||
function OHIFCornerstonePMAPViewport(props: withAppTypes) {
|
||||
// ...
|
||||
const getCornerstoneViewport = useCallback(() => {
|
||||
- const { component: Component } = extensionManager.getModuleEntry(
|
||||
- '@ohif/extension-cornerstone.viewportModule.cornerstone'
|
||||
- );
|
||||
// ...
|
||||
return (
|
||||
- <Component
|
||||
+ <OHIFCornerstoneViewport
|
||||
{...props}
|
||||
// ...
|
||||
- ></Component>
|
||||
+ />
|
||||
);
|
||||
// ...
|
||||
```
|
||||
@@ -0,0 +1,33 @@
|
||||
---
|
||||
title: Hooks
|
||||
summary: List of React hooks available in the platform, these are custom hooks that are used to access the state of the platform
|
||||
---
|
||||
|
||||
# Hooks
|
||||
|
||||
## [useMeasurements](./useMeasurements.md)
|
||||
A React hook that provides mapped measurements from the measurement service with automatic updates when measurements change.
|
||||
|
||||
## [useViewportSegmentations](./useViewportSegmentations.md)
|
||||
A React hook that provides segmentation data and representations for the active viewport with automatic updates when segmentations change.
|
||||
|
||||
## [useMeasurementTracking](./useMeasurementTracking.md)
|
||||
A React hook that provides measurement tracking information for a specific viewport, including tracking state and tracked measurement UIDs.
|
||||
|
||||
## [useViewportDisplaySets](./useViewportDisplaySets.md)
|
||||
A React hook that provides access to display sets associated with a viewport, including background, foreground, overlay, and potential display sets.
|
||||
|
||||
## [useViewportHover](./useViewportHover.md)
|
||||
A React hook that tracks mouse hover state and active status for a specific viewport.
|
||||
|
||||
## [usePatientInfo](./usePatientInfo.md)
|
||||
A React hook that provides patient information from the active display sets and detects when multiple patients are loaded.
|
||||
|
||||
## [useSearchParams](./useSearchParams.md)
|
||||
A React hook that provides access to URL search parameters from both the query string and hash fragment.
|
||||
|
||||
## [useDynamicMaxHeight](./useDynamicMaxHeight.md)
|
||||
A React hook that calculates the maximum height for an element based on its position in the viewport, with automatic recalculation on window resize or data changes.
|
||||
|
||||
## [useSessionStorage](./useSessionStorage.md)
|
||||
A React hook that provides sessionStorage access with automatic JSON parsing/stringifying and an option to clear data when the page unloads.
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
title: useDynamicMaxHeight
|
||||
summary: A React hook that calculates the maximum height for an element based on its position in the viewport, with automatic recalculation on window resize or data changes.
|
||||
---
|
||||
|
||||
# useDynamicMaxHeight
|
||||
|
||||
The `useDynamicMaxHeight` hook calculates the maximum height an element can have based on its position relative to the bottom of the viewport, ensuring it doesn't overflow or get cut off by the viewport edge.
|
||||
|
||||
## Overview
|
||||
|
||||
This hook is useful for creating responsive UI elements that need to fit within the visible area of the screen without causing scrolling or content overflow. It automatically recalculates the maximum height when the window is resized or when specified data changes, ensuring the element always fits properly.
|
||||
|
||||
## Import
|
||||
|
||||
```js
|
||||
import { useDynamicMaxHeight } from '@ohif/ui-next';
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```jsx
|
||||
function DynamicHeightPanel({ data, children }) {
|
||||
const { ref, maxHeight } = useDynamicMaxHeight(data, 30, 200);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
style={{
|
||||
maxHeight,
|
||||
overflow: 'auto',
|
||||
border: '1px solid gray'
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
- `data` (required): Any data that, when changed, should trigger a recalculation of the maximum height. This can be an array, object, or primitive value.
|
||||
- `buffer` (optional): Additional space (in pixels) to leave below the element. Defaults to 20px.
|
||||
- `minHeight` (optional): Minimum height (in pixels) for the element. Defaults to 100px.
|
||||
|
||||
## Returns
|
||||
|
||||
An object containing:
|
||||
|
||||
- `ref`: A React ref object that must be attached to the target DOM element
|
||||
- `maxHeight`: A CSS-compatible string value for the calculated maximum height (e.g., "500px")
|
||||
|
||||
## Implementation Details
|
||||
|
||||
- The hook uses `window.innerHeight` and the element's position to calculate the available space between the element's top edge and the bottom of the viewport.
|
||||
- It subtracts the specified buffer from the available height to ensure there's space below the element.
|
||||
- The calculated height is constrained by the specified minimum height to prevent the element from becoming too small.
|
||||
- The hook uses `requestAnimationFrame` to ensure the initial calculation happens after the component has been rendered and positioned in the DOM.
|
||||
- It automatically recalculates the maximum height when:
|
||||
- The window is resized
|
||||
- The `data`, `buffer`, or `minHeight` dependencies change
|
||||
- The hook properly cleans up event listeners and animation frame requests when the component unmounts.
|
||||
|
||||
This hook is particularly useful for panels, menus, and content areas that need to dynamically adjust their height based on their position in the viewport, ensuring a good user experience without unexpected scrolling or content clipping.
|
||||
@@ -0,0 +1,79 @@
|
||||
---
|
||||
title: useMeasurementTracking
|
||||
summary: A React hook that provides measurement tracking information for a specific viewport, including tracking state and tracked measurement UIDs.
|
||||
---
|
||||
|
||||
# useMeasurementTracking
|
||||
|
||||
The `useMeasurementTracking` hook provides measurement tracking information for a specific viewport, including the tracking state and UIDs of tracked measurements associated with the viewport's series.
|
||||
|
||||
## Overview
|
||||
|
||||
This hook gives components access to tracking information for measurements in a viewport. It monitors the tracked measurements service and measurement service to provide up-to-date tracking states and the list of measurement UIDs associated with the series displayed in the viewport.
|
||||
|
||||
## Import
|
||||
|
||||
```js
|
||||
import { useMeasurementTracking } from '@ohif/extension-cornerstone';
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```jsx
|
||||
function MeasurementTrackingInfo({ viewportId }) {
|
||||
const {
|
||||
isTracked,
|
||||
isLocked,
|
||||
seriesInstanceUID,
|
||||
trackedMeasurementUIDs
|
||||
} = useMeasurementTracking({
|
||||
viewportId,
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>Series UID: {seriesInstanceUID}</div>
|
||||
<div>Tracking Status: {isTracked ? 'Tracked' : 'Not Tracked'}</div>
|
||||
<div>Locked: {isLocked ? 'Yes' : 'No'}</div>
|
||||
<div>Tracked Measurements: {trackedMeasurementUIDs.length}</div>
|
||||
<ul>
|
||||
{trackedMeasurementUIDs.map(uid => (
|
||||
<li key={uid}>{uid}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
- `options` - Configuration options:
|
||||
- `viewportId` (required): The ID of the viewport to track
|
||||
|
||||
## Returns
|
||||
|
||||
An object containing the following properties:
|
||||
|
||||
- `isTracked`: Boolean indicating if the series in the viewport is currently tracked
|
||||
- `isLocked`: Boolean indicating if tracking is enabled (locked) globally
|
||||
- `seriesInstanceUID`: The Series Instance UID of the background display set in the viewport
|
||||
- `trackedMeasurementUIDs`: Array of measurement UIDs that are associated with the tracked series in the viewport
|
||||
|
||||
## Events
|
||||
|
||||
The hook automatically updates when any of these events occur:
|
||||
|
||||
From the tracked measurements service:
|
||||
- `TRACKING_ENABLED`
|
||||
- `TRACKING_DISABLED`
|
||||
- `TRACKED_SERIES_CHANGED`
|
||||
- `SERIES_ADDED`
|
||||
- `SERIES_REMOVED`
|
||||
|
||||
From the measurement service:
|
||||
- `MEASUREMENT_ADDED`
|
||||
- `RAW_MEASUREMENT_ADDED`
|
||||
- `MEASUREMENT_UPDATED`
|
||||
- `MEASUREMENT_REMOVED`
|
||||
- `MEASUREMENTS_CLEARED`
|
||||
@@ -0,0 +1,68 @@
|
||||
---
|
||||
title: useMeasurements
|
||||
summary: A React hook that provides mapped measurements from the measurement service with automatic updates when measurements change.
|
||||
---
|
||||
|
||||
# useMeasurements
|
||||
|
||||
The `useMeasurements` hook provides access to measurements from the measurement service, with automatic updates when measurements are added, updated, or removed.
|
||||
|
||||
## Overview
|
||||
|
||||
This hook retrieves measurements from the measurement service and maps them to a display-friendly format. It monitors various measurement service events and updates the returned measurements automatically when changes occur.
|
||||
|
||||
## Import
|
||||
|
||||
```js
|
||||
import { useMeasurements } from '@ohif/extension-cornerstone';
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```jsx
|
||||
function MeasurementPanel() {
|
||||
const measurementFilter = measurements => measurements.someFilter;
|
||||
|
||||
const measurements = useMeasurements({
|
||||
measurementFilter,
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
{measurements.map(measurement => (
|
||||
<div key={measurement.uid}>
|
||||
<span>{measurement.label}</span>
|
||||
<div>
|
||||
{measurement.displayText.primary.map((text, i) => (
|
||||
<p key={i}>{text}</p>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
- `options` - Configuration options:
|
||||
- `measurementFilter` - Optional function to filter measurements returned by the measurement service.
|
||||
|
||||
## Returns
|
||||
|
||||
An array of mapped measurements with the following structure:
|
||||
|
||||
|
||||
## Events
|
||||
|
||||
The hook automatically updates when any of these measurement service events occur:
|
||||
- `MEASUREMENT_ADDED`
|
||||
- `RAW_MEASUREMENT_ADDED`
|
||||
- `MEASUREMENT_UPDATED`
|
||||
- `MEASUREMENT_REMOVED`
|
||||
- `MEASUREMENTS_CLEARED`
|
||||
|
||||
## Implementation Details
|
||||
|
||||
The hook uses debouncing to prevent excessive re-renders when multiple measurement events occur in rapid succession. It also performs a deep comparison of the measurements to avoid unnecessary state updates when the data hasn't actually changed.
|
||||
@@ -0,0 +1,68 @@
|
||||
---
|
||||
title: usePatientInfo
|
||||
summary: A React hook that provides patient information from the active display sets and detects when multiple patients are loaded.
|
||||
---
|
||||
|
||||
# usePatientInfo
|
||||
|
||||
The `usePatientInfo` hook provides access to basic patient demographic information from the active display sets and also detects when display sets from multiple patients are loaded simultaneously.
|
||||
|
||||
## Overview
|
||||
|
||||
This hook retrieves patient information from the first instance of the first display set added to the viewer. It monitors when new display sets are added and updates the patient information accordingly. It also checks if any of the active display sets are from different patients and provides this information through the `isMixedPatients` flag.
|
||||
|
||||
## Import
|
||||
|
||||
```js
|
||||
import { usePatientInfo } from '@ohif/extension-default';
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```jsx
|
||||
function PatientBanner() {
|
||||
const { patientInfo, isMixedPatients } = usePatientInfo();
|
||||
|
||||
return (
|
||||
<div className="patient-banner">
|
||||
{isMixedPatients && (
|
||||
<div className="warning">Multiple patients loaded</div>
|
||||
)}
|
||||
<div className="patient-name">{patientInfo.PatientName}</div>
|
||||
<div className="patient-details">
|
||||
<span>ID: {patientInfo.PatientID}</span>
|
||||
<span>Sex: {patientInfo.PatientSex}</span>
|
||||
<span>DOB: {patientInfo.PatientDOB}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
This hook doesn't take any parameters.
|
||||
|
||||
## Returns
|
||||
|
||||
An object containing:
|
||||
|
||||
- `patientInfo`: Object with the following properties:
|
||||
- `PatientName`: Formatted patient name
|
||||
- `PatientID`: Patient identifier
|
||||
- `PatientSex`: Patient sex
|
||||
- `PatientDOB`: Formatted patient date of birth
|
||||
- `isMixedPatients`: Boolean indicating whether multiple patients are loaded in the viewer
|
||||
|
||||
## Events
|
||||
|
||||
The hook subscribes to the following display set service events:
|
||||
|
||||
- `DISPLAY_SETS_ADDED`: Updates patient information when new display sets are added to the viewer
|
||||
|
||||
## Implementation Details
|
||||
|
||||
- Patient name and date of birth are formatted using OHIF utility functions.
|
||||
- The hook checks all active display sets to determine if they belong to different patients.
|
||||
- Patient information is initialized with empty strings and updated when display sets are added.
|
||||
- When no instances are available in a display set, the hook attempts to get information from the `instance` property as a fallback.
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
title: useSearchParams
|
||||
summary: A React hook that provides access to URL search parameters from both the query string and hash fragment.
|
||||
---
|
||||
|
||||
# useSearchParams
|
||||
|
||||
The `useSearchParams` hook provides access to URL search parameters, combining both query string parameters and hash fragment parameters into a single URLSearchParams object.
|
||||
|
||||
## Overview
|
||||
|
||||
This hook extends the standard React Router `useLocation` functionality by merging search parameters from both the query string and the hash fragment of the URL. It also provides an option to normalize parameter keys to lowercase for case-insensitive parameter handling.
|
||||
|
||||
## Import
|
||||
|
||||
```js
|
||||
import { useSearchParams } from '@ohif/app';
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```jsx
|
||||
function RouteParameterReader() {
|
||||
const searchParams = useSearchParams();
|
||||
// Or with lowercase keys option
|
||||
const lowerCaseParams = useSearchParams({ lowerCaseKeys: true });
|
||||
|
||||
const studyInstanceUID = searchParams.get('StudyInstanceUID');
|
||||
// With lowerCaseKeys: true
|
||||
const sameStudyUID = lowerCaseParams.get('studyinstanceuid');
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>Study UID: {studyInstanceUID}</div>
|
||||
<div>All Parameters:</div>
|
||||
<ul>
|
||||
{Array.from(searchParams.entries()).map(([key, value]) => (
|
||||
<li key={key}>
|
||||
{key}: {value}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
- `options` (optional): Configuration options
|
||||
- `lowerCaseKeys` - Boolean indicating whether to convert all parameter keys to lowercase (default: false)
|
||||
|
||||
## Returns
|
||||
|
||||
A `URLSearchParams` object containing all parameters from both:
|
||||
- The query string (e.g., `?param1=value1¶m2=value2`)
|
||||
- The hash fragment (e.g., `#param3=value3¶m4=value4`)
|
||||
|
||||
If parameters with the same key exist in both the query string and hash fragment, the hash fragment values take precedence.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
- The hook uses React Router's `useLocation` to access the current URL.
|
||||
- It first creates a URLSearchParams object from the location's search property.
|
||||
- It then creates another URLSearchParams object from the location's hash property (excluding the leading '#').
|
||||
- The parameters from the hash are added to the search parameters, overriding any duplicate keys.
|
||||
- If the `lowerCaseKeys` option is enabled, it creates a new URLSearchParams object with all keys converted to lowercase.
|
||||
|
||||
This is particularly useful in OHIF where parameters may be specified in either the query string or hash fragment, and where case-sensitivity of parameter names might vary across different systems.
|
||||
@@ -0,0 +1,84 @@
|
||||
---
|
||||
title: useSessionStorage
|
||||
summary: A React hook that provides sessionStorage access with automatic JSON parsing/stringifying and an option to clear data when the page unloads.
|
||||
---
|
||||
|
||||
# useSessionStorage
|
||||
|
||||
The `useSessionStorage` hook provides a convenient way to store and retrieve data from the browser's sessionStorage with automatic JSON serialization and deserialization. It also offers the option to automatically clear the stored data when a page unloads.
|
||||
|
||||
## Overview
|
||||
|
||||
This hook wraps the browser's sessionStorage API to provide a more React-friendly interface. It handles JSON serialization/deserialization automatically and maintains the stored values in local state for reactive updates. The hook also includes a unique feature to clear specific items from sessionStorage when the page unloads, which is useful for temporary session data that shouldn't persist.
|
||||
|
||||
## Import
|
||||
|
||||
```js
|
||||
import { useSessionStorage } from '@ohif/ui-next';
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```jsx
|
||||
function UserPreferencesPanel() {
|
||||
const [preferences, setPreferences] = useSessionStorage({
|
||||
key: 'viewer-preferences',
|
||||
defaultValue: { theme: 'dark', fontSize: 'medium' },
|
||||
clearOnUnload: false,
|
||||
});
|
||||
|
||||
const updateTheme = (theme) => {
|
||||
setPreferences({ ...preferences, theme });
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h3>User Preferences</h3>
|
||||
<div>Current Theme: {preferences.theme}</div>
|
||||
<button onClick={() => updateTheme('light')}>Light Theme</button>
|
||||
<button onClick={() => updateTheme('dark')}>Dark Theme</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function TemporaryWorkspace() {
|
||||
const [workspace, setWorkspace] = useSessionStorage({
|
||||
key: 'temp-workspace',
|
||||
defaultValue: { annotations: [] },
|
||||
clearOnUnload: true, // This data will be cleared when the page unloads
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h3>Temporary Workspace</h3>
|
||||
<p>This workspace will be cleared when you leave the page</p>
|
||||
{/* Workspace UI components */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
An options object with the following properties:
|
||||
|
||||
- `key` (required): The sessionStorage key under which to store the data
|
||||
- `defaultValue` (optional): The default value to use if no data exists in sessionStorage for the given key. Defaults to an empty object (`{}`)
|
||||
- `clearOnUnload` (optional): Whether to clear this item from sessionStorage when the page unloads. Defaults to `false`
|
||||
|
||||
## Returns
|
||||
|
||||
An array containing:
|
||||
|
||||
1. The current value from sessionStorage (parsed from JSON)
|
||||
2. A function to update the value in both state and sessionStorage
|
||||
|
||||
The update function automatically handles JSON stringification of the data.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
- The hook uses a global Map (`sessionItemsToClearOnUnload`) to track items that should be cleared when the page unloads.
|
||||
- It utilizes the browser's `visibilitychange` event to implement the clearOnUnload feature. When the page becomes hidden (which happens both when switching tabs and when unloading), items marked for clearing are removed from sessionStorage.
|
||||
- If the page later becomes visible again (e.g., when switching back to the tab), the items are restored from the Map.
|
||||
- The hook's update function merges the new value with the current state using the spread operator, maintaining a similar behavior to React's `setState`.
|
||||
- All data is automatically serialized to JSON before storing in sessionStorage and deserialized when retrieving.
|
||||
@@ -0,0 +1,98 @@
|
||||
---
|
||||
title: useViewportDisplaySets
|
||||
summary: A React hook that provides access to display sets associated with a viewport, including background, foreground, overlay, and potential display sets.
|
||||
---
|
||||
|
||||
# useViewportDisplaySets
|
||||
|
||||
The `useViewportDisplaySets` hook provides access to display sets associated with a viewport, organized into categories based on their role and potential usage.
|
||||
|
||||
## Overview
|
||||
|
||||
This hook retrieves all display sets associated with a specific viewport and categorizes them into background, foreground, overlays, and potential display sets that could be added to the viewport in different roles. It allows components to efficiently access and manage viewport display sets for various UI interactions like layer menus and display set selectors.
|
||||
|
||||
## Import
|
||||
|
||||
```js
|
||||
import { useViewportDisplaySets } from '@ohif/extension-cornerstone';
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```jsx
|
||||
function ViewportLayerControls({ viewportId }) {
|
||||
const {
|
||||
backgroundDisplaySet,
|
||||
foregroundDisplaySets,
|
||||
overlayDisplaySets,
|
||||
potentialOverlayDisplaySets,
|
||||
potentialForegroundDisplaySets,
|
||||
potentialBackgroundDisplaySets,
|
||||
} = useViewportDisplaySets(viewportId);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
<h3>Background</h3>
|
||||
<div>{backgroundDisplaySet?.SeriesDescription}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3>Foreground ({foregroundDisplaySets.length})</h3>
|
||||
{foregroundDisplaySets.map(ds => (
|
||||
<div key={ds.displaySetInstanceUID}>{ds.SeriesDescription}</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3>Overlays ({overlayDisplaySets.length})</h3>
|
||||
{overlayDisplaySets.map(ds => (
|
||||
<div key={ds.displaySetInstanceUID}>{ds.SeriesDescription}</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3>Available Overlays ({potentialOverlayDisplaySets.length})</h3>
|
||||
{potentialOverlayDisplaySets.map(ds => (
|
||||
<div key={ds.displaySetInstanceUID}>{ds.SeriesDescription}</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
- `viewportId` (optional): The ID of the viewport to get display sets for. If not provided, uses the active viewport.
|
||||
- `options` (optional): Configuration options to control which display sets to include:
|
||||
- `includeBackground`: Whether to include the background display set (default: true)
|
||||
- `includeForeground`: Whether to include foreground display sets (default: true)
|
||||
- `includeOverlay`: Whether to include overlay display sets (default: true)
|
||||
- `includePotentialOverlay`: Whether to include potential overlay display sets (default: true)
|
||||
- `includePotentialForeground`: Whether to include potential foreground display sets (default: true)
|
||||
- `includePotentialBackground`: Whether to include potential background display sets (default: true)
|
||||
|
||||
## Returns
|
||||
|
||||
An object containing requested display set collections based on options:
|
||||
|
||||
- `allDisplaySets`: All display sets in the viewer (only if requested)
|
||||
- `viewportDisplaySets`: The display sets currently in the viewport
|
||||
- `backgroundDisplaySet`: The primary display set for the viewport (base image)
|
||||
- `foregroundDisplaySets`: Display sets currently shown with background (non-overlay layers)
|
||||
- `overlayDisplaySets`: Segmentation display sets currently applied as overlays
|
||||
- `potentialOverlayDisplaySets`: Display sets that could be toggled on as overlays (derived modalities)
|
||||
- `potentialForegroundDisplaySets`: Display sets that could be added as foreground layers
|
||||
- `potentialBackgroundDisplaySets`: Display sets that could replace the current background
|
||||
|
||||
Each property is only included if the corresponding option is true.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
- The hook automatically adapts to viewport changes through the `useViewportGrid` hook.
|
||||
- It only fetches and processes display sets that are needed based on the provided options.
|
||||
- Display sets are categorized based on their modality and properties.
|
||||
- Potential display sets are sorted by priority to present the most relevant options first.
|
||||
- Derived overlay modalities (like SEG, SR) are treated differently than other display sets.
|
||||
- The hook uses memoization extensively to optimize performance and prevent unnecessary recalculations.
|
||||
@@ -0,0 +1,57 @@
|
||||
---
|
||||
title: useViewportHover
|
||||
summary: A React hook that tracks mouse hover state and active status for a specific viewport.
|
||||
---
|
||||
|
||||
# useViewportHover
|
||||
|
||||
The `useViewportHover` hook provides information about whether the mouse is currently hovering over a specific viewport and whether that viewport is active.
|
||||
|
||||
## Overview
|
||||
|
||||
This hook monitors mouse movement to track hover state over a viewport element by its ID. It also checks whether the viewport is currently active (selected) in the viewer. This is useful for implementing conditional UI elements or behaviors that depend on user interaction with viewports.
|
||||
|
||||
## Import
|
||||
|
||||
```js
|
||||
import { useViewportHover } from '@ohif/extension-cornerstone';
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```jsx
|
||||
function ViewportOverlay({ viewportId }) {
|
||||
const { isHovered, isActive } = useViewportHover(viewportId);
|
||||
|
||||
return (
|
||||
<div className={`overlay ${isHovered ? 'hovered' : ''} ${isActive ? 'active' : ''}`}>
|
||||
{isHovered && !isActive && (
|
||||
<button>Click to activate</button>
|
||||
)}
|
||||
{isActive && (
|
||||
<div>Active viewport controls</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
- `viewportId` (required): The ID of the viewport to track hover state for
|
||||
|
||||
## Returns
|
||||
|
||||
An object containing the following properties:
|
||||
|
||||
- `isHovered`: Boolean indicating if the mouse is currently hovering over the viewport
|
||||
- `isActive`: Boolean indicating if the viewport is currently the active viewport in the grid
|
||||
|
||||
## Implementation Details
|
||||
|
||||
- The hook uses the DOM to find the viewport element by its `data-viewportId` attribute.
|
||||
- It calculates and maintains the viewport's bounding rectangle to efficiently determine if the mouse is within the viewport's bounds.
|
||||
- The viewport element's rectangle is updated when the window is resized.
|
||||
- Global mouse movement is tracked to determine hover state, rather than relying on traditional mouseenter/mouseleave events.
|
||||
- The hook automatically cleans up event listeners when the component unmounts or the viewport ID changes.
|
||||
- Active viewport state is derived from the viewport grid state using the `useViewportGrid` hook.
|
||||
@@ -0,0 +1,95 @@
|
||||
---
|
||||
title: useViewportSegmentations
|
||||
summary: A React hook that provides segmentation data and representations for the active viewport with automatic updates when segmentations change.
|
||||
---
|
||||
|
||||
# useViewportSegmentations
|
||||
|
||||
The `useViewportSegmentations` hook provides access to segmentation data and their representations for a specific viewport, with automatic updates when segmentations are modified, removed, or representations change.
|
||||
|
||||
## Overview
|
||||
|
||||
This hook retrieves all segmentations and their representations for a given viewport from the segmentation service. It maps the segmentation data to a display-friendly format, including readable text for statistics. The hook monitors various segmentation and viewport events to update automatically when changes occur.
|
||||
|
||||
## Import
|
||||
|
||||
```js
|
||||
import { useViewportSegmentations } from '@ohif/extension-cornerstone';
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```jsx
|
||||
function SegmentationPanel({ viewportId }) {
|
||||
const { segmentationsWithRepresentations, disabled } = useViewportSegmentations({
|
||||
viewportId,
|
||||
subscribeToDataModified: true,
|
||||
debounceTime: 100,
|
||||
});
|
||||
|
||||
if (disabled) {
|
||||
return <div>Segmentations not available for this modality</div>;
|
||||
}
|
||||
|
||||
if (!segmentationsWithRepresentations.length) {
|
||||
return <div>No segmentations available</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{segmentationsWithRepresentations.map(({ segmentation, representation }) => (
|
||||
<div key={segmentation.id}>
|
||||
<h3>{segmentation.label}</h3>
|
||||
{Object.entries(segmentation.segments).map(([segmentIndex, segment]) => (
|
||||
<div key={segmentIndex}>
|
||||
<h4>{segment.label}</h4>
|
||||
{segment.displayText.primary.map((text, i) => (
|
||||
<p key={i}>{text}</p>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
- `options` - Configuration options:
|
||||
- `viewportId` (required): The ID of the viewport to get segmentations for
|
||||
- `subscribeToDataModified` (optional): Whether to subscribe to segmentation data modifications (default: false)
|
||||
- `debounceTime` (optional): Debounce time in milliseconds for updates (default: 0)
|
||||
|
||||
## Returns
|
||||
|
||||
An object with the following properties:
|
||||
|
||||
- `segmentationsWithRepresentations`: An array of objects with each containing:
|
||||
- `representation`: The segmentation representation in the viewport
|
||||
- `segmentation`: The mapped segmentation data with display-friendly properties:
|
||||
- `label`: The segmentation label
|
||||
- `segments`: Object mapping segment indices to segment data:
|
||||
- `label`: Segment label
|
||||
- `color`: Segment color
|
||||
- `displayText`: Organized text for display with `primary` and `secondary` arrays
|
||||
|
||||
- `disabled`: Boolean indicating if segmentations are disabled for the current modality
|
||||
|
||||
## Events
|
||||
|
||||
The hook automatically updates when any of these events occur:
|
||||
- `SEGMENTATION_MODIFIED`
|
||||
- `SEGMENTATION_REMOVED`
|
||||
- `SEGMENTATION_REPRESENTATION_MODIFIED`
|
||||
- `ACTIVE_VIEWPORT_ID_CHANGED`
|
||||
- `GRID_STATE_CHANGED`
|
||||
- `SEGMENTATION_DATA_MODIFIED` (only if `subscribeToDataModified` is true)
|
||||
|
||||
## Implementation Details
|
||||
|
||||
- The hook excludes certain modalities from segmentation display: 'SM', 'OT', 'DOC', 'ECG'.
|
||||
- It uses debouncing to prevent excessive re-renders when multiple segmentation events occur in rapid succession.
|
||||
- Segmentation statistics are automatically mapped to readable text using the customization service.
|
||||
- Nested statistics are displayed with indentation to maintain hierarchy.
|
||||
@@ -23,7 +23,6 @@ import segDisplayEditingTrue from '../../../assets/img/segDisplayEditingTrue.png
|
||||
import segDisplayEditingFalse from '../../../assets/img/segDisplayEditingFalse.png';
|
||||
import thumbnailMenuItemsImage from '../../../assets/img/thumbnailMenuItemsImage.png';
|
||||
import studyMenuItemsImage from '../../../assets/img/studyMenuItemsImage.png';
|
||||
import windowLevelActionMenu from '../../../assets/img/windowLevelActionMenu.png';
|
||||
import viewPortNotificationImage from '../../../assets/img/viewport-notification.png';
|
||||
import captureViewportModal from '../../../assets/img/captureViewportModal.png';
|
||||
import aboutModal from '../../../assets/img/aboutModal.png';
|
||||
@@ -201,31 +200,6 @@ window.config = {
|
||||
],
|
||||
`,
|
||||
},
|
||||
{
|
||||
id: 'viewportActionMenu.windowLevelActionMenu',
|
||||
description:
|
||||
'Configures the display and location of the window level action menu in the viewport.',
|
||||
image: windowLevelActionMenu,
|
||||
default: null,
|
||||
configuration: `
|
||||
window.config = {
|
||||
// rest of window config
|
||||
customizationService: [
|
||||
{
|
||||
'viewportActionMenu.windowLevelActionMenu': {
|
||||
$merge: {
|
||||
location: 0, // Set the location of the menu in the viewport.
|
||||
// 0: topLeft
|
||||
// 1: topRight
|
||||
// 2: bottomLeft
|
||||
// 3: bottomRight
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
`,
|
||||
},
|
||||
{
|
||||
id: 'measurementLabels',
|
||||
description: 'Labels for measurement tools in the viewer that are automatically asked for.',
|
||||
@@ -936,56 +910,6 @@ window.config = {
|
||||
};
|
||||
`,
|
||||
},
|
||||
{
|
||||
id: 'viewportActionMenu.windowLevelActionMenu',
|
||||
description:
|
||||
'Configures the display and location of the window level action menu in the viewport.',
|
||||
image: windowLevelActionMenu,
|
||||
default: null,
|
||||
configuration: `
|
||||
window.config = {
|
||||
// rest of window config
|
||||
customizationService: [
|
||||
{
|
||||
'viewportActionMenu.windowLevelActionMenu': {
|
||||
$merge: {
|
||||
location: 0, // Set the location of the menu in the viewport.
|
||||
// 0: topLeft
|
||||
// 1: topRight
|
||||
// 2: bottomLeft
|
||||
// 3: bottomRight
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
`,
|
||||
},
|
||||
{
|
||||
id: 'viewportActionMenu.dataOverlayMenu',
|
||||
description: 'Configures the display and location of the data overlay in the viewport.',
|
||||
image: segmentationOverlay,
|
||||
default: null,
|
||||
configuration: `
|
||||
window.config = {
|
||||
// rest of window config
|
||||
customizationService: [
|
||||
{
|
||||
'viewportActionMenu.dataOverlayMenu': {
|
||||
$merge: {
|
||||
enabled: true,
|
||||
location: 1, // Set the location of the overlay in the viewport.
|
||||
// 0: topLeft
|
||||
// 1: topRight
|
||||
// 2: bottomLeft
|
||||
// 3: bottomRight
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
`,
|
||||
},
|
||||
{
|
||||
id: 'viewportNotification.beginTrackingMessage',
|
||||
description: 'Define the content to be displayed in begin tracking prompt',
|
||||
|
||||
Reference in new issue
Block a user