@ohif/app 3.8.0-beta.63 → 3.8.0-beta.65

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/{121.bundle.f6c25e845985d96c423c.js → 121.bundle.fda405f29003c308ce09.js} +4 -4
  2. package/dist/{155.bundle.9e3dd18c9a3961232504.js → 155.bundle.a57744809d0f46030ee0.js} +12 -7
  3. package/dist/{188.bundle.528e9ad81159c776affa.js → 188.bundle.c448aed48915741e9f97.js} +2 -2
  4. package/dist/{270.bundle.fd387adff5b064fca506.js → 270.bundle.16ac8114c5c4ce006f4a.js} +51 -28
  5. package/dist/{295.bundle.c132f53e1397ef9d432a.js → 295.bundle.57700cd41fd87e1521b4.js} +4 -4
  6. package/dist/{339.bundle.591a0a6075220b14c249.js → 339.bundle.57dac3644803cefe3e3d.js} +27 -69
  7. package/dist/{68.bundle.efc5ba2a44aa2b96ee1a.js → 41.bundle.58b85dd990fb6fac615e.js} +280 -494
  8. package/dist/{250.bundle.d8b502b7ef6afc79a87e.js → 448.bundle.d195aba3aef25ec286d1.js} +284 -388
  9. package/dist/{530.bundle.ef1ea9d98f7b377a9d3a.js → 530.bundle.72d9812f117036615a38.js} +2 -2
  10. package/dist/{544.bundle.05b543f28d0c124950ef.js → 544.bundle.c3009e245ceb1554c70a.js} +5 -17
  11. package/dist/{559.bundle.fc08eab02848a451ed34.js → 559.bundle.05bd51e94422a2cab116.js} +6 -10
  12. package/dist/{594.bundle.3a5fa2e7d5636ddccb32.js → 594.bundle.14b122ab995e4b13e652.js} +147 -188
  13. package/dist/{50.bundle.55ad62f1f656f5fd5a36.js → 638.bundle.a63003e18bed65f227bb.js} +159 -395
  14. package/dist/699.bundle.efc67171e6d212f25a24.js +774 -0
  15. package/dist/{963.bundle.874395d7b2d80f9e5831.js → 701.bundle.bc40f1a7d5d6b1a4dd38.js} +554 -215
  16. package/dist/{724.bundle.294a41af83a5af33f3cc.js → 724.bundle.3945f8d2e9c8b0b23628.js} +86 -164
  17. package/dist/{862.bundle.c8eba798644149650775.js → 862.bundle.d787dac01f4567560a42.js} +2 -2
  18. package/dist/{889.bundle.a776c87be9e3580f3437.js → 889.bundle.ffc727aa6d1a74f2138d.js} +180 -207
  19. package/dist/{704.bundle.842ada45436f1fb029aa.js → 90.bundle.abde898ebd3c74f521f9.js} +323 -334
  20. package/dist/{905.bundle.73a711d0255cb988fa6e.js → 905.bundle.6b5b42b2403e4676bb3a.js} +119 -89
  21. package/dist/{907.bundle.6932f0458b9e0143c18a.js → 907.bundle.fbd5768fa5b53f9d3f86.js} +2 -14
  22. package/dist/{961.bundle.6cf1599ed3a2871c040f.js → 961.bundle.8bb5a713fc5a3817c6a6.js} +2 -15
  23. package/dist/{987.bundle.e7c041a6dfb4ddb41813.js → 987.bundle.91d4867efedd5b4d84cb.js} +5 -1
  24. package/dist/{app.bundle.22ffa6050ebef98b2066.js → app.bundle.0c24b249dc8b631916ef.js} +2234 -980
  25. package/dist/app.bundle.css +1 -1
  26. package/dist/index.html +1 -1
  27. package/dist/{polySeg.bundle.99be036bab9b7f011b0c.js → polySeg.bundle.c1cec6312eb6c6dc3701.js} +1 -1
  28. package/dist/sw.js +1 -1
  29. package/package.json +17 -17
  30. package/dist/317.bundle.dd0879c5035c4b90fad3.js +0 -562
  31. /package/dist/{164.bundle.710b5db3fef4d536a59f.js → 164.bundle.ff12d6019a627cda2a6c.js} +0 -0
  32. /package/dist/{50.css → 638.css} +0 -0
  33. /package/dist/{963.css → 701.css} +0 -0
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
- (globalThis["webpackChunk"] = globalThis["webpackChunk"] || []).push([[704],{
2
+ (globalThis["webpackChunk"] = globalThis["webpackChunk"] || []).push([[90],{
3
3
 
4
- /***/ 49704:
4
+ /***/ 54090:
5
5
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
6
6
 
7
7
  // ESM COMPAT FLAG
@@ -31,8 +31,8 @@ __webpack_require__.d(utils_namespaceObject, {
31
31
 
32
32
  // EXTERNAL MODULE: ../../../node_modules/dicomweb-client/build/dicomweb-client.es.js
33
33
  var dicomweb_client_es = __webpack_require__(36922);
34
- // EXTERNAL MODULE: ../../core/src/index.ts + 66 modules
35
- var src = __webpack_require__(14283);
34
+ // EXTERNAL MODULE: ../../core/src/index.ts + 67 modules
35
+ var src = __webpack_require__(78198);
36
36
  // EXTERNAL MODULE: ../../core/src/utils/sortStudy.ts
37
37
  var sortStudy = __webpack_require__(45476);
38
38
  ;// CONCATENATED MODULE: ../../../extensions/default/src/DicomWebDataSource/qido.js
@@ -2484,8 +2484,8 @@ var react = __webpack_require__(41766);
2484
2484
  // EXTERNAL MODULE: ../../../node_modules/prop-types/index.js
2485
2485
  var prop_types = __webpack_require__(11374);
2486
2486
  var prop_types_default = /*#__PURE__*/__webpack_require__.n(prop_types);
2487
- // EXTERNAL MODULE: ../../ui/src/index.js + 489 modules
2488
- var ui_src = __webpack_require__(6804);
2487
+ // EXTERNAL MODULE: ../../ui/src/index.js + 497 modules
2488
+ var ui_src = __webpack_require__(58046);
2489
2489
  // EXTERNAL MODULE: ./state/index.js + 1 modules
2490
2490
  var state = __webpack_require__(15575);
2491
2491
  // EXTERNAL MODULE: ../node_modules/react-router-dom/dist/index.js
@@ -2504,48 +2504,49 @@ function _extends() { _extends = Object.assign ? Object.assign.bind() : function
2504
2504
 
2505
2505
 
2506
2506
 
2507
+
2507
2508
  function Toolbar({
2508
2509
  servicesManager
2509
2510
  }) {
2510
2511
  const {
2511
- toolbarService
2512
- } = servicesManager.services;
2513
- const [viewportGrid, viewportGridService] = (0,ui_src/* useViewportGrid */.ih)();
2514
- const [toolbarButtons, setToolbarButtons] = (0,react.useState)([]);
2515
- (0,react.useEffect)(() => {
2516
- const updateToolbar = () => {
2517
- const toolGroupId = viewportGridService.getActiveViewportOptionByKey('toolGroupId') ?? 'default';
2518
- setToolbarButtons(toolbarService.getButtonSection(toolGroupId));
2519
- };
2520
- const {
2521
- unsubscribe
2522
- } = toolbarService.subscribe(toolbarService.EVENTS.TOOL_BAR_MODIFIED, updateToolbar);
2523
- updateToolbar();
2524
- return () => {
2525
- unsubscribe();
2526
- };
2527
- }, [toolbarService, viewportGrid]);
2528
- const onInteraction = (0,react.useCallback)(args => toolbarService.recordInteraction(args), [toolbarService]);
2512
+ toolbarButtons,
2513
+ onInteraction
2514
+ } = (0,src/* useToolbar */.tR)({
2515
+ servicesManager,
2516
+ buttonSection: 'primary'
2517
+ });
2518
+ if (!toolbarButtons.length) {
2519
+ return null;
2520
+ }
2529
2521
  return /*#__PURE__*/react.createElement(react.Fragment, null, toolbarButtons.map(toolDef => {
2522
+ if (!toolDef) {
2523
+ return null;
2524
+ }
2530
2525
  const {
2531
2526
  id,
2532
2527
  Component,
2533
2528
  componentProps
2534
2529
  } = toolDef;
2535
- return (
2536
- /*#__PURE__*/
2537
- // The margin for separating the tools on the toolbar should go here and NOT in each individual component (button) item.
2538
- // This allows for the individual items to be included in other UI components where perhaps alternative margins are desired.
2539
- react.createElement("div", {
2540
- key: id,
2541
- className: classnames_default()('mr-1')
2542
- }, /*#__PURE__*/react.createElement(Component, _extends({
2543
- id: id
2544
- }, componentProps, {
2545
- onInteraction: onInteraction,
2546
- servicesManager: servicesManager
2547
- })))
2548
- );
2530
+ const {
2531
+ disabled
2532
+ } = componentProps;
2533
+ const tool = /*#__PURE__*/react.createElement(Component, _extends({
2534
+ key: id,
2535
+ id: id,
2536
+ onInteraction: onInteraction,
2537
+ servicesManager: servicesManager
2538
+ }, componentProps));
2539
+ return disabled ? /*#__PURE__*/react.createElement(ui_src/* Tooltip */.m_, {
2540
+ key: id,
2541
+ position: "bottom",
2542
+ content: componentProps.label,
2543
+ secondaryContent: 'Not available on the current viewport'
2544
+ }, /*#__PURE__*/react.createElement("div", {
2545
+ className: classnames_default()('mr-1')
2546
+ }, tool)) : /*#__PURE__*/react.createElement("div", {
2547
+ key: id,
2548
+ className: "mr-1"
2549
+ }, tool);
2549
2550
  }));
2550
2551
  }
2551
2552
  ;// CONCATENATED MODULE: ../../../extensions/default/src/ViewerLayout/ViewerHeader.tsx
@@ -2603,8 +2604,8 @@ function ViewerHeader({
2603
2604
  hotkeyDefinitions,
2604
2605
  hotkeyDefaults
2605
2606
  } = hotkeysManager;
2606
- const versionNumber = "3.8.0-beta.63";
2607
- const commitHash = "45b68e841dcb9e28a2ea991c37ee7ac4a8c5b71e";
2607
+ const versionNumber = "3.8.0-beta.65";
2608
+ const commitHash = "617043fe0da5de91fbea4ac33a27f1df16ae1ca6";
2608
2609
  const menuOptions = [{
2609
2610
  title: t('Header:About'),
2610
2611
  icon: 'info',
@@ -2758,18 +2759,12 @@ function ViewerLayout({
2758
2759
  }, []);
2759
2760
  const getComponent = id => {
2760
2761
  const entry = extensionManager.getModuleEntry(id);
2761
- if (!entry) {
2762
- throw new Error(`${id} is not valid for an extension module. 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)`);
2763
- }
2764
- let content;
2765
- if (entry && entry.component) {
2766
- content = entry.component;
2767
- } else {
2768
- throw new Error(`No component found from extension ${id}. Check the reference string to the extension in your Mode configuration`);
2762
+ if (!entry || !entry.component) {
2763
+ 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`);
2769
2764
  }
2770
2765
  return {
2771
2766
  entry,
2772
- content
2767
+ content: entry.component
2773
2768
  };
2774
2769
  };
2775
2770
  const getPanelData = id => {
@@ -2783,7 +2778,8 @@ function ViewerLayout({
2783
2778
  iconLabel: entry.iconLabel,
2784
2779
  label: entry.label,
2785
2780
  name: entry.name,
2786
- content
2781
+ content,
2782
+ contexts: entry.contexts
2787
2783
  };
2788
2784
  };
2789
2785
  (0,react.useEffect)(() => {
@@ -3240,6 +3236,11 @@ function _createStudyBrowserTabs(primaryStudyInstanceUIDs, studyDisplayList, dis
3240
3236
  function getImageSrcFromImageId(cornerstone, imageId) {
3241
3237
  return new Promise((resolve, reject) => {
3242
3238
  const canvas = document.createElement('canvas');
3239
+ // Note: the default width and height of the canvas is 300x150
3240
+ // but we need to set the width and height to the same number since
3241
+ // the thumbnails are usually square and we want to maintain the aspect ratio
3242
+ canvas.width = 128 / window.devicePixelRatio;
3243
+ canvas.height = 128 / window.devicePixelRatio;
3243
3244
  cornerstone.utilities.loadImageToCanvas({
3244
3245
  canvas,
3245
3246
  imageId
@@ -4451,6 +4452,46 @@ function ToolbarLayoutSelector_extends() { ToolbarLayoutSelector_extends = Objec
4451
4452
 
4452
4453
 
4453
4454
 
4455
+ const defaultCommonPresets = [{
4456
+ icon: 'layout-common-1x1',
4457
+ commandOptions: {
4458
+ numRows: 1,
4459
+ numCols: 1
4460
+ }
4461
+ }, {
4462
+ icon: 'layout-common-1x2',
4463
+ commandOptions: {
4464
+ numRows: 1,
4465
+ numCols: 2
4466
+ }
4467
+ }, {
4468
+ icon: 'layout-common-2x2',
4469
+ commandOptions: {
4470
+ numRows: 2,
4471
+ numCols: 2
4472
+ }
4473
+ }, {
4474
+ icon: 'layout-common-2x3',
4475
+ commandOptions: {
4476
+ numRows: 2,
4477
+ numCols: 3
4478
+ }
4479
+ }];
4480
+ const generateAdvancedPresets = hangingProtocolService => {
4481
+ const hangingProtocols = Array.from(hangingProtocolService.protocols.values());
4482
+ return hangingProtocols.map(hp => {
4483
+ if (!hp.isPreset) {
4484
+ return null;
4485
+ }
4486
+ return {
4487
+ icon: hp.icon,
4488
+ title: hp.name,
4489
+ commandOptions: {
4490
+ protocolId: hp.id
4491
+ }
4492
+ };
4493
+ }).filter(preset => preset !== null);
4494
+ };
4454
4495
  function ToolbarLayoutSelectorWithServices({
4455
4496
  servicesManager,
4456
4497
  ...props
@@ -4458,6 +4499,10 @@ function ToolbarLayoutSelectorWithServices({
4458
4499
  const {
4459
4500
  toolbarService
4460
4501
  } = servicesManager.services;
4502
+ const [isDisabled, setIsDisabled] = (0,react.useState)(false);
4503
+ const handleMouseEnter = () => {
4504
+ setIsDisabled(false);
4505
+ };
4461
4506
  const onSelection = (0,react.useCallback)(props => {
4462
4507
  toolbarService.recordInteraction({
4463
4508
  interactionType: 'action',
@@ -4465,23 +4510,49 @@ function ToolbarLayoutSelectorWithServices({
4465
4510
  commandName: 'setViewportGridLayout',
4466
4511
  commandOptions: {
4467
4512
  ...props
4468
- },
4469
- context: 'DEFAULT'
4513
+ }
4470
4514
  }]
4471
4515
  });
4516
+ setIsDisabled(true);
4472
4517
  }, [toolbarService]);
4473
- return /*#__PURE__*/react.createElement(LayoutSelector, ToolbarLayoutSelector_extends({}, props, {
4474
- onSelection: onSelection
4475
- }));
4518
+ const onSelectionPreset = (0,react.useCallback)(props => {
4519
+ toolbarService.recordInteraction({
4520
+ interactionType: 'action',
4521
+ commands: [{
4522
+ commandName: 'setHangingProtocol',
4523
+ commandOptions: {
4524
+ ...props
4525
+ }
4526
+ }]
4527
+ });
4528
+ setIsDisabled(true);
4529
+ }, [toolbarService]);
4530
+ return /*#__PURE__*/react.createElement("div", {
4531
+ onMouseEnter: handleMouseEnter
4532
+ }, /*#__PURE__*/react.createElement(LayoutSelector, ToolbarLayoutSelector_extends({}, props, {
4533
+ onSelection: onSelection,
4534
+ onSelectionPreset: onSelectionPreset,
4535
+ servicesManager: servicesManager,
4536
+ tooltipDisabled: isDisabled
4537
+ })));
4476
4538
  }
4477
4539
  function LayoutSelector({
4478
4540
  rows,
4479
4541
  columns,
4480
4542
  className,
4481
4543
  onSelection,
4544
+ onSelectionPreset,
4545
+ servicesManager,
4546
+ tooltipDisabled,
4482
4547
  ...rest
4483
4548
  }) {
4484
4549
  const [isOpen, setIsOpen] = (0,react.useState)(false);
4550
+ const {
4551
+ customizationService,
4552
+ hangingProtocolService
4553
+ } = servicesManager.services;
4554
+ const commonPresets = customizationService.get('commonPresets') || defaultCommonPresets;
4555
+ const advancedPresets = customizationService.get('advancedPresets') || generateAdvancedPresets(hangingProtocolService);
4485
4556
  const closeOnOutsideClick = () => {
4486
4557
  if (isOpen) {
4487
4558
  setIsOpen(false);
@@ -4493,20 +4564,56 @@ function LayoutSelector({
4493
4564
  window.removeEventListener('click', closeOnOutsideClick);
4494
4565
  };
4495
4566
  }, [isOpen]);
4496
- const onInteractionHandler = () => setIsOpen(!isOpen);
4567
+ const onInteractionHandler = () => {
4568
+ setIsOpen(!isOpen);
4569
+ };
4497
4570
  const DropdownContent = isOpen ? ui_src/* LayoutSelector */.sG : null;
4498
4571
  return /*#__PURE__*/react.createElement(ui_src/* ToolbarButton */.IB, {
4499
4572
  id: "Layout",
4500
- label: "Grid Layout",
4573
+ label: "Layout",
4501
4574
  icon: "tool-layout",
4502
4575
  onInteraction: onInteractionHandler,
4503
4576
  className: className,
4504
4577
  rounded: rest.rounded,
4505
- dropdownContent: DropdownContent !== null && /*#__PURE__*/react.createElement(DropdownContent, {
4578
+ disableToolTip: tooltipDisabled,
4579
+ dropdownContent: DropdownContent !== null && /*#__PURE__*/react.createElement("div", {
4580
+ className: "flex"
4581
+ }, /*#__PURE__*/react.createElement("div", {
4582
+ className: "bg-secondary-dark flex flex-col gap-2.5 p-2"
4583
+ }, /*#__PURE__*/react.createElement("div", {
4584
+ className: "text-aqua-pale text-xs"
4585
+ }, "Common"), /*#__PURE__*/react.createElement("div", {
4586
+ className: "flex gap-4"
4587
+ }, commonPresets.map((preset, index) => /*#__PURE__*/react.createElement(ui_src/* LayoutPreset */.qu, {
4588
+ key: index,
4589
+ classNames: "hover:bg-primary-dark group p-1",
4590
+ icon: preset.icon,
4591
+ commandOptions: preset.commandOptions,
4592
+ onSelection: onSelection
4593
+ }))), /*#__PURE__*/react.createElement("div", {
4594
+ className: "h-[2px] bg-black"
4595
+ }), /*#__PURE__*/react.createElement("div", {
4596
+ className: "text-aqua-pale text-xs"
4597
+ }, "Advanced"), /*#__PURE__*/react.createElement("div", {
4598
+ className: "flex flex-col gap-2.5"
4599
+ }, advancedPresets.map((preset, index) => /*#__PURE__*/react.createElement(ui_src/* LayoutPreset */.qu, {
4600
+ key: index + commonPresets.length,
4601
+ classNames: "hover:bg-primary-dark group flex gap-2 p-1",
4602
+ icon: preset.icon,
4603
+ title: preset.title,
4604
+ commandOptions: preset.commandOptions,
4605
+ onSelection: onSelectionPreset
4606
+ })))), /*#__PURE__*/react.createElement("div", {
4607
+ className: "bg-primary-dark flex flex-col gap-2.5 border-l-2 border-solid border-black p-2"
4608
+ }, /*#__PURE__*/react.createElement("div", {
4609
+ className: "text-aqua-pale text-xs"
4610
+ }, "Custom"), /*#__PURE__*/react.createElement(DropdownContent, {
4506
4611
  rows: rows,
4507
4612
  columns: columns,
4508
4613
  onSelection: onSelection
4509
- }),
4614
+ }), /*#__PURE__*/react.createElement("p", {
4615
+ className: "text-aqua-pale text-xs leading-tight"
4616
+ }, "Hover to select ", /*#__PURE__*/react.createElement("br", null), "rows and columns ", /*#__PURE__*/react.createElement("br", null), " Click to apply"))),
4510
4617
  isActive: isOpen,
4511
4618
  type: "toggle"
4512
4619
  });
@@ -4518,8 +4625,8 @@ LayoutSelector.propTypes = {
4518
4625
  servicesManager: prop_types_default().instanceOf(src/* ServicesManager */.CS)
4519
4626
  };
4520
4627
  LayoutSelector.defaultProps = {
4628
+ columns: 4,
4521
4629
  rows: 3,
4522
- columns: 3,
4523
4630
  onLayoutChange: () => {}
4524
4631
  };
4525
4632
  /* harmony default export */ const ToolbarLayoutSelector = (ToolbarLayoutSelectorWithServices);
@@ -4528,10 +4635,7 @@ function ToolbarSplitButtonWithServices_extends() { ToolbarSplitButtonWithServic
4528
4635
 
4529
4636
 
4530
4637
 
4531
-
4532
4638
  function ToolbarSplitButtonWithServices({
4533
- isRadio,
4534
- isAction,
4535
4639
  groupId,
4536
4640
  primary,
4537
4641
  secondary,
@@ -4543,105 +4647,27 @@ function ToolbarSplitButtonWithServices({
4543
4647
  const {
4544
4648
  toolbarService
4545
4649
  } = servicesManager?.services;
4546
- const handleItemClick = (item, index) => {
4547
- const {
4548
- id,
4549
- type,
4550
- commands
4551
- } = item;
4552
- onInteraction({
4553
- groupId,
4554
- itemId: id,
4555
- interactionType: type,
4556
- commands
4557
- });
4558
- setState(state => ({
4559
- ...state,
4560
- primary: !isAction && isRadio ? {
4561
- ...item,
4562
- index
4563
- } : state.primary,
4564
- isExpanded: false,
4565
- items: getSplitButtonItems(items).filter(item => isRadio && !isAction ? item.index !== index : true)
4566
- }));
4567
- };
4568
4650
 
4569
4651
  /* Bubbles up individual item clicks */
4570
- const getSplitButtonItems = items => items.map((item, index) => ({
4652
+ const getSplitButtonItems = (0,react.useCallback)(items => items.map((item, index) => ({
4571
4653
  ...item,
4572
4654
  index,
4573
- onClick: () => handleItemClick(item, index)
4574
- }));
4575
- const [buttonsState, setButtonState] = (0,react.useState)({
4576
- primaryToolId: '',
4577
- toggles: {},
4578
- groups: {}
4579
- });
4580
- const [state, setState] = (0,react.useState)({
4581
- primary,
4582
- items: getSplitButtonItems(items).filter(item => isRadio && !isAction ? item.id !== primary.id : true)
4583
- });
4584
- const {
4585
- primaryToolId,
4586
- toggles
4587
- } = buttonsState;
4588
- const isPrimaryToggle = state.primary.type === 'toggle';
4589
- const isPrimaryActive = state.primary.type === 'tool' && primaryToolId === state.primary.id || isPrimaryToggle && toggles[state.primary.id] === true;
4590
- const PrimaryButtonComponent = toolbarService?.getButtonComponentForUIType(state.primary.uiType) ?? ui_src/* ToolbarButton */.IB;
4591
- (0,react.useEffect)(() => {
4592
- const {
4593
- unsubscribe
4594
- } = toolbarService.subscribe(toolbarService.EVENTS.TOOL_BAR_STATE_MODIFIED, state => {
4595
- setButtonState({
4596
- ...state
4655
+ onClick: () => {
4656
+ onInteraction({
4657
+ groupId,
4658
+ itemId: item.id,
4659
+ commands: item.commands
4597
4660
  });
4598
- });
4599
- return () => {
4600
- unsubscribe();
4601
- };
4602
- }, [toolbarService]);
4603
- const updatedItems = state.items.map(item => {
4604
- const isActive = item.type === 'tool' && primaryToolId === item.id;
4605
-
4606
- // We could have added the
4607
- // item.type === 'toggle' && toggles[item.id] === true
4608
- // too but that makes the button active when the toggle is active under it
4609
- // which feels weird
4610
- return {
4611
- ...item,
4612
- isActive
4613
- };
4614
- });
4615
- const DefaultListItemRenderer = ({
4616
- type,
4617
- icon,
4618
- label,
4619
- t,
4620
- id
4621
- }) => {
4622
- const isActive = type === 'toggle' && toggles[id] === true;
4623
- return /*#__PURE__*/react.createElement("div", {
4624
- className: classnames_default()('hover:bg-primary-dark flex h-8 w-full flex-row items-center p-3', 'whitespace-pre text-base', isActive && 'bg-primary-dark', isActive ? 'text-[#348CFD]' : 'text-common-bright hover:bg-primary-dark hover:text-primary-light')
4625
- }, icon && /*#__PURE__*/react.createElement("span", {
4626
- className: "mr-4"
4627
- }, /*#__PURE__*/react.createElement(ui_src/* Icon */.In, {
4628
- name: icon,
4629
- className: "h-5 w-5"
4630
- })), /*#__PURE__*/react.createElement("span", {
4631
- className: "mr-5"
4632
- }, t(label)));
4633
- };
4634
- const listItemRenderer = renderer || DefaultListItemRenderer;
4661
+ }
4662
+ })), []);
4663
+ const PrimaryButtonComponent = toolbarService?.getButtonComponentForUIType(primary.uiType) ?? ui_src/* ToolbarButton */.IB;
4664
+ const listItemRenderer = renderer;
4635
4665
  return /*#__PURE__*/react.createElement(ui_src/* SplitButton */.fk, {
4636
- isRadio: isRadio,
4637
- isAction: isAction,
4638
- primary: state.primary,
4666
+ primary: primary,
4639
4667
  secondary: secondary,
4640
- items: updatedItems,
4668
+ items: getSplitButtonItems(items),
4641
4669
  groupId: groupId,
4642
4670
  renderer: listItemRenderer,
4643
- isActive: isPrimaryActive || updatedItems.some(item => item.isActive),
4644
- isToggle: isPrimaryToggle,
4645
4671
  onInteraction: onInteraction,
4646
4672
  Component: props => /*#__PURE__*/react.createElement(PrimaryButtonComponent, ToolbarSplitButtonWithServices_extends({}, props, {
4647
4673
  servicesManager: servicesManager
@@ -4650,11 +4676,9 @@ function ToolbarSplitButtonWithServices({
4650
4676
  }
4651
4677
  ToolbarSplitButtonWithServices.propTypes = {
4652
4678
  isRadio: (prop_types_default()).bool,
4653
- isAction: (prop_types_default()).bool,
4654
4679
  groupId: (prop_types_default()).string,
4655
4680
  primary: prop_types_default().shape({
4656
4681
  id: (prop_types_default()).string.isRequired,
4657
- type: prop_types_default().oneOf(['tool', 'action', 'toggle']).isRequired,
4658
4682
  uiType: (prop_types_default()).string
4659
4683
  }),
4660
4684
  secondary: prop_types_default().shape({
@@ -4662,14 +4686,16 @@ ToolbarSplitButtonWithServices.propTypes = {
4662
4686
  icon: (prop_types_default()).string.isRequired,
4663
4687
  label: (prop_types_default()).string,
4664
4688
  tooltip: (prop_types_default()).string.isRequired,
4665
- isActive: (prop_types_default()).bool
4689
+ disabled: (prop_types_default()).bool,
4690
+ className: (prop_types_default()).string
4666
4691
  }),
4667
4692
  items: prop_types_default().arrayOf(prop_types_default().shape({
4668
4693
  id: (prop_types_default()).string.isRequired,
4669
- type: prop_types_default().oneOf(['tool', 'action', 'toggle']).isRequired,
4670
4694
  icon: (prop_types_default()).string,
4671
4695
  label: (prop_types_default()).string,
4672
- tooltip: (prop_types_default()).string
4696
+ tooltip: (prop_types_default()).string,
4697
+ disabled: (prop_types_default()).bool,
4698
+ className: (prop_types_default()).string
4673
4699
  })),
4674
4700
  renderer: (prop_types_default()).func,
4675
4701
  onInteraction: (prop_types_default()).func.isRequired,
@@ -4684,106 +4710,102 @@ ToolbarSplitButtonWithServices.defaultProps = {
4684
4710
  isAction: false
4685
4711
  };
4686
4712
  /* harmony default export */ const Toolbar_ToolbarSplitButtonWithServices = (ToolbarSplitButtonWithServices);
4687
- ;// CONCATENATED MODULE: ../../../extensions/default/src/Toolbar/ToolbarButtonWithServices.tsx
4688
- function ToolbarButtonWithServices_extends() { ToolbarButtonWithServices_extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return ToolbarButtonWithServices_extends.apply(this, arguments); }
4689
-
4713
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/Toolbar/ToolbarButtonGroupWithServices.tsx
4690
4714
 
4691
4715
 
4692
- function ToolbarButtonWithServices({
4693
- id,
4694
- type,
4695
- commands,
4716
+ function ToolbarButtonGroupWithServices({
4717
+ groupId,
4718
+ items,
4696
4719
  onInteraction,
4697
- servicesManager,
4698
- ...props
4720
+ size
4699
4721
  }) {
4700
- const {
4701
- toolbarService
4702
- } = servicesManager?.services || {};
4703
- const [buttonsState, setButtonState] = (0,react.useState)({
4704
- primaryToolId: '',
4705
- toggles: {},
4706
- groups: {}
4707
- });
4708
- const {
4709
- primaryToolId
4710
- } = buttonsState;
4711
- const isActive = type === 'tool' && id === primaryToolId || type === 'toggle' && buttonsState.toggles[id] === true;
4712
- (0,react.useEffect)(() => {
4713
- const {
4714
- unsubscribe
4715
- } = toolbarService.subscribe(toolbarService.EVENTS.TOOL_BAR_STATE_MODIFIED, state => {
4716
- setButtonState({
4717
- ...state
4722
+ const getSplitButtonItems = (0,react.useCallback)(items => items.map((item, index) => /*#__PURE__*/react.createElement(ui_src/* ToolbarButton */.IB, {
4723
+ key: item.id,
4724
+ icon: item.icon,
4725
+ label: item.label,
4726
+ disabled: item.disabled,
4727
+ className: item.className,
4728
+ id: item.id,
4729
+ size: size,
4730
+ onClick: () => {
4731
+ onInteraction({
4732
+ groupId,
4733
+ itemId: item.id,
4734
+ commands: item.commands
4718
4735
  });
4719
- });
4720
- return () => {
4721
- unsubscribe();
4722
- };
4723
- }, [toolbarService]);
4724
- return /*#__PURE__*/react.createElement(ui_src/* ToolbarButton */.IB, ToolbarButtonWithServices_extends({
4725
- commands: commands,
4726
- id: id,
4727
- type: type,
4728
- isActive: isActive,
4729
- onInteraction: onInteraction
4730
- }, props));
4736
+ }
4737
+ // Note: this is necessary since tooltip will add
4738
+ // default styles to the tooltip container which
4739
+ // we don't want for groups
4740
+ ,
4741
+ toolTipClassName: ""
4742
+ })), [onInteraction, groupId]);
4743
+ return /*#__PURE__*/react.createElement(ui_src/* ButtonGroup */.e2, null, getSplitButtonItems(items));
4731
4744
  }
4732
- ToolbarButtonWithServices.propTypes = {
4733
- id: (prop_types_default()).string.isRequired,
4734
- type: prop_types_default().oneOf(['tool', 'action', 'toggle']).isRequired,
4735
- commands: prop_types_default().arrayOf(prop_types_default().shape({
4736
- commandName: (prop_types_default()).string.isRequired,
4737
- context: (prop_types_default()).string
4738
- })),
4739
- onInteraction: (prop_types_default()).func.isRequired,
4740
- servicesManager: prop_types_default().shape({
4741
- services: prop_types_default().shape({
4742
- toolbarService: prop_types_default().shape({
4743
- subscribe: (prop_types_default()).func.isRequired,
4744
- state: prop_types_default().shape({
4745
- primaryToolId: (prop_types_default()).string,
4746
- toggles: prop_types_default().objectOf((prop_types_default()).bool),
4747
- groups: prop_types_default().objectOf((prop_types_default()).any)
4748
- }).isRequired
4749
- }).isRequired
4750
- }).isRequired
4751
- }).isRequired
4752
- };
4753
- /* harmony default export */ const Toolbar_ToolbarButtonWithServices = (ToolbarButtonWithServices);
4745
+ /* harmony default export */ const Toolbar_ToolbarButtonGroupWithServices = (ToolbarButtonGroupWithServices);
4754
4746
  ;// CONCATENATED MODULE: ../../../extensions/default/src/getToolbarModule.tsx
4755
4747
 
4756
4748
 
4757
4749
 
4758
4750
 
4751
+
4752
+ const getClassName = isToggled => {
4753
+ return {
4754
+ className: isToggled ? '!text-primary-active' : '!text-common-bright hover:!bg-primary-dark hover:text-primary-light'
4755
+ };
4756
+ };
4759
4757
  function getToolbarModule({
4760
4758
  commandsManager,
4761
4759
  servicesManager
4762
4760
  }) {
4761
+ const {
4762
+ cineService
4763
+ } = servicesManager.services;
4763
4764
  return [{
4764
- name: 'ohif.divider',
4765
- defaultComponent: ToolbarDivider,
4766
- clickHandler: () => {}
4767
- }, {
4768
- name: 'ohif.action',
4769
- defaultComponent: Toolbar_ToolbarButtonWithServices,
4770
- clickHandler: () => {}
4771
- }, {
4772
4765
  name: 'ohif.radioGroup',
4773
- defaultComponent: Toolbar_ToolbarButtonWithServices,
4774
- clickHandler: () => {}
4766
+ defaultComponent: ui_src/* ToolbarButton */.IB
4767
+ }, {
4768
+ name: 'ohif.divider',
4769
+ defaultComponent: ToolbarDivider
4775
4770
  }, {
4776
4771
  name: 'ohif.splitButton',
4777
- defaultComponent: Toolbar_ToolbarSplitButtonWithServices,
4778
- clickHandler: () => {}
4772
+ defaultComponent: Toolbar_ToolbarSplitButtonWithServices
4779
4773
  }, {
4780
4774
  name: 'ohif.layoutSelector',
4781
- defaultComponent: ToolbarLayoutSelector,
4782
- clickHandler: (evt, clickedBtn, btnSectionName) => {}
4775
+ defaultComponent: ToolbarLayoutSelector
4776
+ }, {
4777
+ name: 'ohif.buttonGroup',
4778
+ defaultComponent: Toolbar_ToolbarButtonGroupWithServices
4779
+ }, {
4780
+ name: 'evaluate.group.promoteToPrimary',
4781
+ evaluate: ({
4782
+ viewportId,
4783
+ button,
4784
+ itemId
4785
+ }) => {
4786
+ const {
4787
+ items
4788
+ } = button.props;
4789
+ if (!itemId) {
4790
+ return {
4791
+ primary: button.props.primary,
4792
+ items
4793
+ };
4794
+ }
4795
+
4796
+ // other wise we can move the clicked tool to the primary button
4797
+ const clickedItemProps = items.find(item => item.id === itemId || item.itemId === itemId);
4798
+ return {
4799
+ primary: clickedItemProps,
4800
+ items
4801
+ };
4802
+ }
4783
4803
  }, {
4784
- name: 'ohif.toggle',
4785
- defaultComponent: Toolbar_ToolbarButtonWithServices,
4786
- clickHandler: () => {}
4804
+ name: 'evaluate.cine',
4805
+ evaluate: () => {
4806
+ const isToggled = cineService.getState().isCineEnabled;
4807
+ return getClassName(isToggled);
4808
+ }
4787
4809
  }];
4788
4810
  }
4789
4811
  ;// CONCATENATED MODULE: ../../../extensions/default/src/CustomizableContextMenu/ContextMenuItemsBuilder.ts
@@ -4849,7 +4871,6 @@ function findMenu(menus, props, menuIdFilter) {
4849
4871
  }
4850
4872
  current = findIt.next();
4851
4873
  }
4852
- console.log('Menu chosen', menu?.id || 'NONE');
4853
4874
  return menu;
4854
4875
  }
4855
4876
 
@@ -5014,7 +5035,6 @@ class ContextMenuController {
5014
5035
  console.warn('Annotation is locked.');
5015
5036
  return;
5016
5037
  }
5017
- console.log('Getting items from', menus);
5018
5038
  const items = getMenuItems(selectorProps || contextMenuProps, event, menus, menuId);
5019
5039
  this.services.uiDialogService.dismiss({
5020
5040
  id: 'context-menu'
@@ -5833,8 +5853,8 @@ const findViewportsByPosition = (state, {
5833
5853
  };
5834
5854
  };
5835
5855
  /* harmony default export */ const src_findViewportsByPosition = (findViewportsByPosition);
5836
- // EXTERNAL MODULE: ./index.js + 33 modules
5837
- var index = __webpack_require__(45573);
5856
+ // EXTERNAL MODULE: ./index.js + 35 modules
5857
+ var index = __webpack_require__(68870);
5838
5858
  ;// CONCATENATED MODULE: ../../../extensions/default/src/commandsModule.ts
5839
5859
 
5840
5860
 
@@ -5923,45 +5943,6 @@ const commandsModule = ({
5923
5943
  clearMeasurements: () => {
5924
5944
  measurementService.clear();
5925
5945
  },
5926
- /**
5927
- * Toggles off all tools which contain a commandName of setHangingProtocol
5928
- * or toggleHangingProtocol, and which match/don't match the protocol id/stage
5929
- */
5930
- toggleHpTools: () => {
5931
- const {
5932
- protocol,
5933
- stageIndex: toggleStageIndex,
5934
- stage
5935
- } = hangingProtocolService.getActiveProtocol();
5936
- const enableListener = button => {
5937
- if (!button.id) {
5938
- return;
5939
- }
5940
- const {
5941
- commands,
5942
- items,
5943
- primary
5944
- } = button.props || button;
5945
- if (primary) {
5946
- enableListener(primary);
5947
- }
5948
- if (items) {
5949
- items.forEach(enableListener);
5950
- }
5951
- const hpCommand = commands?.find?.(isHangingProtocolCommand);
5952
- if (!hpCommand) {
5953
- return;
5954
- }
5955
- const {
5956
- protocolId,
5957
- stageIndex,
5958
- stageId
5959
- } = hpCommand.commandOptions;
5960
- const isActive = (!protocolId || protocolId === protocol.id) && (stageIndex === undefined || stageIndex === toggleStageIndex) && (!stageId || stageId === stage.id);
5961
- toolbarService.setToggled(button.id, isActive);
5962
- };
5963
- Object.values(toolbarService.getButtons()).forEach(enableListener);
5964
- },
5965
5946
  /**
5966
5947
  * Sets the specified protocol
5967
5948
  * 1. Records any existing state using the viewport grid service
@@ -5993,16 +5974,12 @@ const commandsModule = ({
5993
5974
  stageIndex,
5994
5975
  reset = false
5995
5976
  }) => {
5996
- const primaryToolBeforeHPChange = toolbarService.getActivePrimaryTool();
5997
5977
  try {
5998
5978
  // Stores in the state the display set selector id to displaySetUID mapping
5999
5979
  // Pass in viewportId for the active viewport. This item will get set as
6000
5980
  // the activeViewportId
6001
5981
  const state = viewportGridService.getState();
6002
5982
  const hpInfo = hangingProtocolService.getState();
6003
- const {
6004
- protocol: oldProtocol
6005
- } = hangingProtocolService.getActiveProtocol();
6006
5983
  const stateSyncReduce = reuseCachedLayouts(state, hangingProtocolService, stateSyncService);
6007
5984
  const {
6008
5985
  hangingProtocolStageIndexMap,
@@ -6052,31 +6029,9 @@ const commandsModule = ({
6052
6029
  // multi-study display.
6053
6030
  delete displaySetSelectorMap[`${activeStudyUID || hpInfo.activeStudyUID}:activeDisplaySet:0`];
6054
6031
  stateSyncService.store(stateSyncReduce);
6055
- // This is a default action applied
6056
- actions.toggleHpTools();
6057
-
6058
- // try to use the same tool in the new hanging protocol stage
6059
- const primaryButton = toolbarService.getButton(primaryToolBeforeHPChange);
6060
- if (primaryButton) {
6061
- // is there any type of interaction on this button, if not it might be in the
6062
- // items. This is a bit of a hack, but it works for now.
6063
-
6064
- let interactionType = primaryButton.props?.interactionType;
6065
- if (!interactionType && primaryButton.props?.items) {
6066
- const firstItem = primaryButton.props.items[0];
6067
- interactionType = firstItem.props?.interactionType || firstItem.props?.type;
6068
- }
6069
- if (interactionType) {
6070
- toolbarService.recordInteraction({
6071
- interactionType,
6072
- ...primaryButton.props
6073
- });
6074
- }
6075
- }
6076
6032
  return true;
6077
6033
  } catch (e) {
6078
6034
  console.error(e);
6079
- actions.toggleHpTools();
6080
6035
  uiNotificationService.show({
6081
6036
  title: 'Apply Hanging Protocol',
6082
6037
  message: 'The hanging protocol could not be applied.',
@@ -6418,61 +6373,43 @@ const commandsModule = ({
6418
6373
  commandFn: actions.closeContextMenu
6419
6374
  },
6420
6375
  clearMeasurements: {
6421
- commandFn: actions.clearMeasurements,
6422
- storeContexts: [],
6423
- options: {}
6376
+ commandFn: actions.clearMeasurements
6424
6377
  },
6425
6378
  displayNotification: {
6426
- commandFn: actions.displayNotification,
6427
- storeContexts: [],
6428
- options: {}
6379
+ commandFn: actions.displayNotification
6429
6380
  },
6430
6381
  setHangingProtocol: {
6431
- commandFn: actions.setHangingProtocol,
6432
- storeContexts: [],
6433
- options: {}
6382
+ commandFn: actions.setHangingProtocol
6434
6383
  },
6435
6384
  toggleHangingProtocol: {
6436
- commandFn: actions.toggleHangingProtocol,
6437
- storeContexts: [],
6438
- options: {}
6385
+ commandFn: actions.toggleHangingProtocol
6439
6386
  },
6440
6387
  navigateHistory: {
6441
- commandFn: actions.navigateHistory,
6442
- storeContexts: [],
6443
- options: {}
6388
+ commandFn: actions.navigateHistory
6444
6389
  },
6445
6390
  nextStage: {
6446
6391
  commandFn: actions.deltaStage,
6447
- storeContexts: [],
6448
6392
  options: {
6449
6393
  direction: 1
6450
6394
  }
6451
6395
  },
6452
6396
  previousStage: {
6453
6397
  commandFn: actions.deltaStage,
6454
- storeContexts: [],
6455
6398
  options: {
6456
6399
  direction: -1
6457
6400
  }
6458
6401
  },
6459
6402
  setViewportGridLayout: {
6460
- commandFn: actions.setViewportGridLayout,
6461
- storeContexts: [],
6462
- options: {}
6403
+ commandFn: actions.setViewportGridLayout
6463
6404
  },
6464
6405
  toggleOneUp: {
6465
- commandFn: actions.toggleOneUp,
6466
- storeContexts: [],
6467
- options: {}
6406
+ commandFn: actions.toggleOneUp
6468
6407
  },
6469
6408
  openDICOMTagViewer: {
6470
6409
  commandFn: actions.openDICOMTagViewer
6471
6410
  },
6472
6411
  updateViewportDisplaySet: {
6473
- commandFn: actions.updateViewportDisplaySet,
6474
- storeContexts: [],
6475
- options: {}
6412
+ commandFn: actions.updateViewportDisplaySet
6476
6413
  }
6477
6414
  };
6478
6415
  return {
@@ -7675,11 +7612,16 @@ const init_metadataProvider = src.classes.MetadataProvider;
7675
7612
  */
7676
7613
  function init({
7677
7614
  servicesManager,
7678
- configuration = {}
7615
+ configuration = {},
7616
+ commandsManager
7679
7617
  }) {
7680
7618
  const {
7681
- stateSyncService
7619
+ stateSyncService,
7620
+ toolbarService,
7621
+ cineService,
7622
+ viewportGridService
7682
7623
  } = servicesManager.services;
7624
+ toolbarService.registerEventForToolbarUpdate(cineService, [cineService.EVENTS.CINE_STATE_CHANGED]);
7683
7625
  // Add
7684
7626
  src.DicomMetadataStore.subscribe(src.DicomMetadataStore.EVENTS.INSTANCES_ADDED, handlePETImageMetadata);
7685
7627
 
@@ -7694,6 +7636,12 @@ function init({
7694
7636
  clearOnModeExit: true
7695
7637
  });
7696
7638
 
7639
+ // uiStateStore is a sync state which stores the relevant
7640
+ // UI state for the viewer
7641
+ stateSyncService.register('uiStateStore', {
7642
+ clearOnModeExit: true
7643
+ });
7644
+
7697
7645
  // displaySetSelectorMap stores a map from
7698
7646
  // `<activeStudyUID>:<displaySetSelectorId>:<matchOffset>` to
7699
7647
  // a displaySetInstanceUID, used to display named display sets in
@@ -7710,7 +7658,7 @@ function init({
7710
7658
  });
7711
7659
 
7712
7660
  // Stores a map from the to be applied hanging protocols `<activeStudyUID>:<protocolId>`
7713
- // to the previously applied hanging protolStageIndexMap key, in order to toggle
7661
+ // to the previously applied hanging protocolStageIndexMap key, in order to toggle
7714
7662
  // off the applied protocol and remember the old state.
7715
7663
  stateSyncService.register('toggleHangingProtocol', {
7716
7664
  clearOnModeExit: true
@@ -7722,6 +7670,47 @@ function init({
7722
7670
  stateSyncService.register('viewportsByPosition', {
7723
7671
  clearOnModeExit: true
7724
7672
  });
7673
+
7674
+ // Function to process and subscribe to events for a given set of commands and listeners
7675
+ const subscribeToEvents = listeners => {
7676
+ Object.entries(listeners).forEach(([event, commands]) => {
7677
+ const supportedEvents = [viewportGridService.EVENTS.ACTIVE_VIEWPORT_ID_CHANGED, viewportGridService.EVENTS.VIEWPORTS_READY];
7678
+ if (supportedEvents.includes(event)) {
7679
+ viewportGridService.subscribe(event, eventData => {
7680
+ const viewportId = eventData?.viewportId ?? viewportGridService.getActiveViewportId();
7681
+ commandsManager.run(commands, {
7682
+ viewportId
7683
+ });
7684
+ });
7685
+ }
7686
+ });
7687
+ };
7688
+ toolbarService.subscribe(toolbarService.EVENTS.TOOL_BAR_MODIFIED, state => {
7689
+ const {
7690
+ buttons
7691
+ } = state;
7692
+ for (const [id, button] of Object.entries(buttons)) {
7693
+ const {
7694
+ groupId,
7695
+ items,
7696
+ listeners
7697
+ } = button.props;
7698
+
7699
+ // Handle group items' listeners
7700
+ if (groupId && items) {
7701
+ items.forEach(item => {
7702
+ if (item.listeners) {
7703
+ subscribeToEvents(item.listeners);
7704
+ }
7705
+ });
7706
+ }
7707
+
7708
+ // Handle button listeners
7709
+ if (listeners) {
7710
+ subscribeToEvents(listeners);
7711
+ }
7712
+ }
7713
+ });
7725
7714
  }
7726
7715
  const handlePETImageMetadata = ({
7727
7716
  SeriesInstanceUID,