@ohif/app 3.8.0-beta.7 → 3.8.0-beta.71

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 (93) hide show
  1. package/dist/{220.bundle.f7e1c96c94245e70f2be.js → 109.bundle.b4fee2a22b622839baf5.js} +4466 -3715
  2. package/dist/{471.bundle.49c8d281adbae4a2c4df.js → 121.bundle.21827fec690c01ee9ab3.js} +85 -112
  3. package/dist/{19.bundle.e5579df6d7b74af50b1d.js → 155.bundle.091ace1591aff1f6b679.js} +334 -287
  4. package/dist/{687.bundle.9d0330ea5d61fe3117da.js → 164.bundle.fcc94cd4e142a409769d.js} +22 -38
  5. package/dist/17dd54813d5acc10bf8f.wasm +0 -0
  6. package/dist/{506.bundle.ab8226d3d81abe874544.js → 188.bundle.0081530bd886a18676eb.js} +23 -28
  7. package/dist/191.bundle.ef35ed1f90a988b3952b.js +30360 -0
  8. package/dist/{221.bundle.c2dc03d8fa4235dc1285.js → 2.bundle.ab8937194aad592bc7b4.js} +351 -546
  9. package/dist/20fc4c659b85ccd2a9c0.wasm +0 -0
  10. package/dist/290.bundle.952de53057f98e2c5ef0.js +8883 -0
  11. package/dist/{451.bundle.57c21db5d003c75e9d61.js → 295.bundle.3a0d5062d65296c4bf5d.js} +102 -127
  12. package/dist/{125.bundle.253395f320b72180da63.js → 297.bundle.194d8985ab974839b5b6.js} +7 -8
  13. package/dist/{202.bundle.d3490836f71e001dd30f.js → 342.bundle.9be178d7555a64de203a.js} +544 -860
  14. package/dist/41.bundle.b5a6c70f88cf565cad3e.js +874 -0
  15. package/dist/425.bundle.e44cfce041ba5209a878.js +2957 -0
  16. package/dist/425.css +2 -0
  17. package/dist/{126.bundle.42df2dafc9c0310da188.js → 448.bundle.599d81471e1d7f7962bc.js} +361 -427
  18. package/dist/{957.bundle.9ea4506963ef8b2d84ba.js → 504.bundle.5ccd6d4269fa77a0a7e7.js} +14338 -27291
  19. package/dist/{886.bundle.c8dd3ecc42a4253de278.js → 530.bundle.566bfd08dccb4cf6d98b.js} +75 -105
  20. package/dist/{250.bundle.aea3335667054bdefe36.js → 544.bundle.1110b24e96863d719a95.js} +39 -56
  21. package/dist/{663.bundle.9f359963019cd8ccf8f9.js → 559.bundle.fb8ac10c41eb734e2f3d.js} +151 -147
  22. package/dist/{181.bundle.a62b9f0ec692299acb35.js → 574.bundle.b262cbe9f2afd7275271.js} +1286 -307
  23. package/dist/{181.css → 574.css} +1 -1
  24. package/dist/{410.bundle.38c9d3820e152e89288e.js → 594.bundle.b70ca7a91d85ebd5d8c4.js} +183 -221
  25. package/dist/{776.bundle.004382036bdbd8ee2b95.js → 595.bundle.c25147a450c67defb3d5.js} +3157 -1029
  26. package/dist/{774.bundle.4b2dc46a35012b898e1a.js → 644.bundle.1e77691d2eeb96a423b0.js} +1852 -8945
  27. package/dist/699.bundle.02c15c3cc4c04dbf7f51.js +785 -0
  28. package/dist/{359.bundle.8abe0036a7bf6b5fd115.js → 724.bundle.d50ce9fb0ab01b9378b7.js} +130 -254
  29. package/dist/{757.bundle.ec8301d8e70d2b990f65.js → 726.bundle.c8de818cf1a3ff0cf7d2.js} +512 -879
  30. package/dist/{530.bundle.a03b6f942ace3e1baa1e.js → 835.bundle.15aff0b7433bb0dd6d6d.js} +37 -30
  31. package/dist/{822.bundle.82cdc418f8f56da6060b.js → 862.bundle.809c87a7ba9da6fb29c8.js} +77 -96
  32. package/dist/{236.bundle.c9e70d55e7b2574c1ecd.js → 889.bundle.1c17d0d13e157ac21d38.js} +198 -197
  33. package/dist/{342.bundle.d9668551811e3a88aaa4.js → 90.bundle.27637ef740946d5c8948.js} +1429 -1055
  34. package/dist/{281.bundle.16a2933086a57e60c96c.js → 905.bundle.206e44c3bbd1df1a900b.js} +155 -122
  35. package/dist/{814.bundle.a1aba9c1e3d336008351.js → 907.bundle.11700f7af989b5af8bc3.js} +16 -30
  36. package/dist/{417.bundle.af0a207c29b109f84159.js → 931.bundle.d270a1fda9a2836c3cc5.js} +26 -26
  37. package/dist/{686.bundle.dccef1f36e4bc79bcc48.js → 939.bundle.9d93b2e47c52338747a2.js} +7 -8
  38. package/dist/{12.bundle.37a8b47d2ae587cb9226.js → 961.bundle.a1ffb667eb04cbe07210.js} +16 -31
  39. package/dist/987.bundle.6bdfb3cd8762b8889632.js +122950 -0
  40. package/dist/app-config.js +1 -0
  41. package/dist/app.bundle.css +15 -13
  42. package/dist/{app.bundle.437d085e13599d1e1ced.js → app.bundle.d1c8b09ab30d221fddf0.js} +148259 -61821
  43. package/dist/assets/images/CT-AAA.png +0 -0
  44. package/dist/assets/images/CT-AAA2.png +0 -0
  45. package/dist/assets/images/CT-Air.png +0 -0
  46. package/dist/assets/images/CT-Bone.png +0 -0
  47. package/dist/assets/images/CT-Bones.png +0 -0
  48. package/dist/assets/images/CT-Cardiac.png +0 -0
  49. package/dist/assets/images/CT-Cardiac2.png +0 -0
  50. package/dist/assets/images/CT-Cardiac3.png +0 -0
  51. package/dist/assets/images/CT-Chest-Contrast-Enhanced.png +0 -0
  52. package/dist/assets/images/CT-Chest-Vessels.png +0 -0
  53. package/dist/assets/images/CT-Coronary-Arteries-2.png +0 -0
  54. package/dist/assets/images/CT-Coronary-Arteries-3.png +0 -0
  55. package/dist/assets/images/CT-Coronary-Arteries.png +0 -0
  56. package/dist/assets/images/CT-Cropped-Volume-Bone.png +0 -0
  57. package/dist/assets/images/CT-Fat.png +0 -0
  58. package/dist/assets/images/CT-Liver-Vasculature.png +0 -0
  59. package/dist/assets/images/CT-Lung.png +0 -0
  60. package/dist/assets/images/CT-MIP.png +0 -0
  61. package/dist/assets/images/CT-Muscle.png +0 -0
  62. package/dist/assets/images/CT-Pulmonary-Arteries.png +0 -0
  63. package/dist/assets/images/CT-Soft-Tissue.png +0 -0
  64. package/dist/assets/images/DTI-FA-Brain.png +0 -0
  65. package/dist/assets/images/MR-Angio.png +0 -0
  66. package/dist/assets/images/MR-Default.png +0 -0
  67. package/dist/assets/images/MR-MIP.png +0 -0
  68. package/dist/assets/images/MR-T2-Brain.png +0 -0
  69. package/dist/assets/images/VolumeRendering.png +0 -0
  70. package/dist/cornerstoneDICOMImageLoader.min.js +1 -1
  71. package/dist/cornerstoneDICOMImageLoader.min.js.map +1 -1
  72. package/dist/{dicom-microscopy-viewer.bundle.2c146384eb9466d02ff8.js → dicom-microscopy-viewer.bundle.d3a56dc9f62df5e11019.js} +3 -3
  73. package/dist/index.html +1 -1
  74. package/dist/{index.worker.e62ecca63f1a2e124230.worker.js → index.worker.64c896c4316fcd506666.worker.js} +2 -2
  75. package/dist/index.worker.64c896c4316fcd506666.worker.js.map +1 -0
  76. package/dist/polySeg.bundle.01449e456b7d4a737d4f.js +252 -0
  77. package/dist/serve.json +12 -0
  78. package/dist/sw.js +1 -1
  79. package/package.json +25 -22
  80. package/dist/23.bundle.e008ad788170f2ed5569.js +0 -900
  81. package/dist/604.bundle.a51f83e64004bca5f497.js +0 -1848
  82. package/dist/613.bundle.aed640a7900dbcb688f5.js +0 -532
  83. package/dist/743.bundle.489f7df3a089d4d374e1.js +0 -78007
  84. package/dist/75788f12450d4c5ed494.wasm +0 -0
  85. package/dist/775.bundle.2285e7e0e67878948c0d.js +0 -1009
  86. package/dist/788.bundle.dcd53828d1bb2ac64d04.js +0 -2682
  87. package/dist/82.bundle.5a94dd7645e5c5476f59.js +0 -1049
  88. package/dist/index.worker.e62ecca63f1a2e124230.worker.js.map +0 -1
  89. /package/dist/{19.css → 155.css} +0 -0
  90. /package/dist/{221.css → 2.css} +0 -0
  91. /package/dist/{579.css → 481.css} +0 -0
  92. /package/dist/{250.css → 544.css} +0 -0
  93. /package/dist/{776.css → 595.css} +0 -0
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
- (self["webpackChunk"] = self["webpackChunk"] || []).push([[181],{
2
+ (globalThis["webpackChunk"] = globalThis["webpackChunk"] || []).push([[574],{
3
3
 
4
- /***/ 86181:
4
+ /***/ 71574:
5
5
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
6
6
 
7
7
  // ESM COMPAT FLAG
@@ -13,22 +13,22 @@ __webpack_require__.d(__webpack_exports__, {
13
13
  });
14
14
 
15
15
  // EXTERNAL MODULE: ../../../node_modules/react/index.js
16
- var react = __webpack_require__(43001);
16
+ var react = __webpack_require__(41766);
17
17
  // EXTERNAL MODULE: ../../../node_modules/react-resize-detector/build/index.esm.js
18
- var index_esm = __webpack_require__(7023);
18
+ var index_esm = __webpack_require__(78668);
19
19
  // EXTERNAL MODULE: ../../../node_modules/prop-types/index.js
20
- var prop_types = __webpack_require__(3827);
20
+ var prop_types = __webpack_require__(11374);
21
21
  var prop_types_default = /*#__PURE__*/__webpack_require__.n(prop_types);
22
- // EXTERNAL MODULE: ../../../node_modules/@cornerstonejs/tools/dist/esm/index.js + 348 modules
23
- var esm = __webpack_require__(14957);
24
- // EXTERNAL MODULE: ../../../node_modules/@cornerstonejs/core/dist/esm/index.js + 331 modules
25
- var dist_esm = __webpack_require__(3743);
26
- // EXTERNAL MODULE: ../../core/src/index.ts + 65 modules
27
- var src = __webpack_require__(71771);
28
- // EXTERNAL MODULE: ../../ui/src/index.js + 486 modules
29
- var ui_src = __webpack_require__(22582);
22
+ // EXTERNAL MODULE: ../../../node_modules/@cornerstonejs/tools/dist/esm/index.js + 16 modules
23
+ var esm = __webpack_require__(20767);
24
+ // EXTERNAL MODULE: ../../../node_modules/@cornerstonejs/core/dist/esm/index.js + 383 modules
25
+ var dist_esm = __webpack_require__(50719);
26
+ // EXTERNAL MODULE: ../../core/src/index.ts + 68 modules
27
+ var src = __webpack_require__(85073);
28
+ // EXTERNAL MODULE: ../../ui/src/index.js + 542 modules
29
+ var ui_src = __webpack_require__(48804);
30
30
  // EXTERNAL MODULE: ../../../extensions/cornerstone/src/state.ts
31
- var state = __webpack_require__(73704);
31
+ var state = __webpack_require__(71353);
32
32
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/Viewport/OHIFCornerstoneViewport.css
33
33
  // extracted by mini-css-extract-plugin
34
34
 
@@ -38,16 +38,15 @@ var state = __webpack_require__(73704);
38
38
 
39
39
 
40
40
 
41
- function CornerstoneImageScrollbar(_ref) {
42
- let {
43
- viewportData,
44
- viewportId,
45
- element,
46
- imageSliceData,
47
- setImageSliceData,
48
- scrollbarHeight,
49
- servicesManager
50
- } = _ref;
41
+ function CornerstoneImageScrollbar({
42
+ viewportData,
43
+ viewportId,
44
+ element,
45
+ imageSliceData,
46
+ setImageSliceData,
47
+ scrollbarHeight,
48
+ servicesManager
49
+ }) {
51
50
  const {
52
51
  cineService,
53
52
  cornerstoneViewportService
@@ -140,7 +139,7 @@ function CornerstoneImageScrollbar(_ref) {
140
139
  element.removeEventListener(dist_esm.Enums.Events.VOLUME_NEW_IMAGE, updateVolumeIndex);
141
140
  };
142
141
  }, [viewportData, element]);
143
- return /*#__PURE__*/react.createElement(ui_src/* ImageScrollbar */.Ln, {
142
+ return /*#__PURE__*/react.createElement(ui_src/* ImageScrollbar */.uq, {
144
143
  onChange: evt => onImageScrollbarChange(evt, viewportId),
145
144
  max: imageSliceData.numberOfSlices ? imageSliceData.numberOfSlices - 1 : 0,
146
145
  height: scrollbarHeight,
@@ -158,9 +157,9 @@ CornerstoneImageScrollbar.propTypes = {
158
157
  };
159
158
  /* harmony default export */ const ViewportImageScrollbar = (CornerstoneImageScrollbar);
160
159
  // EXTERNAL MODULE: ../../../node_modules/gl-matrix/esm/index.js + 10 modules
161
- var gl_matrix_esm = __webpack_require__(45451);
160
+ var gl_matrix_esm = __webpack_require__(83636);
162
161
  // EXTERNAL MODULE: ../../../node_modules/moment/moment.js
163
- var moment = __webpack_require__(71271);
162
+ var moment = __webpack_require__(8291);
164
163
  var moment_default = /*#__PURE__*/__webpack_require__.n(moment);
165
164
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/Viewport/Overlays/utils.ts
166
165
 
@@ -183,8 +182,7 @@ function isValidNumber(value) {
183
182
  * @param {number} precision
184
183
  * @returns {number} formatted number.
185
184
  */
186
- function formatNumberPrecision(number) {
187
- let precision = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
185
+ function formatNumberPrecision(number, precision = 0) {
188
186
  if (number !== null) {
189
187
  return parseFloat(number).toFixed(precision);
190
188
  }
@@ -197,8 +195,7 @@ function formatNumberPrecision(number) {
197
195
  * @param {string} strFormat
198
196
  * @returns {string} formatted date.
199
197
  */
200
- function formatDICOMDate(date) {
201
- let strFormat = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'MMM D, YYYY';
198
+ function formatDICOMDate(date, strFormat = 'MMM D, YYYY') {
202
199
  return moment_default()(date, 'YYYYMMDD').format(strFormat);
203
200
  }
204
201
 
@@ -215,8 +212,7 @@ function formatDICOMDate(date) {
215
212
  * @param {string} strFormat
216
213
  * @returns {string} formatted name.
217
214
  */
218
- function formatDICOMTime(time) {
219
- let strFormat = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'HH:mm:ss';
215
+ function formatDICOMTime(time, strFormat = 'HH:mm:ss') {
220
216
  return moment_default()(time, 'HH:mm:ss').format(strFormat);
221
217
  }
222
218
 
@@ -266,104 +262,32 @@ function getCompression(imageId) {
266
262
 
267
263
 
268
264
  const EPSILON = 1e-4;
269
- /**
270
- * Window Level / Center Overlay item
271
- */
272
- function VOIOverlayItem(_ref) {
273
- let {
274
- voi,
275
- customization
276
- } = _ref;
277
- const {
278
- windowWidth,
279
- windowCenter
280
- } = voi;
281
- if (typeof windowCenter !== 'number' || typeof windowWidth !== 'number') {
282
- return null;
283
- }
284
- return /*#__PURE__*/react.createElement("div", {
285
- className: "overlay-item flex flex-row",
286
- style: {
287
- color: customization && customization.color || undefined
288
- }
289
- }, /*#__PURE__*/react.createElement("span", {
290
- className: "mr-1 shrink-0"
291
- }, "W:"), /*#__PURE__*/react.createElement("span", {
292
- className: "ml-1 mr-2 shrink-0 font-light"
293
- }, windowWidth.toFixed(0)), /*#__PURE__*/react.createElement("span", {
294
- className: "mr-1 shrink-0"
295
- }, "L:"), /*#__PURE__*/react.createElement("span", {
296
- className: "ml-1 shrink-0 font-light"
297
- }, windowCenter.toFixed(0)));
298
- }
299
-
300
- /**
301
- * Zoom Level Overlay item
302
- */
303
- function ZoomOverlayItem(_ref2) {
304
- let {
305
- scale,
306
- customization
307
- } = _ref2;
308
- return /*#__PURE__*/react.createElement("div", {
309
- className: "overlay-item flex flex-row",
310
- style: {
311
- color: customization && customization.color || undefined
312
- }
313
- }, /*#__PURE__*/react.createElement("span", {
314
- className: "mr-1 shrink-0"
315
- }, "Zoom:"), /*#__PURE__*/react.createElement("span", {
316
- className: "font-light"
317
- }, scale.toFixed(2), "x"));
318
- }
319
-
320
- /**
321
- * Instance Number Overlay Item
322
- */
323
- function InstanceNumberOverlayItem(_ref3) {
324
- let {
325
- instanceNumber,
326
- imageSliceData,
327
- customization
328
- } = _ref3;
329
- const {
330
- imageIndex,
331
- numberOfSlices
332
- } = imageSliceData;
333
- return /*#__PURE__*/react.createElement("div", {
334
- className: "overlay-item flex flex-row",
335
- style: {
336
- color: customization && customization.color || undefined
337
- }
338
- }, /*#__PURE__*/react.createElement("span", {
339
- className: "mr-1 shrink-0"
340
- }, "I:"), /*#__PURE__*/react.createElement("span", {
341
- className: "font-light"
342
- }, instanceNumber !== undefined && instanceNumber !== null ? `${instanceNumber} (${imageIndex + 1}/${numberOfSlices})` : `${imageIndex + 1}/${numberOfSlices}`));
343
- }
265
+ const OverlayItemComponents = {
266
+ 'ohif.overlayItem.windowLevel': VOIOverlayItem,
267
+ 'ohif.overlayItem.zoomLevel': ZoomOverlayItem,
268
+ 'ohif.overlayItem.instanceNumber': InstanceNumberOverlayItem
269
+ };
344
270
 
345
271
  /**
346
272
  * Customizable Viewport Overlay
347
273
  */
348
- function CustomizableViewportOverlay(_ref4) {
349
- let {
350
- element,
351
- viewportData,
352
- imageSliceData,
353
- viewportId,
354
- servicesManager
355
- } = _ref4;
274
+ function CustomizableViewportOverlay({
275
+ element,
276
+ viewportData,
277
+ imageSliceData,
278
+ viewportId,
279
+ servicesManager
280
+ }) {
356
281
  const {
357
- toolbarService,
358
282
  cornerstoneViewportService,
359
- customizationService
283
+ customizationService,
284
+ toolGroupService
360
285
  } = servicesManager.services;
361
286
  const [voi, setVOI] = (0,react.useState)({
362
287
  windowCenter: null,
363
288
  windowWidth: null
364
289
  });
365
290
  const [scale, setScale] = (0,react.useState)(1);
366
- const [activeTools, setActiveTools] = (0,react.useState)([]);
367
291
  const {
368
292
  imageIndex
369
293
  } = imageSliceData;
@@ -371,26 +295,14 @@ function CustomizableViewportOverlay(_ref4) {
371
295
  const topRightCustomization = customizationService.getModeCustomization('cornerstoneOverlayTopRight');
372
296
  const bottomLeftCustomization = customizationService.getModeCustomization('cornerstoneOverlayBottomLeft');
373
297
  const bottomRightCustomization = customizationService.getModeCustomization('cornerstoneOverlayBottomRight');
374
- const instance = (0,react.useMemo)(() => {
298
+ const instances = (0,react.useMemo)(() => {
375
299
  if (viewportData != null) {
376
- return _getViewportInstance(viewportData, imageIndex);
300
+ return _getViewportInstances(viewportData);
377
301
  } else {
378
302
  return null;
379
303
  }
380
304
  }, [viewportData, imageIndex]);
381
- const instanceNumber = (0,react.useMemo)(() => {
382
- if (viewportData != null) {
383
- return _getInstanceNumber(viewportData, viewportId, imageIndex, cornerstoneViewportService);
384
- }
385
- return null;
386
- }, [viewportData, viewportId, imageIndex, cornerstoneViewportService]);
387
-
388
- /**
389
- * Initial toolbar state
390
- */
391
- (0,react.useEffect)(() => {
392
- setActiveTools(toolbarService.getActiveTools());
393
- }, []);
305
+ const instanceNumber = (0,react.useMemo)(() => viewportData ? getInstanceNumber(viewportData, viewportId, imageIndex, cornerstoneViewportService) : null, [viewportData, viewportId, imageIndex, cornerstoneViewportService]);
394
306
 
395
307
  /**
396
308
  * Updating the VOI when the viewport changes its voi
@@ -457,21 +369,6 @@ function CustomizableViewportOverlay(_ref4) {
457
369
  element.removeEventListener(dist_esm.Enums.Events.CAMERA_MODIFIED, updateScale);
458
370
  };
459
371
  }, [viewportId, viewportData, cornerstoneViewportService, element]);
460
-
461
- /**
462
- * Updating the active tools when the toolbar changes
463
- */
464
- // Todo: this should act on the toolGroups instead of the toolbar state
465
- (0,react.useEffect)(() => {
466
- const {
467
- unsubscribe
468
- } = toolbarService.subscribe(toolbarService.EVENTS.TOOL_BAR_STATE_MODIFIED, () => {
469
- setActiveTools(toolbarService.getActiveTools());
470
- });
471
- return () => {
472
- unsubscribe();
473
- };
474
- }, [toolbarService]);
475
372
  const _renderOverlayItem = (0,react.useCallback)(item => {
476
373
  const overlayItemProps = {
477
374
  element,
@@ -486,87 +383,130 @@ function CustomizableViewportOverlay(_ref4) {
486
383
  formatTime: formatDICOMTime,
487
384
  formatNumberPrecision: formatNumberPrecision
488
385
  },
489
- instance,
490
- // calculated
386
+ instance: instances ? instances[item?.instanceIndex] : null,
491
387
  voi,
492
388
  scale,
493
389
  instanceNumber
494
390
  };
495
- if (item.customizationType === 'ohif.overlayItem.windowLevel') {
496
- return /*#__PURE__*/react.createElement(VOIOverlayItem, overlayItemProps);
497
- } else if (item.customizationType === 'ohif.overlayItem.zoomLevel') {
498
- return /*#__PURE__*/react.createElement(ZoomOverlayItem, overlayItemProps);
499
- } else if (item.customizationType === 'ohif.overlayItem.instanceNumber') {
500
- return /*#__PURE__*/react.createElement(InstanceNumberOverlayItem, overlayItemProps);
391
+ if (!item) {
392
+ return null;
393
+ }
394
+ const {
395
+ customizationType
396
+ } = item;
397
+ const OverlayItemComponent = OverlayItemComponents[customizationType];
398
+ if (OverlayItemComponent) {
399
+ return /*#__PURE__*/react.createElement(OverlayItemComponent, overlayItemProps);
501
400
  } else {
502
401
  const renderItem = customizationService.transform(item);
503
402
  if (typeof renderItem.content === 'function') {
504
403
  return renderItem.content(overlayItemProps);
505
404
  }
506
405
  }
507
- }, [element, viewportData, imageSliceData, viewportId, servicesManager, customizationService, instance, voi, scale, instanceNumber]);
508
- const getTopLeftContent = (0,react.useCallback)(() => {
509
- const items = topLeftCustomization?.items || [{
406
+ }, [element, viewportData, imageSliceData, viewportId, servicesManager, customizationService, instances, voi, scale, instanceNumber]);
407
+ const getContent = (0,react.useCallback)((customization, defaultItems, keyPrefix) => {
408
+ const items = customization?.items ?? defaultItems;
409
+ return /*#__PURE__*/react.createElement(react.Fragment, null, items.map((item, index) => /*#__PURE__*/react.createElement("div", {
410
+ key: `${keyPrefix}_${index}`
411
+ }, item?.condition ? item.condition({
412
+ instance: instances ? instances[item?.instanceIndex] : null,
413
+ formatters: {
414
+ formatDate: formatDICOMDate
415
+ }
416
+ }) ? _renderOverlayItem(item) : null : _renderOverlayItem(item))));
417
+ }, [_renderOverlayItem]);
418
+ const studyDateItem = {
419
+ id: 'StudyDate',
420
+ customizationType: 'ohif.overlayItem',
421
+ label: '',
422
+ title: 'Study date',
423
+ condition: ({
424
+ instance
425
+ }) => instance && instance.StudyDate,
426
+ contentF: ({
427
+ instance,
428
+ formatters: {
429
+ formatDate
430
+ }
431
+ }) => formatDate(instance.StudyDate)
432
+ };
433
+ const seriesDescriptionItem = {
434
+ id: 'SeriesDescription',
435
+ customizationType: 'ohif.overlayItem',
436
+ label: '',
437
+ title: 'Series description',
438
+ attribute: 'SeriesDescription',
439
+ condition: ({
440
+ instance
441
+ }) => {
442
+ return instance && instance.SeriesDescription;
443
+ }
444
+ };
445
+ const topLeftItems = instances ? instances.map((instance, index) => {
446
+ return [{
447
+ ...studyDateItem,
448
+ instanceIndex: index
449
+ }, {
450
+ ...seriesDescriptionItem,
451
+ instanceIndex: index
452
+ }];
453
+ }).flat() : [];
454
+ return /*#__PURE__*/react.createElement(ui_src/* ViewportOverlay */.pU, {
455
+ topLeft:
456
+ /**
457
+ * Inline default overlay items for a more standard expansion
458
+ */
459
+ getContent(topLeftCustomization, [...topLeftItems], 'topLeftOverlayItem'),
460
+ topRight: getContent(topRightCustomization, [], 'topRightOverlayItem'),
461
+ bottomLeft: getContent(bottomLeftCustomization, [{
510
462
  id: 'WindowLevel',
511
463
  customizationType: 'ohif.overlayItem.windowLevel'
512
- }];
513
- return /*#__PURE__*/react.createElement(react.Fragment, null, items.map((item, i) => /*#__PURE__*/react.createElement("div", {
514
- key: `topLeftOverlayItem_${i}`
515
- }, _renderOverlayItem(item))));
516
- }, [topLeftCustomization, _renderOverlayItem]);
517
- const getTopRightContent = (0,react.useCallback)(() => {
518
- const items = topRightCustomization?.items || [{
519
- id: 'InstanceNmber',
464
+ }, {
465
+ id: 'ZoomLevel',
466
+ customizationType: 'ohif.overlayItem.zoomLevel',
467
+ condition: () => {
468
+ const activeToolName = toolGroupService.getActiveToolForViewport(viewportId);
469
+ return activeToolName === 'Zoom';
470
+ }
471
+ }], 'bottomLeftOverlayItem'),
472
+ bottomRight: getContent(bottomRightCustomization, [{
473
+ id: 'InstanceNumber',
520
474
  customizationType: 'ohif.overlayItem.instanceNumber'
521
- }];
522
- return /*#__PURE__*/react.createElement(react.Fragment, null, items.map((item, i) => /*#__PURE__*/react.createElement("div", {
523
- key: `topRightOverlayItem_${i}`
524
- }, _renderOverlayItem(item))));
525
- }, [topRightCustomization, _renderOverlayItem]);
526
- const getBottomLeftContent = (0,react.useCallback)(() => {
527
- const items = bottomLeftCustomization?.items || [];
528
- return /*#__PURE__*/react.createElement(react.Fragment, null, items.map((item, i) => /*#__PURE__*/react.createElement("div", {
529
- key: `bottomLeftOverlayItem_${i}`
530
- }, _renderOverlayItem(item))));
531
- }, [bottomLeftCustomization, _renderOverlayItem]);
532
- const getBottomRightContent = (0,react.useCallback)(() => {
533
- const items = bottomRightCustomization?.items || [];
534
- return /*#__PURE__*/react.createElement(react.Fragment, null, items.map((item, i) => /*#__PURE__*/react.createElement("div", {
535
- key: `bottomRightOverlayItem_${i}`
536
- }, _renderOverlayItem(item))));
537
- }, [bottomRightCustomization, _renderOverlayItem]);
538
- return /*#__PURE__*/react.createElement(ui_src/* ViewportOverlay */.No, {
539
- topLeft: getTopLeftContent(),
540
- topRight: getTopRightContent(),
541
- bottomLeft: getBottomLeftContent(),
542
- bottomRight: getBottomRightContent()
475
+ }], 'bottomRightOverlayItem')
543
476
  });
544
477
  }
545
- function _getViewportInstance(viewportData, imageIndex) {
546
- let imageId = null;
478
+ function _getViewportInstances(viewportData) {
479
+ const imageIds = [];
547
480
  if (viewportData.viewportType === dist_esm.Enums.ViewportType.STACK) {
548
- imageId = viewportData.data.imageIds[imageIndex];
481
+ imageIds.push(viewportData.data.imageIds[0]);
549
482
  } else if (viewportData.viewportType === dist_esm.Enums.ViewportType.ORTHOGRAPHIC) {
550
483
  const volumes = viewportData.data;
551
- if (volumes && volumes.length == 1) {
552
- const volume = volumes[0];
553
- imageId = volume.imageIds[imageIndex];
554
- }
484
+ volumes.forEach(volume => {
485
+ if (!volume?.imageIds) {
486
+ return;
487
+ }
488
+ imageIds.push(volume.imageIds[0]);
489
+ });
555
490
  }
556
- return imageId ? dist_esm.metaData.get('instance', imageId) || {} : {};
491
+ const instances = [];
492
+ imageIds.forEach(imageId => {
493
+ const instance = dist_esm.metaData.get('instance', imageId) || {};
494
+ instances.push(instance);
495
+ });
496
+ return instances;
557
497
  }
558
- function _getInstanceNumber(viewportData, viewportId, imageIndex, cornerstoneViewportService) {
498
+ const getInstanceNumber = (viewportData, viewportId, imageIndex, cornerstoneViewportService) => {
559
499
  let instanceNumber;
560
- if (viewportData.viewportType === dist_esm.Enums.ViewportType.STACK) {
561
- instanceNumber = _getInstanceNumberFromStack(viewportData, imageIndex);
562
- if (!instanceNumber && instanceNumber !== 0) {
563
- return null;
564
- }
565
- } else if (viewportData.viewportType === dist_esm.Enums.ViewportType.ORTHOGRAPHIC) {
566
- instanceNumber = _getInstanceNumberFromVolume(viewportData, imageIndex, viewportId, cornerstoneViewportService);
500
+ switch (viewportData.viewportType) {
501
+ case dist_esm.Enums.ViewportType.STACK:
502
+ instanceNumber = _getInstanceNumberFromStack(viewportData, imageIndex);
503
+ break;
504
+ case dist_esm.Enums.ViewportType.ORTHOGRAPHIC:
505
+ instanceNumber = _getInstanceNumberFromVolume(viewportData, viewportId, cornerstoneViewportService, imageIndex);
506
+ break;
567
507
  }
568
- return instanceNumber;
569
- }
508
+ return instanceNumber ?? null;
509
+ };
570
510
  function _getInstanceNumberFromStack(viewportData, imageIndex) {
571
511
  const imageIds = viewportData.data.imageIds;
572
512
  const imageId = imageIds[imageIndex];
@@ -587,14 +527,16 @@ function _getInstanceNumberFromStack(viewportData, imageIndex) {
587
527
  // Since volume viewports can be in any view direction, they can render
588
528
  // a reconstructed image which don't have imageIds; therefore, no instance and instanceNumber
589
529
  // Here we check if viewport is in the acquisition direction and if so, we get the instanceNumber
590
- function _getInstanceNumberFromVolume(viewportData, viewportId, cornerstoneViewportService) {
591
- const volumes = viewportData.volumes;
592
-
593
- // Todo: support fusion of acquisition plane which has instanceNumber
594
- if (!volumes || volumes.length > 1) {
530
+ function _getInstanceNumberFromVolume(viewportData, viewportId, cornerstoneViewportService, imageIndex) {
531
+ const volumes = viewportData.data;
532
+ if (!volumes) {
595
533
  return;
596
534
  }
597
- const volume = volumes[0];
535
+
536
+ // Todo: support fusion of acquisition plane which has instanceNumber
537
+ const {
538
+ volume
539
+ } = volumes[0];
598
540
  const {
599
541
  direction,
600
542
  imageIds
@@ -612,8 +554,8 @@ function _getInstanceNumberFromVolume(viewportData, viewportId, cornerstoneViewp
612
554
  const scanAxisNormal = direction.slice(6, 9);
613
555
 
614
556
  // check if viewPlaneNormal is parallel to scanAxisNormal
615
- const cross = gl_matrix_esm/* vec3.cross */.R3.cross(gl_matrix_esm/* vec3.create */.R3.create(), viewPlaneNormal, scanAxisNormal);
616
- const isAcquisitionPlane = gl_matrix_esm/* vec3.length */.R3.length(cross) < EPSILON;
557
+ const cross = gl_matrix_esm/* vec3.cross */.eR.cross(gl_matrix_esm/* vec3.create */.eR.create(), viewPlaneNormal, scanAxisNormal);
558
+ const isAcquisitionPlane = gl_matrix_esm/* vec3.length */.eR.length(cross) < EPSILON;
617
559
  if (isAcquisitionPlane) {
618
560
  const imageId = imageIds[imageIndex];
619
561
  if (!imageId) {
@@ -625,6 +567,75 @@ function _getInstanceNumberFromVolume(viewportData, viewportId, cornerstoneViewp
625
567
  return parseInt(instanceNumber);
626
568
  }
627
569
  }
570
+
571
+ /**
572
+ * Window Level / Center Overlay item
573
+ */
574
+ function VOIOverlayItem({
575
+ voi,
576
+ customization
577
+ }) {
578
+ const {
579
+ windowWidth,
580
+ windowCenter
581
+ } = voi;
582
+ if (typeof windowCenter !== 'number' || typeof windowWidth !== 'number') {
583
+ return null;
584
+ }
585
+ return /*#__PURE__*/react.createElement("div", {
586
+ className: "overlay-item flex flex-row",
587
+ style: {
588
+ color: customization && customization.color || undefined
589
+ }
590
+ }, /*#__PURE__*/react.createElement("span", {
591
+ className: "mr-1 shrink-0"
592
+ }, "W:"), /*#__PURE__*/react.createElement("span", {
593
+ className: "ml-1 mr-2 shrink-0"
594
+ }, windowWidth.toFixed(0)), /*#__PURE__*/react.createElement("span", {
595
+ className: "mr-1 shrink-0"
596
+ }, "L:"), /*#__PURE__*/react.createElement("span", {
597
+ className: "ml-1 shrink-0"
598
+ }, windowCenter.toFixed(0)));
599
+ }
600
+
601
+ /**
602
+ * Zoom Level Overlay item
603
+ */
604
+ function ZoomOverlayItem({
605
+ scale,
606
+ customization
607
+ }) {
608
+ return /*#__PURE__*/react.createElement("div", {
609
+ className: "overlay-item flex flex-row",
610
+ style: {
611
+ color: customization && customization.color || undefined
612
+ }
613
+ }, /*#__PURE__*/react.createElement("span", {
614
+ className: "mr-1 shrink-0"
615
+ }, "Zoom:"), /*#__PURE__*/react.createElement("span", null, scale.toFixed(2), "x"));
616
+ }
617
+
618
+ /**
619
+ * Instance Number Overlay Item
620
+ */
621
+ function InstanceNumberOverlayItem({
622
+ instanceNumber,
623
+ imageSliceData,
624
+ customization
625
+ }) {
626
+ const {
627
+ imageIndex,
628
+ numberOfSlices
629
+ } = imageSliceData;
630
+ return /*#__PURE__*/react.createElement("div", {
631
+ className: "overlay-item flex flex-row",
632
+ style: {
633
+ color: customization && customization.color || undefined
634
+ }
635
+ }, /*#__PURE__*/react.createElement("span", null, instanceNumber !== undefined && instanceNumber !== null ? /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement("span", {
636
+ className: "mr-1 shrink-0"
637
+ }, "I:"), /*#__PURE__*/react.createElement("span", null, `${instanceNumber} (${imageIndex + 1}/${numberOfSlices})`)) : `${imageIndex + 1}/${numberOfSlices}`));
638
+ }
628
639
  CustomizableViewportOverlay.propTypes = {
629
640
  viewportData: (prop_types_default()).object,
630
641
  imageIndex: (prop_types_default()).number,
@@ -632,7 +643,7 @@ CustomizableViewportOverlay.propTypes = {
632
643
  };
633
644
  /* harmony default export */ const Overlays_CustomizableViewportOverlay = (CustomizableViewportOverlay);
634
645
  // EXTERNAL MODULE: ../../../node_modules/classnames/index.js
635
- var classnames = __webpack_require__(44921);
646
+ var classnames = __webpack_require__(61466);
636
647
  var classnames_default = /*#__PURE__*/__webpack_require__.n(classnames);
637
648
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/Viewport/Overlays/ViewportOrientationMarkers.css
638
649
  // extracted by mini-css-extract-plugin
@@ -649,15 +660,14 @@ const {
649
660
  getOrientationStringLPS,
650
661
  invertOrientationStringLPS
651
662
  } = esm.utilities.orientation;
652
- function ViewportOrientationMarkers(_ref) {
653
- let {
654
- element,
655
- viewportData,
656
- imageSliceData,
657
- viewportId,
658
- servicesManager,
659
- orientationMarkers = ['top', 'left']
660
- } = _ref;
663
+ function ViewportOrientationMarkers({
664
+ element,
665
+ viewportData,
666
+ imageSliceData,
667
+ viewportId,
668
+ servicesManager,
669
+ orientationMarkers = ['top', 'left']
670
+ }) {
661
671
  // Rotation is in degrees
662
672
  const [rotation, setRotation] = (0,react.useState)(0);
663
673
  const [flipHorizontal, setFlipHorizontal] = (0,react.useState)(false);
@@ -715,8 +725,8 @@ function ViewportOrientationMarkers(_ref) {
715
725
  viewUp,
716
726
  viewPlaneNormal
717
727
  } = viewport.getCamera();
718
- const viewRight = gl_matrix_esm/* vec3.create */.R3.create();
719
- gl_matrix_esm/* vec3.cross */.R3.cross(viewRight, viewUp, viewPlaneNormal);
728
+ const viewRight = gl_matrix_esm/* vec3.create */.eR.create();
729
+ gl_matrix_esm/* vec3.cross */.eR.cross(viewRight, viewUp, viewPlaneNormal);
720
730
  columnCosines = [-viewUp[0], -viewUp[1], -viewUp[2]];
721
731
  rowCosines = viewRight;
722
732
  }
@@ -729,20 +739,15 @@ function ViewportOrientationMarkers(_ref) {
729
739
  console.log('ViewportOrientationMarkers::No viewport');
730
740
  return null;
731
741
  }
732
- const backgroundColor = ohifViewport.getViewportOptions().background;
733
-
734
- // Todo: probably this can be done in a better way in which we identify bright
735
- // background
736
- const isLight = backgroundColor ? dist_esm.utilities.isEqual(backgroundColor, [1, 1, 1]) : false;
737
742
  return orientationMarkers.map((m, index) => /*#__PURE__*/react.createElement("div", {
738
- className: classnames_default()(`${m}-mid orientation-marker`, isLight ? 'text-[#726F7E]' : 'text-[#ccc]'),
743
+ className: classnames_default()('overlay-text', `${m}-mid orientation-marker`, 'text-aqua-pale', 'text-[13px]', 'leading-5'),
739
744
  key: `${m}-mid orientation-marker`
740
745
  }, /*#__PURE__*/react.createElement("div", {
741
746
  className: "orientation-marker-value"
742
747
  }, markers[m])));
743
748
  }, [viewportData, imageSliceData, rotation, flipVertical, flipHorizontal, orientationMarkers, element]);
744
749
  return /*#__PURE__*/react.createElement("div", {
745
- className: "ViewportOrientationMarkers noselect"
750
+ className: "ViewportOrientationMarkers select-none"
746
751
  }, markers);
747
752
  }
748
753
  ViewportOrientationMarkers.propTypes = {
@@ -818,11 +823,10 @@ function _getOrientationMarkers(rowCosines, columnCosines, rotation, flipVertica
818
823
 
819
824
 
820
825
 
821
- function ViewportImageSliceLoadingIndicator(_ref) {
822
- let {
823
- viewportData,
824
- element
825
- } = _ref;
826
+ function ViewportImageSliceLoadingIndicator({
827
+ viewportData,
828
+ element
829
+ }) {
826
830
  const [loading, setLoading] = (0,react.useState)(false);
827
831
  const [error, setError] = (0,react.useState)(false);
828
832
  const loadIndicatorRef = (0,react.useRef)(null);
@@ -961,48 +965,34 @@ function CornerstoneOverlays(props) {
961
965
  }
962
966
  /* harmony default export */ const Overlays_CornerstoneOverlays = (CornerstoneOverlays);
963
967
  // EXTERNAL MODULE: ../../../extensions/cornerstone/src/utils/measurementServiceMappings/utils/getSOPInstanceAttributes.js
964
- var getSOPInstanceAttributes = __webpack_require__(87172);
968
+ var getSOPInstanceAttributes = __webpack_require__(1663);
965
969
  // EXTERNAL MODULE: ./state/index.js + 1 modules
966
- var state_0 = __webpack_require__(62657);
970
+ var state_0 = __webpack_require__(15575);
967
971
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/components/CinePlayer/CinePlayer.tsx
968
972
 
969
973
 
970
974
 
971
975
 
972
- function WrappedCinePlayer(_ref) {
973
- let {
974
- enabledVPElement,
975
- viewportId,
976
- servicesManager
977
- } = _ref;
976
+ function WrappedCinePlayer({
977
+ enabledVPElement,
978
+ viewportId,
979
+ servicesManager
980
+ }) {
978
981
  const {
979
- toolbarService,
980
982
  customizationService,
981
983
  displaySetService,
982
- viewportGridService,
983
- cineService
984
+ viewportGridService
984
985
  } = servicesManager.services;
985
986
  const [{
986
987
  isCineEnabled,
987
988
  cines
988
- }] = (0,ui_src/* useCine */.vQ)();
989
+ }, api] = (0,ui_src/* useCine */.tq)();
989
990
  const [newStackFrameRate, setNewStackFrameRate] = (0,react.useState)(24);
990
- const [appConfig] = (0,state_0/* useAppConfig */.M)();
991
+ const [appConfig] = (0,state_0/* useAppConfig */.r)();
992
+ const isMountedRef = (0,react.useRef)(null);
991
993
  const {
992
- component: CinePlayerComponent = ui_src/* CinePlayer */.H6
994
+ component: CinePlayerComponent = ui_src/* CinePlayer */.F0
993
995
  } = customizationService.get('cinePlayer') ?? {};
994
- const handleCineClose = () => {
995
- toolbarService.recordInteraction({
996
- groupId: 'MoreTools',
997
- interactionType: 'toggle',
998
- commands: [{
999
- commandName: 'toggleCine',
1000
- commandOptions: {},
1001
- toolName: 'cine',
1002
- context: 'CORNERSTONE'
1003
- }]
1004
- });
1005
- };
1006
996
  const cineHandler = () => {
1007
997
  if (!cines || !cines[viewportId] || !enabledVPElement) {
1008
998
  return;
@@ -1012,11 +1002,11 @@ function WrappedCinePlayer(_ref) {
1012
1002
  const frameRate = cine.frameRate || 24;
1013
1003
  const validFrameRate = Math.max(frameRate, 1);
1014
1004
  if (isPlaying) {
1015
- cineService.playClip(enabledVPElement, {
1005
+ api.playClip(enabledVPElement, {
1016
1006
  framesPerSecond: validFrameRate
1017
1007
  });
1018
1008
  } else {
1019
- cineService.stopClip(enabledVPElement);
1009
+ api.stopClip(enabledVPElement);
1020
1010
  }
1021
1011
  };
1022
1012
  const newStackCineHandler = (0,react.useCallback)(() => {
@@ -1038,19 +1028,22 @@ function WrappedCinePlayer(_ref) {
1038
1028
  }
1039
1029
  });
1040
1030
  if (isPlaying) {
1041
- cineService.setIsCineEnabled(isPlaying);
1031
+ api.setIsCineEnabled(isPlaying);
1042
1032
  }
1043
- cineService.setCine({
1033
+ api.setCine({
1044
1034
  id: viewportId,
1045
1035
  isPlaying,
1046
1036
  frameRate
1047
1037
  });
1048
1038
  setNewStackFrameRate(frameRate);
1049
- }, [cineService, displaySetService, viewportId, viewportGridService, cines]);
1039
+ }, [displaySetService, viewportId, viewportGridService, cines]);
1050
1040
  (0,react.useEffect)(() => {
1041
+ isMountedRef.current = true;
1051
1042
  dist_esm.eventTarget.addEventListener(dist_esm.Enums.Events.STACK_VIEWPORT_NEW_STACK, newStackCineHandler);
1052
1043
  return () => {
1053
- cineService.setCine({
1044
+ isMountedRef.current = false;
1045
+ api.stopClip(enabledVPElement);
1046
+ api.setCine({
1054
1047
  id: viewportId,
1055
1048
  isPlaying: false
1056
1049
  });
@@ -1058,28 +1051,35 @@ function WrappedCinePlayer(_ref) {
1058
1051
  };
1059
1052
  }, [enabledVPElement, newStackCineHandler]);
1060
1053
  (0,react.useEffect)(() => {
1061
- if (!cines || !cines[viewportId] || !enabledVPElement) {
1054
+ if (!cines || !cines[viewportId] || !enabledVPElement || !isMountedRef.current) {
1062
1055
  return;
1063
1056
  }
1064
1057
  cineHandler();
1065
1058
  return () => {
1066
- if (enabledVPElement && cines?.[viewportId]?.isPlaying) {
1067
- cineService.stopClip(enabledVPElement);
1068
- }
1059
+ api.stopClip(enabledVPElement);
1069
1060
  };
1070
- }, [cines, viewportId, cineService, enabledVPElement, cineHandler]);
1061
+ }, [cines, viewportId, enabledVPElement, cineHandler]);
1071
1062
  const cine = cines[viewportId];
1072
1063
  const isPlaying = cine && cine.isPlaying || false;
1073
1064
  return isCineEnabled && /*#__PURE__*/react.createElement(CinePlayerComponent, {
1074
1065
  className: "absolute left-1/2 bottom-3 -translate-x-1/2",
1075
1066
  frameRate: newStackFrameRate,
1076
1067
  isPlaying: isPlaying,
1077
- onClose: handleCineClose,
1078
- onPlayPauseChange: isPlaying => cineService.setCine({
1079
- id: viewportId,
1080
- isPlaying
1081
- }),
1082
- onFrameRateChange: frameRate => cineService.setCine({
1068
+ onClose: () => {
1069
+ // also stop the clip
1070
+ api.setCine({
1071
+ id: viewportId,
1072
+ isPlaying: false
1073
+ });
1074
+ api.setIsCineEnabled(false);
1075
+ },
1076
+ onPlayPauseChange: isPlaying => {
1077
+ api.setCine({
1078
+ id: viewportId,
1079
+ isPlaying
1080
+ });
1081
+ },
1082
+ onFrameRateChange: frameRate => api.setCine({
1083
1083
  id: viewportId,
1084
1084
  frameRate
1085
1085
  })
@@ -1089,6 +1089,908 @@ function WrappedCinePlayer(_ref) {
1089
1089
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/components/CinePlayer/index.ts
1090
1090
 
1091
1091
  /* harmony default export */ const components_CinePlayer = (CinePlayer);
1092
+ // EXTERNAL MODULE: ../../../extensions/cornerstone/src/contextProviders/ViewportActionCornersProvider.tsx
1093
+ var ViewportActionCornersProvider = __webpack_require__(76255);
1094
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/components/OHIFViewportActionCorners.tsx
1095
+
1096
+
1097
+
1098
+ function OHIFViewportActionCorners({
1099
+ viewportId
1100
+ }) {
1101
+ const [viewportActionCornersState] = (0,ViewportActionCornersProvider/* useViewportActionCornersContext */.R4)();
1102
+ if (!viewportActionCornersState[viewportId]) {
1103
+ return null;
1104
+ }
1105
+ return /*#__PURE__*/react.createElement(ui_src/* ViewportActionCorners */.R2, {
1106
+ cornerComponents: viewportActionCornersState[viewportId]
1107
+ });
1108
+ }
1109
+ /* harmony default export */ const components_OHIFViewportActionCorners = (OHIFViewportActionCorners);
1110
+ // EXTERNAL MODULE: ../../../node_modules/react-i18next/dist/es/index.js + 15 modules
1111
+ var es = __webpack_require__(80619);
1112
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/components/WindowLevelActionMenu/Colormap.tsx
1113
+
1114
+
1115
+
1116
+ function Colormap({
1117
+ colormaps,
1118
+ viewportId,
1119
+ displaySets,
1120
+ commandsManager,
1121
+ serviceManager
1122
+ }) {
1123
+ const {
1124
+ cornerstoneViewportService
1125
+ } = serviceManager.services;
1126
+ const [activeDisplaySet, setActiveDisplaySet] = (0,react.useState)(displaySets[0]);
1127
+ const [showPreview, setShowPreview] = (0,react.useState)(false);
1128
+ const [prePreviewColormap, setPrePreviewColormap] = (0,react.useState)(null);
1129
+ const showPreviewRef = (0,react.useRef)(showPreview);
1130
+ showPreviewRef.current = showPreview;
1131
+ const prePreviewColormapRef = (0,react.useRef)(prePreviewColormap);
1132
+ prePreviewColormapRef.current = prePreviewColormap;
1133
+ const activeDisplaySetRef = (0,react.useRef)(activeDisplaySet);
1134
+ activeDisplaySetRef.current = activeDisplaySet;
1135
+ const onSetColorLUT = (0,react.useCallback)(props => {
1136
+ // TODO: Better way to check if it's a fusion
1137
+ const oneOpacityColormaps = ['Grayscale', 'X Ray'];
1138
+ const opacity = displaySets.length > 1 && !oneOpacityColormaps.includes(props.colormap.name) ? 0.5 : 1;
1139
+ commandsManager.run({
1140
+ commandName: 'setViewportColormap',
1141
+ commandOptions: {
1142
+ ...props,
1143
+ opacity,
1144
+ immediate: true
1145
+ },
1146
+ context: 'CORNERSTONE'
1147
+ });
1148
+ }, [commandsManager]);
1149
+ const getViewportColormap = (viewportId, displaySet) => {
1150
+ const {
1151
+ displaySetInstanceUID
1152
+ } = displaySet;
1153
+ const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
1154
+ if (viewport instanceof dist_esm.StackViewport) {
1155
+ const {
1156
+ colormap
1157
+ } = viewport.getProperties();
1158
+ if (!colormap) {
1159
+ return colormaps.find(c => c.Name === 'Grayscale') || colormaps[0];
1160
+ }
1161
+ return colormap;
1162
+ }
1163
+ const actorEntries = viewport.getActors();
1164
+ const actorEntry = actorEntries.find(entry => entry.uid.includes(displaySetInstanceUID));
1165
+ const {
1166
+ colormap
1167
+ } = viewport.getProperties(actorEntry.uid);
1168
+ if (!colormap) {
1169
+ return colormaps.find(c => c.Name === 'Grayscale') || colormaps[0];
1170
+ }
1171
+ return colormap;
1172
+ };
1173
+ const buttons = (0,react.useMemo)(() => {
1174
+ return displaySets.map((displaySet, index) => ({
1175
+ children: displaySet.Modality,
1176
+ key: index,
1177
+ style: {
1178
+ minWidth: `calc(100% / ${displaySets.length})`
1179
+ }
1180
+ }));
1181
+ }, [displaySets]);
1182
+ (0,react.useEffect)(() => {
1183
+ setActiveDisplaySet(displaySets[displaySets.length - 1]);
1184
+ }, [displaySets]);
1185
+ return /*#__PURE__*/react.createElement(react.Fragment, null, buttons.length > 1 && /*#__PURE__*/react.createElement("div", {
1186
+ className: "all-in-one-menu-item flex w-full justify-center"
1187
+ }, /*#__PURE__*/react.createElement(ui_src/* ButtonGroup */.e2, {
1188
+ onActiveIndexChange: index => {
1189
+ setActiveDisplaySet(displaySets[index]);
1190
+ setPrePreviewColormap(null);
1191
+ },
1192
+ activeIndex: displaySets.findIndex(ds => ds.displaySetInstanceUID === activeDisplaySetRef.current.displaySetInstanceUID) || 1,
1193
+ className: "w-[70%] text-[10px]"
1194
+ }, buttons.map(({
1195
+ children,
1196
+ key,
1197
+ style
1198
+ }) => /*#__PURE__*/react.createElement("div", {
1199
+ key: key,
1200
+ style: style
1201
+ }, children)))), /*#__PURE__*/react.createElement("div", {
1202
+ className: "all-in-one-menu-item flex w-full justify-center"
1203
+ }, /*#__PURE__*/react.createElement(ui_src/* SwitchButton */.L$, {
1204
+ label: "Preview in viewport",
1205
+ checked: showPreview,
1206
+ onChange: checked => {
1207
+ setShowPreview(checked);
1208
+ }
1209
+ })), /*#__PURE__*/react.createElement(ui_src/* AllInOneMenu.DividerItem */.se.VG, null), /*#__PURE__*/react.createElement(ui_src/* AllInOneMenu.ItemPanel */.se.cV, null, colormaps.map((colormap, index) => /*#__PURE__*/react.createElement(ui_src/* AllInOneMenu.Item */.se.q7, {
1210
+ key: index,
1211
+ label: colormap.description,
1212
+ onClick: () => {
1213
+ onSetColorLUT({
1214
+ viewportId,
1215
+ colormap,
1216
+ displaySetInstanceUID: activeDisplaySetRef.current.displaySetInstanceUID
1217
+ });
1218
+ setPrePreviewColormap(null);
1219
+ },
1220
+ onMouseEnter: () => {
1221
+ if (showPreviewRef.current) {
1222
+ setPrePreviewColormap(getViewportColormap(viewportId, activeDisplaySetRef.current));
1223
+ onSetColorLUT({
1224
+ viewportId,
1225
+ colormap,
1226
+ displaySetInstanceUID: activeDisplaySetRef.current.displaySetInstanceUID
1227
+ });
1228
+ }
1229
+ },
1230
+ onMouseLeave: () => {
1231
+ if (showPreviewRef.current && prePreviewColormapRef.current) {
1232
+ onSetColorLUT({
1233
+ viewportId,
1234
+ colormap: prePreviewColormapRef.current,
1235
+ displaySetInstanceUID: activeDisplaySetRef.current.displaySetInstanceUID
1236
+ });
1237
+ }
1238
+ }
1239
+ }))));
1240
+ }
1241
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/components/WindowLevelActionMenu/Colorbar.tsx
1242
+
1243
+
1244
+
1245
+
1246
+ function setViewportColorbar(viewportId, displaySets, commandsManager, serviceManager, colorbarOptions) {
1247
+ const {
1248
+ cornerstoneViewportService
1249
+ } = serviceManager.services;
1250
+ const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
1251
+ const viewportInfo = cornerstoneViewportService.getViewportInfo(viewportId);
1252
+ const backgroundColor = viewportInfo.getViewportOptions().background;
1253
+ const isLight = backgroundColor ? dist_esm.utilities.isEqual(backgroundColor, [1, 1, 1]) : false;
1254
+ if (isLight) {
1255
+ colorbarOptions.ticks = {
1256
+ position: 'left',
1257
+ style: {
1258
+ font: '12px Arial',
1259
+ color: '#000000',
1260
+ maxNumTicks: 8,
1261
+ tickSize: 5,
1262
+ tickWidth: 1,
1263
+ labelMargin: 3
1264
+ }
1265
+ };
1266
+ }
1267
+ const displaySetInstanceUIDs = [];
1268
+ if (viewport instanceof dist_esm.StackViewport) {
1269
+ displaySetInstanceUIDs.push(viewportId);
1270
+ }
1271
+ if (viewport instanceof dist_esm.VolumeViewport) {
1272
+ displaySets.forEach(ds => {
1273
+ displaySetInstanceUIDs.push(ds.displaySetInstanceUID);
1274
+ });
1275
+ }
1276
+ commandsManager.run({
1277
+ commandName: 'toggleViewportColorbar',
1278
+ commandOptions: {
1279
+ viewportId,
1280
+ options: colorbarOptions,
1281
+ displaySetInstanceUIDs
1282
+ },
1283
+ context: 'CORNERSTONE'
1284
+ });
1285
+ }
1286
+ function Colorbar({
1287
+ viewportId,
1288
+ displaySets,
1289
+ commandsManager,
1290
+ serviceManager,
1291
+ colorbarProperties
1292
+ }) {
1293
+ const {
1294
+ colorbarService
1295
+ } = serviceManager.services;
1296
+ const {
1297
+ width: colorbarWidth,
1298
+ colorbarTickPosition,
1299
+ colorbarContainerPosition,
1300
+ colormaps,
1301
+ colorbarInitialColormap
1302
+ } = colorbarProperties;
1303
+ const [showColorbar, setShowColorbar] = (0,react.useState)(colorbarService.hasColorbar(viewportId));
1304
+ const onSetColorbar = (0,react.useCallback)(() => {
1305
+ setViewportColorbar(viewportId, displaySets, commandsManager, serviceManager, {
1306
+ viewportId,
1307
+ colormaps,
1308
+ ticks: {
1309
+ position: colorbarTickPosition
1310
+ },
1311
+ width: colorbarWidth,
1312
+ position: colorbarContainerPosition,
1313
+ activeColormapName: colorbarInitialColormap
1314
+ });
1315
+ }, [commandsManager]);
1316
+ (0,react.useEffect)(() => {
1317
+ const updateColorbarState = () => {
1318
+ setShowColorbar(colorbarService.hasColorbar(viewportId));
1319
+ };
1320
+ const {
1321
+ unsubscribe
1322
+ } = colorbarService.subscribe(colorbarService.EVENTS.STATE_CHANGED, updateColorbarState);
1323
+ return () => {
1324
+ unsubscribe();
1325
+ };
1326
+ }, [viewportId]);
1327
+ return /*#__PURE__*/react.createElement("div", {
1328
+ className: "all-in-one-menu-item flex w-full justify-center"
1329
+ }, /*#__PURE__*/react.createElement(ui_src/* SwitchButton */.L$, {
1330
+ label: "Display Color bar",
1331
+ checked: showColorbar,
1332
+ onChange: () => {
1333
+ onSetColorbar();
1334
+ }
1335
+ }));
1336
+ }
1337
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/components/WindowLevelActionMenu/WindowLevel.tsx
1338
+
1339
+
1340
+
1341
+ function WindowLevel({
1342
+ viewportId,
1343
+ commandsManager,
1344
+ presets
1345
+ }) {
1346
+ const {
1347
+ t
1348
+ } = (0,es/* useTranslation */.Bd)('WindowLevelActionMenu');
1349
+ const onSetWindowLevel = (0,react.useCallback)(props => {
1350
+ commandsManager.run({
1351
+ commandName: 'setViewportWindowLevel',
1352
+ commandOptions: {
1353
+ ...props
1354
+ },
1355
+ context: 'CORNERSTONE'
1356
+ });
1357
+ }, [commandsManager]);
1358
+ return /*#__PURE__*/react.createElement(ui_src/* AllInOneMenu.ItemPanel */.se.cV, null, /*#__PURE__*/react.createElement(ui_src/* AllInOneMenu.HeaderItem */.se.N5, null, t('Modality Presets', {
1359
+ modality: Object.keys(presets)[0]
1360
+ })), Object.values(presets)[0].map((preset, index) => /*#__PURE__*/react.createElement(ui_src/* AllInOneMenu.Item */.se.q7, {
1361
+ key: index,
1362
+ label: preset.description,
1363
+ secondaryLabel: `${preset.window} / ${preset.level}`,
1364
+ onClick: () => onSetWindowLevel({
1365
+ ...preset,
1366
+ viewportId
1367
+ })
1368
+ })));
1369
+ }
1370
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeRenderingPresetsContent.tsx
1371
+
1372
+
1373
+
1374
+
1375
+ function VolumeRenderingPresetsContent({
1376
+ presets,
1377
+ viewportId,
1378
+ commandsManager,
1379
+ onClose
1380
+ }) {
1381
+ const [filteredPresets, setFilteredPresets] = (0,react.useState)(presets);
1382
+ const [searchValue, setSearchValue] = (0,react.useState)('');
1383
+ const [selectedPreset, setSelectedPreset] = (0,react.useState)(null);
1384
+ const handleSearchChange = (0,react.useCallback)(value => {
1385
+ setSearchValue(value);
1386
+ const filtered = value ? presets.filter(preset => preset.name.toLowerCase().includes(value.toLowerCase())) : presets;
1387
+ setFilteredPresets(filtered);
1388
+ }, [presets]);
1389
+ const handleApply = (0,react.useCallback)(props => {
1390
+ commandsManager.runCommand('setViewportPreset', {
1391
+ ...props
1392
+ });
1393
+ }, [commandsManager]);
1394
+ const formatLabel = (label, maxChars) => {
1395
+ return label.length > maxChars ? `${label.slice(0, maxChars)}...` : label;
1396
+ };
1397
+ return /*#__PURE__*/react.createElement("div", {
1398
+ className: "flex min-h-full w-full flex-col justify-between"
1399
+ }, /*#__PURE__*/react.createElement("div", {
1400
+ className: "border-secondary-light h-[433px] w-full overflow-hidden rounded border bg-black px-2.5"
1401
+ }, /*#__PURE__*/react.createElement("div", {
1402
+ className: "flex h-[46px] w-full items-center justify-start"
1403
+ }, /*#__PURE__*/react.createElement("div", {
1404
+ className: "h-[26px] w-[200px]"
1405
+ }, /*#__PURE__*/react.createElement(ui_src/* InputFilterText */.Cv, {
1406
+ value: searchValue,
1407
+ onDebounceChange: handleSearchChange,
1408
+ placeholder: 'Search all'
1409
+ }))), /*#__PURE__*/react.createElement("div", {
1410
+ className: "ohif-scrollbar overflow h-[385px] w-full overflow-y-auto"
1411
+ }, /*#__PURE__*/react.createElement("div", {
1412
+ className: "grid grid-cols-4 gap-3 pt-2 pr-3"
1413
+ }, filteredPresets.map((preset, index) => /*#__PURE__*/react.createElement("div", {
1414
+ key: index,
1415
+ className: "flex cursor-pointer flex-col items-start",
1416
+ onClick: () => {
1417
+ setSelectedPreset(preset);
1418
+ handleApply({
1419
+ preset: preset.name,
1420
+ viewportId
1421
+ });
1422
+ }
1423
+ }, /*#__PURE__*/react.createElement(ui_src/* Icon */.In, {
1424
+ name: preset.name,
1425
+ className: selectedPreset?.name === preset.name ? 'border-primary-light h-[75px] w-[95px] max-w-none rounded border-2' : 'hover:border-primary-light h-[75px] w-[95px] max-w-none rounded border-2 border-black'
1426
+ }), /*#__PURE__*/react.createElement("label", {
1427
+ className: "text-aqua-pale mt-2 text-left text-xs"
1428
+ }, formatLabel(preset.name, 11))))))), /*#__PURE__*/react.createElement("footer", {
1429
+ className: "flex h-[60px] w-full items-center justify-end"
1430
+ }, /*#__PURE__*/react.createElement("div", {
1431
+ className: "flex"
1432
+ }, /*#__PURE__*/react.createElement(ui_src/* Button */.$n, {
1433
+ name: "Cancel",
1434
+ size: ui_src/* ButtonEnums.size */.Ny.Ej.medium,
1435
+ type: ui_src/* ButtonEnums.type */.Ny.NW.secondary,
1436
+ onClick: onClose
1437
+ }, ' ', "Cancel", ' '))));
1438
+ }
1439
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeRenderingPresets.tsx
1440
+
1441
+
1442
+
1443
+ function VolumeRenderingPresets({
1444
+ viewportId,
1445
+ serviceManager,
1446
+ commandsManager,
1447
+ volumeRenderingPresets
1448
+ }) {
1449
+ const {
1450
+ uiModalService
1451
+ } = serviceManager.services;
1452
+ const onClickPresets = () => {
1453
+ uiModalService.show({
1454
+ content: VolumeRenderingPresetsContent,
1455
+ title: 'Rendering Presets',
1456
+ movable: true,
1457
+ contentProps: {
1458
+ onClose: uiModalService.hide,
1459
+ presets: volumeRenderingPresets,
1460
+ viewportId,
1461
+ commandsManager
1462
+ },
1463
+ containerDimensions: 'h-[543px] w-[460px]',
1464
+ contentDimensions: 'h-[493px] w-[460px] pl-[12px]'
1465
+ });
1466
+ };
1467
+ return /*#__PURE__*/react.createElement(ui_src/* AllInOneMenu.Item */.se.q7, {
1468
+ label: "Rendering Presets",
1469
+ icon: /*#__PURE__*/react.createElement(ui_src/* Icon */.In, {
1470
+ name: "VolumeRendering"
1471
+ }),
1472
+ rightIcon: /*#__PURE__*/react.createElement(ui_src/* Icon */.In, {
1473
+ name: "action-new-dialog"
1474
+ }),
1475
+ onClick: onClickPresets
1476
+ });
1477
+ }
1478
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeRenderingQuality.tsx
1479
+
1480
+ function VolumeRenderingQuality({
1481
+ volumeRenderingQualityRange,
1482
+ commandsManager,
1483
+ serviceManager,
1484
+ viewportId
1485
+ }) {
1486
+ const {
1487
+ cornerstoneViewportService
1488
+ } = serviceManager.services;
1489
+ const {
1490
+ min,
1491
+ max,
1492
+ step
1493
+ } = volumeRenderingQualityRange;
1494
+ const [quality, setQuality] = (0,react.useState)(null);
1495
+ const onChange = (0,react.useCallback)(value => {
1496
+ commandsManager.runCommand('setVolumeRenderingQulaity', {
1497
+ viewportId,
1498
+ volumeQuality: value
1499
+ });
1500
+ setQuality(value);
1501
+ }, [commandsManager, viewportId]);
1502
+ const calculateBackground = value => {
1503
+ const percentage = (value - 0) / (1 - 0) * 100;
1504
+ return `linear-gradient(to right, #5acce6 0%, #5acce6 ${percentage}%, #3a3f99 ${percentage}%, #3a3f99 100%)`;
1505
+ };
1506
+ (0,react.useEffect)(() => {
1507
+ const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
1508
+ const {
1509
+ actor
1510
+ } = viewport.getActors()[0];
1511
+ const mapper = actor.getMapper();
1512
+ const image = mapper.getInputData();
1513
+ const spacing = image.getSpacing();
1514
+ const sampleDistance = mapper.getSampleDistance();
1515
+ const averageSpacing = spacing.reduce((a, b) => a + b) / 3.0;
1516
+ if (sampleDistance === averageSpacing) {
1517
+ setQuality(1);
1518
+ } else {
1519
+ setQuality(Math.sqrt(averageSpacing / (sampleDistance * 0.5)));
1520
+ }
1521
+ }, [cornerstoneViewportService, viewportId]);
1522
+ return /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement("div", {
1523
+ className: "all-in-one-menu-item flex w-full flex-row !items-center justify-between gap-[10px]"
1524
+ }, /*#__PURE__*/react.createElement("label", {
1525
+ className: "block text-white",
1526
+ htmlFor: "volume"
1527
+ }, "Quality"), quality !== null && /*#__PURE__*/react.createElement("input", {
1528
+ className: "bg-inputfield-main h-2 w-[120px] cursor-pointer appearance-none rounded-lg",
1529
+ value: quality,
1530
+ id: "volume",
1531
+ max: max,
1532
+ min: min,
1533
+ type: "range",
1534
+ step: step,
1535
+ onChange: e => onChange(parseInt(e.target.value, 10)),
1536
+ style: {
1537
+ background: calculateBackground((quality - min) / (max - min))
1538
+ }
1539
+ })));
1540
+ }
1541
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeShift.tsx
1542
+
1543
+ function VolumeShift({
1544
+ viewportId,
1545
+ commandsManager,
1546
+ serviceManager
1547
+ }) {
1548
+ const {
1549
+ cornerstoneViewportService
1550
+ } = serviceManager.services;
1551
+ const [minShift, setMinShift] = (0,react.useState)(null);
1552
+ const [maxShift, setMaxShift] = (0,react.useState)(null);
1553
+ const [shift, setShift] = (0,react.useState)(cornerstoneViewportService.getCornerstoneViewport(viewportId)?.shiftedBy || 0);
1554
+ const [step, setStep] = (0,react.useState)(null);
1555
+ const [isBlocking, setIsBlocking] = (0,react.useState)(false);
1556
+ const prevShiftRef = (0,react.useRef)(shift);
1557
+ const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
1558
+ const {
1559
+ actor
1560
+ } = viewport.getActors()[0];
1561
+ const ofun = actor.getProperty().getScalarOpacity(0);
1562
+ (0,react.useEffect)(() => {
1563
+ if (isBlocking) {
1564
+ return;
1565
+ }
1566
+ const range = ofun.getRange();
1567
+ const transferFunctionWidth = range[1] - range[0];
1568
+ const minShift = -transferFunctionWidth;
1569
+ const maxShift = transferFunctionWidth;
1570
+ setMinShift(minShift);
1571
+ setMaxShift(maxShift);
1572
+ setStep(Math.pow(10, Math.floor(Math.log10(transferFunctionWidth / 500))));
1573
+ }, [cornerstoneViewportService, viewportId, actor, ofun, isBlocking]);
1574
+ const onChangeRange = (0,react.useCallback)(newShift => {
1575
+ const shiftDifference = newShift - prevShiftRef.current;
1576
+ prevShiftRef.current = newShift;
1577
+ viewport.shiftedBy = newShift;
1578
+ commandsManager.runCommand('shiftVolumeOpacityPoints', {
1579
+ viewportId,
1580
+ shift: shiftDifference
1581
+ });
1582
+ }, [commandsManager, viewportId, viewport]);
1583
+ const calculateBackground = value => {
1584
+ const percentage = (value - 0) / (1 - 0) * 100;
1585
+ return `linear-gradient(to right, #5acce6 0%, #5acce6 ${percentage}%, #3a3f99 ${percentage}%, #3a3f99 100%)`;
1586
+ };
1587
+ return /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement("div", {
1588
+ className: "all-in-one-menu-item flex w-full flex-row !items-center justify-between gap-[10px]"
1589
+ }, /*#__PURE__*/react.createElement("label", {
1590
+ className: "block text-white",
1591
+ htmlFor: "shift"
1592
+ }, "Shift"), step !== null && /*#__PURE__*/react.createElement("input", {
1593
+ className: "bg-inputfield-main h-2 w-[120px] cursor-pointer appearance-none rounded-lg",
1594
+ value: shift,
1595
+ onChange: e => {
1596
+ const shiftValue = parseInt(e.target.value, 10);
1597
+ setShift(shiftValue);
1598
+ onChangeRange(shiftValue);
1599
+ },
1600
+ id: "shift",
1601
+ onMouseDown: () => setIsBlocking(true),
1602
+ onMouseUp: () => setIsBlocking(false),
1603
+ max: maxShift,
1604
+ min: minShift,
1605
+ type: "range",
1606
+ step: step,
1607
+ style: {
1608
+ background: calculateBackground((shift - minShift) / (maxShift - minShift))
1609
+ }
1610
+ })));
1611
+ }
1612
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeLighting.tsx
1613
+
1614
+ function VolumeLighting({
1615
+ serviceManager,
1616
+ commandsManager,
1617
+ viewportId
1618
+ }) {
1619
+ const {
1620
+ cornerstoneViewportService
1621
+ } = serviceManager.services;
1622
+ const [ambient, setAmbient] = (0,react.useState)(null);
1623
+ const [diffuse, setDiffuse] = (0,react.useState)(null);
1624
+ const [specular, setSpecular] = (0,react.useState)(null);
1625
+ const onAmbientChange = (0,react.useCallback)(() => {
1626
+ commandsManager.runCommand('setVolumeLighting', {
1627
+ viewportId,
1628
+ options: {
1629
+ ambient
1630
+ }
1631
+ });
1632
+ }, [ambient, commandsManager, viewportId]);
1633
+ const onDiffuseChange = (0,react.useCallback)(() => {
1634
+ commandsManager.runCommand('setVolumeLighting', {
1635
+ viewportId,
1636
+ options: {
1637
+ diffuse
1638
+ }
1639
+ });
1640
+ }, [diffuse, commandsManager, viewportId]);
1641
+ const onSpecularChange = (0,react.useCallback)(() => {
1642
+ commandsManager.runCommand('setVolumeLighting', {
1643
+ viewportId,
1644
+ options: {
1645
+ specular
1646
+ }
1647
+ });
1648
+ }, [specular, commandsManager, viewportId]);
1649
+ const calculateBackground = value => {
1650
+ const percentage = (value - 0) / (1 - 0) * 100;
1651
+ return `linear-gradient(to right, #5acce6 0%, #5acce6 ${percentage}%, #3a3f99 ${percentage}%, #3a3f99 100%)`;
1652
+ };
1653
+ (0,react.useEffect)(() => {
1654
+ const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
1655
+ const {
1656
+ actor
1657
+ } = viewport.getActors()[0];
1658
+ const ambient = actor.getProperty().getAmbient();
1659
+ const diffuse = actor.getProperty().getDiffuse();
1660
+ const specular = actor.getProperty().getSpecular();
1661
+ setAmbient(ambient);
1662
+ setDiffuse(diffuse);
1663
+ setSpecular(specular);
1664
+ }, [viewportId, cornerstoneViewportService]);
1665
+ return /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement("div", {
1666
+ className: "all-in-one-menu-item flex w-full flex-row !items-center justify-between gap-[10px]"
1667
+ }, /*#__PURE__*/react.createElement("label", {
1668
+ className: "block text-white",
1669
+ htmlFor: "ambient"
1670
+ }, "Ambient"), ambient !== null && /*#__PURE__*/react.createElement("input", {
1671
+ className: "bg-inputfield-main h-2 w-[120px] cursor-pointer appearance-none rounded-lg",
1672
+ value: ambient,
1673
+ onChange: e => {
1674
+ setAmbient(e.target.value);
1675
+ onAmbientChange();
1676
+ },
1677
+ id: "ambient",
1678
+ max: 1,
1679
+ min: 0,
1680
+ type: "range",
1681
+ step: 0.1,
1682
+ style: {
1683
+ background: calculateBackground(ambient)
1684
+ }
1685
+ })), /*#__PURE__*/react.createElement("div", {
1686
+ className: "all-in-one-menu-item flex w-full flex-row !items-center justify-between gap-[10px]"
1687
+ }, /*#__PURE__*/react.createElement("label", {
1688
+ className: "block text-white",
1689
+ htmlFor: "diffuse"
1690
+ }, "Diffuse"), diffuse !== null && /*#__PURE__*/react.createElement("input", {
1691
+ className: "bg-inputfield-main h-2 w-[120px] cursor-pointer appearance-none rounded-lg",
1692
+ value: diffuse,
1693
+ onChange: e => {
1694
+ setDiffuse(e.target.value);
1695
+ onDiffuseChange();
1696
+ },
1697
+ id: "diffuse",
1698
+ max: 1,
1699
+ min: 0,
1700
+ type: "range",
1701
+ step: 0.1,
1702
+ style: {
1703
+ background: calculateBackground(diffuse)
1704
+ }
1705
+ })), /*#__PURE__*/react.createElement("div", {
1706
+ className: "all-in-one-menu-item flex w-full flex-row !items-center justify-between gap-[10px]"
1707
+ }, /*#__PURE__*/react.createElement("label", {
1708
+ className: "block text-white",
1709
+ htmlFor: "specular"
1710
+ }, "Specular"), specular !== null && /*#__PURE__*/react.createElement("input", {
1711
+ className: "bg-inputfield-main h-2 w-[120px] cursor-pointer appearance-none rounded-lg",
1712
+ value: specular,
1713
+ onChange: e => {
1714
+ setSpecular(e.target.value);
1715
+ onSpecularChange();
1716
+ },
1717
+ id: "specular",
1718
+ max: 1,
1719
+ min: 0,
1720
+ type: "range",
1721
+ step: 0.1,
1722
+ style: {
1723
+ background: calculateBackground(specular)
1724
+ }
1725
+ })));
1726
+ }
1727
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeShade.tsx
1728
+
1729
+
1730
+ function VolumeShade({
1731
+ commandsManager,
1732
+ viewportId,
1733
+ serviceManager
1734
+ }) {
1735
+ const {
1736
+ cornerstoneViewportService
1737
+ } = serviceManager.services;
1738
+ const [shade, setShade] = (0,react.useState)(true);
1739
+ const [key, setKey] = (0,react.useState)(0);
1740
+ const onShadeChange = (0,react.useCallback)(checked => {
1741
+ commandsManager.runCommand('setVolumeLighting', {
1742
+ viewportId,
1743
+ options: {
1744
+ shade: checked
1745
+ }
1746
+ });
1747
+ }, [commandsManager, viewportId]);
1748
+ (0,react.useEffect)(() => {
1749
+ const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
1750
+ const {
1751
+ actor
1752
+ } = viewport.getActors()[0];
1753
+ const shade = actor.getProperty().getShade();
1754
+ setShade(shade);
1755
+ setKey(key + 1);
1756
+ }, [viewportId, cornerstoneViewportService]);
1757
+ return /*#__PURE__*/react.createElement(ui_src/* SwitchButton */.L$, {
1758
+ key: key,
1759
+ label: "Shade",
1760
+ checked: shade,
1761
+ onChange: () => {
1762
+ setShade(!shade);
1763
+ onShadeChange(!shade);
1764
+ }
1765
+ });
1766
+ }
1767
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeRenderingOptions.tsx
1768
+
1769
+
1770
+
1771
+
1772
+
1773
+
1774
+ function VolumeRenderingOptions({
1775
+ viewportId,
1776
+ commandsManager,
1777
+ volumeRenderingQualityRange,
1778
+ serviceManager
1779
+ }) {
1780
+ return /*#__PURE__*/react.createElement(ui_src/* AllInOneMenu.ItemPanel */.se.cV, null, /*#__PURE__*/react.createElement(VolumeRenderingQuality, {
1781
+ viewportId: viewportId,
1782
+ commandsManager: commandsManager,
1783
+ serviceManager: serviceManager,
1784
+ volumeRenderingQualityRange: volumeRenderingQualityRange
1785
+ }), /*#__PURE__*/react.createElement(VolumeShift, {
1786
+ viewportId: viewportId,
1787
+ commandsManager: commandsManager,
1788
+ serviceManager: serviceManager
1789
+ }), /*#__PURE__*/react.createElement("div", {
1790
+ className: "all-in-one-menu-item flex w-full justify-start"
1791
+ }, /*#__PURE__*/react.createElement("div", {
1792
+ className: "text-aqua-pale text-[13px]"
1793
+ }, "LIGHTING")), /*#__PURE__*/react.createElement(ui_src/* AllInOneMenu.DividerItem */.se.VG, null), /*#__PURE__*/react.createElement("div", {
1794
+ className: "all-in-one-menu-item flex w-full justify-center"
1795
+ }, /*#__PURE__*/react.createElement(VolumeShade, {
1796
+ commandsManager: commandsManager,
1797
+ serviceManager: serviceManager,
1798
+ viewportId: viewportId
1799
+ })), /*#__PURE__*/react.createElement(VolumeLighting, {
1800
+ viewportId: viewportId,
1801
+ commandsManager: commandsManager,
1802
+ serviceManager: serviceManager
1803
+ }));
1804
+ }
1805
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/components/WindowLevelActionMenu/WindowLevelActionMenu.tsx
1806
+
1807
+
1808
+
1809
+
1810
+
1811
+
1812
+
1813
+
1814
+
1815
+
1816
+
1817
+
1818
+ function WindowLevelActionMenu({
1819
+ viewportId,
1820
+ element,
1821
+ presets,
1822
+ verticalDirection,
1823
+ horizontalDirection,
1824
+ commandsManager,
1825
+ serviceManager,
1826
+ colorbarProperties,
1827
+ displaySets,
1828
+ volumeRenderingPresets,
1829
+ volumeRenderingQualityRange
1830
+ }) {
1831
+ const {
1832
+ colormaps,
1833
+ colorbarContainerPosition,
1834
+ colorbarInitialColormap,
1835
+ colorbarTickPosition,
1836
+ width: colorbarWidth
1837
+ } = colorbarProperties;
1838
+ const {
1839
+ colorbarService,
1840
+ cornerstoneViewportService
1841
+ } = serviceManager.services;
1842
+ const viewportInfo = cornerstoneViewportService.getViewportInfo(viewportId);
1843
+ const backgroundColor = viewportInfo.getViewportOptions().background;
1844
+ const isLight = backgroundColor ? dist_esm.utilities.isEqual(backgroundColor, [1, 1, 1]) : false;
1845
+ const nonImageModalities = ['SR', 'SEG', 'SM', 'RTSTRUCT', 'RTPLAN', 'RTDOSE'];
1846
+ const {
1847
+ t
1848
+ } = (0,es/* useTranslation */.Bd)('WindowLevelActionMenu');
1849
+ const [viewportGrid] = (0,ui_src/* useViewportGrid */.ih)();
1850
+ const {
1851
+ activeViewportId
1852
+ } = viewportGrid;
1853
+ const [vpHeight, setVpHeight] = (0,react.useState)(element?.clientHeight);
1854
+ const [menuKey, setMenuKey] = (0,react.useState)(0);
1855
+ const [is3DVolume, setIs3DVolume] = (0,react.useState)(false);
1856
+ const onSetColorbar = (0,react.useCallback)(() => {
1857
+ setViewportColorbar(viewportId, displaySets, commandsManager, serviceManager, {
1858
+ colormaps,
1859
+ ticks: {
1860
+ position: colorbarTickPosition
1861
+ },
1862
+ width: colorbarWidth,
1863
+ position: colorbarContainerPosition,
1864
+ activeColormapName: colorbarInitialColormap
1865
+ });
1866
+ }, [commandsManager]);
1867
+ (0,react.useEffect)(() => {
1868
+ const newVpHeight = element?.clientHeight;
1869
+ if (vpHeight !== newVpHeight) {
1870
+ setVpHeight(newVpHeight);
1871
+ }
1872
+ }, [element, vpHeight]);
1873
+ (0,react.useEffect)(() => {
1874
+ if (!colorbarService.hasColorbar(viewportId)) {
1875
+ return;
1876
+ }
1877
+ window.setTimeout(() => {
1878
+ colorbarService.removeColorbar(viewportId);
1879
+ onSetColorbar();
1880
+ }, 0);
1881
+ }, [viewportId]);
1882
+ (0,react.useEffect)(() => {
1883
+ if (colorbarService.hasColorbar(viewportId)) {
1884
+ colorbarService.removeColorbar(viewportId);
1885
+ }
1886
+ }, [displaySets]);
1887
+ (0,react.useEffect)(() => {
1888
+ setMenuKey(menuKey + 1);
1889
+ const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
1890
+ if (viewport instanceof dist_esm.VolumeViewport3D) {
1891
+ setIs3DVolume(true);
1892
+ } else {
1893
+ setIs3DVolume(false);
1894
+ }
1895
+ }, [displaySets, viewportId, presets, volumeRenderingQualityRange, volumeRenderingPresets, colorbarProperties, activeViewportId, viewportGrid]);
1896
+ return /*#__PURE__*/react.createElement(ui_src/* AllInOneMenu.IconMenu */.se.dd, {
1897
+ icon: "viewport-window-level",
1898
+ verticalDirection: verticalDirection,
1899
+ horizontalDirection: horizontalDirection,
1900
+ iconClassName: classnames_default()(
1901
+ // Visible on hover and for the active viewport
1902
+ activeViewportId === viewportId ? 'visible' : 'invisible group-hover:visible', 'flex shrink-0 cursor-pointer rounded active:text-white', isLight ? 'text-aqua-pale hover:bg-secondary-dark' : 'text-primary-light hover:bg-secondary-light/60'),
1903
+ menuStyle: {
1904
+ maxHeight: vpHeight - 32,
1905
+ minWidth: 218
1906
+ },
1907
+ onVisibilityChange: () => {
1908
+ setVpHeight(element.clientHeight);
1909
+ },
1910
+ menuKey: menuKey
1911
+ }, /*#__PURE__*/react.createElement(ui_src/* AllInOneMenu.ItemPanel */.se.cV, null, !is3DVolume && /*#__PURE__*/react.createElement(Colorbar, {
1912
+ viewportId: viewportId,
1913
+ displaySets: displaySets.filter(ds => !nonImageModalities.includes(ds.Modality)),
1914
+ commandsManager: commandsManager,
1915
+ serviceManager: serviceManager,
1916
+ colorbarProperties: colorbarProperties
1917
+ }), colormaps && !is3DVolume && /*#__PURE__*/react.createElement(ui_src/* AllInOneMenu.SubMenu */.se.g8, {
1918
+ key: "colorLUTPresets",
1919
+ itemLabel: "Color LUT",
1920
+ itemIcon: "icon-color-lut"
1921
+ }, /*#__PURE__*/react.createElement(Colormap, {
1922
+ colormaps: colormaps,
1923
+ viewportId: viewportId,
1924
+ displaySets: displaySets.filter(ds => !nonImageModalities.includes(ds.Modality)),
1925
+ commandsManager: commandsManager,
1926
+ serviceManager: serviceManager
1927
+ })), presets && !is3DVolume && /*#__PURE__*/react.createElement(ui_src/* AllInOneMenu.SubMenu */.se.g8, {
1928
+ key: "windowLevelPresets",
1929
+ itemLabel: t('Modality Window Presets', {
1930
+ modality: Object.keys(presets)[0]
1931
+ }),
1932
+ itemIcon: "viewport-window-level"
1933
+ }, /*#__PURE__*/react.createElement(WindowLevel, {
1934
+ viewportId: viewportId,
1935
+ commandsManager: commandsManager,
1936
+ presets: presets
1937
+ })), volumeRenderingPresets && is3DVolume && /*#__PURE__*/react.createElement(VolumeRenderingPresets, {
1938
+ serviceManager: serviceManager,
1939
+ viewportId: viewportId,
1940
+ commandsManager: commandsManager,
1941
+ volumeRenderingPresets: volumeRenderingPresets
1942
+ }), volumeRenderingQualityRange && is3DVolume && /*#__PURE__*/react.createElement(ui_src/* AllInOneMenu.SubMenu */.se.g8, {
1943
+ itemLabel: "Rendering Options"
1944
+ }, /*#__PURE__*/react.createElement(VolumeRenderingOptions, {
1945
+ viewportId: viewportId,
1946
+ commandsManager: commandsManager,
1947
+ volumeRenderingQualityRange: volumeRenderingQualityRange,
1948
+ serviceManager: serviceManager
1949
+ }))));
1950
+ }
1951
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/components/WindowLevelActionMenu/getWindowLevelActionMenu.tsx
1952
+
1953
+
1954
+ function getWindowLevelActionMenu({
1955
+ viewportId,
1956
+ element,
1957
+ displaySets,
1958
+ servicesManager,
1959
+ commandsManager,
1960
+ verticalDirection,
1961
+ horizontalDirection
1962
+ }) {
1963
+ const {
1964
+ customizationService
1965
+ } = servicesManager.services;
1966
+ const {
1967
+ presets
1968
+ } = customizationService.get('cornerstone.windowLevelPresets');
1969
+ const colorbarProperties = customizationService.get('cornerstone.colorbar');
1970
+ const {
1971
+ volumeRenderingPresets,
1972
+ volumeRenderingQualityRange
1973
+ } = customizationService.get('cornerstone.3dVolumeRendering');
1974
+ const displaySetPresets = displaySets.filter(displaySet => presets[displaySet.Modality]).map(displaySet => {
1975
+ return {
1976
+ [displaySet.Modality]: presets[displaySet.Modality]
1977
+ };
1978
+ });
1979
+ const hasMenu = displaySetPresets.length > 0;
1980
+ return hasMenu ? /*#__PURE__*/react.createElement(WindowLevelActionMenu, {
1981
+ viewportId: viewportId,
1982
+ element: element,
1983
+ presets: displaySetPresets[0],
1984
+ verticalDirection: verticalDirection,
1985
+ horizontalDirection: horizontalDirection,
1986
+ commandsManager: commandsManager,
1987
+ serviceManager: servicesManager,
1988
+ colorbarProperties: colorbarProperties,
1989
+ displaySets: displaySets,
1990
+ volumeRenderingPresets: volumeRenderingPresets,
1991
+ volumeRenderingQualityRange: volumeRenderingQualityRange
1992
+ }) : null;
1993
+ }
1092
1994
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/Viewport/OHIFCornerstoneViewport.tsx
1093
1995
 
1094
1996
 
@@ -1102,6 +2004,9 @@ function WrappedCinePlayer(_ref) {
1102
2004
 
1103
2005
 
1104
2006
 
2007
+
2008
+
2009
+
1105
2010
  const STACK = 'stack';
1106
2011
 
1107
2012
  /**
@@ -1166,19 +2071,20 @@ const OHIFCornerstoneViewport = /*#__PURE__*/react.memo(props => {
1166
2071
  viewportOptions,
1167
2072
  displaySetOptions,
1168
2073
  servicesManager,
1169
- commandsManager,
1170
2074
  onElementEnabled,
1171
2075
  onElementDisabled,
1172
2076
  isJumpToMeasurementDisabled,
1173
2077
  // Note: you SHOULD NOT use the initialImageIdOrIndex for manipulation
1174
2078
  // of the imageData in the OHIFCornerstoneViewport. This prop is used
1175
2079
  // to set the initial state of the viewport's first image to render
1176
- initialImageIndex
2080
+ initialImageIndex,
2081
+ onReady
1177
2082
  } = props;
1178
2083
  const viewportId = viewportOptions.viewportId;
1179
2084
  const [scrollbarHeight, setScrollbarHeight] = (0,react.useState)('100px');
1180
2085
  const [enabledVPElement, setEnabledVPElement] = (0,react.useState)(null);
1181
2086
  const elementRef = (0,react.useRef)();
2087
+ const [appConfig] = (0,state_0/* useAppConfig */.r)();
1182
2088
  const {
1183
2089
  measurementService,
1184
2090
  displaySetService,
@@ -1188,12 +2094,14 @@ const OHIFCornerstoneViewport = /*#__PURE__*/react.memo(props => {
1188
2094
  cornerstoneViewportService,
1189
2095
  cornerstoneCacheService,
1190
2096
  viewportGridService,
1191
- stateSyncService
2097
+ stateSyncService,
2098
+ viewportActionCornersService,
2099
+ customizationService
1192
2100
  } = servicesManager.services;
1193
- const [viewportDialogState] = (0,ui_src/* useViewportDialog */.en)();
2101
+ const [viewportDialogState] = (0,ui_src/* useViewportDialog */.OR)();
1194
2102
  // useCallback for scroll bar height calculation
1195
2103
  const setImageScrollBarHeight = (0,react.useCallback)(() => {
1196
- const scrollbarHeight = `${elementRef.current.clientHeight - 20}px`;
2104
+ const scrollbarHeight = `${elementRef.current.clientHeight - 40}px`;
1197
2105
  setScrollbarHeight(scrollbarHeight);
1198
2106
  }, [elementRef]);
1199
2107
 
@@ -1209,6 +2117,7 @@ const OHIFCornerstoneViewport = /*#__PURE__*/react.memo(props => {
1209
2117
  const syncGroups = viewportInfo.getSyncGroups();
1210
2118
  toolGroupService.removeViewportFromToolGroup(viewportId, renderingEngineId);
1211
2119
  syncGroupService.removeViewportFromSyncGroup(viewportId, renderingEngineId, syncGroups);
2120
+ viewportActionCornersService.clear(viewportId);
1212
2121
  }, [viewportId]);
1213
2122
  const elementEnabledHandler = (0,react.useCallback)(evt => {
1214
2123
  // check this is this element reference and return early if doesn't match
@@ -1220,15 +2129,21 @@ const OHIFCornerstoneViewport = /*#__PURE__*/react.memo(props => {
1220
2129
  element
1221
2130
  } = evt.detail;
1222
2131
  const viewportInfo = cornerstoneViewportService.getViewportInfo(viewportId);
1223
- (0,state/* setEnabledElement */.Yc)(viewportId, element);
2132
+ (0,state/* setEnabledElement */.ye)(viewportId, element);
1224
2133
  setEnabledVPElement(element);
1225
2134
  const renderingEngineId = viewportInfo.getRenderingEngineId();
1226
2135
  const toolGroupId = viewportInfo.getToolGroupId();
1227
2136
  const syncGroups = viewportInfo.getSyncGroups();
1228
2137
  toolGroupService.addViewportToToolGroup(viewportId, renderingEngineId, toolGroupId);
1229
2138
  syncGroupService.addViewportToSyncGroup(viewportId, renderingEngineId, syncGroups);
2139
+ const synchronizersStore = stateSyncService.getState().synchronizersStore;
2140
+ if (synchronizersStore?.[viewportId]?.length) {
2141
+ // If the viewport used to have a synchronizer, re apply it again
2142
+ _rehydrateSynchronizers(synchronizersStore, viewportId, syncGroupService);
2143
+ }
1230
2144
  if (onElementEnabled) {
1231
2145
  onElementEnabled(evt);
2146
+ onReady?.(evt);
1232
2147
  }
1233
2148
  }, [viewportId, onElementEnabled, toolGroupService]);
1234
2149
 
@@ -1242,13 +2157,17 @@ const OHIFCornerstoneViewport = /*#__PURE__*/react.memo(props => {
1242
2157
  if (!viewportInfo) {
1243
2158
  return;
1244
2159
  }
1245
- cleanUpServices(viewportInfo);
1246
2160
  cornerstoneViewportService.storePresentation({
1247
2161
  viewportId
1248
2162
  });
2163
+
2164
+ // This should be done after the store presentation since synchronizers
2165
+ // will get cleaned up and they need the viewportInfo to be present
2166
+ cleanUpServices(viewportInfo);
1249
2167
  if (onElementDisabled) {
1250
2168
  onElementDisabled(viewportInfo);
1251
2169
  }
2170
+ cornerstoneViewportService.disableElement(viewportId);
1252
2171
  dist_esm.eventTarget.removeEventListener(dist_esm.Enums.Events.ELEMENT_ENABLED, elementEnabledHandler);
1253
2172
  };
1254
2173
  }, []);
@@ -1264,11 +2183,10 @@ const OHIFCornerstoneViewport = /*#__PURE__*/react.memo(props => {
1264
2183
  (0,react.useEffect)(() => {
1265
2184
  const {
1266
2185
  unsubscribe
1267
- } = displaySetService.subscribe(displaySetService.EVENTS.DISPLAY_SET_SERIES_METADATA_INVALIDATED, async _ref => {
1268
- let {
1269
- displaySetInstanceUID: invalidatedDisplaySetInstanceUID,
1270
- invalidateData
1271
- } = _ref;
2186
+ } = displaySetService.subscribe(displaySetService.EVENTS.DISPLAY_SET_SERIES_METADATA_INVALIDATED, async ({
2187
+ displaySetInstanceUID: invalidatedDisplaySetInstanceUID,
2188
+ invalidateData
2189
+ }) => {
1272
2190
  if (!invalidateData) {
1273
2191
  return;
1274
2192
  }
@@ -1295,10 +2213,9 @@ const OHIFCornerstoneViewport = /*#__PURE__*/react.memo(props => {
1295
2213
  // The presentation state will have been stored previously by closing
1296
2214
  // a viewport. Otherwise, this viewport will be unchanged and the
1297
2215
  // presentation information will be directly carried over.
1298
- const {
1299
- lutPresentationStore,
1300
- positionPresentationStore
1301
- } = stateSyncService.getState();
2216
+ const state = stateSyncService.getState();
2217
+ const lutPresentationStore = state.lutPresentationStore;
2218
+ const positionPresentationStore = state.positionPresentationStore;
1302
2219
  const {
1303
2220
  presentationIds
1304
2221
  } = viewportOptions;
@@ -1351,12 +2268,36 @@ const OHIFCornerstoneViewport = /*#__PURE__*/react.memo(props => {
1351
2268
  unsubscribeFromJumpToMeasurementEvents();
1352
2269
  };
1353
2270
  }, [displaySets, elementRef, viewportId]);
2271
+
2272
+ // Set up the window level action menu in the viewport action corners.
2273
+ (0,react.useEffect)(() => {
2274
+ // Doing an === check here because the default config value when not set is true
2275
+ if (appConfig.addWindowLevelActionMenu === false) {
2276
+ return;
2277
+ }
2278
+
2279
+ // TODO: In the future we should consider using the customization service
2280
+ // to determine if and in which corner various action components should go.
2281
+ const wlActionMenu = getWindowLevelActionMenu({
2282
+ viewportId,
2283
+ element: elementRef.current,
2284
+ displaySets,
2285
+ servicesManager,
2286
+ commandsManager,
2287
+ verticalDirection: ui_src/* AllInOneMenu.VerticalDirection */.se.mq.TopToBottom,
2288
+ horizontalDirection: ui_src/* AllInOneMenu.HorizontalDirection */.se.Iu.RightToLeft
2289
+ });
2290
+ viewportActionCornersService.setComponent({
2291
+ viewportId,
2292
+ id: 'windowLevelActionMenu',
2293
+ component: wlActionMenu,
2294
+ location: viewportActionCornersService.LOCATIONS.topRight,
2295
+ indexPriority: -100
2296
+ });
2297
+ }, [displaySets, viewportId, viewportActionCornersService, servicesManager, commandsManager]);
1354
2298
  return /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement("div", {
1355
2299
  className: "viewport-wrapper"
1356
- }, /*#__PURE__*/react.createElement(index_esm/* default */.ZP, {
1357
- refreshMode: "debounce",
1358
- refreshRate: 50 // Wait 50 ms after last move to render
1359
- ,
2300
+ }, /*#__PURE__*/react.createElement(index_esm/* default */.Ay, {
1360
2301
  onResize: onResize,
1361
2302
  targetRef: elementRef.current
1362
2303
  }), /*#__PURE__*/react.createElement("div", {
@@ -1379,15 +2320,17 @@ const OHIFCornerstoneViewport = /*#__PURE__*/react.memo(props => {
1379
2320
  viewportId: viewportId,
1380
2321
  servicesManager: servicesManager
1381
2322
  })), /*#__PURE__*/react.createElement("div", {
1382
- className: "absolute w-full"
1383
- }, viewportDialogState.viewportId === viewportId && /*#__PURE__*/react.createElement(ui_src/* Notification */.P_, {
2323
+ className: "absolute top-[24px] w-full"
2324
+ }, viewportDialogState.viewportId === viewportId && /*#__PURE__*/react.createElement(ui_src/* Notification */.Eg, {
1384
2325
  id: "viewport-notification",
1385
2326
  message: viewportDialogState.message,
1386
2327
  type: viewportDialogState.type,
1387
2328
  actions: viewportDialogState.actions,
1388
2329
  onSubmit: viewportDialogState.onSubmit,
1389
2330
  onOutsideClick: viewportDialogState.onOutsideClick
1390
- })));
2331
+ })), /*#__PURE__*/react.createElement(components_OHIFViewportActionCorners, {
2332
+ viewportId: viewportId
2333
+ }));
1391
2334
  }, areEqual);
1392
2335
  function _subscribeToJumpToMeasurementEvents(measurementService, displaySetService, elementRef, viewportId, displaySets, viewportGridService, cornerstoneViewportService) {
1393
2336
  const {
@@ -1470,7 +2413,7 @@ function _jumpToMeasurement(measurement, targetElementRef, viewportId, measureme
1470
2413
  const {
1471
2414
  SOPInstanceUID: aSOPInstanceUID,
1472
2415
  frameNumber: aFrameNumber
1473
- } = (0,getSOPInstanceAttributes/* default */.Z)(imageId);
2416
+ } = (0,getSOPInstanceAttributes/* default */.A)(imageId);
1474
2417
  return aSOPInstanceUID === SOPInstanceUID && (!frameNumber || frameNumber === aFrameNumber);
1475
2418
  });
1476
2419
  } else {
@@ -1501,6 +2444,42 @@ function _jumpToMeasurement(measurement, targetElementRef, viewportId, measureme
1501
2444
  cacheJumpToMeasurementEvent = null;
1502
2445
  }
1503
2446
  }
2447
+ function _rehydrateSynchronizers(synchronizersStore, viewportId, syncGroupService) {
2448
+ synchronizersStore[viewportId].forEach(synchronizerObj => {
2449
+ if (!synchronizerObj.id) {
2450
+ return;
2451
+ }
2452
+ const {
2453
+ id,
2454
+ sourceViewports,
2455
+ targetViewports
2456
+ } = synchronizerObj;
2457
+ const synchronizer = syncGroupService.getSynchronizer(id);
2458
+ if (!synchronizer) {
2459
+ return;
2460
+ }
2461
+ const sourceViewportInfo = sourceViewports.find(sourceViewport => sourceViewport.viewportId === viewportId);
2462
+ const targetViewportInfo = targetViewports.find(targetViewport => targetViewport.viewportId === viewportId);
2463
+ const isSourceViewportInSynchronizer = synchronizer.getSourceViewports().find(sourceViewport => sourceViewport.viewportId === viewportId);
2464
+ const isTargetViewportInSynchronizer = synchronizer.getTargetViewports().find(targetViewport => targetViewport.viewportId === viewportId);
2465
+
2466
+ // if the viewport was previously a source viewport, add it again
2467
+ if (sourceViewportInfo && !isSourceViewportInSynchronizer) {
2468
+ synchronizer.addSource({
2469
+ viewportId: sourceViewportInfo.viewportId,
2470
+ renderingEngineId: sourceViewportInfo.renderingEngineId
2471
+ });
2472
+ }
2473
+
2474
+ // if the viewport was previously a target viewport, add it again
2475
+ if (targetViewportInfo && !isTargetViewportInSynchronizer) {
2476
+ synchronizer.addTarget({
2477
+ viewportId: targetViewportInfo.viewportId,
2478
+ renderingEngineId: targetViewportInfo.renderingEngineId
2479
+ });
2480
+ }
2481
+ });
2482
+ }
1504
2483
 
1505
2484
  // Component displayName
1506
2485
  OHIFCornerstoneViewport.displayName = 'OHIFCornerstoneViewport';