@ohif/app 3.0.0 → 3.5.0

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 (125) hide show
  1. package/dist/151.bundle.07bac9172580a60fae7a.js +2579 -0
  2. package/dist/192.bundle.62be5f0ef9705a485071.js +894 -0
  3. package/dist/199.bundle.2286f24cf0a068e7f50c.js +480 -0
  4. package/dist/205.bundle.39e6c847d618ad2b1b7a.js +62 -0
  5. package/dist/208.bundle.23748a85dfdc79c05d3a.js +864 -0
  6. package/dist/270.bundle.abbdb5348274bae3e8bc.js +23906 -0
  7. package/dist/283.bundle.33f99a75a5e2d9333da2.js +2939 -0
  8. package/dist/295.bundle.5105ce962be15c92484d.js +48 -0
  9. package/dist/331.bundle.7ac7b142d249d14fd99e.js +73034 -0
  10. package/dist/351.bundle.c5d7279ef42e30f61e08.js +1471 -0
  11. package/dist/351.css +3 -0
  12. package/dist/36785fbd89b0e17f6099.wasm +0 -0
  13. package/dist/381.bundle.0905e683605fcbc0895f.js +1009 -0
  14. package/dist/404.bundle.0f7a500421f246153d89.js +706 -0
  15. package/dist/50.bundle.4cb103cd20f5ffccf927.js +324 -0
  16. package/dist/5004fdc02f329ce53b69.wasm +0 -0
  17. package/dist/531.bundle.1bc152c87c7e2e987d2b.js +5935 -0
  18. package/dist/55.bundle.a5a215e13a8511f7aee7.js +685 -0
  19. package/dist/55.css +3 -0
  20. package/dist/569.bundle.d147c0aa0604f8ea2094.js +514 -0
  21. package/dist/581.bundle.646c89c5c3e3ee096363.js +508 -0
  22. package/dist/606.bundle.5d876f5f3dd8287f0a28.js +4939 -0
  23. package/dist/610.min.worker.js +2 -0
  24. package/dist/610.min.worker.js.map +1 -0
  25. package/dist/616.bundle.bec4736d8c9513e62856.js +686 -0
  26. package/dist/62ab5d58a2bea7b5a1dc.wasm +0 -0
  27. package/dist/642.bundle.030d908e22c8ff5611f3.js +169 -0
  28. package/dist/65916ef3def695744bda.wasm +0 -0
  29. package/dist/664.bundle.4792c88ae0d6d4b5ed13.js +901 -0
  30. package/dist/707.bundle.0a74aa3e61ed002eb3c6.js +9049 -0
  31. package/dist/707.css +1 -0
  32. package/dist/728.bundle.d13856835357400fef82.js +26221 -0
  33. package/dist/744.bundle.53b07e48e07a11e920ac.js +2355 -0
  34. package/dist/75788f12450d4c5ed494.wasm +0 -0
  35. package/dist/75a0c2dfe07b824c7d21.wasm +0 -0
  36. package/dist/780.bundle.f60ac1906e0ae080dee8.js +4769 -0
  37. package/dist/790.bundle.b4df2c5d78a2a565b150.js +454 -0
  38. package/dist/799.bundle.3fff638815e355b0bdfd.js +271 -0
  39. package/dist/806.css +1 -0
  40. package/dist/82.bundle.a24015533196e05d190e.js +6104 -0
  41. package/dist/917.bundle.a094ae9e9de6df4119ae.js +196 -0
  42. package/dist/926.bundle.dbc9d0e591cb9217fda2.js +72552 -0
  43. package/dist/935.bundle.deeffff0e4f7b528e3c3.js +1849 -0
  44. package/dist/945.min.worker.js +2 -0
  45. package/dist/945.min.worker.js.map +1 -0
  46. package/dist/953.bundle.c14d9eb6400f697019ee.js +449 -0
  47. package/dist/973.bundle.4100cf103686b64938d1.js +261 -0
  48. package/dist/976.bundle.2720eb892514e1818018.js +2725 -0
  49. package/dist/984.bundle.157fc66ea5040e1364af.js +1842 -0
  50. package/dist/_headers +6 -0
  51. package/dist/_redirects +6 -0
  52. package/dist/app-config.js +215 -0
  53. package/dist/app.bundle.253eeb2a7ee986e89c50.js +154621 -0
  54. package/dist/app.bundle.css +21 -0
  55. package/dist/assets/android-chrome-144x144.png +0 -0
  56. package/dist/assets/android-chrome-192x192.png +0 -0
  57. package/dist/assets/android-chrome-256x256.png +0 -0
  58. package/dist/assets/android-chrome-36x36.png +0 -0
  59. package/dist/assets/android-chrome-384x384.png +0 -0
  60. package/dist/assets/android-chrome-48x48.png +0 -0
  61. package/dist/assets/android-chrome-512x512.png +0 -0
  62. package/dist/assets/android-chrome-72x72.png +0 -0
  63. package/dist/assets/android-chrome-96x96.png +0 -0
  64. package/dist/assets/apple-touch-icon-1024x1024.png +0 -0
  65. package/dist/assets/apple-touch-icon-114x114.png +0 -0
  66. package/dist/assets/apple-touch-icon-120x120.png +0 -0
  67. package/dist/assets/apple-touch-icon-144x144.png +0 -0
  68. package/dist/assets/apple-touch-icon-152x152.png +0 -0
  69. package/dist/assets/apple-touch-icon-167x167.png +0 -0
  70. package/dist/assets/apple-touch-icon-180x180.png +0 -0
  71. package/dist/assets/apple-touch-icon-57x57.png +0 -0
  72. package/dist/assets/apple-touch-icon-60x60.png +0 -0
  73. package/dist/assets/apple-touch-icon-72x72.png +0 -0
  74. package/dist/assets/apple-touch-icon-76x76.png +0 -0
  75. package/dist/assets/apple-touch-icon-precomposed.png +0 -0
  76. package/dist/assets/apple-touch-icon.png +0 -0
  77. package/dist/assets/apple-touch-startup-image-1182x2208.png +0 -0
  78. package/dist/assets/apple-touch-startup-image-1242x2148.png +0 -0
  79. package/dist/assets/apple-touch-startup-image-1496x2048.png +0 -0
  80. package/dist/assets/apple-touch-startup-image-1536x2008.png +0 -0
  81. package/dist/assets/apple-touch-startup-image-320x460.png +0 -0
  82. package/dist/assets/apple-touch-startup-image-640x1096.png +0 -0
  83. package/dist/assets/apple-touch-startup-image-640x920.png +0 -0
  84. package/dist/assets/apple-touch-startup-image-748x1024.png +0 -0
  85. package/dist/assets/apple-touch-startup-image-750x1294.png +0 -0
  86. package/dist/assets/apple-touch-startup-image-768x1004.png +0 -0
  87. package/dist/assets/browserconfig.xml +12 -0
  88. package/dist/assets/coast-228x228.png +0 -0
  89. package/dist/assets/favicon-16x16.png +0 -0
  90. package/dist/assets/favicon-32x32.png +0 -0
  91. package/dist/assets/favicon.ico +0 -0
  92. package/dist/assets/firefox_app_128x128.png +0 -0
  93. package/dist/assets/firefox_app_512x512.png +0 -0
  94. package/dist/assets/firefox_app_60x60.png +0 -0
  95. package/dist/assets/manifest.webapp +14 -0
  96. package/dist/assets/mstile-144x144.png +0 -0
  97. package/dist/assets/mstile-150x150.png +0 -0
  98. package/dist/assets/mstile-310x150.png +0 -0
  99. package/dist/assets/mstile-310x310.png +0 -0
  100. package/dist/assets/mstile-70x70.png +0 -0
  101. package/dist/assets/yandex-browser-50x50.png +0 -0
  102. package/dist/assets/yandex-browser-manifest.json +9 -0
  103. package/dist/b6b803111e2d06a825bd.wasm +0 -0
  104. package/dist/c22b37c3488e1d6c3aa4.wasm +0 -0
  105. package/dist/cornerstoneDICOMImageLoader.min.js +2 -0
  106. package/dist/cornerstoneDICOMImageLoader.min.js.map +1 -0
  107. package/dist/dicom-microscopy-viewer.bundle.aa60bdf008c32c39cfd7.js +12 -0
  108. package/dist/dicomMicroscopyViewer.min.js +3 -0
  109. package/dist/dicomMicroscopyViewer.min.js.LICENSE.txt +29 -0
  110. package/dist/es6-shim.min.js +12 -0
  111. package/dist/google.js +75 -0
  112. package/dist/index.html +1 -0
  113. package/dist/index.worker.ea71efba2ce63c499055.worker.js +2 -0
  114. package/dist/index.worker.ea71efba2ce63c499055.worker.js.map +1 -0
  115. package/dist/index.worker.min.worker.js +2 -0
  116. package/dist/index.worker.min.worker.js.map +1 -0
  117. package/dist/init-service-worker.js +59 -0
  118. package/dist/manifest.json +59 -0
  119. package/dist/ohif-logo-light.svg +15 -0
  120. package/dist/ohif-logo.svg +15 -0
  121. package/dist/oidc-client.min.js +46 -0
  122. package/dist/polyfill.min.js +1 -0
  123. package/dist/silent-refresh.html +16 -0
  124. package/dist/sw.js +56 -0
  125. package/package.json +24 -23
@@ -0,0 +1,2355 @@
1
+ "use strict";
2
+ (globalThis["webpackChunk"] = globalThis["webpackChunk"] || []).push([[744],{
3
+
4
+ /***/ 43418:
5
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
6
+
7
+
8
+ // EXPORTS
9
+ __webpack_require__.d(__webpack_exports__, {
10
+ "Z": () => (/* binding */ src_getContextModule),
11
+ "I": () => (/* reexport */ useTrackedMeasurements)
12
+ });
13
+
14
+ // EXTERNAL MODULE: ../../../node_modules/react/index.js
15
+ var react = __webpack_require__(32735);
16
+ // EXTERNAL MODULE: ../../../node_modules/prop-types/index.js
17
+ var prop_types = __webpack_require__(60216);
18
+ var prop_types_default = /*#__PURE__*/__webpack_require__.n(prop_types);
19
+ // EXTERNAL MODULE: ../../../node_modules/xstate/es/index.js + 22 modules
20
+ var es = __webpack_require__(87519);
21
+ // EXTERNAL MODULE: ../../../node_modules/@xstate/react/lib/index.js
22
+ var lib = __webpack_require__(38531);
23
+ // EXTERNAL MODULE: ../../ui/src/index.js + 449 modules
24
+ var src = __webpack_require__(43803);
25
+ ;// CONCATENATED MODULE: ../../../extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/measurementTrackingMachine.js
26
+
27
+ const RESPONSE = {
28
+ NO_NEVER: -1,
29
+ CANCEL: 0,
30
+ CREATE_REPORT: 1,
31
+ ADD_SERIES: 2,
32
+ SET_STUDY_AND_SERIES: 3,
33
+ NO_NOT_FOR_SERIES: 4,
34
+ HYDRATE_REPORT: 5
35
+ };
36
+ const machineConfiguration = {
37
+ id: 'measurementTracking',
38
+ initial: 'idle',
39
+ context: {
40
+ trackedStudy: '',
41
+ trackedSeries: [],
42
+ ignoredSeries: [],
43
+ //
44
+ prevTrackedStudy: '',
45
+ prevTrackedSeries: [],
46
+ prevIgnoredSeries: [],
47
+ //
48
+ ignoredSRSeriesForHydration: [],
49
+ isDirty: false
50
+ },
51
+ states: {
52
+ off: {
53
+ type: 'final'
54
+ },
55
+ idle: {
56
+ entry: 'clearContext',
57
+ on: {
58
+ TRACK_SERIES: 'promptBeginTracking',
59
+ // Unused? We may only do PROMPT_HYDRATE_SR now?
60
+ SET_TRACKED_SERIES: [{
61
+ target: 'tracking',
62
+ actions: ['setTrackedStudyAndMultipleSeries', 'setIsDirtyToClean']
63
+ }],
64
+ PROMPT_HYDRATE_SR: {
65
+ target: 'promptHydrateStructuredReport',
66
+ cond: 'hasNotIgnoredSRSeriesForHydration'
67
+ },
68
+ RESTORE_PROMPT_HYDRATE_SR: 'promptHydrateStructuredReport',
69
+ HYDRATE_SR: 'hydrateStructuredReport'
70
+ }
71
+ },
72
+ promptBeginTracking: {
73
+ invoke: {
74
+ src: 'promptBeginTracking',
75
+ onDone: [{
76
+ target: 'tracking',
77
+ actions: ['setTrackedStudyAndSeries', 'setIsDirty'],
78
+ cond: 'shouldSetStudyAndSeries'
79
+ }, {
80
+ target: 'off',
81
+ cond: 'shouldKillMachine'
82
+ }, {
83
+ target: 'idle'
84
+ }],
85
+ onError: {
86
+ target: 'idle'
87
+ }
88
+ }
89
+ },
90
+ tracking: {
91
+ on: {
92
+ TRACK_SERIES: [{
93
+ target: 'promptTrackNewStudy',
94
+ cond: 'isNewStudy'
95
+ }, {
96
+ target: 'promptTrackNewSeries',
97
+ cond: 'isNewSeries'
98
+ }],
99
+ UNTRACK_SERIES: [{
100
+ target: 'tracking',
101
+ actions: ['removeTrackedSeries', 'setIsDirty'],
102
+ cond: 'hasRemainingTrackedSeries'
103
+ }, {
104
+ target: 'idle'
105
+ }],
106
+ SET_TRACKED_SERIES: [{
107
+ target: 'tracking',
108
+ actions: ['setTrackedStudyAndMultipleSeries']
109
+ }],
110
+ SAVE_REPORT: 'promptSaveReport',
111
+ SET_DIRTY: [{
112
+ target: 'tracking',
113
+ actions: ['setIsDirty'],
114
+ cond: 'shouldSetDirty'
115
+ }, {
116
+ target: 'tracking'
117
+ }]
118
+ }
119
+ },
120
+ promptTrackNewSeries: {
121
+ invoke: {
122
+ src: 'promptTrackNewSeries',
123
+ onDone: [{
124
+ target: 'tracking',
125
+ actions: ['addTrackedSeries', 'setIsDirty'],
126
+ cond: 'shouldAddSeries'
127
+ }, {
128
+ target: 'tracking',
129
+ actions: ['discardPreviouslyTrackedMeasurements', 'setTrackedStudyAndSeries', 'setIsDirty'],
130
+ cond: 'shouldSetStudyAndSeries'
131
+ }, {
132
+ target: 'promptSaveReport',
133
+ cond: 'shouldPromptSaveReport'
134
+ }, {
135
+ target: 'tracking'
136
+ }],
137
+ onError: {
138
+ target: 'idle'
139
+ }
140
+ }
141
+ },
142
+ promptTrackNewStudy: {
143
+ invoke: {
144
+ src: 'promptTrackNewStudy',
145
+ onDone: [{
146
+ target: 'tracking',
147
+ actions: ['discardPreviouslyTrackedMeasurements', 'setTrackedStudyAndSeries', 'setIsDirty'],
148
+ cond: 'shouldSetStudyAndSeries'
149
+ }, {
150
+ target: 'tracking',
151
+ actions: ['ignoreSeries'],
152
+ cond: 'shouldAddIgnoredSeries'
153
+ }, {
154
+ target: 'promptSaveReport',
155
+ cond: 'shouldPromptSaveReport'
156
+ }, {
157
+ target: 'tracking'
158
+ }],
159
+ onError: {
160
+ target: 'idle'
161
+ }
162
+ }
163
+ },
164
+ promptSaveReport: {
165
+ invoke: {
166
+ src: 'promptSaveReport',
167
+ onDone: [
168
+ // "clicked the save button"
169
+ // - should clear all measurements
170
+ // - show DICOM SR
171
+ {
172
+ target: 'idle',
173
+ actions: ['clearAllMeasurements', 'showStructuredReportDisplaySetInActiveViewport'],
174
+ cond: 'shouldSaveAndContinueWithSameReport'
175
+ },
176
+ // "starting a new report"
177
+ // - remove "just saved" measurements
178
+ // - start tracking a new study + report
179
+ {
180
+ target: 'tracking',
181
+ actions: ['discardPreviouslyTrackedMeasurements', 'setTrackedStudyAndSeries'],
182
+ cond: 'shouldSaveAndStartNewReport'
183
+ },
184
+ // Cancel, back to tracking
185
+ {
186
+ target: 'tracking'
187
+ }],
188
+ onError: {
189
+ target: 'idle'
190
+ }
191
+ }
192
+ },
193
+ promptHydrateStructuredReport: {
194
+ invoke: {
195
+ src: 'promptHydrateStructuredReport',
196
+ onDone: [{
197
+ target: 'tracking',
198
+ actions: ['setTrackedStudyAndMultipleSeries', 'jumpToFirstMeasurementInActiveViewport', 'setIsDirtyToClean'],
199
+ cond: 'shouldHydrateStructuredReport'
200
+ }, {
201
+ target: 'idle',
202
+ actions: ['ignoreHydrationForSRSeries'],
203
+ cond: 'shouldIgnoreHydrationForSR'
204
+ }],
205
+ onError: {
206
+ target: 'idle'
207
+ }
208
+ }
209
+ },
210
+ hydrateStructuredReport: {
211
+ invoke: {
212
+ src: 'hydrateStructuredReport',
213
+ onDone: [{
214
+ target: 'tracking',
215
+ actions: ['setTrackedStudyAndMultipleSeries', 'jumpToFirstMeasurementInActiveViewport', 'setIsDirtyToClean']
216
+ }],
217
+ onError: {
218
+ target: 'idle'
219
+ }
220
+ }
221
+ }
222
+ },
223
+ strict: true
224
+ };
225
+ const defaultOptions = {
226
+ services: {
227
+ promptBeginTracking: (ctx, evt) => {
228
+ // return { userResponse, StudyInstanceUID, SeriesInstanceUID }
229
+ },
230
+ promptTrackNewStudy: (ctx, evt) => {
231
+ // return { userResponse, StudyInstanceUID, SeriesInstanceUID }
232
+ },
233
+ promptTrackNewSeries: (ctx, evt) => {
234
+ // return { userResponse, StudyInstanceUID, SeriesInstanceUID }
235
+ }
236
+ },
237
+ actions: {
238
+ discardPreviouslyTrackedMeasurements: (ctx, evt) => {
239
+ console.log('discardPreviouslyTrackedMeasurements: not implemented');
240
+ },
241
+ clearAllMeasurements: (ctx, evt) => {
242
+ console.log('clearAllMeasurements: not implemented');
243
+ },
244
+ jumpToFirstMeasurementInActiveViewport: (ctx, evt) => {
245
+ console.warn('jumpToFirstMeasurementInActiveViewport: not implemented');
246
+ },
247
+ showStructuredReportDisplaySetInActiveViewport: (ctx, evt) => {
248
+ console.warn('showStructuredReportDisplaySetInActiveViewport: not implemented');
249
+ },
250
+ clearContext: (0,es.assign)({
251
+ trackedStudy: '',
252
+ trackedSeries: [],
253
+ ignoredSeries: [],
254
+ prevTrackedStudy: '',
255
+ prevTrackedSeries: [],
256
+ prevIgnoredSeries: []
257
+ }),
258
+ // Promise resolves w/ `evt.data.*`
259
+ setTrackedStudyAndSeries: (0,es.assign)((ctx, evt) => ({
260
+ prevTrackedStudy: ctx.trackedStudy,
261
+ prevTrackedSeries: ctx.trackedSeries.slice(),
262
+ prevIgnoredSeries: ctx.ignoredSeries.slice(),
263
+ //
264
+ trackedStudy: evt.data.StudyInstanceUID,
265
+ trackedSeries: [evt.data.SeriesInstanceUID],
266
+ ignoredSeries: []
267
+ })),
268
+ setTrackedStudyAndMultipleSeries: (0,es.assign)((ctx, evt) => {
269
+ const studyInstanceUID = evt.StudyInstanceUID || evt.data.StudyInstanceUID;
270
+ const seriesInstanceUIDs = evt.SeriesInstanceUIDs || evt.data.SeriesInstanceUIDs;
271
+ return {
272
+ prevTrackedStudy: ctx.trackedStudy,
273
+ prevTrackedSeries: ctx.trackedSeries.slice(),
274
+ prevIgnoredSeries: ctx.ignoredSeries.slice(),
275
+ //
276
+ trackedStudy: studyInstanceUID,
277
+ trackedSeries: [...ctx.trackedSeries, ...seriesInstanceUIDs],
278
+ ignoredSeries: []
279
+ };
280
+ }),
281
+ setIsDirtyToClean: (0,es.assign)((ctx, evt) => ({
282
+ isDirty: false
283
+ })),
284
+ setIsDirty: (0,es.assign)((ctx, evt) => ({
285
+ isDirty: true
286
+ })),
287
+ ignoreSeries: (0,es.assign)((ctx, evt) => ({
288
+ prevIgnoredSeries: [...ctx.ignoredSeries],
289
+ ignoredSeries: [...ctx.ignoredSeries, evt.data.SeriesInstanceUID]
290
+ })),
291
+ ignoreHydrationForSRSeries: (0,es.assign)((ctx, evt) => ({
292
+ ignoredSRSeriesForHydration: [...ctx.ignoredSRSeriesForHydration, evt.data.srSeriesInstanceUID]
293
+ })),
294
+ addTrackedSeries: (0,es.assign)((ctx, evt) => ({
295
+ prevTrackedSeries: [...ctx.trackedSeries],
296
+ trackedSeries: [...ctx.trackedSeries, evt.data.SeriesInstanceUID]
297
+ })),
298
+ removeTrackedSeries: (0,es.assign)((ctx, evt) => ({
299
+ prevTrackedSeries: ctx.trackedSeries.slice().filter(ser => ser !== evt.SeriesInstanceUID),
300
+ trackedSeries: ctx.trackedSeries.slice().filter(ser => ser !== evt.SeriesInstanceUID)
301
+ }))
302
+ },
303
+ guards: {
304
+ // We set dirty any time we performan an action that:
305
+ // - Tracks a new study
306
+ // - Tracks a new series
307
+ // - Adds a measurement to an already tracked study/series
308
+ //
309
+ // We set clean any time we restore from an SR
310
+ //
311
+ // This guard/condition is specific to "new measurements"
312
+ // to make sure we only track dirty when the new measurement is specific
313
+ // to a series we're already tracking
314
+ //
315
+ // tl;dr
316
+ // Any report change, that is not a hydration of an existing report, should
317
+ // result in a "dirty" report
318
+ //
319
+ // Where dirty means there would be "loss of data" if we blew away measurements
320
+ // without creating a new SR.
321
+ shouldSetDirty: (ctx, evt) => {
322
+ return (
323
+ // When would this happen?
324
+ evt.SeriesInstanceUID === undefined || ctx.trackedSeries.includes(evt.SeriesInstanceUID)
325
+ );
326
+ },
327
+ shouldKillMachine: (ctx, evt) => evt.data && evt.data.userResponse === RESPONSE.NO_NEVER,
328
+ shouldAddSeries: (ctx, evt) => evt.data && evt.data.userResponse === RESPONSE.ADD_SERIES,
329
+ shouldSetStudyAndSeries: (ctx, evt) => evt.data && evt.data.userResponse === RESPONSE.SET_STUDY_AND_SERIES,
330
+ shouldAddIgnoredSeries: (ctx, evt) => evt.data && evt.data.userResponse === RESPONSE.NO_NOT_FOR_SERIES,
331
+ shouldPromptSaveReport: (ctx, evt) => evt.data && evt.data.userResponse === RESPONSE.CREATE_REPORT,
332
+ shouldIgnoreHydrationForSR: (ctx, evt) => evt.data && evt.data.userResponse === RESPONSE.CANCEL,
333
+ shouldSaveAndContinueWithSameReport: (ctx, evt) => evt.data && evt.data.userResponse === RESPONSE.CREATE_REPORT && evt.data.isBackupSave === true,
334
+ shouldSaveAndStartNewReport: (ctx, evt) => evt.data && evt.data.userResponse === RESPONSE.CREATE_REPORT && evt.data.isBackupSave === false,
335
+ shouldHydrateStructuredReport: (ctx, evt) => evt.data && evt.data.userResponse === RESPONSE.HYDRATE_REPORT,
336
+ // Has more than 1, or SeriesInstanceUID is not in list
337
+ // --> Post removal would have non-empty trackedSeries array
338
+ hasRemainingTrackedSeries: (ctx, evt) => ctx.trackedSeries.length > 1 || !ctx.trackedSeries.includes(evt.SeriesInstanceUID),
339
+ hasNotIgnoredSRSeriesForHydration: (ctx, evt) => {
340
+ return !ctx.ignoredSRSeriesForHydration.includes(evt.SeriesInstanceUID);
341
+ },
342
+ isNewStudy: (ctx, evt) => !ctx.ignoredSeries.includes(evt.SeriesInstanceUID) && ctx.trackedStudy !== evt.StudyInstanceUID,
343
+ isNewSeries: (ctx, evt) => !ctx.ignoredSeries.includes(evt.SeriesInstanceUID) && !ctx.trackedSeries.includes(evt.SeriesInstanceUID)
344
+ }
345
+ };
346
+
347
+ ;// CONCATENATED MODULE: ../../../extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/promptBeginTracking.js
348
+ const promptBeginTracking_RESPONSE = {
349
+ NO_NEVER: -1,
350
+ CANCEL: 0,
351
+ CREATE_REPORT: 1,
352
+ ADD_SERIES: 2,
353
+ SET_STUDY_AND_SERIES: 3
354
+ };
355
+ function promptBeginTracking(_ref, ctx, evt) {
356
+ let {
357
+ servicesManager,
358
+ extensionManager
359
+ } = _ref;
360
+ const {
361
+ uiViewportDialogService
362
+ } = servicesManager.services;
363
+ const {
364
+ viewportIndex,
365
+ StudyInstanceUID,
366
+ SeriesInstanceUID
367
+ } = evt;
368
+ return new Promise(async function (resolve, reject) {
369
+ let promptResult = await _askTrackMeasurements(uiViewportDialogService, viewportIndex);
370
+ resolve({
371
+ userResponse: promptResult,
372
+ StudyInstanceUID,
373
+ SeriesInstanceUID,
374
+ viewportIndex
375
+ });
376
+ });
377
+ }
378
+ function _askTrackMeasurements(uiViewportDialogService, viewportIndex) {
379
+ return new Promise(function (resolve, reject) {
380
+ const message = 'Track measurements for this series?';
381
+ const actions = [{
382
+ id: 'prompt-begin-tracking-cancel',
383
+ type: 'cancel',
384
+ text: 'No',
385
+ value: promptBeginTracking_RESPONSE.CANCEL
386
+ }, {
387
+ id: 'prompt-begin-tracking-no-do-not-ask-again',
388
+ type: 'secondary',
389
+ text: 'No, do not ask again',
390
+ value: promptBeginTracking_RESPONSE.NO_NEVER
391
+ }, {
392
+ id: 'prompt-begin-tracking-yes',
393
+ type: 'primary',
394
+ text: 'Yes',
395
+ value: promptBeginTracking_RESPONSE.SET_STUDY_AND_SERIES
396
+ }];
397
+ const onSubmit = result => {
398
+ uiViewportDialogService.hide();
399
+ resolve(result);
400
+ };
401
+ uiViewportDialogService.show({
402
+ viewportIndex,
403
+ id: 'measurement-tracking-prompt-begin-tracking',
404
+ type: 'info',
405
+ message,
406
+ actions,
407
+ onSubmit,
408
+ onOutsideClick: () => {
409
+ uiViewportDialogService.hide();
410
+ resolve(promptBeginTracking_RESPONSE.CANCEL);
411
+ }
412
+ });
413
+ });
414
+ }
415
+ /* harmony default export */ const TrackedMeasurementsContext_promptBeginTracking = (promptBeginTracking);
416
+ ;// CONCATENATED MODULE: ../../../extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/promptTrackNewSeries.js
417
+ const promptTrackNewSeries_RESPONSE = {
418
+ NO_NEVER: -1,
419
+ CANCEL: 0,
420
+ CREATE_REPORT: 1,
421
+ ADD_SERIES: 2,
422
+ SET_STUDY_AND_SERIES: 3,
423
+ NO_NOT_FOR_SERIES: 4
424
+ };
425
+ function promptTrackNewSeries(_ref, ctx, evt) {
426
+ let {
427
+ servicesManager,
428
+ extensionManager
429
+ } = _ref;
430
+ const {
431
+ UIViewportDialogService
432
+ } = servicesManager.services;
433
+ const {
434
+ viewportIndex,
435
+ StudyInstanceUID,
436
+ SeriesInstanceUID
437
+ } = evt;
438
+ return new Promise(async function (resolve, reject) {
439
+ let promptResult = await _askShouldAddMeasurements(UIViewportDialogService, viewportIndex);
440
+ if (promptResult === promptTrackNewSeries_RESPONSE.CREATE_REPORT) {
441
+ promptResult = ctx.isDirty ? await _askSaveDiscardOrCancel(UIViewportDialogService, viewportIndex) : promptTrackNewSeries_RESPONSE.SET_STUDY_AND_SERIES;
442
+ }
443
+ resolve({
444
+ userResponse: promptResult,
445
+ StudyInstanceUID,
446
+ SeriesInstanceUID,
447
+ viewportIndex,
448
+ isBackupSave: false
449
+ });
450
+ });
451
+ }
452
+ function _askShouldAddMeasurements(uiViewportDialogService, viewportIndex) {
453
+ return new Promise(function (resolve, reject) {
454
+ const message = 'Do you want to add this measurement to the existing report?';
455
+ const actions = [{
456
+ type: 'cancel',
457
+ text: 'Cancel',
458
+ value: promptTrackNewSeries_RESPONSE.CANCEL
459
+ }, {
460
+ type: 'secondary',
461
+ text: 'Create new report',
462
+ value: promptTrackNewSeries_RESPONSE.CREATE_REPORT
463
+ }, {
464
+ type: 'primary',
465
+ text: 'Add to existing report',
466
+ value: promptTrackNewSeries_RESPONSE.ADD_SERIES
467
+ }];
468
+ const onSubmit = result => {
469
+ uiViewportDialogService.hide();
470
+ resolve(result);
471
+ };
472
+ uiViewportDialogService.show({
473
+ viewportIndex,
474
+ type: 'info',
475
+ message,
476
+ actions,
477
+ onSubmit,
478
+ onOutsideClick: () => {
479
+ uiViewportDialogService.hide();
480
+ resolve(promptTrackNewSeries_RESPONSE.CANCEL);
481
+ }
482
+ });
483
+ });
484
+ }
485
+ function _askSaveDiscardOrCancel(UIViewportDialogService, viewportIndex) {
486
+ return new Promise(function (resolve, reject) {
487
+ const message = 'You have existing tracked measurements. What would you like to do with your existing tracked measurements?';
488
+ const actions = [{
489
+ type: 'cancel',
490
+ text: 'Cancel',
491
+ value: promptTrackNewSeries_RESPONSE.CANCEL
492
+ }, {
493
+ type: 'secondary',
494
+ text: 'Save',
495
+ value: promptTrackNewSeries_RESPONSE.CREATE_REPORT
496
+ }, {
497
+ type: 'primary',
498
+ text: 'Discard',
499
+ value: promptTrackNewSeries_RESPONSE.SET_STUDY_AND_SERIES
500
+ }];
501
+ const onSubmit = result => {
502
+ UIViewportDialogService.hide();
503
+ resolve(result);
504
+ };
505
+ UIViewportDialogService.show({
506
+ viewportIndex,
507
+ type: 'warning',
508
+ message,
509
+ actions,
510
+ onSubmit,
511
+ onOutsideClick: () => {
512
+ UIViewportDialogService.hide();
513
+ resolve(promptTrackNewSeries_RESPONSE.CANCEL);
514
+ }
515
+ });
516
+ });
517
+ }
518
+ /* harmony default export */ const TrackedMeasurementsContext_promptTrackNewSeries = (promptTrackNewSeries);
519
+ ;// CONCATENATED MODULE: ../../../extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/promptTrackNewStudy.js
520
+ const promptTrackNewStudy_RESPONSE = {
521
+ NO_NEVER: -1,
522
+ CANCEL: 0,
523
+ CREATE_REPORT: 1,
524
+ ADD_SERIES: 2,
525
+ SET_STUDY_AND_SERIES: 3,
526
+ NO_NOT_FOR_SERIES: 4
527
+ };
528
+ function promptTrackNewStudy(_ref, ctx, evt) {
529
+ let {
530
+ servicesManager,
531
+ extensionManager
532
+ } = _ref;
533
+ const {
534
+ UIViewportDialogService
535
+ } = servicesManager.services;
536
+ const {
537
+ viewportIndex,
538
+ StudyInstanceUID,
539
+ SeriesInstanceUID
540
+ } = evt;
541
+ return new Promise(async function (resolve, reject) {
542
+ let promptResult = await promptTrackNewStudy_askTrackMeasurements(UIViewportDialogService, viewportIndex);
543
+ if (promptResult === promptTrackNewStudy_RESPONSE.SET_STUDY_AND_SERIES) {
544
+ promptResult = ctx.isDirty ? await promptTrackNewStudy_askSaveDiscardOrCancel(UIViewportDialogService, viewportIndex) : promptTrackNewStudy_RESPONSE.SET_STUDY_AND_SERIES;
545
+ }
546
+ resolve({
547
+ userResponse: promptResult,
548
+ StudyInstanceUID,
549
+ SeriesInstanceUID,
550
+ viewportIndex,
551
+ isBackupSave: false
552
+ });
553
+ });
554
+ }
555
+ function promptTrackNewStudy_askTrackMeasurements(UIViewportDialogService, viewportIndex) {
556
+ return new Promise(function (resolve, reject) {
557
+ const message = 'Track measurements for this series?';
558
+ const actions = [{
559
+ type: 'cancel',
560
+ text: 'No',
561
+ value: promptTrackNewStudy_RESPONSE.CANCEL
562
+ }, {
563
+ type: 'secondary',
564
+ text: 'No, do not ask again for this series',
565
+ value: promptTrackNewStudy_RESPONSE.NO_NOT_FOR_SERIES
566
+ }, {
567
+ type: 'primary',
568
+ text: 'Yes',
569
+ value: promptTrackNewStudy_RESPONSE.SET_STUDY_AND_SERIES
570
+ }];
571
+ const onSubmit = result => {
572
+ UIViewportDialogService.hide();
573
+ resolve(result);
574
+ };
575
+ UIViewportDialogService.show({
576
+ viewportIndex,
577
+ type: 'info',
578
+ message,
579
+ actions,
580
+ onSubmit,
581
+ onOutsideClick: () => {
582
+ UIViewportDialogService.hide();
583
+ resolve(promptTrackNewStudy_RESPONSE.CANCEL);
584
+ }
585
+ });
586
+ });
587
+ }
588
+ function promptTrackNewStudy_askSaveDiscardOrCancel(UIViewportDialogService, viewportIndex) {
589
+ return new Promise(function (resolve, reject) {
590
+ const message = 'Measurements cannot span across multiple studies. Do you want to save your tracked measurements?';
591
+ const actions = [{
592
+ type: 'cancel',
593
+ text: 'Cancel',
594
+ value: promptTrackNewStudy_RESPONSE.CANCEL
595
+ }, {
596
+ type: 'secondary',
597
+ text: 'No, discard previously tracked series & measurements',
598
+ value: promptTrackNewStudy_RESPONSE.SET_STUDY_AND_SERIES
599
+ }, {
600
+ type: 'primary',
601
+ text: 'Yes',
602
+ value: promptTrackNewStudy_RESPONSE.CREATE_REPORT
603
+ }];
604
+ const onSubmit = result => {
605
+ UIViewportDialogService.hide();
606
+ resolve(result);
607
+ };
608
+ UIViewportDialogService.show({
609
+ viewportIndex,
610
+ type: 'warning',
611
+ message,
612
+ actions,
613
+ onSubmit,
614
+ onOutsideClick: () => {
615
+ UIViewportDialogService.hide();
616
+ resolve(promptTrackNewStudy_RESPONSE.CANCEL);
617
+ }
618
+ });
619
+ });
620
+ }
621
+ /* harmony default export */ const TrackedMeasurementsContext_promptTrackNewStudy = (promptTrackNewStudy);
622
+ // EXTERNAL MODULE: ../../core/src/index.ts + 101 modules
623
+ var core_src = __webpack_require__(48501);
624
+ ;// CONCATENATED MODULE: ../../../extensions/measurement-tracking/src/_shared/createReportAsync.tsx
625
+
626
+
627
+
628
+ /**
629
+ *
630
+ * @param {*} servicesManager
631
+ * @param {*} dataSource
632
+ * @param {*} measurements
633
+ * @param {*} options
634
+ * @returns {string[]} displaySetInstanceUIDs
635
+ */
636
+ async function createReportAsync(servicesManager, commandsManager, dataSource, measurements, options) {
637
+ const {
638
+ displaySetService,
639
+ uiNotificationService,
640
+ uiDialogService
641
+ } = servicesManager.services;
642
+ const loadingDialogId = uiDialogService.create({
643
+ showOverlay: true,
644
+ isDraggable: false,
645
+ centralize: true,
646
+ // TODO: Create a loading indicator component + zeplin design?
647
+ content: Loading
648
+ });
649
+ try {
650
+ const naturalizedReport = await commandsManager.runCommand('storeMeasurements', {
651
+ measurementData: measurements,
652
+ dataSource,
653
+ additionalFindingTypes: ['ArrowAnnotate'],
654
+ options
655
+ }, 'CORNERSTONE_STRUCTURED_REPORT');
656
+
657
+ // The "Mode" route listens for DicomMetadataStore changes
658
+ // When a new instance is added, it listens and
659
+ // automatically calls makeDisplaySets
660
+ core_src.DicomMetadataStore.addInstances([naturalizedReport], true);
661
+ const displaySetInstanceUID = displaySetService.getMostRecentDisplaySet();
662
+ uiNotificationService.show({
663
+ title: 'Create Report',
664
+ message: 'Measurements saved successfully',
665
+ type: 'success'
666
+ });
667
+ return [displaySetInstanceUID];
668
+ } catch (error) {
669
+ uiNotificationService.show({
670
+ title: 'Create Report',
671
+ message: error.message || 'Failed to store measurements',
672
+ type: 'error'
673
+ });
674
+ } finally {
675
+ uiDialogService.dismiss({
676
+ id: loadingDialogId
677
+ });
678
+ }
679
+ }
680
+ function Loading() {
681
+ return /*#__PURE__*/react.createElement("div", {
682
+ className: "text-primary-active"
683
+ }, "Loading...");
684
+ }
685
+ /* harmony default export */ const _shared_createReportAsync = (createReportAsync);
686
+ ;// CONCATENATED MODULE: ../../../extensions/measurement-tracking/src/_shared/PROMPT_RESPONSES.js
687
+ const PROMPT_RESPONSES_RESPONSE = {
688
+ NO_NEVER: -1,
689
+ CANCEL: 0,
690
+ CREATE_REPORT: 1,
691
+ ADD_SERIES: 2,
692
+ SET_STUDY_AND_SERIES: 3,
693
+ NO_NOT_FOR_SERIES: 4
694
+ };
695
+ /* harmony default export */ const PROMPT_RESPONSES = (PROMPT_RESPONSES_RESPONSE);
696
+ ;// CONCATENATED MODULE: ../../../extensions/measurement-tracking/src/_shared/createReportDialogPrompt.tsx
697
+ /* eslint-disable react/display-name */
698
+
699
+
700
+
701
+ function createReportDialogPrompt(uiDialogService) {
702
+ return new Promise(function (resolve, reject) {
703
+ let dialogId = undefined;
704
+ const _handleClose = () => {
705
+ // Dismiss dialog
706
+ uiDialogService.dismiss({
707
+ id: dialogId
708
+ });
709
+ // Notify of cancel action
710
+ resolve({
711
+ action: PROMPT_RESPONSES.CANCEL,
712
+ value: undefined
713
+ });
714
+ };
715
+
716
+ /**
717
+ *
718
+ * @param {string} param0.action - value of action performed
719
+ * @param {string} param0.value - value from input field
720
+ */
721
+ const _handleFormSubmit = _ref => {
722
+ let {
723
+ action,
724
+ value
725
+ } = _ref;
726
+ uiDialogService.dismiss({
727
+ id: dialogId
728
+ });
729
+ switch (action.id) {
730
+ case 'save':
731
+ resolve({
732
+ action: PROMPT_RESPONSES.CREATE_REPORT,
733
+ value: value.label
734
+ });
735
+ break;
736
+ case 'cancel':
737
+ resolve({
738
+ action: PROMPT_RESPONSES.CANCEL,
739
+ value: undefined
740
+ });
741
+ break;
742
+ }
743
+ };
744
+ dialogId = uiDialogService.create({
745
+ centralize: true,
746
+ isDraggable: false,
747
+ content: src/* Dialog */.Vq,
748
+ useLastPosition: false,
749
+ showOverlay: true,
750
+ contentProps: {
751
+ title: 'Provide a name for your report',
752
+ value: {
753
+ label: ''
754
+ },
755
+ noCloseButton: true,
756
+ onClose: _handleClose,
757
+ actions: [{
758
+ id: 'cancel',
759
+ text: 'Cancel',
760
+ type: 'primary'
761
+ }, {
762
+ id: 'save',
763
+ text: 'Save',
764
+ type: 'secondary'
765
+ }],
766
+ // TODO: Should be on button press...
767
+ onSubmit: _handleFormSubmit,
768
+ body: _ref2 => {
769
+ let {
770
+ value,
771
+ setValue
772
+ } = _ref2;
773
+ const onChangeHandler = event => {
774
+ event.persist();
775
+ setValue(value => ({
776
+ ...value,
777
+ label: event.target.value
778
+ }));
779
+ };
780
+ const onKeyPressHandler = event => {
781
+ if (event.key === 'Enter') {
782
+ uiDialogService.dismiss({
783
+ id: dialogId
784
+ });
785
+ resolve({
786
+ action: PROMPT_RESPONSES.CREATE_REPORT,
787
+ value: value.label
788
+ });
789
+ }
790
+ };
791
+ return /*#__PURE__*/react.createElement("div", {
792
+ className: "p-4 bg-primary-dark"
793
+ }, /*#__PURE__*/react.createElement(src/* Input */.II, {
794
+ autoFocus: true,
795
+ className: "mt-2 bg-black border-primary-main",
796
+ type: "text",
797
+ containerClassName: "mr-2",
798
+ value: value.label,
799
+ onChange: onChangeHandler,
800
+ onKeyPress: onKeyPressHandler
801
+ }));
802
+ }
803
+ }
804
+ });
805
+ });
806
+ }
807
+ ;// CONCATENATED MODULE: ../../../extensions/measurement-tracking/src/_shared/getNextSRSeriesNumber.js
808
+ const MIN_SR_SERIES_NUMBER = 4700;
809
+ function getNextSRSeriesNumber(displaySetService) {
810
+ const activeDisplaySets = displaySetService.getActiveDisplaySets();
811
+ const srDisplaySets = activeDisplaySets.filter(ds => ds.Modality === 'SR');
812
+ const srSeriesNumbers = srDisplaySets.map(ds => ds.SeriesNumber);
813
+ const maxSeriesNumber = Math.max(...srSeriesNumbers, MIN_SR_SERIES_NUMBER);
814
+ return maxSeriesNumber + 1;
815
+ }
816
+ ;// CONCATENATED MODULE: ../../../extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/promptSaveReport.js
817
+
818
+
819
+
820
+
821
+ function promptSaveReport(_ref, ctx, evt) {
822
+ let {
823
+ servicesManager,
824
+ commandsManager,
825
+ extensionManager
826
+ } = _ref;
827
+ const {
828
+ uiDialogService,
829
+ measurementService,
830
+ displaySetService
831
+ } = servicesManager.services;
832
+ const viewportIndex = evt.viewportIndex === undefined ? evt.data.viewportIndex : evt.viewportIndex;
833
+ const isBackupSave = evt.isBackupSave === undefined ? evt.data.isBackupSave : evt.isBackupSave;
834
+ const StudyInstanceUID = evt?.data?.StudyInstanceUID;
835
+ const SeriesInstanceUID = evt?.data?.SeriesInstanceUID;
836
+ const {
837
+ trackedStudy,
838
+ trackedSeries
839
+ } = ctx;
840
+ let displaySetInstanceUIDs;
841
+ return new Promise(async function (resolve, reject) {
842
+ // TODO: Fallback if (uiDialogService) {
843
+ const promptResult = await createReportDialogPrompt(uiDialogService);
844
+ if (promptResult.action === PROMPT_RESPONSES.CREATE_REPORT) {
845
+ const dataSources = extensionManager.getDataSources();
846
+ const dataSource = dataSources[0];
847
+ const measurements = measurementService.getMeasurements();
848
+ const trackedMeasurements = measurements.filter(m => trackedStudy === m.referenceStudyUID && trackedSeries.includes(m.referenceSeriesUID));
849
+ const SeriesDescription =
850
+ // isUndefinedOrEmpty
851
+ promptResult.value === undefined || promptResult.value === '' ? 'Research Derived Series' // default
852
+ : promptResult.value; // provided value
853
+
854
+ const SeriesNumber = getNextSRSeriesNumber(displaySetService);
855
+ displaySetInstanceUIDs = await _shared_createReportAsync(servicesManager, commandsManager, dataSource, trackedMeasurements, {
856
+ SeriesDescription,
857
+ SeriesNumber
858
+ });
859
+ } else if (promptResult.action === PROMPT_RESPONSES.CANCEL) {
860
+ // Do nothing
861
+ }
862
+ resolve({
863
+ userResponse: promptResult.action,
864
+ createdDisplaySetInstanceUIDs: displaySetInstanceUIDs,
865
+ StudyInstanceUID,
866
+ SeriesInstanceUID,
867
+ viewportIndex,
868
+ isBackupSave
869
+ });
870
+ });
871
+ }
872
+ /* harmony default export */ const TrackedMeasurementsContext_promptSaveReport = (promptSaveReport);
873
+ // EXTERNAL MODULE: ../../../extensions/cornerstone-dicom-sr/src/index.tsx + 15 modules
874
+ var cornerstone_dicom_sr_src = __webpack_require__(65948);
875
+ ;// CONCATENATED MODULE: ../../../extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/promptHydrateStructuredReport.js
876
+
877
+ const promptHydrateStructuredReport_RESPONSE = {
878
+ NO_NEVER: -1,
879
+ CANCEL: 0,
880
+ CREATE_REPORT: 1,
881
+ ADD_SERIES: 2,
882
+ SET_STUDY_AND_SERIES: 3,
883
+ NO_NOT_FOR_SERIES: 4,
884
+ HYDRATE_REPORT: 5
885
+ };
886
+ function promptHydrateStructuredReport(_ref, ctx, evt) {
887
+ let {
888
+ servicesManager,
889
+ extensionManager
890
+ } = _ref;
891
+ const {
892
+ uiViewportDialogService,
893
+ displaySetService
894
+ } = servicesManager.services;
895
+ const {
896
+ viewportIndex,
897
+ displaySetInstanceUID
898
+ } = evt;
899
+ const srDisplaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
900
+ return new Promise(async function (resolve, reject) {
901
+ const promptResult = await promptHydrateStructuredReport_askTrackMeasurements(uiViewportDialogService, viewportIndex);
902
+
903
+ // Need to do action here... So we can set state...
904
+ let StudyInstanceUID, SeriesInstanceUIDs;
905
+ if (promptResult === promptHydrateStructuredReport_RESPONSE.HYDRATE_REPORT) {
906
+ console.warn('!! HYDRATING STRUCTURED REPORT');
907
+ const hydrationResult = (0,cornerstone_dicom_sr_src.hydrateStructuredReport)({
908
+ servicesManager,
909
+ extensionManager
910
+ }, displaySetInstanceUID);
911
+ StudyInstanceUID = hydrationResult.StudyInstanceUID;
912
+ SeriesInstanceUIDs = hydrationResult.SeriesInstanceUIDs;
913
+ }
914
+ resolve({
915
+ userResponse: promptResult,
916
+ displaySetInstanceUID: evt.displaySetInstanceUID,
917
+ srSeriesInstanceUID: srDisplaySet.SeriesInstanceUID,
918
+ viewportIndex,
919
+ StudyInstanceUID,
920
+ SeriesInstanceUIDs
921
+ });
922
+ });
923
+ }
924
+ function promptHydrateStructuredReport_askTrackMeasurements(uiViewportDialogService, viewportIndex) {
925
+ return new Promise(function (resolve, reject) {
926
+ const message = 'Do you want to continue tracking measurements for this study?';
927
+ const actions = [{
928
+ type: 'secondary',
929
+ text: 'No',
930
+ value: promptHydrateStructuredReport_RESPONSE.CANCEL
931
+ }, {
932
+ type: 'primary',
933
+ text: 'Yes',
934
+ value: promptHydrateStructuredReport_RESPONSE.HYDRATE_REPORT
935
+ }];
936
+ const onSubmit = result => {
937
+ uiViewportDialogService.hide();
938
+ resolve(result);
939
+ };
940
+ uiViewportDialogService.show({
941
+ viewportIndex,
942
+ type: 'info',
943
+ message,
944
+ actions,
945
+ onSubmit,
946
+ onOutsideClick: () => {
947
+ uiViewportDialogService.hide();
948
+ resolve(promptHydrateStructuredReport_RESPONSE.CANCEL);
949
+ }
950
+ });
951
+ });
952
+ }
953
+ /* harmony default export */ const TrackedMeasurementsContext_promptHydrateStructuredReport = (promptHydrateStructuredReport);
954
+ ;// CONCATENATED MODULE: ../../../extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/hydrateStructuredReport.tsx
955
+
956
+ function hydrateStructuredReport(_ref, ctx, evt) {
957
+ let {
958
+ servicesManager,
959
+ extensionManager
960
+ } = _ref;
961
+ const {
962
+ displaySetService
963
+ } = servicesManager.services;
964
+ const {
965
+ viewportIndex,
966
+ displaySetInstanceUID
967
+ } = evt;
968
+ const srDisplaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
969
+ return new Promise((resolve, reject) => {
970
+ const hydrationResult = (0,cornerstone_dicom_sr_src.hydrateStructuredReport)({
971
+ servicesManager,
972
+ extensionManager
973
+ }, displaySetInstanceUID);
974
+ const StudyInstanceUID = hydrationResult.StudyInstanceUID;
975
+ const SeriesInstanceUIDs = hydrationResult.SeriesInstanceUIDs;
976
+ resolve({
977
+ displaySetInstanceUID: evt.displaySetInstanceUID,
978
+ srSeriesInstanceUID: srDisplaySet.SeriesInstanceUID,
979
+ viewportIndex,
980
+ StudyInstanceUID,
981
+ SeriesInstanceUIDs
982
+ });
983
+ });
984
+ }
985
+ /* harmony default export */ const TrackedMeasurementsContext_hydrateStructuredReport = (hydrateStructuredReport);
986
+ ;// CONCATENATED MODULE: ../../../extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/TrackedMeasurementsContext.tsx
987
+
988
+
989
+
990
+
991
+
992
+
993
+
994
+
995
+
996
+
997
+
998
+
999
+ const TrackedMeasurementsContext = /*#__PURE__*/react.createContext();
1000
+ TrackedMeasurementsContext.displayName = 'TrackedMeasurementsContext';
1001
+ const useTrackedMeasurements = () => (0,react.useContext)(TrackedMeasurementsContext);
1002
+ const SR_SOPCLASSHANDLERID = '@ohif/extension-cornerstone-dicom-sr.sopClassHandlerModule.dicom-sr';
1003
+
1004
+ /**
1005
+ *
1006
+ * @param {*} param0
1007
+ */
1008
+ function TrackedMeasurementsContextProvider(_ref, // Bound by consumer
1009
+ _ref2 // Component props
1010
+ ) {
1011
+ let {
1012
+ servicesManager,
1013
+ commandsManager,
1014
+ extensionManager
1015
+ } = _ref;
1016
+ let {
1017
+ children
1018
+ } = _ref2;
1019
+ const [viewportGrid, viewportGridService] = (0,src/* useViewportGrid */.O_)();
1020
+ const {
1021
+ activeViewportIndex,
1022
+ viewports
1023
+ } = viewportGrid;
1024
+ const {
1025
+ measurementService,
1026
+ displaySetService
1027
+ } = servicesManager.services;
1028
+ const machineOptions = Object.assign({}, defaultOptions);
1029
+ machineOptions.actions = Object.assign({}, machineOptions.actions, {
1030
+ jumpToFirstMeasurementInActiveViewport: (ctx, evt) => {
1031
+ const {
1032
+ trackedStudy,
1033
+ trackedSeries
1034
+ } = ctx;
1035
+ const measurements = measurementService.getMeasurements();
1036
+ const trackedMeasurements = measurements.filter(m => trackedStudy === m.referenceStudyUID && trackedSeries.includes(m.referenceSeriesUID));
1037
+ console.log('jumping to measurement reset viewport', viewportGrid.activeViewportIndex, trackedMeasurements[0]);
1038
+ const referencedDisplaySetUID = trackedMeasurements[0].displaySetInstanceUID;
1039
+ const referencedDisplaySet = displaySetService.getDisplaySetByUID(referencedDisplaySetUID);
1040
+ const referencedImages = referencedDisplaySet.images;
1041
+ const isVolumeIdReferenced = referencedImages[0].imageId.startsWith('volumeId');
1042
+ const measurementData = trackedMeasurements[0].data;
1043
+ let imageIndex = 0;
1044
+ if (!isVolumeIdReferenced && measurementData) {
1045
+ // if it is imageId referenced find the index of the imageId, we don't have
1046
+ // support for volumeId referenced images yet
1047
+ imageIndex = referencedImages.findIndex(image => {
1048
+ const imageIdToUse = Object.keys(measurementData)[0].substring(8);
1049
+ return image.imageId === imageIdToUse;
1050
+ });
1051
+ if (imageIndex === -1) {
1052
+ console.warn('Could not find image index for tracked measurement, using 0');
1053
+ imageIndex = 0;
1054
+ }
1055
+ }
1056
+ viewportGridService.setDisplaySetsForViewport({
1057
+ viewportIndex: viewportGrid.activeViewportIndex,
1058
+ displaySetInstanceUIDs: [referencedDisplaySetUID],
1059
+ viewportOptions: {
1060
+ initialImageOptions: {
1061
+ index: imageIndex
1062
+ }
1063
+ }
1064
+ });
1065
+ },
1066
+ showStructuredReportDisplaySetInActiveViewport: (ctx, evt) => {
1067
+ if (evt.data.createdDisplaySetInstanceUIDs.length > 0) {
1068
+ const StructuredReportDisplaySetInstanceUID = evt.data.createdDisplaySetInstanceUIDs[0].displaySetInstanceUID;
1069
+ viewportGridService.setDisplaySetsForViewport({
1070
+ viewportIndex: evt.data.viewportIndex,
1071
+ displaySetInstanceUIDs: [StructuredReportDisplaySetInstanceUID]
1072
+ });
1073
+ }
1074
+ },
1075
+ discardPreviouslyTrackedMeasurements: (ctx, evt) => {
1076
+ const measurements = measurementService.getMeasurements();
1077
+ const filteredMeasurements = measurements.filter(ms => ctx.prevTrackedSeries.includes(ms.referenceSeriesUID));
1078
+ const measurementIds = filteredMeasurements.map(fm => fm.id);
1079
+ for (let i = 0; i < measurementIds.length; i++) {
1080
+ measurementService.remove(measurementIds[i]);
1081
+ }
1082
+ },
1083
+ clearAllMeasurements: (ctx, evt) => {
1084
+ const measurements = measurementService.getMeasurements();
1085
+ const measurementIds = measurements.map(fm => fm.uid);
1086
+ for (let i = 0; i < measurementIds.length; i++) {
1087
+ measurementService.remove(measurementIds[i]);
1088
+ }
1089
+ }
1090
+ });
1091
+ machineOptions.services = Object.assign({}, machineOptions.services, {
1092
+ promptBeginTracking: TrackedMeasurementsContext_promptBeginTracking.bind(null, {
1093
+ servicesManager,
1094
+ extensionManager
1095
+ }),
1096
+ promptTrackNewSeries: TrackedMeasurementsContext_promptTrackNewSeries.bind(null, {
1097
+ servicesManager,
1098
+ extensionManager
1099
+ }),
1100
+ promptTrackNewStudy: TrackedMeasurementsContext_promptTrackNewStudy.bind(null, {
1101
+ servicesManager,
1102
+ extensionManager
1103
+ }),
1104
+ promptSaveReport: TrackedMeasurementsContext_promptSaveReport.bind(null, {
1105
+ servicesManager,
1106
+ commandsManager,
1107
+ extensionManager
1108
+ }),
1109
+ promptHydrateStructuredReport: TrackedMeasurementsContext_promptHydrateStructuredReport.bind(null, {
1110
+ servicesManager,
1111
+ extensionManager
1112
+ }),
1113
+ hydrateStructuredReport: TrackedMeasurementsContext_hydrateStructuredReport.bind(null, {
1114
+ servicesManager,
1115
+ extensionManager
1116
+ })
1117
+ });
1118
+
1119
+ // TODO: IMPROVE
1120
+ // - Add measurement_updated to cornerstone; debounced? (ext side, or consumption?)
1121
+ // - Friendlier transition/api in front of measurementTracking machine?
1122
+ // - Blocked: viewport overlay shouldn't clip when resized
1123
+ // TODO: PRIORITY
1124
+ // - Fix "ellipses" series description dynamic truncate length
1125
+ // - Fix viewport border resize
1126
+ // - created/destroyed hooks for extensions (cornerstone measurement subscriptions in it's `init`)
1127
+
1128
+ const measurementTrackingMachine = (0,es.Machine)(machineConfiguration, machineOptions);
1129
+ const [trackedMeasurements, sendTrackedMeasurementsEvent, trackedMeasurementsService] = (0,lib.useMachine)(measurementTrackingMachine);
1130
+
1131
+ // ~~ Listen for changes to ViewportGrid for potential SRs hung in panes when idle
1132
+ (0,react.useEffect)(() => {
1133
+ if (viewports.length > 0) {
1134
+ const activeViewport = viewports[activeViewportIndex];
1135
+ if (!activeViewport || !activeViewport?.displaySetInstanceUIDs?.length) {
1136
+ return;
1137
+ }
1138
+
1139
+ // Todo: Getting the first displaySetInstanceUID is wrong, but we don't have
1140
+ // tracking fusion viewports yet. This should change when we do.
1141
+ const {
1142
+ displaySetService
1143
+ } = servicesManager.services;
1144
+ const displaySet = displaySetService.getDisplaySetByUID(activeViewport.displaySetInstanceUIDs[0]);
1145
+ if (!displaySet) {
1146
+ return;
1147
+ }
1148
+
1149
+ // If this is an SR produced by our SR SOPClassHandler,
1150
+ // and it hasn't been loaded yet, do that now so we
1151
+ // can check if it can be rehydrated or not.
1152
+ //
1153
+ // Note: This happens:
1154
+ // - If the viewport is not currently an OHIFCornerstoneSRViewport
1155
+ // - If the displaySet has never been hung
1156
+ //
1157
+ // Otherwise, the displaySet will be loaded by the useEffect handler
1158
+ // listening to displaySet changes inside OHIFCornerstoneSRViewport.
1159
+ // The issue here is that this handler in TrackedMeasurementsContext
1160
+ // ends up occurring before the Viewport is created, so the displaySet
1161
+ // is not loaded yet, and isRehydratable is undefined unless we call load().
1162
+ if (displaySet.SOPClassHandlerId === SR_SOPCLASSHANDLERID && !displaySet.isLoaded && displaySet.load) {
1163
+ displaySet.load();
1164
+ }
1165
+
1166
+ // Magic string
1167
+ // load function added by our sopClassHandler module
1168
+ if (displaySet.SOPClassHandlerId === SR_SOPCLASSHANDLERID && displaySet.isRehydratable === true) {
1169
+ console.log('sending event...', trackedMeasurements);
1170
+ sendTrackedMeasurementsEvent('PROMPT_HYDRATE_SR', {
1171
+ displaySetInstanceUID: displaySet.displaySetInstanceUID,
1172
+ SeriesInstanceUID: displaySet.SeriesInstanceUID,
1173
+ viewportIndex: activeViewportIndex
1174
+ });
1175
+ }
1176
+ }
1177
+ }, [activeViewportIndex, sendTrackedMeasurementsEvent, servicesManager.services, viewports]);
1178
+ return /*#__PURE__*/react.createElement(TrackedMeasurementsContext.Provider, {
1179
+ value: [trackedMeasurements, sendTrackedMeasurementsEvent]
1180
+ }, children);
1181
+ }
1182
+ TrackedMeasurementsContextProvider.propTypes = {
1183
+ children: prop_types_default().oneOf([(prop_types_default()).func, (prop_types_default()).node]),
1184
+ servicesManager: (prop_types_default()).object.isRequired,
1185
+ commandsManager: (prop_types_default()).object.isRequired,
1186
+ extensionManager: (prop_types_default()).object.isRequired
1187
+ };
1188
+
1189
+ ;// CONCATENATED MODULE: ../../../extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/index.js
1190
+
1191
+ ;// CONCATENATED MODULE: ../../../extensions/measurement-tracking/src/contexts/index.js
1192
+
1193
+ ;// CONCATENATED MODULE: ../../../extensions/measurement-tracking/src/getContextModule.tsx
1194
+
1195
+ function getContextModule(_ref) {
1196
+ let {
1197
+ servicesManager,
1198
+ extensionManager,
1199
+ commandsManager
1200
+ } = _ref;
1201
+ const BoundTrackedMeasurementsContextProvider = TrackedMeasurementsContextProvider.bind(null, {
1202
+ servicesManager,
1203
+ extensionManager,
1204
+ commandsManager
1205
+ });
1206
+ return [{
1207
+ name: 'TrackedMeasurementsContext',
1208
+ context: TrackedMeasurementsContext,
1209
+ provider: BoundTrackedMeasurementsContextProvider
1210
+ }];
1211
+ }
1212
+
1213
+ /* harmony default export */ const src_getContextModule = (getContextModule);
1214
+
1215
+ /***/ }),
1216
+
1217
+ /***/ 43744:
1218
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
1219
+
1220
+ // ESM COMPAT FLAG
1221
+ __webpack_require__.r(__webpack_exports__);
1222
+
1223
+ // EXPORTS
1224
+ __webpack_require__.d(__webpack_exports__, {
1225
+ "default": () => (/* binding */ measurement_tracking_src)
1226
+ });
1227
+
1228
+ // EXTERNAL MODULE: ../../../extensions/measurement-tracking/src/getContextModule.tsx + 14 modules
1229
+ var getContextModule = __webpack_require__(43418);
1230
+ // EXTERNAL MODULE: ../../../node_modules/react/index.js
1231
+ var react = __webpack_require__(32735);
1232
+ // EXTERNAL MODULE: ../../../node_modules/prop-types/index.js
1233
+ var prop_types = __webpack_require__(60216);
1234
+ var prop_types_default = /*#__PURE__*/__webpack_require__.n(prop_types);
1235
+ // EXTERNAL MODULE: ../../core/src/index.ts + 101 modules
1236
+ var src = __webpack_require__(48501);
1237
+ // EXTERNAL MODULE: ../../ui/src/index.js + 449 modules
1238
+ var ui_src = __webpack_require__(43803);
1239
+ ;// CONCATENATED MODULE: ../../../extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/PanelStudyBrowserTracking.tsx
1240
+
1241
+
1242
+
1243
+
1244
+
1245
+ const {
1246
+ formatDate
1247
+ } = src.utils;
1248
+
1249
+ /**
1250
+ *
1251
+ * @param {*} param0
1252
+ */
1253
+ function PanelStudyBrowserTracking(_ref) {
1254
+ let {
1255
+ servicesManager,
1256
+ getImageSrc,
1257
+ getStudiesForPatientByMRN,
1258
+ requestDisplaySetCreationForStudy,
1259
+ dataSource
1260
+ } = _ref;
1261
+ const {
1262
+ measurementService,
1263
+ displaySetService,
1264
+ uiDialogService,
1265
+ hangingProtocolService,
1266
+ uiNotificationService
1267
+ } = servicesManager.services;
1268
+
1269
+ // Normally you nest the components so the tree isn't so deep, and the data
1270
+ // doesn't have to have such an intense shape. This works well enough for now.
1271
+ // Tabs --> Studies --> DisplaySets --> Thumbnails
1272
+ const {
1273
+ StudyInstanceUIDs
1274
+ } = (0,ui_src/* useImageViewer */.zG)();
1275
+ const [{
1276
+ activeViewportIndex,
1277
+ viewports,
1278
+ numCols,
1279
+ numRows
1280
+ }, viewportGridService] = (0,ui_src/* useViewportGrid */.O_)();
1281
+ const [trackedMeasurements, sendTrackedMeasurementsEvent] = (0,getContextModule/* useTrackedMeasurements */.I)();
1282
+ const [activeTabName, setActiveTabName] = (0,react.useState)('primary');
1283
+ const [expandedStudyInstanceUIDs, setExpandedStudyInstanceUIDs] = (0,react.useState)([...StudyInstanceUIDs]);
1284
+ const [studyDisplayList, setStudyDisplayList] = (0,react.useState)([]);
1285
+ const [displaySets, setDisplaySets] = (0,react.useState)([]);
1286
+ const [thumbnailImageSrcMap, setThumbnailImageSrcMap] = (0,react.useState)({});
1287
+ const [jumpToDisplaySet, setJumpToDisplaySet] = (0,react.useState)(null);
1288
+ const onDoubleClickThumbnailHandler = displaySetInstanceUID => {
1289
+ let updatedViewports = [];
1290
+ const viewportIndex = activeViewportIndex;
1291
+ try {
1292
+ updatedViewports = hangingProtocolService.getViewportsRequireUpdate(viewportIndex, displaySetInstanceUID);
1293
+ } catch (error) {
1294
+ console.warn(error);
1295
+ uiNotificationService.show({
1296
+ title: 'Thumbnail Double Click',
1297
+ message: 'The selected display sets could not be added to the viewport due to a mismatch in the Hanging Protocol rules.',
1298
+ type: 'info',
1299
+ duration: 3000
1300
+ });
1301
+ }
1302
+ viewportGridService.setDisplaySetsForViewports(updatedViewports);
1303
+ };
1304
+ const activeViewportDisplaySetInstanceUIDs = viewports[activeViewportIndex]?.displaySetInstanceUIDs;
1305
+ (0,react.useEffect)(() => {
1306
+ const added = measurementService.EVENTS.MEASUREMENT_ADDED;
1307
+ const addedRaw = measurementService.EVENTS.RAW_MEASUREMENT_ADDED;
1308
+ const subscriptions = [];
1309
+ [added, addedRaw].forEach(evt => {
1310
+ subscriptions.push(measurementService.subscribe(evt, _ref2 => {
1311
+ let {
1312
+ source,
1313
+ measurement
1314
+ } = _ref2;
1315
+ const {
1316
+ referenceSeriesUID: SeriesInstanceUID,
1317
+ referenceStudyUID: StudyInstanceUID
1318
+ } = measurement;
1319
+ sendTrackedMeasurementsEvent('SET_DIRTY', {
1320
+ SeriesInstanceUID
1321
+ });
1322
+ sendTrackedMeasurementsEvent('TRACK_SERIES', {
1323
+ viewportIndex: activeViewportIndex,
1324
+ StudyInstanceUID,
1325
+ SeriesInstanceUID
1326
+ });
1327
+ }).unsubscribe);
1328
+ });
1329
+ return () => {
1330
+ subscriptions.forEach(unsub => {
1331
+ unsub();
1332
+ });
1333
+ };
1334
+ }, [measurementService, activeViewportIndex, sendTrackedMeasurementsEvent]);
1335
+ const {
1336
+ trackedSeries
1337
+ } = trackedMeasurements.context;
1338
+
1339
+ // ~~ studyDisplayList
1340
+ (0,react.useEffect)(() => {
1341
+ // Fetch all studies for the patient in each primary study
1342
+ async function fetchStudiesForPatient(StudyInstanceUID) {
1343
+ // current study qido
1344
+ const qidoForStudyUID = await dataSource.query.studies.search({
1345
+ studyInstanceUid: StudyInstanceUID
1346
+ });
1347
+ let qidoStudiesForPatient = qidoForStudyUID;
1348
+
1349
+ // try to fetch the prior studies based on the patientID if the
1350
+ // server can respond.
1351
+ try {
1352
+ qidoStudiesForPatient = await getStudiesForPatientByMRN(qidoForStudyUID);
1353
+ } catch (error) {
1354
+ console.warn(error);
1355
+ }
1356
+ const mappedStudies = _mapDataSourceStudies(qidoStudiesForPatient);
1357
+ const actuallyMappedStudies = mappedStudies.map(qidoStudy => {
1358
+ return {
1359
+ studyInstanceUid: qidoStudy.StudyInstanceUID,
1360
+ date: formatDate(qidoStudy.StudyDate),
1361
+ description: qidoStudy.StudyDescription,
1362
+ modalities: qidoStudy.ModalitiesInStudy,
1363
+ numInstances: qidoStudy.NumInstances
1364
+ };
1365
+ });
1366
+ setStudyDisplayList(prevArray => {
1367
+ const ret = [...prevArray];
1368
+ for (const study of actuallyMappedStudies) {
1369
+ if (!prevArray.find(it => it.studyInstanceUid === study.studyInstanceUid)) {
1370
+ ret.push(study);
1371
+ }
1372
+ }
1373
+ return ret;
1374
+ });
1375
+ }
1376
+ StudyInstanceUIDs.forEach(sid => fetchStudiesForPatient(sid));
1377
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1378
+ }, [StudyInstanceUIDs, getStudiesForPatientByMRN]);
1379
+
1380
+ // ~~ Initial Thumbnails
1381
+ (0,react.useEffect)(() => {
1382
+ const currentDisplaySets = displaySetService.activeDisplaySets;
1383
+ if (!currentDisplaySets.length) {
1384
+ return;
1385
+ }
1386
+ currentDisplaySets.forEach(async dSet => {
1387
+ const newImageSrcEntry = {};
1388
+ const displaySet = displaySetService.getDisplaySetByUID(dSet.displaySetInstanceUID);
1389
+ const imageIds = dataSource.getImageIdsForDisplaySet(displaySet);
1390
+ const imageId = imageIds[Math.floor(imageIds.length / 2)];
1391
+
1392
+ // TODO: Is it okay that imageIds are not returned here for SR displaysets?
1393
+ if (imageId) {
1394
+ // When the image arrives, render it and store the result in the thumbnailImgSrcMap
1395
+ newImageSrcEntry[dSet.displaySetInstanceUID] = await getImageSrc(imageId);
1396
+ setThumbnailImageSrcMap(prevState => {
1397
+ return {
1398
+ ...prevState,
1399
+ ...newImageSrcEntry
1400
+ };
1401
+ });
1402
+ }
1403
+ });
1404
+ }, [displaySetService, dataSource, getImageSrc]);
1405
+
1406
+ // ~~ displaySets
1407
+ (0,react.useEffect)(() => {
1408
+ const currentDisplaySets = displaySetService.activeDisplaySets;
1409
+ if (!currentDisplaySets.length) {
1410
+ return;
1411
+ }
1412
+ const mappedDisplaySets = _mapDisplaySets(currentDisplaySets, thumbnailImageSrcMap, trackedSeries, viewports, viewportGridService, dataSource, displaySetService, uiDialogService, uiNotificationService);
1413
+ setDisplaySets(mappedDisplaySets);
1414
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1415
+ }, [displaySetService.activeDisplaySets, trackedSeries, viewports, dataSource, thumbnailImageSrcMap]);
1416
+
1417
+ // ~~ subscriptions --> displaySets
1418
+ (0,react.useEffect)(() => {
1419
+ // DISPLAY_SETS_ADDED returns an array of DisplaySets that were added
1420
+ const SubscriptionDisplaySetsAdded = displaySetService.subscribe(displaySetService.EVENTS.DISPLAY_SETS_ADDED, data => {
1421
+ const {
1422
+ displaySetsAdded,
1423
+ options
1424
+ } = data;
1425
+ displaySetsAdded.forEach(async dSet => {
1426
+ const displaySetInstanceUID = dSet.displaySetInstanceUID;
1427
+ const newImageSrcEntry = {};
1428
+ const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
1429
+ if (options.madeInClient) {
1430
+ setJumpToDisplaySet(displaySetInstanceUID);
1431
+ }
1432
+ const imageIds = dataSource.getImageIdsForDisplaySet(displaySet);
1433
+ const imageId = imageIds[Math.floor(imageIds.length / 2)];
1434
+
1435
+ // TODO: Is it okay that imageIds are not returned here for SR displaysets?
1436
+ if (imageId) {
1437
+ // When the image arrives, render it and store the result in the thumbnailImgSrcMap
1438
+ newImageSrcEntry[displaySetInstanceUID] = await getImageSrc(imageId);
1439
+ setThumbnailImageSrcMap(prevState => {
1440
+ return {
1441
+ ...prevState,
1442
+ ...newImageSrcEntry
1443
+ };
1444
+ });
1445
+ }
1446
+ });
1447
+ });
1448
+
1449
+ // TODO: Will this always hold _all_ the displaySets we care about?
1450
+ // DISPLAY_SETS_CHANGED returns `DisplaySerService.activeDisplaySets`
1451
+ const SubscriptionDisplaySetsChanged = displaySetService.subscribe(displaySetService.EVENTS.DISPLAY_SETS_CHANGED, changedDisplaySets => {
1452
+ const mappedDisplaySets = _mapDisplaySets(changedDisplaySets, thumbnailImageSrcMap, trackedSeries, viewports, viewportGridService, dataSource, displaySetService, uiDialogService, uiNotificationService);
1453
+ setDisplaySets(mappedDisplaySets);
1454
+ });
1455
+ return () => {
1456
+ SubscriptionDisplaySetsAdded.unsubscribe();
1457
+ SubscriptionDisplaySetsChanged.unsubscribe();
1458
+ };
1459
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1460
+ }, [displaySetService, dataSource, getImageSrc, thumbnailImageSrcMap, trackedSeries, viewports]);
1461
+ const tabs = _createStudyBrowserTabs(StudyInstanceUIDs, studyDisplayList, displaySets, hangingProtocolService);
1462
+
1463
+ // TODO: Should not fire this on "close"
1464
+ function _handleStudyClick(StudyInstanceUID) {
1465
+ const shouldCollapseStudy = expandedStudyInstanceUIDs.includes(StudyInstanceUID);
1466
+ const updatedExpandedStudyInstanceUIDs = shouldCollapseStudy ? [...expandedStudyInstanceUIDs.filter(stdyUid => stdyUid !== StudyInstanceUID)] : [...expandedStudyInstanceUIDs, StudyInstanceUID];
1467
+ setExpandedStudyInstanceUIDs(updatedExpandedStudyInstanceUIDs);
1468
+ if (!shouldCollapseStudy) {
1469
+ const madeInClient = true;
1470
+ requestDisplaySetCreationForStudy(displaySetService, StudyInstanceUID, madeInClient);
1471
+ }
1472
+ }
1473
+ (0,react.useEffect)(() => {
1474
+ if (jumpToDisplaySet) {
1475
+ // Get element by displaySetInstanceUID
1476
+ const displaySetInstanceUID = jumpToDisplaySet;
1477
+ const element = document.getElementById(`thumbnail-${displaySetInstanceUID}`);
1478
+ if (element && typeof element.scrollIntoView === 'function') {
1479
+ // TODO: Any way to support IE here?
1480
+ element.scrollIntoView({
1481
+ behavior: 'smooth'
1482
+ });
1483
+ setJumpToDisplaySet(null);
1484
+ }
1485
+ }
1486
+ }, [jumpToDisplaySet, expandedStudyInstanceUIDs, activeTabName]);
1487
+ (0,react.useEffect)(() => {
1488
+ if (!jumpToDisplaySet) {
1489
+ return;
1490
+ }
1491
+ const displaySetInstanceUID = jumpToDisplaySet;
1492
+ // Set the activeTabName and expand the study
1493
+ const thumbnailLocation = _findTabAndStudyOfDisplaySet(displaySetInstanceUID, tabs);
1494
+ if (!thumbnailLocation) {
1495
+ console.warn('jumpToThumbnail: displaySet thumbnail not found.');
1496
+ return;
1497
+ }
1498
+ const {
1499
+ tabName,
1500
+ StudyInstanceUID
1501
+ } = thumbnailLocation;
1502
+ setActiveTabName(tabName);
1503
+ const studyExpanded = expandedStudyInstanceUIDs.includes(StudyInstanceUID);
1504
+ if (!studyExpanded) {
1505
+ const updatedExpandedStudyInstanceUIDs = [...expandedStudyInstanceUIDs, StudyInstanceUID];
1506
+ setExpandedStudyInstanceUIDs(updatedExpandedStudyInstanceUIDs);
1507
+ }
1508
+ }, [expandedStudyInstanceUIDs, jumpToDisplaySet, tabs]);
1509
+ return /*#__PURE__*/react.createElement(ui_src/* StudyBrowser */.eX, {
1510
+ tabs: tabs,
1511
+ servicesManager: servicesManager,
1512
+ activeTabName: activeTabName,
1513
+ expandedStudyInstanceUIDs: expandedStudyInstanceUIDs,
1514
+ onClickStudy: _handleStudyClick,
1515
+ onClickTab: clickedTabName => {
1516
+ setActiveTabName(clickedTabName);
1517
+ },
1518
+ onClickUntrack: displaySetInstanceUID => {
1519
+ const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
1520
+ // TODO: shift this somewhere else where we're centralizing this logic?
1521
+ // Potentially a helper from displaySetInstanceUID to this
1522
+ sendTrackedMeasurementsEvent('UNTRACK_SERIES', {
1523
+ SeriesInstanceUID: displaySet.SeriesInstanceUID
1524
+ });
1525
+ },
1526
+ onClickThumbnail: () => {},
1527
+ onDoubleClickThumbnail: onDoubleClickThumbnailHandler,
1528
+ activeDisplaySetInstanceUIDs: activeViewportDisplaySetInstanceUIDs
1529
+ });
1530
+ }
1531
+ PanelStudyBrowserTracking.propTypes = {
1532
+ servicesManager: (prop_types_default()).object.isRequired,
1533
+ dataSource: prop_types_default().shape({
1534
+ getImageIdsForDisplaySet: (prop_types_default()).func.isRequired
1535
+ }).isRequired,
1536
+ getImageSrc: (prop_types_default()).func.isRequired,
1537
+ getStudiesForPatientByMRN: (prop_types_default()).func.isRequired,
1538
+ requestDisplaySetCreationForStudy: (prop_types_default()).func.isRequired
1539
+ };
1540
+ /* harmony default export */ const PanelStudyBrowserTracking_PanelStudyBrowserTracking = (PanelStudyBrowserTracking);
1541
+
1542
+ /**
1543
+ * Maps from the DataSource's format to a naturalized object
1544
+ *
1545
+ * @param {*} studies
1546
+ */
1547
+ function _mapDataSourceStudies(studies) {
1548
+ return studies.map(study => {
1549
+ // TODO: Why does the data source return in this format?
1550
+ return {
1551
+ AccessionNumber: study.accession,
1552
+ StudyDate: study.date,
1553
+ StudyDescription: study.description,
1554
+ NumInstances: study.instances,
1555
+ ModalitiesInStudy: study.modalities,
1556
+ PatientID: study.mrn,
1557
+ PatientName: study.patientName,
1558
+ StudyInstanceUID: study.studyInstanceUid,
1559
+ StudyTime: study.time
1560
+ };
1561
+ });
1562
+ }
1563
+ function _mapDisplaySets(displaySets, thumbnailImageSrcMap, trackedSeriesInstanceUIDs, viewports,
1564
+ // TODO: make array of `displaySetInstanceUIDs`?
1565
+ viewportGridService, dataSource, displaySetService, uiDialogService, uiNotificationService) {
1566
+ const thumbnailDisplaySets = [];
1567
+ const thumbnailNoImageDisplaySets = [];
1568
+ displaySets.filter(ds => !ds.excludeFromThumbnailBrowser).forEach(ds => {
1569
+ const imageSrc = thumbnailImageSrcMap[ds.displaySetInstanceUID];
1570
+ const componentType = _getComponentType(ds.Modality);
1571
+ const numPanes = viewportGridService.getNumViewportPanes();
1572
+ const viewportIdentificator = numPanes === 1 ? [] : viewports.reduce((acc, viewportData, index) => {
1573
+ if (index < numPanes && viewportData?.displaySetInstanceUIDs?.includes(ds.displaySetInstanceUID)) {
1574
+ acc.push(viewportData.viewportLabel);
1575
+ }
1576
+ return acc;
1577
+ }, []);
1578
+ const array = componentType === 'thumbnailTracked' ? thumbnailDisplaySets : thumbnailNoImageDisplaySets;
1579
+ const {
1580
+ displaySetInstanceUID
1581
+ } = ds;
1582
+ const thumbnailProps = {
1583
+ displaySetInstanceUID,
1584
+ description: ds.SeriesDescription,
1585
+ seriesNumber: ds.SeriesNumber,
1586
+ modality: ds.Modality,
1587
+ seriesDate: formatDate(ds.SeriesDate),
1588
+ numInstances: ds.numImageFrames,
1589
+ countIcon: ds.countIcon,
1590
+ StudyInstanceUID: ds.StudyInstanceUID,
1591
+ componentType,
1592
+ imageSrc,
1593
+ dragData: {
1594
+ type: 'displayset',
1595
+ displaySetInstanceUID
1596
+ // .. Any other data to pass
1597
+ },
1598
+
1599
+ isTracked: trackedSeriesInstanceUIDs.includes(ds.SeriesInstanceUID),
1600
+ viewportIdentificator
1601
+ };
1602
+ if (componentType === 'thumbnailNoImage') {
1603
+ if (dataSource.reject && dataSource.reject.series) {
1604
+ thumbnailProps.canReject = true;
1605
+ thumbnailProps.onReject = () => {
1606
+ uiDialogService.create({
1607
+ id: 'ds-reject-sr',
1608
+ centralize: true,
1609
+ isDraggable: false,
1610
+ showOverlay: true,
1611
+ content: ui_src/* Dialog */.Vq,
1612
+ contentProps: {
1613
+ title: 'Delete Report',
1614
+ body: () => /*#__PURE__*/react.createElement("div", {
1615
+ className: "p-4 text-white bg-primary-dark"
1616
+ }, /*#__PURE__*/react.createElement("p", null, "Are you sure you want to delete this report?"), /*#__PURE__*/react.createElement("p", null, "This action cannot be undone.")),
1617
+ actions: [{
1618
+ id: 'cancel',
1619
+ text: 'Cancel',
1620
+ type: 'secondary'
1621
+ }, {
1622
+ id: 'yes',
1623
+ text: 'Yes',
1624
+ type: 'primary',
1625
+ classes: ['reject-yes-button']
1626
+ }],
1627
+ onClose: () => uiDialogService.dismiss({
1628
+ id: 'ds-reject-sr'
1629
+ }),
1630
+ onShow: () => {
1631
+ const yesButton = document.querySelector('.reject-yes-button');
1632
+ yesButton.focus();
1633
+ },
1634
+ onSubmit: async _ref3 => {
1635
+ let {
1636
+ action
1637
+ } = _ref3;
1638
+ switch (action.id) {
1639
+ case 'yes':
1640
+ try {
1641
+ await dataSource.reject.series(ds.StudyInstanceUID, ds.SeriesInstanceUID);
1642
+ displaySetService.deleteDisplaySet(displaySetInstanceUID);
1643
+ uiDialogService.dismiss({
1644
+ id: 'ds-reject-sr'
1645
+ });
1646
+ uiNotificationService.show({
1647
+ title: 'Delete Report',
1648
+ message: 'Report deleted successfully',
1649
+ type: 'success'
1650
+ });
1651
+ } catch (error) {
1652
+ uiDialogService.dismiss({
1653
+ id: 'ds-reject-sr'
1654
+ });
1655
+ uiNotificationService.show({
1656
+ title: 'Delete Report',
1657
+ message: 'Failed to delete report',
1658
+ type: 'error'
1659
+ });
1660
+ }
1661
+ break;
1662
+ case 'cancel':
1663
+ uiDialogService.dismiss({
1664
+ id: 'ds-reject-sr'
1665
+ });
1666
+ break;
1667
+ }
1668
+ }
1669
+ }
1670
+ });
1671
+ };
1672
+ } else {
1673
+ thumbnailProps.canReject = false;
1674
+ }
1675
+ }
1676
+ array.push(thumbnailProps);
1677
+ });
1678
+ return [...thumbnailDisplaySets, ...thumbnailNoImageDisplaySets];
1679
+ }
1680
+ const thumbnailNoImageModalities = ['SR', 'SEG', 'SM', 'RTSTRUCT', 'RTPLAN', 'RTDOSE', 'DOC', 'OT'];
1681
+ function _getComponentType(Modality) {
1682
+ if (thumbnailNoImageModalities.includes(Modality)) {
1683
+ return 'thumbnailNoImage';
1684
+ }
1685
+ return 'thumbnailTracked';
1686
+ }
1687
+
1688
+ /**
1689
+ *
1690
+ * @param {string[]} primaryStudyInstanceUIDs
1691
+ * @param {object[]} studyDisplayList
1692
+ * @param {string} studyDisplayList.studyInstanceUid
1693
+ * @param {string} studyDisplayList.date
1694
+ * @param {string} studyDisplayList.description
1695
+ * @param {string} studyDisplayList.modalities
1696
+ * @param {number} studyDisplayList.numInstances
1697
+ * @param {object[]} displaySets
1698
+ * @returns tabs - The prop object expected by the StudyBrowser component
1699
+ */
1700
+ function _createStudyBrowserTabs(primaryStudyInstanceUIDs, studyDisplayList, displaySets, hangingProtocolService) {
1701
+ const primaryStudies = [];
1702
+ const recentStudies = [];
1703
+ const allStudies = [];
1704
+
1705
+ // Iterate over each study...
1706
+ studyDisplayList.forEach(study => {
1707
+ // Find it's display sets
1708
+ const displaySetsForStudy = displaySets.filter(ds => ds.StudyInstanceUID === study.studyInstanceUid);
1709
+
1710
+ // Sort them
1711
+ const dsSortFn = hangingProtocolService.getDisplaySetSortFunction();
1712
+ displaySetsForStudy.sort(dsSortFn);
1713
+
1714
+ /* Sort by series number, then by series date
1715
+ displaySetsForStudy.sort((a, b) => {
1716
+ if (a.seriesNumber !== b.seriesNumber) {
1717
+ return a.seriesNumber - b.seriesNumber;
1718
+ }
1719
+ const seriesDateA = Date.parse(a.seriesDate);
1720
+ const seriesDateB = Date.parse(b.seriesDate);
1721
+ return seriesDateA - seriesDateB;
1722
+ });
1723
+ */
1724
+
1725
+ // Map the study to it's tab/view representation
1726
+ const tabStudy = Object.assign({}, study, {
1727
+ displaySets: displaySetsForStudy
1728
+ });
1729
+
1730
+ // Add the "tab study" to the 'primary', 'recent', and/or 'all' tab group(s)
1731
+ if (primaryStudyInstanceUIDs.includes(study.studyInstanceUid)) {
1732
+ primaryStudies.push(tabStudy);
1733
+ allStudies.push(tabStudy);
1734
+ } else {
1735
+ // TODO: Filter allStudies to dates within one year of current date
1736
+ recentStudies.push(tabStudy);
1737
+ allStudies.push(tabStudy);
1738
+ }
1739
+ });
1740
+
1741
+ // Newest first
1742
+ const _byDate = (a, b) => {
1743
+ const dateA = Date.parse(a);
1744
+ const dateB = Date.parse(b);
1745
+ return dateB - dateA;
1746
+ };
1747
+ const tabs = [{
1748
+ name: 'primary',
1749
+ label: 'Primary',
1750
+ studies: primaryStudies.sort((studyA, studyB) => _byDate(studyA.date, studyB.date))
1751
+ }, {
1752
+ name: 'recent',
1753
+ label: 'Recent',
1754
+ studies: recentStudies.sort((studyA, studyB) => _byDate(studyA.date, studyB.date))
1755
+ }, {
1756
+ name: 'all',
1757
+ label: 'All',
1758
+ studies: allStudies.sort((studyA, studyB) => _byDate(studyA.date, studyB.date))
1759
+ }];
1760
+ return tabs;
1761
+ }
1762
+ function _findTabAndStudyOfDisplaySet(displaySetInstanceUID, tabs) {
1763
+ for (let t = 0; t < tabs.length; t++) {
1764
+ const {
1765
+ studies
1766
+ } = tabs[t];
1767
+ for (let s = 0; s < studies.length; s++) {
1768
+ const {
1769
+ displaySets
1770
+ } = studies[s];
1771
+ for (let d = 0; d < displaySets.length; d++) {
1772
+ const displaySet = displaySets[d];
1773
+ if (displaySet.displaySetInstanceUID === displaySetInstanceUID) {
1774
+ return {
1775
+ tabName: tabs[t].name,
1776
+ StudyInstanceUID: studies[s].studyInstanceUid
1777
+ };
1778
+ }
1779
+ }
1780
+ }
1781
+ }
1782
+ }
1783
+ ;// CONCATENATED MODULE: ../../../extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/getImageSrcFromImageId.js
1784
+ /**
1785
+ * @param {*} cornerstone
1786
+ * @param {*} imageId
1787
+ */
1788
+ function getImageSrcFromImageId(cornerstone, imageId) {
1789
+ return new Promise((resolve, reject) => {
1790
+ const canvas = document.createElement('canvas');
1791
+ cornerstone.utilities.loadImageToCanvas({
1792
+ canvas,
1793
+ imageId
1794
+ }).then(imageId => {
1795
+ resolve(canvas.toDataURL());
1796
+ }).catch(reject);
1797
+ });
1798
+ }
1799
+ /* harmony default export */ const PanelStudyBrowserTracking_getImageSrcFromImageId = (getImageSrcFromImageId);
1800
+ ;// CONCATENATED MODULE: ../../../extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/requestDisplaySetCreationForStudy.js
1801
+ function requestDisplaySetCreationForStudy(dataSource, displaySetService, StudyInstanceUID, madeInClient) {
1802
+ if (displaySetService.activeDisplaySets.some(displaySet => displaySet.StudyInstanceUID === StudyInstanceUID)) {
1803
+ return;
1804
+ }
1805
+ dataSource.retrieve.series.metadata({
1806
+ StudyInstanceUID,
1807
+ madeInClient
1808
+ });
1809
+ }
1810
+ /* harmony default export */ const PanelStudyBrowserTracking_requestDisplaySetCreationForStudy = (requestDisplaySetCreationForStudy);
1811
+ ;// CONCATENATED MODULE: ../../../extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/index.tsx
1812
+
1813
+
1814
+ //
1815
+
1816
+
1817
+
1818
+ function _getStudyForPatientUtility(extensionManager) {
1819
+ const utilityModule = extensionManager.getModuleEntry('@ohif/extension-default.utilityModule.common');
1820
+ const {
1821
+ getStudiesForPatientByMRN
1822
+ } = utilityModule.exports;
1823
+ return getStudiesForPatientByMRN;
1824
+ }
1825
+
1826
+ /**
1827
+ * Wraps the PanelStudyBrowser and provides features afforded by managers/services
1828
+ *
1829
+ * @param {object} params
1830
+ * @param {object} commandsManager
1831
+ * @param {object} extensionManager
1832
+ */
1833
+ function WrappedPanelStudyBrowserTracking(_ref) {
1834
+ let {
1835
+ commandsManager,
1836
+ extensionManager,
1837
+ servicesManager
1838
+ } = _ref;
1839
+ const dataSource = extensionManager.getActiveDataSource()[0];
1840
+ const getStudiesForPatientByMRN = _getStudyForPatientUtility(extensionManager);
1841
+ const _getStudiesForPatientByMRN = getStudiesForPatientByMRN.bind(null, dataSource);
1842
+ const _getImageSrcFromImageId = _createGetImageSrcFromImageIdFn(extensionManager);
1843
+ const _requestDisplaySetCreationForStudy = PanelStudyBrowserTracking_requestDisplaySetCreationForStudy.bind(null, dataSource);
1844
+ return /*#__PURE__*/react.createElement(PanelStudyBrowserTracking_PanelStudyBrowserTracking, {
1845
+ servicesManager: servicesManager,
1846
+ dataSource: dataSource,
1847
+ getImageSrc: _getImageSrcFromImageId,
1848
+ getStudiesForPatientByMRN: _getStudiesForPatientByMRN,
1849
+ requestDisplaySetCreationForStudy: _requestDisplaySetCreationForStudy
1850
+ });
1851
+ }
1852
+
1853
+ /**
1854
+ * Grabs cornerstone library reference using a dependent command from
1855
+ * the @ohif/extension-cornerstone extension. Then creates a helper function
1856
+ * that can take an imageId and return an image src.
1857
+ *
1858
+ * @param {func} getCommand - CommandManager's getCommand method
1859
+ * @returns {func} getImageSrcFromImageId - A utility function powered by
1860
+ * cornerstone
1861
+ */
1862
+ function _createGetImageSrcFromImageIdFn(extensionManager) {
1863
+ const utilities = extensionManager.getModuleEntry('@ohif/extension-cornerstone.utilityModule.common');
1864
+ try {
1865
+ const {
1866
+ cornerstone
1867
+ } = utilities.exports.getCornerstoneLibraries();
1868
+ return PanelStudyBrowserTracking_getImageSrcFromImageId.bind(null, cornerstone);
1869
+ } catch (ex) {
1870
+ throw new Error('Required command not found');
1871
+ }
1872
+ }
1873
+ WrappedPanelStudyBrowserTracking.propTypes = {
1874
+ commandsManager: (prop_types_default()).object.isRequired,
1875
+ extensionManager: (prop_types_default()).object.isRequired,
1876
+ servicesManager: (prop_types_default()).object.isRequired
1877
+ };
1878
+ /* harmony default export */ const panels_PanelStudyBrowserTracking = (WrappedPanelStudyBrowserTracking);
1879
+ // EXTERNAL MODULE: ./hooks/index.js + 3 modules
1880
+ var hooks = __webpack_require__(39852);
1881
+ // EXTERNAL MODULE: ../../../node_modules/react-i18next/dist/es/index.js + 15 modules
1882
+ var es = __webpack_require__(21572);
1883
+ ;// CONCATENATED MODULE: ../../../extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/ActionButtons.tsx
1884
+
1885
+
1886
+
1887
+
1888
+ function ActionButtons(_ref) {
1889
+ let {
1890
+ onExportClick,
1891
+ onCreateReportClick,
1892
+ disabled
1893
+ } = _ref;
1894
+ const {
1895
+ t
1896
+ } = (0,es/* useTranslation */.$G)('MeasurementTable');
1897
+ return /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement(ui_src/* Button */.zx, {
1898
+ className: "text-base px-2 py-2",
1899
+ size: "initial",
1900
+ variant: disabled ? 'disabled' : 'outlined',
1901
+ color: "black",
1902
+ border: "primaryActive",
1903
+ onClick: onExportClick,
1904
+ disabled: disabled
1905
+ }, t('Export')), /*#__PURE__*/react.createElement(ui_src/* Button */.zx, {
1906
+ className: "ml-2 px-2 text-base",
1907
+ variant: disabled ? 'disabled' : 'outlined',
1908
+ size: "initial",
1909
+ color: "black",
1910
+ border: "primaryActive",
1911
+ onClick: onCreateReportClick,
1912
+ disabled: disabled
1913
+ }, t('Create Report')));
1914
+ }
1915
+ ActionButtons.propTypes = {
1916
+ onExportClick: (prop_types_default()).func,
1917
+ onCreateReportClick: (prop_types_default()).func,
1918
+ disabled: (prop_types_default()).bool
1919
+ };
1920
+ ActionButtons.defaultProps = {
1921
+ onExportClick: () => alert('Export'),
1922
+ onCreateReportClick: () => alert('Create Report'),
1923
+ disabled: false
1924
+ };
1925
+ /* harmony default export */ const PanelMeasurementTableTracking_ActionButtons = (ActionButtons);
1926
+ // EXTERNAL MODULE: ../../../node_modules/lodash.debounce/index.js
1927
+ var lodash_debounce = __webpack_require__(40001);
1928
+ var lodash_debounce_default = /*#__PURE__*/__webpack_require__.n(lodash_debounce);
1929
+ ;// CONCATENATED MODULE: ../../../extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/index.tsx
1930
+
1931
+
1932
+
1933
+
1934
+
1935
+
1936
+
1937
+
1938
+ const {
1939
+ downloadCSVReport
1940
+ } = src.utils;
1941
+ const {
1942
+ formatDate: PanelMeasurementTableTracking_formatDate
1943
+ } = src.utils;
1944
+ const DISPLAY_STUDY_SUMMARY_INITIAL_VALUE = {
1945
+ key: undefined,
1946
+ //
1947
+ date: '',
1948
+ // '07-Sep-2010',
1949
+ modality: '',
1950
+ // 'CT',
1951
+ description: '' // 'CHEST/ABD/PELVIS W CONTRAST',
1952
+ };
1953
+
1954
+ function PanelMeasurementTableTracking(_ref) {
1955
+ let {
1956
+ servicesManager,
1957
+ extensionManager
1958
+ } = _ref;
1959
+ const [viewportGrid, viewportGridService] = (0,ui_src/* useViewportGrid */.O_)();
1960
+ const [measurementChangeTimestamp, setMeasurementsUpdated] = (0,react.useState)(Date.now().toString());
1961
+ const debouncedMeasurementChangeTimestamp = (0,hooks/* useDebounce */.Nr)(measurementChangeTimestamp, 200);
1962
+ const {
1963
+ measurementService,
1964
+ uiDialogService,
1965
+ displaySetService
1966
+ } = servicesManager.services;
1967
+ const [trackedMeasurements, sendTrackedMeasurementsEvent] = (0,getContextModule/* useTrackedMeasurements */.I)();
1968
+ const {
1969
+ trackedStudy,
1970
+ trackedSeries
1971
+ } = trackedMeasurements.context;
1972
+ const [displayStudySummary, setDisplayStudySummary] = (0,react.useState)(DISPLAY_STUDY_SUMMARY_INITIAL_VALUE);
1973
+ const [displayMeasurements, setDisplayMeasurements] = (0,react.useState)([]);
1974
+ const measurementsPanelRef = (0,react.useRef)(null);
1975
+ (0,react.useEffect)(() => {
1976
+ const measurements = measurementService.getMeasurements();
1977
+ const filteredMeasurements = measurements.filter(m => trackedStudy === m.referenceStudyUID && trackedSeries.includes(m.referenceSeriesUID));
1978
+ const mappedMeasurements = filteredMeasurements.map(m => _mapMeasurementToDisplay(m, measurementService.VALUE_TYPES, displaySetService));
1979
+ setDisplayMeasurements(mappedMeasurements);
1980
+ // eslint-ignore-next-line
1981
+ }, [measurementService, trackedStudy, trackedSeries, debouncedMeasurementChangeTimestamp]);
1982
+ const updateDisplayStudySummary = async () => {
1983
+ if (trackedMeasurements.matches('tracking')) {
1984
+ const StudyInstanceUID = trackedStudy;
1985
+ const studyMeta = src.DicomMetadataStore.getStudy(StudyInstanceUID);
1986
+ const instanceMeta = studyMeta.series[0].instances[0];
1987
+ const {
1988
+ StudyDate,
1989
+ StudyDescription
1990
+ } = instanceMeta;
1991
+ const modalities = new Set();
1992
+ studyMeta.series.forEach(series => {
1993
+ if (trackedSeries.includes(series.SeriesInstanceUID)) {
1994
+ modalities.add(series.instances[0].Modality);
1995
+ }
1996
+ });
1997
+ const modality = Array.from(modalities).join('/');
1998
+ if (displayStudySummary.key !== StudyInstanceUID) {
1999
+ setDisplayStudySummary({
2000
+ key: StudyInstanceUID,
2001
+ date: StudyDate,
2002
+ // TODO: Format: '07-Sep-2010'
2003
+ modality,
2004
+ description: StudyDescription
2005
+ });
2006
+ }
2007
+ } else if (trackedStudy === '' || trackedStudy === undefined) {
2008
+ setDisplayStudySummary(DISPLAY_STUDY_SUMMARY_INITIAL_VALUE);
2009
+ }
2010
+ };
2011
+
2012
+ // ~~ DisplayStudySummary
2013
+ (0,react.useEffect)(() => {
2014
+ updateDisplayStudySummary();
2015
+ }, [displayStudySummary.key, trackedMeasurements, trackedStudy, updateDisplayStudySummary]);
2016
+
2017
+ // TODO: Better way to consolidated, debounce, check on change?
2018
+ // Are we exposing the right API for measurementService?
2019
+ // This watches for ALL measurementService changes. It updates a timestamp,
2020
+ // which is debounced. After a brief period of inactivity, this triggers
2021
+ // a re-render where we grab up-to-date measurements
2022
+ (0,react.useEffect)(() => {
2023
+ const added = measurementService.EVENTS.MEASUREMENT_ADDED;
2024
+ const addedRaw = measurementService.EVENTS.RAW_MEASUREMENT_ADDED;
2025
+ const updated = measurementService.EVENTS.MEASUREMENT_UPDATED;
2026
+ const removed = measurementService.EVENTS.MEASUREMENT_REMOVED;
2027
+ const cleared = measurementService.EVENTS.MEASUREMENTS_CLEARED;
2028
+ const subscriptions = [];
2029
+ [added, addedRaw, updated, removed, cleared].forEach(evt => {
2030
+ subscriptions.push(measurementService.subscribe(evt, () => {
2031
+ setMeasurementsUpdated(Date.now().toString());
2032
+ if (evt === added) {
2033
+ lodash_debounce_default()(() => {
2034
+ measurementsPanelRef.current.scrollTop = measurementsPanelRef.current.scrollHeight;
2035
+ }, 300)();
2036
+ }
2037
+ }).unsubscribe);
2038
+ });
2039
+ return () => {
2040
+ subscriptions.forEach(unsub => {
2041
+ unsub();
2042
+ });
2043
+ };
2044
+ }, [measurementService, sendTrackedMeasurementsEvent]);
2045
+ async function exportReport() {
2046
+ const measurements = measurementService.getMeasurements();
2047
+ const trackedMeasurements = measurements.filter(m => trackedStudy === m.referenceStudyUID && trackedSeries.includes(m.referenceSeriesUID));
2048
+ downloadCSVReport(trackedMeasurements, measurementService);
2049
+ }
2050
+ const jumpToImage = _ref2 => {
2051
+ let {
2052
+ uid,
2053
+ isActive
2054
+ } = _ref2;
2055
+ measurementService.jumpToMeasurement(viewportGrid.activeViewportIndex, uid);
2056
+ onMeasurementItemClickHandler({
2057
+ uid,
2058
+ isActive
2059
+ });
2060
+ };
2061
+ const onMeasurementItemEditHandler = _ref3 => {
2062
+ let {
2063
+ uid,
2064
+ isActive
2065
+ } = _ref3;
2066
+ const measurement = measurementService.getMeasurement(uid);
2067
+ jumpToImage({
2068
+ uid,
2069
+ isActive
2070
+ });
2071
+ const onSubmitHandler = _ref4 => {
2072
+ let {
2073
+ action,
2074
+ value
2075
+ } = _ref4;
2076
+ switch (action.id) {
2077
+ case 'save':
2078
+ {
2079
+ measurementService.update(uid, {
2080
+ ...measurement,
2081
+ ...value
2082
+ }, true);
2083
+ }
2084
+ }
2085
+ uiDialogService.dismiss({
2086
+ id: 'enter-annotation'
2087
+ });
2088
+ };
2089
+ uiDialogService.create({
2090
+ id: 'enter-annotation',
2091
+ centralize: true,
2092
+ isDraggable: false,
2093
+ showOverlay: true,
2094
+ content: ui_src/* Dialog */.Vq,
2095
+ contentProps: {
2096
+ title: 'Enter your annotation',
2097
+ noCloseButton: true,
2098
+ value: {
2099
+ label: measurement.label || ''
2100
+ },
2101
+ body: _ref5 => {
2102
+ let {
2103
+ value,
2104
+ setValue
2105
+ } = _ref5;
2106
+ const onChangeHandler = event => {
2107
+ event.persist();
2108
+ setValue(value => ({
2109
+ ...value,
2110
+ label: event.target.value
2111
+ }));
2112
+ };
2113
+ const onKeyPressHandler = event => {
2114
+ if (event.key === 'Enter') {
2115
+ onSubmitHandler({
2116
+ value,
2117
+ action: {
2118
+ id: 'save'
2119
+ }
2120
+ });
2121
+ }
2122
+ };
2123
+ return /*#__PURE__*/react.createElement("div", {
2124
+ className: "p-4 bg-primary-dark"
2125
+ }, /*#__PURE__*/react.createElement(ui_src/* Input */.II, {
2126
+ autoFocus: true,
2127
+ id: "annotation",
2128
+ className: "mt-2 bg-black border-primary-main",
2129
+ type: "text",
2130
+ containerClassName: "mr-2",
2131
+ value: value.label,
2132
+ onChange: onChangeHandler,
2133
+ onKeyPress: onKeyPressHandler
2134
+ }));
2135
+ },
2136
+ actions: [
2137
+ // temp: swap button types until colors are updated
2138
+ {
2139
+ id: 'cancel',
2140
+ text: 'Cancel',
2141
+ type: 'primary'
2142
+ }, {
2143
+ id: 'save',
2144
+ text: 'Save',
2145
+ type: 'secondary'
2146
+ }],
2147
+ onSubmit: onSubmitHandler
2148
+ }
2149
+ });
2150
+ };
2151
+ const onMeasurementItemClickHandler = _ref6 => {
2152
+ let {
2153
+ uid,
2154
+ isActive
2155
+ } = _ref6;
2156
+ if (!isActive) {
2157
+ const measurements = [...displayMeasurements];
2158
+ const measurement = measurements.find(m => m.uid === uid);
2159
+ measurements.forEach(m => m.isActive = m.uid !== uid ? false : true);
2160
+ measurement.isActive = true;
2161
+ setDisplayMeasurements(measurements);
2162
+ }
2163
+ };
2164
+ const displayMeasurementsWithoutFindings = displayMeasurements.filter(dm => dm.measurementType !== measurementService.VALUE_TYPES.POINT);
2165
+ const additionalFindings = displayMeasurements.filter(dm => dm.measurementType === measurementService.VALUE_TYPES.POINT);
2166
+ return /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement("div", {
2167
+ className: "overflow-x-hidden overflow-y-auto invisible-scrollbar",
2168
+ ref: measurementsPanelRef,
2169
+ "data-cy": 'trackedMeasurements-panel'
2170
+ }, displayStudySummary.key && /*#__PURE__*/react.createElement(ui_src/* StudySummary */.YL, {
2171
+ date: PanelMeasurementTableTracking_formatDate(displayStudySummary.date),
2172
+ modality: displayStudySummary.modality,
2173
+ description: displayStudySummary.description
2174
+ }), /*#__PURE__*/react.createElement(ui_src/* MeasurementTable */.wt, {
2175
+ title: "Measurements",
2176
+ data: displayMeasurementsWithoutFindings,
2177
+ servicesManager: servicesManager,
2178
+ onClick: jumpToImage,
2179
+ onEdit: onMeasurementItemEditHandler
2180
+ }), additionalFindings.length !== 0 && /*#__PURE__*/react.createElement(ui_src/* MeasurementTable */.wt, {
2181
+ title: "Additional Findings",
2182
+ data: additionalFindings,
2183
+ servicesManager: servicesManager,
2184
+ onClick: jumpToImage,
2185
+ onEdit: onMeasurementItemEditHandler
2186
+ })), /*#__PURE__*/react.createElement("div", {
2187
+ className: "flex justify-center p-4"
2188
+ }, /*#__PURE__*/react.createElement(PanelMeasurementTableTracking_ActionButtons, {
2189
+ onExportClick: exportReport,
2190
+ onCreateReportClick: () => {
2191
+ sendTrackedMeasurementsEvent('SAVE_REPORT', {
2192
+ viewportIndex: viewportGrid.activeViewportIndex,
2193
+ isBackupSave: true
2194
+ });
2195
+ },
2196
+ disabled: additionalFindings.length === 0 && displayMeasurementsWithoutFindings.length === 0
2197
+ })));
2198
+ }
2199
+ PanelMeasurementTableTracking.propTypes = {
2200
+ servicesManager: prop_types_default().shape({
2201
+ services: prop_types_default().shape({
2202
+ measurementService: prop_types_default().shape({
2203
+ getMeasurements: (prop_types_default()).func.isRequired,
2204
+ VALUE_TYPES: (prop_types_default()).object.isRequired
2205
+ }).isRequired
2206
+ }).isRequired
2207
+ }).isRequired
2208
+ };
2209
+
2210
+ // TODO: This could be a measurementService mapper
2211
+ function _mapMeasurementToDisplay(measurement, types, displaySetService) {
2212
+ const {
2213
+ referenceStudyUID,
2214
+ referenceSeriesUID,
2215
+ SOPInstanceUID
2216
+ } = measurement;
2217
+
2218
+ // TODO: We don't deal with multiframe well yet, would need to update
2219
+ // This in OHIF-312 when we add FrameIndex to measurements.
2220
+
2221
+ const instance = src.DicomMetadataStore.getInstance(referenceStudyUID, referenceSeriesUID, SOPInstanceUID);
2222
+ const displaySets = displaySetService.getDisplaySetsForSeries(referenceSeriesUID);
2223
+ if (!displaySets[0] || !displaySets[0].images) {
2224
+ throw new Error('The tracked measurements panel should only be tracking "stack" displaySets.');
2225
+ }
2226
+ const {
2227
+ displayText: baseDisplayText,
2228
+ uid,
2229
+ label: baseLabel,
2230
+ type,
2231
+ selected,
2232
+ findingSites,
2233
+ finding
2234
+ } = measurement;
2235
+ const firstSite = findingSites?.[0];
2236
+ const label = baseLabel || finding?.text || firstSite?.text || '(empty)';
2237
+ let displayText = baseDisplayText || [];
2238
+ if (findingSites) {
2239
+ const siteText = [];
2240
+ findingSites.forEach(site => {
2241
+ if (site?.text !== label) siteText.push(site.text);
2242
+ });
2243
+ displayText = [...siteText, ...displayText];
2244
+ }
2245
+ if (finding && finding?.text !== label) {
2246
+ displayText = [finding.text, ...displayText];
2247
+ }
2248
+ return {
2249
+ uid,
2250
+ label,
2251
+ baseLabel,
2252
+ measurementType: type,
2253
+ displayText,
2254
+ baseDisplayText,
2255
+ isActive: selected,
2256
+ finding,
2257
+ findingSites
2258
+ };
2259
+ }
2260
+ /* harmony default export */ const panels_PanelMeasurementTableTracking = (PanelMeasurementTableTracking);
2261
+ ;// CONCATENATED MODULE: ../../../extensions/measurement-tracking/src/panels/index.js
2262
+
2263
+
2264
+
2265
+ ;// CONCATENATED MODULE: ../../../extensions/measurement-tracking/src/getPanelModule.tsx
2266
+
2267
+
2268
+ // TODO:
2269
+ // - No loading UI exists yet
2270
+ // - cancel promises when component is destroyed
2271
+ // - show errors in UI for thumbnails if promise fails
2272
+ function getPanelModule(_ref) {
2273
+ let {
2274
+ commandsManager,
2275
+ extensionManager,
2276
+ servicesManager
2277
+ } = _ref;
2278
+ return [{
2279
+ name: 'seriesList',
2280
+ iconName: 'group-layers',
2281
+ iconLabel: 'Studies',
2282
+ label: 'Studies',
2283
+ component: panels_PanelStudyBrowserTracking.bind(null, {
2284
+ commandsManager,
2285
+ extensionManager,
2286
+ servicesManager
2287
+ })
2288
+ }, {
2289
+ name: 'trackedMeasurements',
2290
+ iconName: 'tab-linear',
2291
+ iconLabel: 'Measure',
2292
+ label: 'Measurements',
2293
+ component: panels_PanelMeasurementTableTracking.bind(null, {
2294
+ commandsManager,
2295
+ extensionManager,
2296
+ servicesManager
2297
+ })
2298
+ }];
2299
+ }
2300
+ /* harmony default export */ const src_getPanelModule = (getPanelModule);
2301
+ ;// CONCATENATED MODULE: ../../../extensions/measurement-tracking/src/getViewportModule.tsx
2302
+ function _extends() { _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 _extends.apply(this, arguments); }
2303
+
2304
+ const Component = /*#__PURE__*/react.lazy(() => {
2305
+ return __webpack_require__.e(/* import() */ 799).then(__webpack_require__.bind(__webpack_require__, 91799));
2306
+ });
2307
+ const OHIFCornerstoneViewport = props => {
2308
+ return /*#__PURE__*/react.createElement(react.Suspense, {
2309
+ fallback: /*#__PURE__*/react.createElement("div", null, "Loading...")
2310
+ }, /*#__PURE__*/react.createElement(Component, props));
2311
+ };
2312
+ function getViewportModule(_ref) {
2313
+ let {
2314
+ servicesManager,
2315
+ commandsManager,
2316
+ extensionManager
2317
+ } = _ref;
2318
+ const ExtendedOHIFCornerstoneTrackingViewport = props => {
2319
+ return /*#__PURE__*/react.createElement(OHIFCornerstoneViewport, _extends({
2320
+ servicesManager: servicesManager,
2321
+ commandsManager: commandsManager,
2322
+ extensionManager: extensionManager
2323
+ }, props));
2324
+ };
2325
+ return [{
2326
+ name: 'cornerstone-tracked',
2327
+ component: ExtendedOHIFCornerstoneTrackingViewport
2328
+ }];
2329
+ }
2330
+ /* harmony default export */ const src_getViewportModule = (getViewportModule);
2331
+ ;// CONCATENATED MODULE: ../../../extensions/measurement-tracking/package.json
2332
+ const package_namespaceObject = JSON.parse('{"u2":"@ohif/extension-measurement-tracking"}');
2333
+ ;// CONCATENATED MODULE: ../../../extensions/measurement-tracking/src/id.js
2334
+
2335
+ const id = package_namespaceObject.u2;
2336
+
2337
+ ;// CONCATENATED MODULE: ../../../extensions/measurement-tracking/src/index.tsx
2338
+
2339
+
2340
+
2341
+
2342
+ const measurementTrackingExtension = {
2343
+ /**
2344
+ * Only required property. Should be a unique value across all extensions.
2345
+ */
2346
+ id: id,
2347
+ getContextModule: getContextModule/* default */.Z,
2348
+ getPanelModule: src_getPanelModule,
2349
+ getViewportModule: src_getViewportModule
2350
+ };
2351
+ /* harmony default export */ const measurement_tracking_src = (measurementTrackingExtension);
2352
+
2353
+ /***/ })
2354
+
2355
+ }]);