@ohif/app 3.7.0-beta.11 → 3.7.0-beta.110

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 (66) hide show
  1. package/dist/{917.bundle.b9983325adf849bff6fd.js → 12.bundle.5ea15cb6633d8028e47d.js} +6 -6
  2. package/dist/{295.bundle.957b1159fec14b9199a1.js → 125.bundle.253395f320b72180da63.js} +6 -6
  3. package/dist/{351.bundle.0742237651aef9694a65.js → 181.bundle.ceb057236403bcb630ac.js} +226 -204
  4. package/dist/{351.css → 181.css} +1 -1
  5. package/dist/{744.bundle.4c4b884f90eb70482821.js → 19.bundle.03f809886c36c388d05c.js} +240 -381
  6. package/dist/{606.bundle.5d876f5f3dd8287f0a28.js → 202.bundle.d3490836f71e001dd30f.js} +2089 -692
  7. package/dist/{926.bundle.dbc9d0e591cb9217fda2.js → 220.bundle.f7e1c96c94245e70f2be.js} +990 -400
  8. package/dist/221.bundle.dc6dac346d724d6baeae.js +1779 -0
  9. package/dist/221.css +2 -0
  10. package/dist/{664.bundle.09abae984223969d1bde.js → 23.bundle.e008ad788170f2ed5569.js} +5 -6
  11. package/dist/{976.bundle.9cc2382162214ea0af2b.js → 236.bundle.7b906cd27864d65f32c0.js} +89 -105
  12. package/dist/{55.bundle.550a823e75eb608e8d5e.js → 250.bundle.8084960e3318cda37317.js} +52 -36
  13. package/dist/{973.bundle.4584df05b320b94cace5.js → 281.bundle.c9854cc25c839e49c2c8.js} +18 -14
  14. package/dist/{82.bundle.9a0e7f08d4bce18d302f.js → 342.bundle.7d6c1e6bda1c67d729a7.js} +1802 -489
  15. package/dist/{404.bundle.3d65ff813eead20462d3.js → 359.bundle.72d017719489ff11057b.js} +47 -134
  16. package/dist/{192.bundle.950e5380ea63c6d635d5.js → 370.bundle.e55d75ff1bdccee16cde.js} +117 -103
  17. package/dist/{790.bundle.7327fec7833ceea2784b.js → 410.bundle.5b41c68cb0f210a83f13.js} +11 -9
  18. package/dist/{151.bundle.31ea35044218837bf73f.js → 417.bundle.af0a207c29b109f84159.js} +49 -17
  19. package/dist/{569.bundle.c8e771a8d28e237b32be.js → 451.bundle.9fd36f52ff69594f0669.js} +86 -106
  20. package/dist/{581.bundle.dc6197189f7c88c27d4c.js → 471.bundle.b3d77b83b1593c09a504.js} +78 -99
  21. package/dist/{199.bundle.f50ffd85a4334de2f5c1.js → 506.bundle.468dc6db2a9bfa96bb44.js} +11 -9
  22. package/dist/{531.bundle.2a82fb1d69e5b57cc72b.js → 530.bundle.a03b6f942ace3e1baa1e.js} +726 -447
  23. package/dist/579.css +1 -0
  24. package/dist/{935.bundle.deeffff0e4f7b528e3c3.js → 604.bundle.a51f83e64004bca5f497.js} +2 -3
  25. package/dist/613.bundle.74d59dec1983ef3efcdc.js +532 -0
  26. package/dist/{984.bundle.119557c018e6371b4628.js → 663.bundle.a6196baf5853e7d3f7c7.js} +68 -38
  27. package/dist/{205.bundle.b5a473c200dcf2bbcdb4.js → 686.bundle.dccef1f36e4bc79bcc48.js} +6 -6
  28. package/dist/{50.bundle.65ff60818862eda39d12.js → 687.bundle.e2c9c42ad3989a14d513.js} +218 -9
  29. package/dist/{331.bundle.bd0c13931a21d53086c9.js → 743.bundle.489f7df3a089d4d374e1.js} +26294 -21326
  30. package/dist/757.bundle.ec8301d8e70d2b990f65.js +17067 -0
  31. package/dist/{728.bundle.d13856835357400fef82.js → 774.bundle.4b2dc46a35012b898e1a.js} +95 -64
  32. package/dist/{381.bundle.0905e683605fcbc0895f.js → 775.bundle.2285e7e0e67878948c0d.js} +16 -16
  33. package/dist/{283.bundle.1015e87c3a47b1f1379c.js → 788.bundle.f4493409508bdffa7af8.js} +120 -370
  34. package/dist/{642.bundle.8905e515ce593e57ceb1.js → 814.bundle.10a2cbf02b044387e68b.js} +6 -6
  35. package/dist/{707.bundle.f774f3e4a687ddd60a32.js → 82.bundle.9c6461625afd2e38b997.js} +1203 -804
  36. package/dist/{799.bundle.758558e64147e5aad612.js → 822.bundle.891f2e57b1b7bc2f4cb4.js} +81 -34
  37. package/dist/{953.bundle.3b0189ebc11cf0946f18.js → 886.bundle.4b3a7f2079d085fdbcb3.js} +34 -29
  38. package/dist/945.min.worker.js +1 -1
  39. package/dist/945.min.worker.js.map +1 -1
  40. package/dist/{270.bundle.4564621556b0f963a004.js → 957.bundle.9ea4506963ef8b2d84ba.js} +7095 -979
  41. package/dist/{208.bundle.7f610a302dc54c4924da.js → 99.bundle.334c4bd4e4e81aaf45ad.js} +86 -105
  42. package/dist/_redirects +1 -1
  43. package/dist/app-config.js +35 -17
  44. package/dist/app.bundle.css +13 -12
  45. package/dist/{app.bundle.3d598a4738bdc22950d3.js → app.bundle.fd6ac18b8874825722a0.js} +72771 -67313
  46. package/dist/assets/yandex-browser-manifest.json +1 -1
  47. package/dist/cornerstoneDICOMImageLoader.min.js +1 -1
  48. package/dist/cornerstoneDICOMImageLoader.min.js.map +1 -1
  49. package/dist/{dicom-microscopy-viewer.bundle.aa60bdf008c32c39cfd7.js → dicom-microscopy-viewer.bundle.2c146384eb9466d02ff8.js} +5 -4
  50. package/dist/es6-shim.min.js +3569 -2
  51. package/dist/google.js +8 -7
  52. package/dist/index.html +2 -1
  53. package/dist/{index.worker.1c69152d710fa7b84bce.worker.js → index.worker.e62ecca63f1a2e124230.worker.js} +2 -2
  54. package/dist/index.worker.e62ecca63f1a2e124230.worker.js.map +1 -0
  55. package/dist/init-service-worker.js +3 -5
  56. package/dist/oidc-client.min.js +10857 -39
  57. package/dist/polyfill.min.js +184 -1
  58. package/dist/silent-refresh.html +18 -9
  59. package/dist/sw.js +1 -1
  60. package/package.json +21 -22
  61. package/dist/616.bundle.de530ae226dfa5573f6e.js +0 -685
  62. package/dist/780.bundle.fd0f13dc92e9caa0581e.js +0 -4769
  63. package/dist/index.worker.1c69152d710fa7b84bce.worker.js.map +0 -1
  64. /package/dist/{806.css → 19.css} +0 -0
  65. /package/dist/{55.css → 250.css} +0 -0
  66. /package/dist/{707.css → 82.css} +0 -0
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
- (globalThis["webpackChunk"] = globalThis["webpackChunk"] || []).push([[82],{
2
+ (self["webpackChunk"] = self["webpackChunk"] || []).push([[342],{
3
3
 
4
- /***/ 96082:
4
+ /***/ 56342:
5
5
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
6
6
 
7
7
  // ESM COMPAT FLAG
@@ -11,6 +11,8 @@ __webpack_require__.r(__webpack_exports__);
11
11
  __webpack_require__.d(__webpack_exports__, {
12
12
  ContextMenuController: () => (/* reexport */ ContextMenuController),
13
13
  CustomizableContextMenuTypes: () => (/* reexport */ types_namespaceObject),
14
+ createReportAsync: () => (/* reexport */ Actions_createReportAsync),
15
+ createReportDialogPrompt: () => (/* reexport */ CreateReportDialogPrompt),
14
16
  "default": () => (/* binding */ default_src),
15
17
  dicomWebUtils: () => (/* reexport */ utils_namespaceObject),
16
18
  getStudiesForPatientByMRN: () => (/* reexport */ Panels_getStudiesForPatientByMRN)
@@ -28,11 +30,11 @@ __webpack_require__.d(utils_namespaceObject, {
28
30
  });
29
31
 
30
32
  // EXTERNAL MODULE: ../../../node_modules/dicomweb-client/build/dicomweb-client.es.js
31
- var dicomweb_client_es = __webpack_require__(75935);
32
- // EXTERNAL MODULE: ../../core/src/index.ts + 101 modules
33
- var src = __webpack_require__(48501);
33
+ var dicomweb_client_es = __webpack_require__(97604);
34
+ // EXTERNAL MODULE: ../../core/src/index.ts + 65 modules
35
+ var src = __webpack_require__(71771);
34
36
  // EXTERNAL MODULE: ../../core/src/utils/sortStudy.ts
35
- var sortStudy = __webpack_require__(87853);
37
+ var sortStudy = __webpack_require__(62971);
36
38
  ;// CONCATENATED MODULE: ../../../extensions/default/src/DicomWebDataSource/qido.js
37
39
  /**
38
40
  * QIDO - Query based on ID for DICOM Objects
@@ -378,7 +380,7 @@ function getImageId(_ref) {
378
380
  }
379
381
  }
380
382
  // EXTERNAL MODULE: ../../../node_modules/dcmjs/build/dcmjs.es.js
381
- var dcmjs_es = __webpack_require__(22737);
383
+ var dcmjs_es = __webpack_require__(67540);
382
384
  ;// CONCATENATED MODULE: ../../../extensions/default/src/DicomWebDataSource/wado/retrieveMetadataLoader.js
383
385
  /**
384
386
  * Class to define inheritance of load retrieve strategy.
@@ -638,7 +640,7 @@ const StudyMetaDataPromises = new Map();
638
640
  *
639
641
  * @param {Object} server Object with server configuration parameters
640
642
  * @param {string} StudyInstanceUID The UID of the Study to be retrieved
641
- * @param {boolean} enabledStudyLazyLoad Whether the study metadata should be loaded asynchronusly.
643
+ * @param {boolean} enabledStudyLazyLoad Whether the study metadata should be loaded asynchronously.
642
644
  * @param {function} storeInstancesCallback A callback used to store the retrieved instance metadata.
643
645
  * @param {Object} [filters] - Object containing filters to be applied on retrieve metadata process
644
646
  * @param {string} [filter.seriesInstanceUID] - series instance uid to filter results against
@@ -708,12 +710,16 @@ class StaticWadoClient extends dicomweb_client_es.api.DICOMwebClient {
708
710
  * @returns
709
711
  */
710
712
  async searchForStudies(options) {
711
- if (!this.staticWado) return super.searchForStudies(options);
713
+ if (!this.staticWado) {
714
+ return super.searchForStudies(options);
715
+ }
712
716
  const searchResult = await super.searchForStudies(options);
713
717
  const {
714
718
  queryParams
715
719
  } = options;
716
- if (!queryParams) return searchResult;
720
+ if (!queryParams) {
721
+ return searchResult;
722
+ }
717
723
  const lowerParams = this.toLowerParams(queryParams);
718
724
  const filtered = searchResult.filter(study => {
719
725
  for (const key of Object.keys(StaticWadoClient.studyFilterKeys)) {
@@ -726,12 +732,16 @@ class StaticWadoClient extends dicomweb_client_es.api.DICOMwebClient {
726
732
  return filtered;
727
733
  }
728
734
  async searchForSeries(options) {
729
- if (!this.staticWado) return super.searchForSeries(options);
735
+ if (!this.staticWado) {
736
+ return super.searchForSeries(options);
737
+ }
730
738
  const searchResult = await super.searchForSeries(options);
731
739
  const {
732
740
  queryParams
733
741
  } = options;
734
- if (!queryParams) return searchResult;
742
+ if (!queryParams) {
743
+ return searchResult;
744
+ }
735
745
  const lowerParams = this.toLowerParams(queryParams);
736
746
  const filtered = searchResult.filter(series => {
737
747
  for (const key of Object.keys(StaticWadoClient.seriesFilterKeys)) {
@@ -767,8 +777,12 @@ class StaticWadoClient extends dicomweb_client_es.api.DICOMwebClient {
767
777
  actual = actual.Alphabetic;
768
778
  }
769
779
  if (typeof actual == 'string') {
770
- if (actual.length === 0) return true;
771
- if (desired.length === 0 || desired === '*') return true;
780
+ if (actual.length === 0) {
781
+ return true;
782
+ }
783
+ if (desired.length === 0 || desired === '*') {
784
+ return true;
785
+ }
772
786
  if (desired[0] === '*' && desired[desired.length - 1] === '*') {
773
787
  // console.log(`Comparing ${actual} to ${desired.substring(1, desired.length - 1)}`)
774
788
  return actual.indexOf(desired.substring(1, desired.length - 1)) != -1;
@@ -783,9 +797,13 @@ class StaticWadoClient extends dicomweb_client_es.api.DICOMwebClient {
783
797
 
784
798
  /** Compares a pair of dates to see if the value is within the range */
785
799
  compareDateRange(range, value) {
786
- if (!value) return true;
800
+ if (!value) {
801
+ return true;
802
+ }
787
803
  const dash = range.indexOf('-');
788
- if (dash === -1) return this.compareValues(range, value);
804
+ if (dash === -1) {
805
+ return this.compareValues(range, value);
806
+ }
789
807
  const start = range.substring(0, dash);
790
808
  const end = range.substring(dash + 1);
791
809
  return (!start || value >= start) && (!end || value <= end);
@@ -802,12 +820,18 @@ class StaticWadoClient extends dicomweb_client_es.api.DICOMwebClient {
802
820
  */
803
821
  filterItem(key, queryParams, study, sourceFilterMap) {
804
822
  const altKey = sourceFilterMap[key] || key;
805
- if (!queryParams) return true;
823
+ if (!queryParams) {
824
+ return true;
825
+ }
806
826
  const testValue = queryParams[key] || queryParams[altKey];
807
- if (!testValue) return true;
827
+ if (!testValue) {
828
+ return true;
829
+ }
808
830
  const valueElem = study[key] || study[altKey];
809
- if (!valueElem) return false;
810
- if (valueElem.vr == 'DA') {
831
+ if (!valueElem) {
832
+ return false;
833
+ }
834
+ if (valueElem.vr === 'DA' && valueElem.Value?.[0]) {
811
835
  return this.compareDateRange(testValue, valueElem.Value[0]);
812
836
  }
813
837
  const value = valueElem.Value;
@@ -867,8 +891,12 @@ const getDirectURL = (config, params) => {
867
891
  singlepart: fetchPart = 'video'
868
892
  } = params;
869
893
  const value = instance[tag];
870
- if (!value) return undefined;
871
- if (value.DirectRetrieveURL) return value.DirectRetrieveURL;
894
+ if (!value) {
895
+ return undefined;
896
+ }
897
+ if (value.DirectRetrieveURL) {
898
+ return value.DirectRetrieveURL;
899
+ }
872
900
  if (value.InlineBinary) {
873
901
  const blob = src.utils.b64toBlob(value.InlineBinary, defaultType);
874
902
  value.DirectRetrieveURL = URL.createObjectURL(blob);
@@ -991,65 +1019,69 @@ const metadataProvider = src.classes.MetadataProvider;
991
1019
  * @param {string|bool} singlepart - indicates of the retrieves can fetch singlepart. Options are bulkdata, video, image or boolean true
992
1020
  */
993
1021
  function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
994
- const {
995
- qidoRoot,
996
- wadoRoot,
997
- enableStudyLazyLoad,
998
- supportsFuzzyMatching,
999
- supportsWildcard,
1000
- supportsReject,
1001
- staticWado,
1002
- singlepart
1003
- } = dicomWebConfig;
1004
- const dicomWebConfigCopy = JSON.parse(JSON.stringify(dicomWebConfig));
1005
- const qidoConfig = {
1006
- url: qidoRoot,
1007
- staticWado,
1008
- singlepart,
1009
- headers: userAuthenticationService.getAuthorizationHeader(),
1010
- errorInterceptor: src/* errorHandler */.Po.getHTTPErrorHandler()
1011
- };
1012
- const wadoConfig = {
1013
- url: wadoRoot,
1014
- staticWado,
1015
- singlepart,
1016
- headers: userAuthenticationService.getAuthorizationHeader(),
1017
- errorInterceptor: src/* errorHandler */.Po.getHTTPErrorHandler()
1018
- };
1019
-
1020
- // TODO -> Two clients sucks, but its better than 1000.
1021
- // TODO -> We'll need to merge auth later.
1022
- const qidoDicomWebClient = staticWado ? new StaticWadoClient(qidoConfig) : new dicomweb_client_es.api.DICOMwebClient(qidoConfig);
1023
- const wadoDicomWebClient = staticWado ? new StaticWadoClient(wadoConfig) : new dicomweb_client_es.api.DICOMwebClient(wadoConfig);
1022
+ let dicomWebConfigCopy, qidoConfig, wadoConfig, qidoDicomWebClient, wadoDicomWebClient, getAuthrorizationHeader, generateWadoHeader;
1024
1023
  const implementation = {
1025
1024
  initialize: _ref => {
1026
1025
  let {
1027
1026
  params,
1028
1027
  query
1029
1028
  } = _ref;
1030
- const {
1031
- StudyInstanceUIDs: paramsStudyInstanceUIDs
1032
- } = params;
1033
- const queryStudyInstanceUIDs = src.utils.splitComma(query.getAll('StudyInstanceUIDs'));
1034
- const StudyInstanceUIDs = queryStudyInstanceUIDs.length && queryStudyInstanceUIDs || paramsStudyInstanceUIDs;
1035
- const StudyInstanceUIDsAsArray = StudyInstanceUIDs && Array.isArray(StudyInstanceUIDs) ? StudyInstanceUIDs : [StudyInstanceUIDs];
1036
- return StudyInstanceUIDsAsArray;
1029
+ if (dicomWebConfig.onConfiguration && typeof dicomWebConfig.onConfiguration === 'function') {
1030
+ dicomWebConfig = dicomWebConfig.onConfiguration(dicomWebConfig, {
1031
+ params,
1032
+ query
1033
+ });
1034
+ }
1035
+ dicomWebConfigCopy = JSON.parse(JSON.stringify(dicomWebConfig));
1036
+ getAuthrorizationHeader = () => {
1037
+ const xhrRequestHeaders = {};
1038
+ const authHeaders = userAuthenticationService.getAuthorizationHeader();
1039
+ if (authHeaders && authHeaders.Authorization) {
1040
+ xhrRequestHeaders.Authorization = authHeaders.Authorization;
1041
+ }
1042
+ return xhrRequestHeaders;
1043
+ };
1044
+ generateWadoHeader = () => {
1045
+ let authorizationHeader = getAuthrorizationHeader();
1046
+ //Generate accept header depending on config params
1047
+ let formattedAcceptHeader = src.utils.generateAcceptHeader(dicomWebConfig.acceptHeader, dicomWebConfig.requestTransferSyntaxUID, dicomWebConfig.omitQuotationForMultipartRequest);
1048
+ return {
1049
+ ...authorizationHeader,
1050
+ Accept: formattedAcceptHeader
1051
+ };
1052
+ };
1053
+ qidoConfig = {
1054
+ url: dicomWebConfig.qidoRoot,
1055
+ staticWado: dicomWebConfig.staticWado,
1056
+ singlepart: dicomWebConfig.singlepart,
1057
+ headers: userAuthenticationService.getAuthorizationHeader(),
1058
+ errorInterceptor: src/* errorHandler */.Po.getHTTPErrorHandler()
1059
+ };
1060
+ wadoConfig = {
1061
+ url: dicomWebConfig.wadoRoot,
1062
+ staticWado: dicomWebConfig.staticWado,
1063
+ singlepart: dicomWebConfig.singlepart,
1064
+ headers: userAuthenticationService.getAuthorizationHeader(),
1065
+ errorInterceptor: src/* errorHandler */.Po.getHTTPErrorHandler()
1066
+ };
1067
+
1068
+ // TODO -> Two clients sucks, but its better than 1000.
1069
+ // TODO -> We'll need to merge auth later.
1070
+ qidoDicomWebClient = dicomWebConfig.staticWado ? new StaticWadoClient(qidoConfig) : new dicomweb_client_es.api.DICOMwebClient(qidoConfig);
1071
+ wadoDicomWebClient = dicomWebConfig.staticWado ? new StaticWadoClient(wadoConfig) : new dicomweb_client_es.api.DICOMwebClient(wadoConfig);
1037
1072
  },
1038
1073
  query: {
1039
1074
  studies: {
1040
1075
  mapParams: mapParams.bind(),
1041
1076
  search: async function (origParams) {
1042
- const headers = userAuthenticationService.getAuthorizationHeader();
1043
- if (headers) {
1044
- qidoDicomWebClient.headers = headers;
1045
- }
1077
+ qidoDicomWebClient.headers = getAuthrorizationHeader();
1046
1078
  const {
1047
1079
  studyInstanceUid,
1048
1080
  seriesInstanceUid,
1049
1081
  ...mappedParams
1050
1082
  } = mapParams(origParams, {
1051
- supportsFuzzyMatching,
1052
- supportsWildcard
1083
+ supportsFuzzyMatching: dicomWebConfig.supportsFuzzyMatching,
1084
+ supportsWildcard: dicomWebConfig.supportsWildcard
1053
1085
  }) || {};
1054
1086
  const results = await search(qidoDicomWebClient, undefined, undefined, mappedParams);
1055
1087
  return processResults(results);
@@ -1059,10 +1091,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
1059
1091
  series: {
1060
1092
  // mapParams: mapParams.bind(),
1061
1093
  search: async function (studyInstanceUid) {
1062
- const headers = userAuthenticationService.getAuthorizationHeader();
1063
- if (headers) {
1064
- qidoDicomWebClient.headers = headers;
1065
- }
1094
+ qidoDicomWebClient.headers = getAuthrorizationHeader();
1066
1095
  const results = await seriesInStudy(qidoDicomWebClient, studyInstanceUid);
1067
1096
  return processSeriesResults(results);
1068
1097
  }
@@ -1071,10 +1100,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
1071
1100
 
1072
1101
  instances: {
1073
1102
  search: (studyInstanceUid, queryParameters) => {
1074
- const headers = userAuthenticationService.getAuthorizationHeader();
1075
- if (headers) {
1076
- qidoDicomWebClient.headers = headers;
1077
- }
1103
+ qidoDicomWebClient.headers = getAuthrorizationHeader();
1078
1104
  search.call(undefined, qidoDicomWebClient, studyInstanceUid, null, queryParameters);
1079
1105
  }
1080
1106
  }
@@ -1093,8 +1119,8 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
1093
1119
  */
1094
1120
  directURL: params => {
1095
1121
  return utils_getDirectURL({
1096
- wadoRoot,
1097
- singlepart
1122
+ wadoRoot: dicomWebConfig.wadoRoot,
1123
+ singlepart: dicomWebConfig.singlepart
1098
1124
  }, params);
1099
1125
  },
1100
1126
  bulkDataURI: async _ref2 => {
@@ -1102,6 +1128,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
1102
1128
  StudyInstanceUID,
1103
1129
  BulkDataURI
1104
1130
  } = _ref2;
1131
+ qidoDicomWebClient.headers = getAuthrorizationHeader();
1105
1132
  const options = {
1106
1133
  multipart: false,
1107
1134
  BulkDataURI,
@@ -1121,14 +1148,10 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
1121
1148
  sortFunction,
1122
1149
  madeInClient = false
1123
1150
  } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
1124
- const headers = userAuthenticationService.getAuthorizationHeader();
1125
- if (headers) {
1126
- wadoDicomWebClient.headers = headers;
1127
- }
1128
1151
  if (!StudyInstanceUID) {
1129
1152
  throw new Error('Unable to query for SeriesMetadata without StudyInstanceUID');
1130
1153
  }
1131
- if (enableStudyLazyLoad) {
1154
+ if (dicomWebConfig.enableStudyLazyLoad) {
1132
1155
  return implementation._retrieveSeriesMetadataAsync(StudyInstanceUID, filters, sortCriteria, sortFunction, madeInClient);
1133
1156
  }
1134
1157
  return implementation._retrieveSeriesMetadataSync(StudyInstanceUID, filters, sortCriteria, sortFunction, madeInClient);
@@ -1137,10 +1160,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
1137
1160
  },
1138
1161
  store: {
1139
1162
  dicom: async (dataset, request) => {
1140
- const headers = userAuthenticationService.getAuthorizationHeader();
1141
- if (headers) {
1142
- wadoDicomWebClient.headers = headers;
1143
- }
1163
+ wadoDicomWebClient.headers = getAuthrorizationHeader();
1144
1164
  if (dataset instanceof ArrayBuffer) {
1145
1165
  const options = {
1146
1166
  datasets: [dataset],
@@ -1149,7 +1169,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
1149
1169
  await wadoDicomWebClient.storeInstances(options);
1150
1170
  } else {
1151
1171
  const meta = {
1152
- FileMetaInformationVersion: dataset._meta.FileMetaInformationVersion.Value,
1172
+ FileMetaInformationVersion: dataset._meta?.FileMetaInformationVersion?.Value,
1153
1173
  MediaStorageSOPClassUID: dataset.SOPClassUID,
1154
1174
  MediaStorageSOPInstanceUID: dataset.SOPInstanceUID,
1155
1175
  TransferSyntaxUID: EXPLICIT_VR_LITTLE_ENDIAN,
@@ -1170,7 +1190,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
1170
1190
  },
1171
1191
  _retrieveSeriesMetadataSync: async (StudyInstanceUID, filters, sortCriteria, sortFunction, madeInClient) => {
1172
1192
  const enableStudyLazyLoad = false;
1173
-
1193
+ wadoDicomWebClient.headers = generateWadoHeader();
1174
1194
  // data is all SOPInstanceUIDs
1175
1195
  const data = await retrieveStudyMetadata(wadoDicomWebClient, StudyInstanceUID, enableStudyLazyLoad, filters, sortCriteria, sortFunction);
1176
1196
 
@@ -1199,6 +1219,8 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
1199
1219
  instance
1200
1220
  });
1201
1221
  instance.imageId = imageId;
1222
+ instance.wadoRoot = dicomWebConfig.wadoRoot;
1223
+ instance.wadoUri = dicomWebConfig.wadoUri;
1202
1224
  metadataProvider.addImageIdToUIDs(imageId, {
1203
1225
  StudyInstanceUID,
1204
1226
  SeriesInstanceUID: instance.SeriesInstanceUID,
@@ -1215,6 +1237,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
1215
1237
  _retrieveSeriesMetadataAsync: async function (StudyInstanceUID, filters, sortCriteria, sortFunction) {
1216
1238
  let madeInClient = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
1217
1239
  const enableStudyLazyLoad = true;
1240
+ wadoDicomWebClient.headers = generateWadoHeader();
1218
1241
  // Get Series
1219
1242
  const {
1220
1243
  preLoadData: seriesSummaryMetadata,
@@ -1230,7 +1253,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
1230
1253
  const addRetrieveBulkData = instance => {
1231
1254
  const naturalized = naturalizeDataset(instance);
1232
1255
 
1233
- // if we konw the server doesn't use bulkDataURI, then don't
1256
+ // if we know the server doesn't use bulkDataURI, then don't
1234
1257
  if (!dicomWebConfig.bulkDataURI?.enabled) {
1235
1258
  return naturalized;
1236
1259
  }
@@ -1356,10 +1379,23 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
1356
1379
  },
1357
1380
  getConfig() {
1358
1381
  return dicomWebConfigCopy;
1382
+ },
1383
+ getStudyInstanceUIDs(_ref4) {
1384
+ let {
1385
+ params,
1386
+ query
1387
+ } = _ref4;
1388
+ const {
1389
+ StudyInstanceUIDs: paramsStudyInstanceUIDs
1390
+ } = params;
1391
+ const queryStudyInstanceUIDs = src.utils.splitComma(query.getAll('StudyInstanceUIDs'));
1392
+ const StudyInstanceUIDs = queryStudyInstanceUIDs.length && queryStudyInstanceUIDs || paramsStudyInstanceUIDs;
1393
+ const StudyInstanceUIDsAsArray = StudyInstanceUIDs && Array.isArray(StudyInstanceUIDs) ? StudyInstanceUIDs : [StudyInstanceUIDs];
1394
+ return StudyInstanceUIDsAsArray;
1359
1395
  }
1360
1396
  };
1361
- if (supportsReject) {
1362
- implementation.reject = dcm4cheeReject(wadoRoot);
1397
+ if (dicomWebConfig.supportsReject) {
1398
+ implementation.reject = dcm4cheeReject(dicomWebConfig.wadoRoot);
1363
1399
  }
1364
1400
  return src/* IWebApiDataSource */.Is.create(implementation);
1365
1401
  }
@@ -1375,7 +1411,8 @@ const mappings = {
1375
1411
  patientId: 'PatientID'
1376
1412
  };
1377
1413
  let _store = {
1378
- urls: []
1414
+ urls: [],
1415
+ studyInstanceUIDMap: new Map() // map of urls to array of study instance UIDs
1379
1416
  // {
1380
1417
  // url: url1
1381
1418
  // studies: [Study1, Study2], // if multiple studies
@@ -1403,17 +1440,17 @@ const findStudies = (key, value) => {
1403
1440
  };
1404
1441
  function createDicomJSONApi(dicomJsonConfig) {
1405
1442
  const {
1406
- name,
1407
1443
  wadoRoot
1408
1444
  } = dicomJsonConfig;
1409
1445
  const implementation = {
1410
1446
  initialize: async _ref => {
1411
1447
  let {
1412
- params,
1413
1448
  query,
1414
1449
  url
1415
1450
  } = _ref;
1416
- if (!url) url = query.get('url');
1451
+ if (!url) {
1452
+ url = query.get('url');
1453
+ }
1417
1454
  let metaData = getMetaDataByURL(url);
1418
1455
 
1419
1456
  // if we have already cached the data from this specific url
@@ -1425,8 +1462,7 @@ function createDicomJSONApi(dicomJsonConfig) {
1425
1462
  });
1426
1463
  }
1427
1464
  const response = await fetch(url);
1428
- let data = await response.json();
1429
- const studyInstanceUIDs = data.studies.map(study => study.StudyInstanceUID);
1465
+ const data = await response.json();
1430
1466
  let StudyInstanceUID;
1431
1467
  let SeriesInstanceUID;
1432
1468
  data.studies.forEach(study => {
@@ -1452,7 +1488,7 @@ function createDicomJSONApi(dicomJsonConfig) {
1452
1488
  url,
1453
1489
  studies: [...data.studies]
1454
1490
  });
1455
- return studyInstanceUIDs;
1491
+ _store.studyInstanceUIDMap.set(url, data.studies.map(study => study.StudyInstanceUID));
1456
1492
  },
1457
1493
  query: {
1458
1494
  studies: {
@@ -1479,18 +1515,18 @@ function createDicomJSONApi(dicomJsonConfig) {
1479
1515
  });
1480
1516
  },
1481
1517
  processResults: () => {
1482
- console.debug(' DICOMJson QUERY processResults');
1518
+ console.warn(' DICOMJson QUERY processResults not implemented');
1483
1519
  }
1484
1520
  },
1485
1521
  series: {
1486
1522
  // mapParams: mapParams.bind(),
1487
1523
  search: () => {
1488
- console.debug(' DICOMJson QUERY SERIES SEARCH');
1524
+ console.warn(' DICOMJson QUERY SERIES SEARCH not implemented');
1489
1525
  }
1490
1526
  },
1491
1527
  instances: {
1492
1528
  search: () => {
1493
- console.debug(' DICOMJson QUERY instances SEARCH');
1529
+ console.warn(' DICOMJson QUERY instances SEARCH not implemented');
1494
1530
  }
1495
1531
  }
1496
1532
  },
@@ -1512,7 +1548,7 @@ function createDicomJSONApi(dicomJsonConfig) {
1512
1548
  return utils_getDirectURL(wadoRoot, params);
1513
1549
  },
1514
1550
  series: {
1515
- metadata: function () {
1551
+ metadata: async function () {
1516
1552
  let {
1517
1553
  StudyInstanceUID,
1518
1554
  madeInClient = false,
@@ -1561,14 +1597,16 @@ function createDicomJSONApi(dicomJsonConfig) {
1561
1597
  return obj;
1562
1598
  });
1563
1599
  storeInstances(instances);
1564
- if (index === numberOfSeries - 1) setSuccessFlag();
1600
+ if (index === numberOfSeries - 1) {
1601
+ setSuccessFlag();
1602
+ }
1565
1603
  });
1566
1604
  }
1567
1605
  }
1568
1606
  },
1569
1607
  store: {
1570
1608
  dicom: () => {
1571
- console.debug(' DICOMJson store dicom');
1609
+ console.warn(' DICOMJson store dicom not implemented');
1572
1610
  }
1573
1611
  },
1574
1612
  getImageIdsForDisplaySet(displaySet) {
@@ -1608,6 +1646,14 @@ function createDicomJSONApi(dicomJsonConfig) {
1608
1646
  frame
1609
1647
  });
1610
1648
  return imageIds;
1649
+ },
1650
+ getStudyInstanceUIDs: _ref3 => {
1651
+ let {
1652
+ params,
1653
+ query
1654
+ } = _ref3;
1655
+ const url = query.get('url');
1656
+ return _store.studyInstanceUIDMap.get(url);
1611
1657
  }
1612
1658
  };
1613
1659
  return src/* IWebApiDataSource */.Is.create(implementation);
@@ -1628,8 +1674,12 @@ const END_MODALITIES = {
1628
1674
  };
1629
1675
  const compareValue = function (v1, v2) {
1630
1676
  let def = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
1631
- if (v1 === v2) return def;
1632
- if (v1 < v2) return -1;
1677
+ if (v1 === v2) {
1678
+ return def;
1679
+ }
1680
+ if (v1 < v2) {
1681
+ return -1;
1682
+ }
1633
1683
  return 1;
1634
1684
  };
1635
1685
 
@@ -1660,19 +1710,6 @@ function createDicomLocalApi(dicomLocalConfig) {
1660
1710
  params,
1661
1711
  query
1662
1712
  } = _ref;
1663
- const {
1664
- StudyInstanceUIDs: paramsStudyInstanceUIDs
1665
- } = params;
1666
- const queryStudyInstanceUIDs = query.getAll('StudyInstanceUIDs');
1667
- const StudyInstanceUIDs = queryStudyInstanceUIDs || paramsStudyInstanceUIDs;
1668
- const StudyInstanceUIDsAsArray = StudyInstanceUIDs && Array.isArray(StudyInstanceUIDs) ? StudyInstanceUIDs : [StudyInstanceUIDs];
1669
-
1670
- // Put SRs at the end of series list to make sure images are loaded first
1671
- StudyInstanceUIDsAsArray.forEach(StudyInstanceUID => {
1672
- const study = src.DicomMetadataStore.getStudy(StudyInstanceUID);
1673
- study.series = study.series.sort(customSort);
1674
- });
1675
- return StudyInstanceUIDsAsArray;
1676
1713
  },
1677
1714
  query: {
1678
1715
  studies: {
@@ -1711,7 +1748,7 @@ function createDicomLocalApi(dicomLocalConfig) {
1711
1748
  });
1712
1749
  },
1713
1750
  processResults: () => {
1714
- console.debug(' DICOMLocal QUERY processResults');
1751
+ console.warn(' DICOMLocal QUERY processResults not implemented');
1715
1752
  }
1716
1753
  },
1717
1754
  series: {
@@ -1733,7 +1770,7 @@ function createDicomLocalApi(dicomLocalConfig) {
1733
1770
  },
1734
1771
  instances: {
1735
1772
  search: () => {
1736
- console.debug(' DICOMLocal QUERY instances SEARCH');
1773
+ console.warn(' DICOMLocal QUERY instances SEARCH not implemented');
1737
1774
  }
1738
1775
  }
1739
1776
  },
@@ -1854,6 +1891,29 @@ function createDicomLocalApi(dicomLocalConfig) {
1854
1891
  },
1855
1892
  deleteStudyMetadataPromise() {
1856
1893
  console.log('deleteStudyMetadataPromise not implemented');
1894
+ },
1895
+ getStudyInstanceUIDs: _ref3 => {
1896
+ let {
1897
+ params,
1898
+ query
1899
+ } = _ref3;
1900
+ const {
1901
+ StudyInstanceUIDs: paramsStudyInstanceUIDs
1902
+ } = params;
1903
+ const queryStudyInstanceUIDs = query.getAll('StudyInstanceUIDs');
1904
+ const StudyInstanceUIDs = queryStudyInstanceUIDs || paramsStudyInstanceUIDs;
1905
+ const StudyInstanceUIDsAsArray = StudyInstanceUIDs && Array.isArray(StudyInstanceUIDs) ? StudyInstanceUIDs : [StudyInstanceUIDs];
1906
+
1907
+ // Put SRs at the end of series list to make sure images are loaded first
1908
+ let isStudyInCache = false;
1909
+ StudyInstanceUIDsAsArray.forEach(StudyInstanceUID => {
1910
+ const study = src.DicomMetadataStore.getStudy(StudyInstanceUID);
1911
+ if (study) {
1912
+ study.series = study.series.sort(customSort);
1913
+ isStudyInCache = true;
1914
+ }
1915
+ });
1916
+ return isStudyInCache ? StudyInstanceUIDsAsArray : [];
1857
1917
  }
1858
1918
  };
1859
1919
  return src/* IWebApiDataSource */.Is.create(implementation);
@@ -1882,13 +1942,6 @@ function createDicomWebProxyApi(dicomWebProxyConfig, UserAuthenticationService)
1882
1942
  params,
1883
1943
  query
1884
1944
  } = _ref;
1885
- let studyInstanceUIDs = [];
1886
-
1887
- // there seem to be a couple of variations of the case for this parameter
1888
- const queryStudyInstanceUIDs = query.get('studyInstanceUIDs') || query.get('studyInstanceUids');
1889
- if (!queryStudyInstanceUIDs) {
1890
- throw new Error(`No studyInstanceUids in request for '${name}'`);
1891
- }
1892
1945
  const url = query.get('url');
1893
1946
  if (!url) {
1894
1947
  throw new Error(`No url for '${name}'`);
@@ -1898,10 +1951,12 @@ function createDicomWebProxyApi(dicomWebProxyConfig, UserAuthenticationService)
1898
1951
  if (!data.servers?.dicomWeb?.[0]) {
1899
1952
  throw new Error('Invalid configuration returned by url');
1900
1953
  }
1901
- dicomWebDelegate = createDicomWebApi(data.servers.dicomWeb[0], UserAuthenticationService);
1902
- studyInstanceUIDs = queryStudyInstanceUIDs.split(';');
1954
+ dicomWebDelegate = createDicomWebApi(data.servers.dicomWeb[0].configuration, UserAuthenticationService);
1955
+ dicomWebDelegate.initialize({
1956
+ params,
1957
+ query
1958
+ });
1903
1959
  }
1904
- return studyInstanceUIDs;
1905
1960
  },
1906
1961
  query: {
1907
1962
  studies: {
@@ -1921,7 +1976,7 @@ function createDicomWebProxyApi(dicomWebProxyConfig, UserAuthenticationService)
1921
1976
  return dicomWebDelegate.retrieve.directURL(...arguments);
1922
1977
  },
1923
1978
  series: {
1924
- metadata: function () {
1979
+ metadata: async function () {
1925
1980
  return dicomWebDelegate.retrieve.series.metadata(...arguments);
1926
1981
  }
1927
1982
  }
@@ -1939,6 +1994,21 @@ function createDicomWebProxyApi(dicomWebProxyConfig, UserAuthenticationService)
1939
1994
  },
1940
1995
  getImageIdsForInstance: function () {
1941
1996
  return dicomWebDelegate.getImageIdsForInstance(...arguments);
1997
+ },
1998
+ getStudyInstanceUIDs(_ref2) {
1999
+ let {
2000
+ params,
2001
+ query
2002
+ } = _ref2;
2003
+ let studyInstanceUIDs = [];
2004
+
2005
+ // there seem to be a couple of variations of the case for this parameter
2006
+ const queryStudyInstanceUIDs = query.get('studyInstanceUIDs') || query.get('studyInstanceUids');
2007
+ if (!queryStudyInstanceUIDs) {
2008
+ throw new Error(`No studyInstanceUids in request for '${name}'`);
2009
+ }
2010
+ studyInstanceUIDs = queryStudyInstanceUIDs.split(';');
2011
+ return studyInstanceUIDs;
1942
2012
  }
1943
2013
  };
1944
2014
  return src/* IWebApiDataSource */.Is.create(implementation);
@@ -1978,24 +2048,24 @@ function getDataSourcesModule() {
1978
2048
  }
1979
2049
  /* harmony default export */ const src_getDataSourcesModule = (getDataSourcesModule);
1980
2050
  // EXTERNAL MODULE: ../../../node_modules/react/index.js
1981
- var react = __webpack_require__(32735);
2051
+ var react = __webpack_require__(43001);
1982
2052
  // EXTERNAL MODULE: ../../../node_modules/prop-types/index.js
1983
- var prop_types = __webpack_require__(60216);
2053
+ var prop_types = __webpack_require__(3827);
1984
2054
  var prop_types_default = /*#__PURE__*/__webpack_require__.n(prop_types);
2055
+ // EXTERNAL MODULE: ../../ui/src/index.js + 485 modules
2056
+ var ui_src = __webpack_require__(71783);
2057
+ // EXTERNAL MODULE: ./state/index.js + 1 modules
2058
+ var state = __webpack_require__(62657);
1985
2059
  // EXTERNAL MODULE: ../node_modules/react-router-dom/dist/index.js
1986
- var dist = __webpack_require__(65783);
2060
+ var dist = __webpack_require__(62474);
1987
2061
  // EXTERNAL MODULE: ../../../node_modules/react-i18next/dist/es/index.js + 15 modules
1988
- var es = __webpack_require__(21572);
2062
+ var es = __webpack_require__(69190);
1989
2063
  // EXTERNAL MODULE: ../node_modules/react-router/dist/index.js
1990
- var react_router_dist = __webpack_require__(14935);
1991
- // EXTERNAL MODULE: ../../ui/src/index.js + 452 modules
1992
- var ui_src = __webpack_require__(28619);
1993
- // EXTERNAL MODULE: ../../i18n/src/index.js + 95 modules
1994
- var i18n_src = __webpack_require__(93058);
1995
- // EXTERNAL MODULE: ./state/index.js + 1 modules
1996
- var state = __webpack_require__(22896);
2064
+ var react_router_dist = __webpack_require__(85066);
2065
+ // EXTERNAL MODULE: ../../i18n/src/index.js + 134 modules
2066
+ var i18n_src = __webpack_require__(50376);
1997
2067
  // EXTERNAL MODULE: ../../../node_modules/classnames/index.js
1998
- var classnames = __webpack_require__(40841);
2068
+ var classnames = __webpack_require__(44921);
1999
2069
  var classnames_default = /*#__PURE__*/__webpack_require__.n(classnames);
2000
2070
  ;// CONCATENATED MODULE: ../../../extensions/default/src/Toolbar/Toolbar.tsx
2001
2071
  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); }
@@ -2009,48 +2079,21 @@ function Toolbar(_ref) {
2009
2079
  toolbarService
2010
2080
  } = servicesManager.services;
2011
2081
  const [toolbarButtons, setToolbarButtons] = (0,react.useState)([]);
2012
- const [buttonState, setButtonState] = (0,react.useState)({
2013
- primaryToolId: '',
2014
- toggles: {},
2015
- groups: {}
2016
- });
2017
-
2018
- // Could track buttons and state separately...?
2019
2082
  (0,react.useEffect)(() => {
2020
2083
  const {
2021
- unsubscribe: unsub1
2084
+ unsubscribe
2022
2085
  } = toolbarService.subscribe(toolbarService.EVENTS.TOOL_BAR_MODIFIED, () => setToolbarButtons(toolbarService.getButtonSection('primary')));
2023
- const {
2024
- unsubscribe: unsub2
2025
- } = toolbarService.subscribe(toolbarService.EVENTS.TOOL_BAR_STATE_MODIFIED, () => setButtonState({
2026
- ...toolbarService.state
2027
- }));
2028
2086
  return () => {
2029
- unsub1();
2030
- unsub2();
2087
+ unsubscribe();
2031
2088
  };
2032
2089
  }, [toolbarService]);
2033
- return /*#__PURE__*/react.createElement(react.Fragment, null, toolbarButtons.map((toolDef, index) => {
2090
+ const onInteraction = (0,react.useCallback)(args => toolbarService.recordInteraction(args), [toolbarService]);
2091
+ return /*#__PURE__*/react.createElement(react.Fragment, null, toolbarButtons.map(toolDef => {
2034
2092
  const {
2035
2093
  id,
2036
2094
  Component,
2037
2095
  componentProps
2038
2096
  } = toolDef;
2039
- // TODO: ...
2040
-
2041
- // isActive if:
2042
- // - id is primary?
2043
- // - id is in list of "toggled on"?
2044
- let isActive;
2045
- if (componentProps.type === 'toggle') {
2046
- isActive = buttonState.toggles[id];
2047
- }
2048
- // Also need... to filter list for splitButton, and set primary based on most recently clicked
2049
- // Also need to kill the radioGroup button's magic logic
2050
- // Everything should be reactive off these props, so commands can inform ToolbarService
2051
-
2052
- // These can... Trigger toolbar events based on updates?
2053
- // Then sync using useEffect, or simply modify the state here?
2054
2097
  return (
2055
2098
  /*#__PURE__*/
2056
2099
  // The margin for separating the tools on the toolbar should go here and NOT in each individual component (button) item.
@@ -2061,16 +2104,13 @@ function Toolbar(_ref) {
2061
2104
  }, /*#__PURE__*/react.createElement(Component, _extends({
2062
2105
  id: id
2063
2106
  }, componentProps, {
2064
- bState: buttonState,
2065
- isActive: isActive,
2066
- onInteraction: args => toolbarService.recordInteraction(args),
2107
+ onInteraction: onInteraction,
2067
2108
  servicesManager: servicesManager
2068
2109
  })))
2069
2110
  );
2070
2111
  }));
2071
2112
  }
2072
- ;// CONCATENATED MODULE: ../../../extensions/default/src/ViewerLayout/index.tsx
2073
-
2113
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/ViewerLayout/ViewerHeader.tsx
2074
2114
 
2075
2115
 
2076
2116
 
@@ -2085,20 +2125,11 @@ const {
2085
2125
  defaultLanguage,
2086
2126
  currentLanguage
2087
2127
  } = i18n_src["default"];
2088
- function ViewerLayout(_ref) {
2128
+ function ViewerHeader(_ref) {
2089
2129
  let {
2090
- // From Extension Module Params
2091
- extensionManager,
2092
- servicesManager,
2093
2130
  hotkeysManager,
2094
- commandsManager,
2095
- // From Modes
2096
- viewports,
2097
- ViewportGridComp,
2098
- leftPanels = [],
2099
- rightPanels = [],
2100
- leftPanelDefaultClosed = false,
2101
- rightPanelDefaultClosed = false
2131
+ extensionManager,
2132
+ servicesManager
2102
2133
  } = _ref;
2103
2134
  const [appConfig] = (0,state/* useAppConfig */.M)();
2104
2135
  const navigate = (0,dist/* useNavigate */.s0)();
@@ -2108,16 +2139,12 @@ function ViewerLayout(_ref) {
2108
2139
  pathname
2109
2140
  } = location;
2110
2141
  const dataSourceIdx = pathname.indexOf('/', 1);
2111
- // const search =
2112
- // dataSourceIdx === -1
2113
- // ? undefined
2114
- // : `datasources=${pathname.substring(dataSourceIdx + 1)}`;
2115
-
2116
- // Todo: Handle parameters in a better way.
2117
2142
  const query = new URLSearchParams(window.location.search);
2118
2143
  const configUrl = query.get('configUrl');
2144
+ const dataSourceName = pathname.substring(dataSourceIdx + 1);
2145
+ const existingDataSource = extensionManager.getDataSources(dataSourceName);
2119
2146
  const searchQuery = new URLSearchParams();
2120
- if (dataSourceIdx !== -1) {
2147
+ if (dataSourceIdx !== -1 && existingDataSource) {
2121
2148
  searchQuery.append('datasources', pathname.substring(dataSourceIdx + 1));
2122
2149
  }
2123
2150
  if (configUrl) {
@@ -2135,16 +2162,12 @@ function ViewerLayout(_ref) {
2135
2162
  show,
2136
2163
  hide
2137
2164
  } = (0,ui_src/* useModal */.dd)();
2138
- const [showLoadingIndicator, setShowLoadingIndicator] = (0,react.useState)(appConfig.showLoadingIndicator);
2139
- const {
2140
- hangingProtocolService
2141
- } = servicesManager.services;
2142
2165
  const {
2143
2166
  hotkeyDefinitions,
2144
2167
  hotkeyDefaults
2145
2168
  } = hotkeysManager;
2146
- const versionNumber = "3.7.0-beta.11";
2147
- const commitHash = "d51211f6b97a7ad07cd87470a8f251a728cd7b28";
2169
+ const versionNumber = "3.7.0-beta.110";
2170
+ const commitHash = "157b88c909d3289cb89ace731c1f9a19d40797ac";
2148
2171
  const menuOptions = [{
2149
2172
  title: t('Header:About'),
2150
2173
  icon: 'info',
@@ -2178,7 +2201,9 @@ function ViewerLayout(_ref) {
2178
2201
  hotkeyDefinitions,
2179
2202
  language
2180
2203
  } = _ref2;
2181
- i18n_src["default"].changeLanguage(language.value);
2204
+ if (language.value !== currentLanguage().value) {
2205
+ i18n_src["default"].changeLanguage(language.value);
2206
+ }
2182
2207
  hotkeysManager.setHotkeys(hotkeyDefinitions);
2183
2208
  hide();
2184
2209
  },
@@ -2196,6 +2221,91 @@ function ViewerLayout(_ref) {
2196
2221
  }
2197
2222
  });
2198
2223
  }
2224
+ return /*#__PURE__*/react.createElement(ui_src/* Header */.h4, {
2225
+ menuOptions: menuOptions,
2226
+ isReturnEnabled: !!appConfig.showStudyList,
2227
+ onClickReturnButton: onClickReturnButton,
2228
+ WhiteLabeling: appConfig.whiteLabeling
2229
+ }, /*#__PURE__*/react.createElement(ui_src/* ErrorBoundary */.SV, {
2230
+ context: "Primary Toolbar"
2231
+ }, /*#__PURE__*/react.createElement("div", {
2232
+ className: "relative flex justify-center"
2233
+ }, /*#__PURE__*/react.createElement(Toolbar, {
2234
+ servicesManager: servicesManager
2235
+ }))));
2236
+ }
2237
+ /* harmony default export */ const ViewerLayout_ViewerHeader = (ViewerHeader);
2238
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/Components/SidePanelWithServices.tsx
2239
+
2240
+
2241
+ const SidePanelWithServices = _ref => {
2242
+ let {
2243
+ servicesManager,
2244
+ side,
2245
+ className,
2246
+ activeTabIndex: activeTabIndexProp,
2247
+ tabs
2248
+ } = _ref;
2249
+ const panelService = servicesManager?.services?.panelService;
2250
+
2251
+ // Tracks whether this SidePanel has been opened at least once since this SidePanel was inserted into the DOM.
2252
+ // Thus going to the Study List page and back to the viewer resets this flag for a SidePanel.
2253
+ const [hasBeenOpened, setHasBeenOpened] = (0,react.useState)(false);
2254
+ const [activeTabIndex, setActiveTabIndex] = (0,react.useState)(activeTabIndexProp);
2255
+ (0,react.useEffect)(() => {
2256
+ if (panelService) {
2257
+ const activatePanelSubscription = panelService.subscribe(panelService.EVENTS.ACTIVATE_PANEL, activatePanelEvent => {
2258
+ if (!hasBeenOpened || activatePanelEvent.forceActive) {
2259
+ const tabIndex = tabs.findIndex(tab => tab.id === activatePanelEvent.panelId);
2260
+ if (tabIndex !== -1) {
2261
+ setActiveTabIndex(tabIndex);
2262
+ }
2263
+ }
2264
+ });
2265
+ return () => {
2266
+ activatePanelSubscription.unsubscribe();
2267
+ };
2268
+ }
2269
+ }, [tabs, hasBeenOpened, panelService]);
2270
+ return /*#__PURE__*/react.createElement(ui_src/* SidePanel */.hs, {
2271
+ side: side,
2272
+ className: className,
2273
+ activeTabIndex: activeTabIndex,
2274
+ tabs: tabs,
2275
+ onOpen: () => {
2276
+ setHasBeenOpened(true);
2277
+ }
2278
+ });
2279
+ };
2280
+ /* harmony default export */ const Components_SidePanelWithServices = (SidePanelWithServices);
2281
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/ViewerLayout/index.tsx
2282
+
2283
+
2284
+
2285
+
2286
+
2287
+
2288
+
2289
+ function ViewerLayout(_ref) {
2290
+ let {
2291
+ // From Extension Module Params
2292
+ extensionManager,
2293
+ servicesManager,
2294
+ hotkeysManager,
2295
+ commandsManager,
2296
+ // From Modes
2297
+ viewports,
2298
+ ViewportGridComp,
2299
+ leftPanels = [],
2300
+ rightPanels = [],
2301
+ leftPanelDefaultClosed = false,
2302
+ rightPanelDefaultClosed = false
2303
+ } = _ref;
2304
+ const [appConfig] = (0,state/* useAppConfig */.M)();
2305
+ const {
2306
+ hangingProtocolService
2307
+ } = servicesManager.services;
2308
+ const [showLoadingIndicator, setShowLoadingIndicator] = (0,react.useState)(appConfig.showLoadingIndicator);
2199
2309
 
2200
2310
  /**
2201
2311
  * Set body classes (tailwindcss) that don't allow vertical
@@ -2213,7 +2323,7 @@ function ViewerLayout(_ref) {
2213
2323
  const getComponent = id => {
2214
2324
  const entry = extensionManager.getModuleEntry(id);
2215
2325
  if (!entry) {
2216
- throw new Error(`${id} is not a valid entry for an extension module, please check your configuration or make sure the extension is registered.`);
2326
+ throw new Error(`${id} is not valid for an extension module. Please verify your configuration or ensure that the extension is properly registered. It's also possible that your mode is utilizing a module from an extension that hasn't been included in its dependencies (add the extension to the "extensionDependencies" array in your mode's index.js file)`);
2217
2327
  }
2218
2328
  let content;
2219
2329
  if (entry && entry.component) {
@@ -2266,19 +2376,12 @@ function ViewerLayout(_ref) {
2266
2376
  const leftPanelComponents = leftPanels.map(getPanelData);
2267
2377
  const rightPanelComponents = rightPanels.map(getPanelData);
2268
2378
  const viewportComponents = viewports.map(getViewportComponentData);
2269
- return /*#__PURE__*/react.createElement("div", null, /*#__PURE__*/react.createElement(ui_src/* Header */.h4, {
2270
- menuOptions: menuOptions,
2271
- isReturnEnabled: !!appConfig.showStudyList,
2272
- onClickReturnButton: onClickReturnButton,
2273
- WhiteLabeling: appConfig.whiteLabeling
2274
- }, /*#__PURE__*/react.createElement(ui_src/* ErrorBoundary */.SV, {
2275
- context: "Primary Toolbar"
2276
- }, /*#__PURE__*/react.createElement("div", {
2277
- className: "relative flex justify-center"
2278
- }, /*#__PURE__*/react.createElement(Toolbar, {
2379
+ return /*#__PURE__*/react.createElement("div", null, /*#__PURE__*/react.createElement(ViewerLayout_ViewerHeader, {
2380
+ hotkeysManager: hotkeysManager,
2381
+ extensionManager: extensionManager,
2279
2382
  servicesManager: servicesManager
2280
- })))), /*#__PURE__*/react.createElement("div", {
2281
- className: "bg-black flex flex-row items-stretch w-full overflow-hidden flex-nowrap relative",
2383
+ }), /*#__PURE__*/react.createElement("div", {
2384
+ className: "relative flex w-full flex-row flex-nowrap items-stretch overflow-hidden bg-black",
2282
2385
  style: {
2283
2386
  height: 'calc(100vh - 52px'
2284
2387
  }
@@ -2286,15 +2389,15 @@ function ViewerLayout(_ref) {
2286
2389
  className: "h-full w-full bg-black"
2287
2390
  }), leftPanelComponents.length ? /*#__PURE__*/react.createElement(ui_src/* ErrorBoundary */.SV, {
2288
2391
  context: "Left Panel"
2289
- }, /*#__PURE__*/react.createElement(ui_src/* SidePanel */.hs, {
2392
+ }, /*#__PURE__*/react.createElement(Components_SidePanelWithServices, {
2290
2393
  side: "left",
2291
2394
  activeTabIndex: leftPanelDefaultClosed ? null : 0,
2292
2395
  tabs: leftPanelComponents,
2293
2396
  servicesManager: servicesManager
2294
2397
  })) : null, /*#__PURE__*/react.createElement("div", {
2295
- className: "flex flex-col flex-1 h-full"
2398
+ className: "flex h-full flex-1 flex-col"
2296
2399
  }, /*#__PURE__*/react.createElement("div", {
2297
- className: "flex items-center justify-center flex-1 h-full overflow-hidden bg-black relative"
2400
+ className: "relative flex h-full flex-1 items-center justify-center overflow-hidden bg-black"
2298
2401
  }, /*#__PURE__*/react.createElement(ui_src/* ErrorBoundary */.SV, {
2299
2402
  context: "Grid"
2300
2403
  }, /*#__PURE__*/react.createElement(ViewportGridComp, {
@@ -2303,7 +2406,7 @@ function ViewerLayout(_ref) {
2303
2406
  commandsManager: commandsManager
2304
2407
  })))), rightPanelComponents.length ? /*#__PURE__*/react.createElement(ui_src/* ErrorBoundary */.SV, {
2305
2408
  context: "Right Panel"
2306
- }, /*#__PURE__*/react.createElement(ui_src/* SidePanel */.hs, {
2409
+ }, /*#__PURE__*/react.createElement(Components_SidePanelWithServices, {
2307
2410
  side: "right",
2308
2411
  activeTabIndex: rightPanelDefaultClosed ? null : 0,
2309
2412
  tabs: rightPanelComponents,
@@ -2323,7 +2426,8 @@ ViewerLayout.propTypes = {
2323
2426
  leftPanelDefaultClosed: (prop_types_default()).bool.isRequired,
2324
2427
  rightPanelDefaultClosed: (prop_types_default()).bool.isRequired,
2325
2428
  /** Responsible for rendering our grid of viewports; provided by consuming application */
2326
- children: prop_types_default().oneOfType([(prop_types_default()).node, (prop_types_default()).func]).isRequired
2429
+ children: prop_types_default().oneOfType([(prop_types_default()).node, (prop_types_default()).func]).isRequired,
2430
+ viewports: (prop_types_default()).array
2327
2431
  };
2328
2432
  /* harmony default export */ const src_ViewerLayout = (ViewerLayout);
2329
2433
  ;// CONCATENATED MODULE: ../../../extensions/default/src/getLayoutTemplateModule.js
@@ -2364,6 +2468,7 @@ ViewerLayout.propTypes = {
2364
2468
 
2365
2469
 
2366
2470
 
2471
+
2367
2472
  const {
2368
2473
  sortStudyInstances,
2369
2474
  formatDate
@@ -2386,6 +2491,8 @@ function PanelStudyBrowser(_ref) {
2386
2491
  displaySetService,
2387
2492
  uiNotificationService
2388
2493
  } = servicesManager.services;
2494
+ const navigate = (0,dist/* useNavigate */.s0)();
2495
+
2389
2496
  // Normally you nest the components so the tree isn't so deep, and the data
2390
2497
  // doesn't have to have such an intense shape. This works well enough for now.
2391
2498
  // Tabs --> Studies --> DisplaySets --> Thumbnails
@@ -2393,7 +2500,7 @@ function PanelStudyBrowser(_ref) {
2393
2500
  StudyInstanceUIDs
2394
2501
  } = (0,ui_src/* useImageViewer */.zG)();
2395
2502
  const [{
2396
- activeViewportIndex,
2503
+ activeViewportId,
2397
2504
  viewports
2398
2505
  }, viewportGridService] = (0,ui_src/* useViewportGrid */.O_)();
2399
2506
  const [activeTabName, setActiveTabName] = (0,react.useState)('primary');
@@ -2401,12 +2508,11 @@ function PanelStudyBrowser(_ref) {
2401
2508
  const [studyDisplayList, setStudyDisplayList] = (0,react.useState)([]);
2402
2509
  const [displaySets, setDisplaySets] = (0,react.useState)([]);
2403
2510
  const [thumbnailImageSrcMap, setThumbnailImageSrcMap] = (0,react.useState)({});
2404
- const isMounted = (0,react.useRef)(true);
2405
2511
  const onDoubleClickThumbnailHandler = displaySetInstanceUID => {
2406
2512
  let updatedViewports = [];
2407
- const viewportIndex = activeViewportIndex;
2513
+ const viewportId = activeViewportId;
2408
2514
  try {
2409
- updatedViewports = hangingProtocolService.getViewportsRequireUpdate(viewportIndex, displaySetInstanceUID);
2515
+ updatedViewports = hangingProtocolService.getViewportsRequireUpdate(viewportId, displaySetInstanceUID);
2410
2516
  } catch (error) {
2411
2517
  console.warn(error);
2412
2518
  uiNotificationService.show({
@@ -2427,6 +2533,10 @@ function PanelStudyBrowser(_ref) {
2427
2533
  const qidoForStudyUID = await dataSource.query.studies.search({
2428
2534
  studyInstanceUid: StudyInstanceUID
2429
2535
  });
2536
+ if (!qidoForStudyUID?.length) {
2537
+ navigate('/notfoundstudy', '_self');
2538
+ throw new Error('Invalid study URL');
2539
+ }
2430
2540
  let qidoStudiesForPatient = qidoForStudyUID;
2431
2541
 
2432
2542
  // try to fetch the prior studies based on the patientID if the
@@ -2457,8 +2567,7 @@ function PanelStudyBrowser(_ref) {
2457
2567
  });
2458
2568
  }
2459
2569
  StudyInstanceUIDs.forEach(sid => fetchStudiesForPatient(sid));
2460
- // eslint-disable-next-line react-hooks/exhaustive-deps
2461
- }, [StudyInstanceUIDs, getStudiesForPatientByMRN]);
2570
+ }, [StudyInstanceUIDs, dataSource, getStudiesForPatientByMRN, navigate]);
2462
2571
 
2463
2572
  // // ~~ Initial Thumbnails
2464
2573
  (0,react.useEffect)(() => {
@@ -2470,24 +2579,19 @@ function PanelStudyBrowser(_ref) {
2470
2579
  const imageId = imageIds[Math.floor(imageIds.length / 2)];
2471
2580
 
2472
2581
  // TODO: Is it okay that imageIds are not returned here for SR displaySets?
2473
- if (imageId) {
2474
- // When the image arrives, render it and store the result in the thumbnailImgSrcMap
2475
- newImageSrcEntry[dSet.displaySetInstanceUID] = await getImageSrc(imageId);
2476
- if (isMounted.current) {
2477
- setThumbnailImageSrcMap(prevState => {
2478
- return {
2479
- ...prevState,
2480
- ...newImageSrcEntry
2481
- };
2482
- });
2483
- }
2582
+ if (!imageId || displaySet?.unsupported) {
2583
+ return;
2484
2584
  }
2585
+ // When the image arrives, render it and store the result in the thumbnailImgSrcMap
2586
+ newImageSrcEntry[dSet.displaySetInstanceUID] = await getImageSrc(imageId);
2587
+ setThumbnailImageSrcMap(prevState => {
2588
+ return {
2589
+ ...prevState,
2590
+ ...newImageSrcEntry
2591
+ };
2592
+ });
2485
2593
  });
2486
- return () => {
2487
- isMounted.current = false;
2488
- };
2489
- // eslint-disable-next-line react-hooks/exhaustive-deps
2490
- }, []);
2594
+ }, [StudyInstanceUIDs, dataSource, displaySetService, getImageSrc]);
2491
2595
 
2492
2596
  // ~~ displaySets
2493
2597
  (0,react.useEffect)(() => {
@@ -2496,50 +2600,59 @@ function PanelStudyBrowser(_ref) {
2496
2600
  const mappedDisplaySets = _mapDisplaySets(currentDisplaySets, thumbnailImageSrcMap);
2497
2601
  sortStudyInstances(mappedDisplaySets);
2498
2602
  setDisplaySets(mappedDisplaySets);
2499
- // eslint-disable-next-line react-hooks/exhaustive-deps
2500
- }, [thumbnailImageSrcMap]);
2603
+ }, [StudyInstanceUIDs, thumbnailImageSrcMap, displaySetService]);
2501
2604
 
2502
2605
  // ~~ subscriptions --> displaySets
2503
2606
  (0,react.useEffect)(() => {
2504
2607
  // DISPLAY_SETS_ADDED returns an array of DisplaySets that were added
2505
2608
  const SubscriptionDisplaySetsAdded = displaySetService.subscribe(displaySetService.EVENTS.DISPLAY_SETS_ADDED, data => {
2506
2609
  const {
2507
- displaySetsAdded
2610
+ displaySetsAdded,
2611
+ options
2508
2612
  } = data;
2509
2613
  displaySetsAdded.forEach(async dSet => {
2510
2614
  const newImageSrcEntry = {};
2511
2615
  const displaySet = displaySetService.getDisplaySetByUID(dSet.displaySetInstanceUID);
2616
+ if (displaySet?.unsupported) {
2617
+ return;
2618
+ }
2512
2619
  const imageIds = dataSource.getImageIdsForDisplaySet(displaySet);
2513
2620
  const imageId = imageIds[Math.floor(imageIds.length / 2)];
2514
2621
 
2515
2622
  // TODO: Is it okay that imageIds are not returned here for SR displaysets?
2516
- if (imageId) {
2517
- // When the image arrives, render it and store the result in the thumbnailImgSrcMap
2518
- newImageSrcEntry[dSet.displaySetInstanceUID] = await getImageSrc(imageId, dSet.initialViewport);
2519
- if (isMounted.current) {
2520
- setThumbnailImageSrcMap(prevState => {
2521
- return {
2522
- ...prevState,
2523
- ...newImageSrcEntry
2524
- };
2525
- });
2526
- }
2623
+ if (!imageId) {
2624
+ return;
2527
2625
  }
2528
- });
2529
- });
2530
-
2626
+ // When the image arrives, render it and store the result in the thumbnailImgSrcMap
2627
+ newImageSrcEntry[dSet.displaySetInstanceUID] = await getImageSrc(imageId, dSet.initialViewport);
2628
+ setThumbnailImageSrcMap(prevState => {
2629
+ return {
2630
+ ...prevState,
2631
+ ...newImageSrcEntry
2632
+ };
2633
+ });
2634
+ });
2635
+ });
2636
+ return () => {
2637
+ SubscriptionDisplaySetsAdded.unsubscribe();
2638
+ };
2639
+ }, [getImageSrc, dataSource, displaySetService]);
2640
+ (0,react.useEffect)(() => {
2531
2641
  // TODO: Will this always hold _all_ the displaySets we care about?
2532
2642
  // DISPLAY_SETS_CHANGED returns `DisplaySerService.activeDisplaySets`
2533
2643
  const SubscriptionDisplaySetsChanged = displaySetService.subscribe(displaySetService.EVENTS.DISPLAY_SETS_CHANGED, changedDisplaySets => {
2534
2644
  const mappedDisplaySets = _mapDisplaySets(changedDisplaySets, thumbnailImageSrcMap);
2535
2645
  setDisplaySets(mappedDisplaySets);
2536
2646
  });
2647
+ const SubscriptionDisplaySetMetaDataInvalidated = displaySetService.subscribe(displaySetService.EVENTS.DISPLAY_SET_SERIES_METADATA_INVALIDATED, () => {
2648
+ const mappedDisplaySets = _mapDisplaySets(displaySetService.getActiveDisplaySets(), thumbnailImageSrcMap);
2649
+ setDisplaySets(mappedDisplaySets);
2650
+ });
2537
2651
  return () => {
2538
- SubscriptionDisplaySetsAdded.unsubscribe();
2539
2652
  SubscriptionDisplaySetsChanged.unsubscribe();
2653
+ SubscriptionDisplaySetMetaDataInvalidated.unsubscribe();
2540
2654
  };
2541
- // eslint-disable-next-line react-hooks/exhaustive-deps
2542
- }, []);
2655
+ }, [StudyInstanceUIDs, thumbnailImageSrcMap, displaySetService]);
2543
2656
  const tabs = _createStudyBrowserTabs(StudyInstanceUIDs, studyDisplayList, displaySets);
2544
2657
 
2545
2658
  // TODO: Should not fire this on "close"
@@ -2554,7 +2667,7 @@ function PanelStudyBrowser(_ref) {
2554
2667
  requestDisplaySetCreationForStudy(displaySetService, StudyInstanceUID, madeInClient);
2555
2668
  }
2556
2669
  }
2557
- const activeDisplaySetInstanceUIDs = viewports[activeViewportIndex]?.displaySetInstanceUIDs;
2670
+ const activeDisplaySetInstanceUIDs = viewports.get(activeViewportId)?.displaySetInstanceUIDs;
2558
2671
  return /*#__PURE__*/react.createElement(ui_src/* StudyBrowser */.eX, {
2559
2672
  tabs: tabs,
2560
2673
  servicesManager: servicesManager,
@@ -2605,7 +2718,7 @@ function _mapDisplaySets(displaySets, thumbnailImageSrcMap) {
2605
2718
  const thumbnailNoImageDisplaySets = [];
2606
2719
  displaySets.filter(ds => !ds.excludeFromThumbnailBrowser).forEach(ds => {
2607
2720
  const imageSrc = thumbnailImageSrcMap[ds.displaySetInstanceUID];
2608
- const componentType = _getComponentType(ds.Modality);
2721
+ const componentType = _getComponentType(ds);
2609
2722
  const array = componentType === 'thumbnail' ? thumbnailDisplaySets : thumbnailNoImageDisplaySets;
2610
2723
  array.push({
2611
2724
  displaySetInstanceUID: ds.displaySetInstanceUID,
@@ -2617,21 +2730,23 @@ function _mapDisplaySets(displaySets, thumbnailImageSrcMap) {
2617
2730
  numInstances: ds.numImageFrames,
2618
2731
  countIcon: ds.countIcon,
2619
2732
  StudyInstanceUID: ds.StudyInstanceUID,
2733
+ messages: ds.messages,
2620
2734
  componentType,
2621
2735
  imageSrc,
2622
2736
  dragData: {
2623
2737
  type: 'displayset',
2624
2738
  displaySetInstanceUID: ds.displaySetInstanceUID
2625
2739
  // .. Any other data to pass
2626
- }
2740
+ },
2741
+
2742
+ isHydratedForDerivedDisplaySet: ds.isHydrated
2627
2743
  });
2628
2744
  });
2629
-
2630
2745
  return [...thumbnailDisplaySets, ...thumbnailNoImageDisplaySets];
2631
2746
  }
2632
2747
  const thumbnailNoImageModalities = ['SR', 'SEG', 'SM', 'RTSTRUCT', 'RTPLAN', 'RTDOSE'];
2633
- function _getComponentType(Modality) {
2634
- if (thumbnailNoImageModalities.includes(Modality)) {
2748
+ function _getComponentType(ds) {
2749
+ if (thumbnailNoImageModalities.includes(ds.Modality) || ds?.unsupported) {
2635
2750
  // TODO probably others.
2636
2751
  return 'thumbnailNoImage';
2637
2752
  }
@@ -2798,7 +2913,7 @@ function ActionButtons(_ref) {
2798
2913
  const {
2799
2914
  t
2800
2915
  } = (0,es/* useTranslation */.$G)('MeasurementTable');
2801
- return /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement(ui_src/* ButtonGroup */.hE, {
2916
+ return /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement(ui_src/* LegacyButtonGroup */.HO, {
2802
2917
  color: "black",
2803
2918
  size: "inherit"
2804
2919
  }, /*#__PURE__*/react.createElement(ui_src/* LegacyButton */.mN, {
@@ -2819,17 +2934,16 @@ ActionButtons.defaultProps = {
2819
2934
  };
2820
2935
  /* harmony default export */ const Panels_ActionButtons = (ActionButtons);
2821
2936
  // EXTERNAL MODULE: ../../../node_modules/lodash.debounce/index.js
2822
- var lodash_debounce = __webpack_require__(40001);
2937
+ var lodash_debounce = __webpack_require__(8324);
2823
2938
  var lodash_debounce_default = /*#__PURE__*/__webpack_require__.n(lodash_debounce);
2824
2939
  ;// CONCATENATED MODULE: ../../../extensions/default/src/Panels/createReportDialogPrompt.tsx
2825
- /* eslint-disable react/display-name */
2826
2940
 
2827
2941
 
2828
2942
  const CREATE_REPORT_DIALOG_RESPONSE = {
2829
2943
  CANCEL: 0,
2830
2944
  CREATE_REPORT: 1
2831
2945
  };
2832
- function createReportDialogPrompt(uiDialogService, _ref) {
2946
+ function CreateReportDialogPrompt(uiDialogService, _ref) {
2833
2947
  let {
2834
2948
  extensionManager
2835
2949
  } = _ref;
@@ -2906,11 +3020,11 @@ function createReportDialogPrompt(uiDialogService, _ref) {
2906
3020
  actions: [{
2907
3021
  id: 'cancel',
2908
3022
  text: 'Cancel',
2909
- type: ui_src/* ButtonEnums.type */.LZ.U.secondary
3023
+ type: ui_src/* ButtonEnums.type */.LZ.dt.secondary
2910
3024
  }, {
2911
3025
  id: 'save',
2912
3026
  text: 'Save',
2913
- type: ui_src/* ButtonEnums.type */.LZ.U.primary
3027
+ type: ui_src/* ButtonEnums.type */.LZ.dt.primary
2914
3028
  }],
2915
3029
  // TODO: Should be on button press...
2916
3030
  onSubmit: _handleFormSubmit,
@@ -2937,9 +3051,11 @@ function createReportDialogPrompt(uiDialogService, _ref) {
2937
3051
  });
2938
3052
  }
2939
3053
  };
2940
- return /*#__PURE__*/react.createElement(react.Fragment, null, dataSourcesOpts.length > 1 && /*#__PURE__*/react.createElement(ui_src/* Select */.Ph, {
3054
+ return /*#__PURE__*/react.createElement(react.Fragment, null, dataSourcesOpts.length > 1 && window.config?.allowMultiSelectExport && /*#__PURE__*/react.createElement("div", null, /*#__PURE__*/react.createElement("label", {
3055
+ className: "text-[14px] leading-[1.2] text-white"
3056
+ }, "Data Source"), /*#__PURE__*/react.createElement(ui_src/* Select */.Ph, {
2941
3057
  closeMenuOnSelect: true,
2942
- className: "mr-2 bg-black border-primary-main",
3058
+ className: "border-primary-main mt-2 bg-black",
2943
3059
  options: dataSourcesOpts,
2944
3060
  placeholder: dataSourcesOpts.find(option => option.value === value.dataSourceName).placeHolder,
2945
3061
  value: value.dataSourceName,
@@ -2950,17 +3066,19 @@ function createReportDialogPrompt(uiDialogService, _ref) {
2950
3066
  }));
2951
3067
  },
2952
3068
  isClearable: false
2953
- }), /*#__PURE__*/react.createElement(ui_src/* Input */.II, {
3069
+ })), /*#__PURE__*/react.createElement("div", {
3070
+ className: "mt-3"
3071
+ }, /*#__PURE__*/react.createElement(ui_src/* Input */.II, {
2954
3072
  autoFocus: true,
2955
3073
  label: "Enter the report name",
2956
3074
  labelClassName: "text-white text-[14px] leading-[1.2]",
2957
- className: "bg-black border-primary-main",
3075
+ className: "border-primary-main bg-black",
2958
3076
  type: "text",
2959
3077
  value: value.label,
2960
3078
  onChange: onChangeHandler,
2961
3079
  onKeyPress: onKeyPressHandler,
2962
3080
  required: true
2963
- }));
3081
+ })));
2964
3082
  }
2965
3083
  }
2966
3084
  });
@@ -2973,12 +3091,13 @@ function createReportDialogPrompt(uiDialogService, _ref) {
2973
3091
  /**
2974
3092
  *
2975
3093
  * @param {*} servicesManager
2976
- * @param {*} dataSource
2977
- * @param {*} measurements
2978
- * @param {*} options
2979
- * @returns {string[]} displaySetInstanceUIDs
2980
3094
  */
2981
- async function createReportAsync(servicesManager, commandsManager, dataSource, measurements, options) {
3095
+ async function createReportAsync(_ref) {
3096
+ let {
3097
+ servicesManager,
3098
+ getReport,
3099
+ reportType = 'measurement'
3100
+ } = _ref;
2982
3101
  const {
2983
3102
  displaySetService,
2984
3103
  uiNotificationService,
@@ -2988,32 +3107,27 @@ async function createReportAsync(servicesManager, commandsManager, dataSource, m
2988
3107
  showOverlay: true,
2989
3108
  isDraggable: false,
2990
3109
  centralize: true,
2991
- // TODO: Create a loading indicator component + zeplin design?
2992
3110
  content: Loading
2993
3111
  });
2994
3112
  try {
2995
- const naturalizedReport = await commandsManager.runCommand('storeMeasurements', {
2996
- measurementData: measurements,
2997
- dataSource,
2998
- additionalFindingTypes: ['ArrowAnnotate'],
2999
- options
3000
- }, 'CORNERSTONE_STRUCTURED_REPORT');
3113
+ const naturalizedReport = await getReport();
3001
3114
 
3002
3115
  // The "Mode" route listens for DicomMetadataStore changes
3003
3116
  // When a new instance is added, it listens and
3004
3117
  // automatically calls makeDisplaySets
3005
3118
  src.DicomMetadataStore.addInstances([naturalizedReport], true);
3006
- const displaySetInstanceUID = displaySetService.getMostRecentDisplaySet();
3119
+ const displaySet = displaySetService.getMostRecentDisplaySet();
3120
+ const displaySetInstanceUID = displaySet.displaySetInstanceUID;
3007
3121
  uiNotificationService.show({
3008
3122
  title: 'Create Report',
3009
- message: 'Measurements saved successfully',
3123
+ message: `${reportType} saved successfully`,
3010
3124
  type: 'success'
3011
3125
  });
3012
3126
  return [displaySetInstanceUID];
3013
3127
  } catch (error) {
3014
3128
  uiNotificationService.show({
3015
3129
  title: 'Create Report',
3016
- message: error.message || 'Failed to store measurements',
3130
+ message: error.message || `Failed to store ${reportType}`,
3017
3131
  type: 'error'
3018
3132
  });
3019
3133
  } finally {
@@ -3105,7 +3219,7 @@ function PanelMeasurementTable(_ref) {
3105
3219
  } = _ref;
3106
3220
  const [viewportGrid, viewportGridService] = (0,ui_src/* useViewportGrid */.O_)();
3107
3221
  const {
3108
- activeViewportIndex,
3222
+ activeViewportId,
3109
3223
  viewports
3110
3224
  } = viewportGrid;
3111
3225
  const {
@@ -3148,7 +3262,7 @@ function PanelMeasurementTable(_ref) {
3148
3262
  }
3149
3263
  async function createReport() {
3150
3264
  // filter measurements that are added to the active study
3151
- const activeViewport = viewports[activeViewportIndex];
3265
+ const activeViewport = viewports.get(activeViewportId);
3152
3266
  const measurements = measurementService.getMeasurements();
3153
3267
  const displaySet = displaySetService.getDisplaySetByUID(activeViewport.displaySetInstanceUIDs[0]);
3154
3268
  const trackedMeasurements = measurements.filter(m => displaySet.StudyInstanceUID === m.referenceStudyUID);
@@ -3161,7 +3275,7 @@ function PanelMeasurementTable(_ref) {
3161
3275
  });
3162
3276
  return;
3163
3277
  }
3164
- const promptResult = await createReportDialogPrompt(uiDialogService, {
3278
+ const promptResult = await CreateReportDialogPrompt(uiDialogService, {
3165
3279
  extensionManager
3166
3280
  });
3167
3281
  if (promptResult.action === CREATE_REPORT_DIALOG_RESPONSE.CREATE_REPORT) {
@@ -3172,10 +3286,21 @@ function PanelMeasurementTable(_ref) {
3172
3286
  promptResult.value === undefined || promptResult.value === '' ? 'Research Derived Series' // default
3173
3287
  : promptResult.value; // provided value
3174
3288
 
3175
- // Re-use an existing series having the same series description to avoid
3289
+ // Reuse an existing series having the same series description to avoid
3176
3290
  // creating too many series instances.
3177
3291
  const options = findSRWithSameSeriesDescription(SeriesDescription, displaySetService);
3178
- return Actions_createReportAsync(servicesManager, commandsManager, dataSource, trackedMeasurements, options);
3292
+ const getReport = async () => {
3293
+ return commandsManager.runCommand('storeMeasurements', {
3294
+ measurementData: trackedMeasurements,
3295
+ dataSource,
3296
+ additionalFindingTypes: ['ArrowAnnotate'],
3297
+ options
3298
+ }, 'CORNERSTONE_STRUCTURED_REPORT');
3299
+ };
3300
+ return Actions_createReportAsync({
3301
+ servicesManager,
3302
+ getReport
3303
+ });
3179
3304
  }
3180
3305
  }
3181
3306
  const jumpToImage = _ref2 => {
@@ -3183,7 +3308,7 @@ function PanelMeasurementTable(_ref) {
3183
3308
  uid,
3184
3309
  isActive
3185
3310
  } = _ref2;
3186
- measurementService.jumpToMeasurement(viewportGrid.activeViewportIndex, uid);
3311
+ measurementService.jumpToMeasurement(viewportGrid.activeViewportId, uid);
3187
3312
  onMeasurementItemClickHandler({
3188
3313
  uid,
3189
3314
  isActive
@@ -3255,7 +3380,7 @@ function PanelMeasurementTable(_ref) {
3255
3380
  labelClassName: "text-white text-[14px] leading-[1.2]",
3256
3381
  autoFocus: true,
3257
3382
  id: "annotation",
3258
- className: "bg-black border-primary-main",
3383
+ className: "border-primary-main bg-black",
3259
3384
  type: "text",
3260
3385
  value: value.label,
3261
3386
  onChange: onChangeHandler,
@@ -3265,11 +3390,11 @@ function PanelMeasurementTable(_ref) {
3265
3390
  actions: [{
3266
3391
  id: 'cancel',
3267
3392
  text: 'Cancel',
3268
- type: ui_src/* ButtonEnums.type */.LZ.U.secondary
3393
+ type: ui_src/* ButtonEnums.type */.LZ.dt.secondary
3269
3394
  }, {
3270
3395
  id: 'save',
3271
3396
  text: 'Save',
3272
- type: ui_src/* ButtonEnums.type */.LZ.U.primary
3397
+ type: ui_src/* ButtonEnums.type */.LZ.dt.primary
3273
3398
  }],
3274
3399
  onSubmit: onSubmitHandler
3275
3400
  }
@@ -3289,7 +3414,7 @@ function PanelMeasurementTable(_ref) {
3289
3414
  }
3290
3415
  };
3291
3416
  return /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement("div", {
3292
- className: "overflow-x-hidden overflow-y-auto ohif-scrollbar",
3417
+ className: "ohif-scrollbar overflow-y-auto overflow-x-hidden",
3293
3418
  "data-cy": 'measurements-panel'
3294
3419
  }, /*#__PURE__*/react.createElement(ui_src/* MeasurementTable */.wt, {
3295
3420
  title: "Measurements",
@@ -3316,7 +3441,7 @@ function _getMappedMeasurements(measurementService) {
3316
3441
 
3317
3442
  /**
3318
3443
  * Map the measurements to the display text.
3319
- * Adds finding and site inforamtion to the displayText and/or label,
3444
+ * Adds finding and site information to the displayText and/or label,
3320
3445
  * and provides as 'displayText' and 'label', while providing the original
3321
3446
  * values as baseDisplayText and baseLabel
3322
3447
  */
@@ -3336,7 +3461,9 @@ function _mapMeasurementToDisplay(measurement, index, types) {
3336
3461
  if (findingSites) {
3337
3462
  const siteText = [];
3338
3463
  findingSites.forEach(site => {
3339
- if (site?.text !== label) siteText.push(site.text);
3464
+ if (site?.text !== label) {
3465
+ siteText.push(site.text);
3466
+ }
3340
3467
  });
3341
3468
  displayText = [...siteText, ...displayText];
3342
3469
  }
@@ -3360,6 +3487,7 @@ function _mapMeasurementToDisplay(measurement, index, types) {
3360
3487
 
3361
3488
 
3362
3489
 
3490
+
3363
3491
  ;// CONCATENATED MODULE: ../../../extensions/default/src/getPanelModule.tsx
3364
3492
 
3365
3493
 
@@ -3384,7 +3512,7 @@ function getPanelModule(_ref) {
3384
3512
  };
3385
3513
  return [{
3386
3514
  name: 'seriesList',
3387
- iconName: 'group-layers',
3515
+ iconName: 'tab-studies',
3388
3516
  iconLabel: 'Studies',
3389
3517
  label: 'Studies',
3390
3518
  component: Panels_WrappedPanelStudyBrowser.bind(null, {
@@ -3403,25 +3531,340 @@ function getPanelModule(_ref) {
3403
3531
  }
3404
3532
  /* harmony default export */ const src_getPanelModule = (getPanelModule);
3405
3533
  // EXTERNAL MODULE: ../../core/src/utils/isImage.js
3406
- var isImage = __webpack_require__(81596);
3534
+ var isImage = __webpack_require__(11835);
3407
3535
  // EXTERNAL MODULE: ../../core/src/utils/sopClassDictionary.js
3408
- var sopClassDictionary = __webpack_require__(32746);
3536
+ var sopClassDictionary = __webpack_require__(24369);
3409
3537
  // EXTERNAL MODULE: ../../core/src/classes/ImageSet.ts
3410
- var ImageSet = __webpack_require__(4797);
3538
+ var ImageSet = __webpack_require__(13950);
3411
3539
  // EXTERNAL MODULE: ../../core/src/utils/isDisplaySetReconstructable.js
3412
- var isDisplaySetReconstructable = __webpack_require__(51227);
3540
+ var isDisplaySetReconstructable = __webpack_require__(89359);
3413
3541
  ;// CONCATENATED MODULE: ../../../extensions/default/package.json
3414
3542
  const package_namespaceObject = JSON.parse('{"u2":"@ohif/extension-default"}');
3415
3543
  ;// CONCATENATED MODULE: ../../../extensions/default/src/id.js
3416
3544
 
3417
3545
  const id = package_namespaceObject.u2;
3418
3546
 
3547
+ // EXTERNAL MODULE: ../../core/src/utils/sortInstancesByPosition.ts
3548
+ var sortInstancesByPosition = __webpack_require__(87425);
3549
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/utils/validations/checkMultiframe.ts
3550
+
3551
+
3552
+
3553
+ /**
3554
+ * Check various multi frame issues. It calls OHIF core functions
3555
+ * @param {*} multiFrameInstance
3556
+ * @param {*} warnings
3557
+ */
3558
+ function checkMultiFrame(multiFrameInstance, messages) {
3559
+ if (!(0,isDisplaySetReconstructable/* hasPixelMeasurements */.hu)(multiFrameInstance)) {
3560
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.MULTIFRAME_NO_PIXEL_MEASUREMENTS);
3561
+ }
3562
+ if (!(0,isDisplaySetReconstructable/* hasOrientation */.sb)(multiFrameInstance)) {
3563
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.MULTIFRAME_NO_ORIENTATION);
3564
+ }
3565
+ if (!(0,isDisplaySetReconstructable/* hasPosition */.kN)(multiFrameInstance)) {
3566
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.MULTIFRAME_NO_POSITION_INFORMATION);
3567
+ }
3568
+ }
3569
+ // EXTERNAL MODULE: ../../core/src/utils/toNumber.js
3570
+ var toNumber = __webpack_require__(94972);
3571
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/utils/validations/areAllImageDimensionsEqual.ts
3572
+
3573
+
3574
+ /**
3575
+ * Check if the frames in a series has different dimensions
3576
+ * @param {*} instances
3577
+ * @returns
3578
+ */
3579
+ function areAllImageDimensionsEqual(instances) {
3580
+ if (!instances?.length) {
3581
+ return false;
3582
+ }
3583
+ const firstImage = instances[0];
3584
+ const firstImageRows = (0,toNumber/* default */.Z)(firstImage.Rows);
3585
+ const firstImageColumns = (0,toNumber/* default */.Z)(firstImage.Columns);
3586
+ for (let i = 1; i < instances.length; i++) {
3587
+ const instance = instances[i];
3588
+ const {
3589
+ Rows,
3590
+ Columns
3591
+ } = instance;
3592
+ if (Rows !== firstImageRows || Columns !== firstImageColumns) {
3593
+ return false;
3594
+ }
3595
+ }
3596
+ return true;
3597
+ }
3598
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/utils/validations/areAllImageComponentsEqual.ts
3599
+
3600
+
3601
+ /**
3602
+ * Check if all voxels in series images has same number of components (samplesPerPixel)
3603
+ * @param {*} instances
3604
+ * @returns
3605
+ */
3606
+ function areAllImageComponentsEqual(instances) {
3607
+ if (!instances?.length) {
3608
+ return false;
3609
+ }
3610
+ const firstImage = instances[0];
3611
+ const firstImageSamplesPerPixel = (0,toNumber/* default */.Z)(firstImage.SamplesPerPixel);
3612
+ for (let i = 1; i < instances.length; i++) {
3613
+ const instance = instances[i];
3614
+ const {
3615
+ SamplesPerPixel
3616
+ } = instance;
3617
+ if (SamplesPerPixel !== firstImageSamplesPerPixel) {
3618
+ return false;
3619
+ }
3620
+ }
3621
+ return true;
3622
+ }
3623
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/utils/validations/areAllImageOrientationsEqual.ts
3624
+
3625
+
3626
+
3627
+ /**
3628
+ * Check is the series has frames with different orientations
3629
+ * @param {*} instances
3630
+ * @returns
3631
+ */
3632
+ function areAllImageOrientationsEqual(instances) {
3633
+ if (!instances?.length) {
3634
+ return false;
3635
+ }
3636
+ const firstImage = instances[0];
3637
+ const firstImageOrientationPatient = (0,toNumber/* default */.Z)(firstImage.ImageOrientationPatient);
3638
+ for (let i = 1; i < instances.length; i++) {
3639
+ const instance = instances[i];
3640
+ const imageOrientationPatient = (0,toNumber/* default */.Z)(instance.ImageOrientationPatient);
3641
+ if (!(0,isDisplaySetReconstructable/* _isSameOrientation */.NB)(imageOrientationPatient, firstImageOrientationPatient)) {
3642
+ return false;
3643
+ }
3644
+ }
3645
+ return true;
3646
+ }
3647
+ // EXTERNAL MODULE: ../../../node_modules/gl-matrix/esm/index.js + 10 modules
3648
+ var esm = __webpack_require__(45451);
3649
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/utils/calculateScanAxisNormal.ts
3650
+
3651
+
3652
+ /**
3653
+ * Calculates the scanAxisNormal based on a image orientation vector extract from a frame
3654
+ * @param {*} imageOrientation
3655
+ * @returns
3656
+ */
3657
+ function calculateScanAxisNormal(imageOrientation) {
3658
+ const rowCosineVec = esm/* vec3.fromValues */.R3.fromValues(imageOrientation[0], imageOrientation[1], imageOrientation[2]);
3659
+ const colCosineVec = esm/* vec3.fromValues */.R3.fromValues(imageOrientation[3], imageOrientation[4], imageOrientation[5]);
3660
+ return esm/* vec3.cross */.R3.cross(esm/* vec3.create */.R3.create(), rowCosineVec, colCosineVec);
3661
+ }
3662
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/utils/validations/areAllImagePositionsEqual.ts
3663
+
3664
+
3665
+
3666
+
3667
+
3668
+ /**
3669
+ * Checks if there is a position shift between consecutive frames
3670
+ * @param {*} previousPosition
3671
+ * @param {*} actualPosition
3672
+ * @param {*} scanAxisNormal
3673
+ * @param {*} averageSpacingBetweenFrames
3674
+ * @returns
3675
+ */
3676
+ function _checkSeriesPositionShift(previousPosition, actualPosition, scanAxisNormal, averageSpacingBetweenFrames) {
3677
+ // predicted position should be the previous position added by the multiplication
3678
+ // of the scanAxisNormal and the average spacing between frames
3679
+ const predictedPosition = esm/* vec3.scaleAndAdd */.R3.scaleAndAdd(esm/* vec3.create */.R3.create(), previousPosition, scanAxisNormal, averageSpacingBetweenFrames);
3680
+ return esm/* vec3.distance */.R3.distance(actualPosition, predictedPosition) > averageSpacingBetweenFrames;
3681
+ }
3682
+
3683
+ /**
3684
+ * Checks if a series has position shifts between consecutive frames
3685
+ * @param {*} instances
3686
+ * @returns
3687
+ */
3688
+ function areAllImagePositionsEqual(instances) {
3689
+ if (!instances?.length) {
3690
+ return false;
3691
+ }
3692
+ const firstImageOrientationPatient = (0,toNumber/* default */.Z)(instances[0].ImageOrientationPatient);
3693
+ if (!firstImageOrientationPatient) {
3694
+ return false;
3695
+ }
3696
+ const scanAxisNormal = calculateScanAxisNormal(firstImageOrientationPatient);
3697
+ const firstImagePositionPatient = (0,toNumber/* default */.Z)(instances[0].ImagePositionPatient);
3698
+ const lastIpp = (0,toNumber/* default */.Z)(instances[instances.length - 1].ImagePositionPatient);
3699
+ const averageSpacingBetweenFrames = (0,isDisplaySetReconstructable/* _getPerpendicularDistance */.Xn)(firstImagePositionPatient, lastIpp) / (instances.length - 1);
3700
+ let previousImagePositionPatient = firstImagePositionPatient;
3701
+ for (let i = 1; i < instances.length; i++) {
3702
+ const instance = instances[i];
3703
+ const imagePositionPatient = (0,toNumber/* default */.Z)(instance.ImagePositionPatient);
3704
+ if (_checkSeriesPositionShift(previousImagePositionPatient, imagePositionPatient, scanAxisNormal, averageSpacingBetweenFrames)) {
3705
+ return false;
3706
+ }
3707
+ previousImagePositionPatient = imagePositionPatient;
3708
+ }
3709
+ return true;
3710
+ }
3711
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/utils/validations/areAllImageSpacingEqual.ts
3712
+
3713
+
3714
+
3715
+ /**
3716
+ * Checks if series has spacing issues
3717
+ * @param {*} instances
3718
+ * @param {*} warnings
3719
+ */
3720
+ function areAllImageSpacingEqual(instances, messages) {
3721
+ if (!instances?.length) {
3722
+ return;
3723
+ }
3724
+ const firstImagePositionPatient = (0,toNumber/* default */.Z)(instances[0].ImagePositionPatient);
3725
+ if (!firstImagePositionPatient) {
3726
+ return;
3727
+ }
3728
+ const lastIpp = (0,toNumber/* default */.Z)(instances[instances.length - 1].ImagePositionPatient);
3729
+ const averageSpacingBetweenFrames = (0,isDisplaySetReconstructable/* _getPerpendicularDistance */.Xn)(firstImagePositionPatient, lastIpp) / (instances.length - 1);
3730
+ let previousImagePositionPatient = firstImagePositionPatient;
3731
+ const issuesFound = [];
3732
+ for (let i = 1; i < instances.length; i++) {
3733
+ const instance = instances[i];
3734
+ const imagePositionPatient = (0,toNumber/* default */.Z)(instance.ImagePositionPatient);
3735
+ const spacingBetweenFrames = (0,isDisplaySetReconstructable/* _getPerpendicularDistance */.Xn)(imagePositionPatient, previousImagePositionPatient);
3736
+ const spacingIssue = (0,isDisplaySetReconstructable/* _getSpacingIssue */.bg)(spacingBetweenFrames, averageSpacingBetweenFrames);
3737
+ if (spacingIssue) {
3738
+ const issue = spacingIssue.issue;
3739
+
3740
+ // avoid multiple warning of the same thing
3741
+ if (!issuesFound.includes(issue)) {
3742
+ issuesFound.push(issue);
3743
+ if (issue === isDisplaySetReconstructable/* reconstructionIssues */.e1.MISSING_FRAMES) {
3744
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.MISSING_FRAMES);
3745
+ } else if (issue === isDisplaySetReconstructable/* reconstructionIssues */.e1.IRREGULAR_SPACING) {
3746
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.IRREGULAR_SPACING);
3747
+ }
3748
+ }
3749
+ // we just want to find issues not how many
3750
+ if (issuesFound.length > 1) {
3751
+ break;
3752
+ }
3753
+ }
3754
+ previousImagePositionPatient = imagePositionPatient;
3755
+ }
3756
+ }
3757
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/utils/validations/checkSingleFrames.ts
3758
+
3759
+
3760
+
3761
+
3762
+
3763
+
3764
+
3765
+ /**
3766
+ * Runs various checks in a single frame series
3767
+ * @param {*} instances
3768
+ * @param {*} warnings
3769
+ */
3770
+ function checkSingleFrames(instances, messages) {
3771
+ if (instances.length > 2) {
3772
+ if (!areAllImageDimensionsEqual(instances)) {
3773
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.INCONSISTENT_DIMENSIONS);
3774
+ }
3775
+ if (!areAllImageComponentsEqual(instances)) {
3776
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.INCONSISTENT_COMPONENTS);
3777
+ }
3778
+ if (!areAllImageOrientationsEqual(instances)) {
3779
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.INCONSISTENT_ORIENTATIONS);
3780
+ }
3781
+ if (!areAllImagePositionsEqual(instances)) {
3782
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.INCONSISTENT_POSITION_INFORMATION);
3783
+ }
3784
+ areAllImageSpacingEqual(instances, messages);
3785
+ }
3786
+ }
3787
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/getDisplaySetMessages.ts
3788
+
3789
+
3790
+
3791
+
3792
+
3793
+ /**
3794
+ * Checks if a series is reconstructable to a 3D volume.
3795
+ *
3796
+ * @param {Object[]} instances An array of `OHIFInstanceMetadata` objects.
3797
+ */
3798
+ function getDisplaySetMessages(instances, isReconstructable) {
3799
+ const messages = new src/* DisplaySetMessageList */.iK();
3800
+ if (!instances.length) {
3801
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.NO_VALID_INSTANCES);
3802
+ return;
3803
+ }
3804
+ const firstInstance = instances[0];
3805
+ const {
3806
+ Modality,
3807
+ ImageType,
3808
+ NumberOfFrames
3809
+ } = firstInstance;
3810
+ // Due to current requirements, LOCALIZER series doesn't have any messages
3811
+ if (ImageType?.includes('LOCALIZER')) {
3812
+ return messages;
3813
+ }
3814
+ if (!isDisplaySetReconstructable/* constructableModalities */.M6.includes(Modality)) {
3815
+ return messages;
3816
+ }
3817
+ const isMultiframe = NumberOfFrames > 1;
3818
+ // Can't reconstruct if all instances don't have the ImagePositionPatient.
3819
+ if (!isMultiframe && !instances.every(instance => instance.ImagePositionPatient)) {
3820
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.NO_POSITION_INFORMATION);
3821
+ }
3822
+ const sortedInstances = (0,sortInstancesByPosition/* default */.Z)(instances);
3823
+ isMultiframe ? checkMultiFrame(sortedInstances[0], messages) : checkSingleFrames(sortedInstances, messages);
3824
+ if (!isReconstructable) {
3825
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.NOT_RECONSTRUCTABLE);
3826
+ }
3827
+ return messages;
3828
+ }
3829
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/getDisplaySetsFromUnsupportedSeries.js
3830
+
3831
+
3832
+ /**
3833
+ * Default handler for a instance list with an unsupported sopClassUID
3834
+ */
3835
+ function getDisplaySetsFromUnsupportedSeries(instances) {
3836
+ const imageSet = new ImageSet/* default */.Z(instances);
3837
+ const messages = new src/* DisplaySetMessageList */.iK();
3838
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.UNSUPPORTED_DISPLAYSET);
3839
+ const instance = instances[0];
3840
+ imageSet.setAttributes({
3841
+ displaySetInstanceUID: imageSet.uid,
3842
+ // create a local alias for the imageSet UID
3843
+ SeriesDate: instance.SeriesDate,
3844
+ SeriesTime: instance.SeriesTime,
3845
+ SeriesInstanceUID: instance.SeriesInstanceUID,
3846
+ StudyInstanceUID: instance.StudyInstanceUID,
3847
+ SeriesNumber: instance.SeriesNumber || 0,
3848
+ FrameRate: instance.FrameTime,
3849
+ SOPClassUID: instance.SOPClassUID,
3850
+ SeriesDescription: instance.SeriesDescription || '',
3851
+ Modality: instance.Modality,
3852
+ numImageFrames: instances.length,
3853
+ unsupported: true,
3854
+ SOPClassHandlerId: 'unsupported',
3855
+ isReconstructable: false,
3856
+ messages
3857
+ });
3858
+ return [imageSet];
3859
+ }
3419
3860
  ;// CONCATENATED MODULE: ../../../extensions/default/src/getSopClassHandlerModule.js
3420
3861
 
3421
3862
 
3422
3863
 
3423
3864
 
3424
3865
 
3866
+
3867
+
3425
3868
  const sopClassHandlerName = 'stack';
3426
3869
  const isMultiFrame = instance => {
3427
3870
  return instance.NumberOfFrames > 1;
@@ -3432,9 +3875,9 @@ const makeDisplaySet = instances => {
3432
3875
  const {
3433
3876
  value: isReconstructable,
3434
3877
  averageSpacingBetweenFrames
3435
- } = (0,isDisplaySetReconstructable/* default */.Z)(instances);
3436
-
3878
+ } = (0,isDisplaySetReconstructable/* default */.ZP)(instances);
3437
3879
  // set appropriate attributes to image set...
3880
+ const messages = getDisplaySetMessages(instances, isReconstructable);
3438
3881
  imageSet.setAttributes({
3439
3882
  displaySetInstanceUID: imageSet.uid,
3440
3883
  // create a local alias for the imageSet UID
@@ -3452,6 +3895,7 @@ const makeDisplaySet = instances => {
3452
3895
  numImageFrames: instances.length,
3453
3896
  SOPClassHandlerId: `${id}.sopClassHandlerModule.${sopClassHandlerName}`,
3454
3897
  isReconstructable,
3898
+ messages,
3455
3899
  averageSpacingBetweenFrames: averageSpacingBetweenFrames || null
3456
3900
  });
3457
3901
 
@@ -3558,6 +4002,10 @@ function getSopClassHandlerModule() {
3558
4002
  name: sopClassHandlerName,
3559
4003
  sopClassUids,
3560
4004
  getDisplaySetsFromSeries
4005
+ }, {
4006
+ name: 'not-supported-display-sets-handler',
4007
+ sopClassUids: [],
4008
+ getDisplaySetsFromSeries: getDisplaySetsFromUnsupportedSeries
3561
4009
  }];
3562
4010
  }
3563
4011
  /* harmony default export */ const src_getSopClassHandlerModule = (getSopClassHandlerModule);
@@ -3565,44 +4013,53 @@ function getSopClassHandlerModule() {
3565
4013
 
3566
4014
  function ToolbarDivider() {
3567
4015
  return /*#__PURE__*/react.createElement("span", {
3568
- className: "self-center w-4 h-8 mx-2 border-l border-common-dark"
4016
+ className: "border-common-dark mx-2 h-8 w-4 self-center border-l"
3569
4017
  });
3570
4018
  }
3571
4019
  ;// CONCATENATED MODULE: ../../../extensions/default/src/Toolbar/ToolbarLayoutSelector.tsx
4020
+ function ToolbarLayoutSelector_extends() { ToolbarLayoutSelector_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 ToolbarLayoutSelector_extends.apply(this, arguments); }
3572
4021
 
3573
4022
 
3574
4023
 
3575
4024
 
3576
- function LayoutSelector(_ref) {
4025
+ function ToolbarLayoutSelectorWithServices(_ref) {
3577
4026
  let {
3578
- rows,
3579
- columns,
3580
- className,
3581
4027
  servicesManager,
3582
- ...rest
4028
+ ...props
3583
4029
  } = _ref;
3584
- const [isOpen, setIsOpen] = (0,react.useState)(false);
3585
4030
  const {
3586
- hangingProtocolService,
3587
4031
  toolbarService
3588
4032
  } = servicesManager.services;
4033
+ const onSelection = (0,react.useCallback)(props => {
4034
+ toolbarService.recordInteraction({
4035
+ interactionType: 'action',
4036
+ commands: [{
4037
+ commandName: 'setViewportGridLayout',
4038
+ commandOptions: {
4039
+ ...props
4040
+ },
4041
+ context: 'DEFAULT'
4042
+ }]
4043
+ });
4044
+ }, [toolbarService]);
4045
+ return /*#__PURE__*/react.createElement(LayoutSelector, ToolbarLayoutSelector_extends({}, props, {
4046
+ onSelection: onSelection
4047
+ }));
4048
+ }
4049
+ function LayoutSelector(_ref2) {
4050
+ let {
4051
+ rows,
4052
+ columns,
4053
+ className,
4054
+ onSelection,
4055
+ ...rest
4056
+ } = _ref2;
4057
+ const [isOpen, setIsOpen] = (0,react.useState)(false);
3589
4058
  const closeOnOutsideClick = () => {
3590
4059
  if (isOpen) {
3591
4060
  setIsOpen(false);
3592
4061
  }
3593
4062
  };
3594
- (0,react.useEffect)(() => {
3595
- const {
3596
- unsubscribe
3597
- } = hangingProtocolService.subscribe(hangingProtocolService.EVENTS.PROTOCOL_CHANGED, evt => {
3598
- const {
3599
- protocol
3600
- } = evt;
3601
- });
3602
- return () => {
3603
- unsubscribe();
3604
- };
3605
- }, [hangingProtocolService]);
3606
4063
  (0,react.useEffect)(() => {
3607
4064
  window.addEventListener('click', closeOnOutsideClick);
3608
4065
  return () => {
@@ -3611,18 +4068,6 @@ function LayoutSelector(_ref) {
3611
4068
  }, [isOpen]);
3612
4069
  const onInteractionHandler = () => setIsOpen(!isOpen);
3613
4070
  const DropdownContent = isOpen ? ui_src/* LayoutSelector */.OF : null;
3614
- const onSelectionHandler = props => {
3615
- toolbarService.recordInteraction({
3616
- interactionType: 'action',
3617
- commands: [{
3618
- commandName: 'setViewportGridLayout',
3619
- commandOptions: {
3620
- ...props
3621
- },
3622
- context: 'DEFAULT'
3623
- }]
3624
- });
3625
- };
3626
4071
  return /*#__PURE__*/react.createElement(ui_src/* ToolbarButton */.hA, {
3627
4072
  id: "Layout",
3628
4073
  label: "Grid Layout",
@@ -3633,7 +4078,7 @@ function LayoutSelector(_ref) {
3633
4078
  dropdownContent: DropdownContent !== null && /*#__PURE__*/react.createElement(DropdownContent, {
3634
4079
  rows: rows,
3635
4080
  columns: columns,
3636
- onSelection: onSelectionHandler
4081
+ onSelection: onSelection
3637
4082
  }),
3638
4083
  isActive: isOpen,
3639
4084
  type: "toggle"
@@ -3650,10 +4095,238 @@ LayoutSelector.defaultProps = {
3650
4095
  columns: 3,
3651
4096
  onLayoutChange: () => {}
3652
4097
  };
3653
- /* harmony default export */ const ToolbarLayoutSelector = (LayoutSelector);
3654
- ;// CONCATENATED MODULE: ../../../extensions/default/src/Toolbar/ToolbarSplitButton.tsx
4098
+ /* harmony default export */ const ToolbarLayoutSelector = (ToolbarLayoutSelectorWithServices);
4099
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/Toolbar/ToolbarSplitButtonWithServices.tsx
4100
+ function ToolbarSplitButtonWithServices_extends() { ToolbarSplitButtonWithServices_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 ToolbarSplitButtonWithServices_extends.apply(this, arguments); }
4101
+
4102
+
4103
+
4104
+
4105
+ function ToolbarSplitButtonWithServices(_ref) {
4106
+ let {
4107
+ isRadio,
4108
+ isAction,
4109
+ groupId,
4110
+ primary,
4111
+ secondary,
4112
+ items,
4113
+ renderer,
4114
+ onInteraction,
4115
+ servicesManager
4116
+ } = _ref;
4117
+ const {
4118
+ toolbarService
4119
+ } = servicesManager?.services;
4120
+ const handleItemClick = (item, index) => {
4121
+ const {
4122
+ id,
4123
+ type,
4124
+ commands
4125
+ } = item;
4126
+ onInteraction({
4127
+ groupId,
4128
+ itemId: id,
4129
+ interactionType: type,
4130
+ commands
4131
+ });
4132
+ setState(state => ({
4133
+ ...state,
4134
+ primary: !isAction && isRadio ? {
4135
+ ...item,
4136
+ index
4137
+ } : state.primary,
4138
+ isExpanded: false,
4139
+ items: getSplitButtonItems(items).filter(item => isRadio && !isAction ? item.index !== index : true)
4140
+ }));
4141
+ };
4142
+
4143
+ /* Bubbles up individual item clicks */
4144
+ const getSplitButtonItems = items => items.map((item, index) => ({
4145
+ ...item,
4146
+ index,
4147
+ onClick: () => handleItemClick(item, index)
4148
+ }));
4149
+ const [buttonsState, setButtonState] = (0,react.useState)({
4150
+ primaryToolId: '',
4151
+ toggles: {},
4152
+ groups: {}
4153
+ });
4154
+ const [state, setState] = (0,react.useState)({
4155
+ primary,
4156
+ items: getSplitButtonItems(items).filter(item => isRadio && !isAction ? item.id !== primary.id : true)
4157
+ });
4158
+ const {
4159
+ primaryToolId,
4160
+ toggles
4161
+ } = buttonsState;
4162
+ const isPrimaryToggle = state.primary.type === 'toggle';
4163
+ const isPrimaryActive = state.primary.type === 'tool' && primaryToolId === state.primary.id || isPrimaryToggle && toggles[state.primary.id] === true;
4164
+ const PrimaryButtonComponent = toolbarService?.getButtonComponentForUIType(state.primary.uiType) ?? ui_src/* ToolbarButton */.hA;
4165
+ (0,react.useEffect)(() => {
4166
+ const {
4167
+ unsubscribe
4168
+ } = toolbarService.subscribe(toolbarService.EVENTS.TOOL_BAR_STATE_MODIFIED, state => {
4169
+ setButtonState({
4170
+ ...state
4171
+ });
4172
+ });
4173
+ return () => {
4174
+ unsubscribe();
4175
+ };
4176
+ }, [toolbarService]);
4177
+ const updatedItems = state.items.map(item => {
4178
+ const isActive = item.type === 'tool' && primaryToolId === item.id;
4179
+
4180
+ // We could have added the
4181
+ // item.type === 'toggle' && toggles[item.id] === true
4182
+ // too but that makes the button active when the toggle is active under it
4183
+ // which feels weird
4184
+ return {
4185
+ ...item,
4186
+ isActive
4187
+ };
4188
+ });
4189
+ const DefaultListItemRenderer = _ref2 => {
4190
+ let {
4191
+ type,
4192
+ icon,
4193
+ label,
4194
+ t,
4195
+ id
4196
+ } = _ref2;
4197
+ const isActive = type === 'toggle' && toggles[id] === true;
4198
+ return /*#__PURE__*/react.createElement("div", {
4199
+ className: classnames_default()('hover:bg-primary-dark flex h-8 w-full flex-row items-center p-3', 'whitespace-pre text-base', isActive && 'bg-primary-dark', isActive ? 'text-[#348CFD]' : 'text-common-bright hover:bg-primary-dark hover:text-primary-light')
4200
+ }, icon && /*#__PURE__*/react.createElement("span", {
4201
+ className: "mr-4"
4202
+ }, /*#__PURE__*/react.createElement(ui_src/* Icon */.JO, {
4203
+ name: icon,
4204
+ className: "h-5 w-5"
4205
+ })), /*#__PURE__*/react.createElement("span", {
4206
+ className: "mr-5"
4207
+ }, t(label)));
4208
+ };
4209
+ const listItemRenderer = renderer || DefaultListItemRenderer;
4210
+ return /*#__PURE__*/react.createElement(ui_src/* SplitButton */.aW, {
4211
+ isRadio: isRadio,
4212
+ isAction: isAction,
4213
+ primary: state.primary,
4214
+ secondary: secondary,
4215
+ items: updatedItems,
4216
+ groupId: groupId,
4217
+ renderer: listItemRenderer,
4218
+ isActive: isPrimaryActive || updatedItems.some(item => item.isActive),
4219
+ isToggle: isPrimaryToggle,
4220
+ onInteraction: onInteraction,
4221
+ Component: props => /*#__PURE__*/react.createElement(PrimaryButtonComponent, ToolbarSplitButtonWithServices_extends({}, props, {
4222
+ servicesManager: servicesManager
4223
+ }))
4224
+ });
4225
+ }
4226
+ ToolbarSplitButtonWithServices.propTypes = {
4227
+ isRadio: (prop_types_default()).bool,
4228
+ isAction: (prop_types_default()).bool,
4229
+ groupId: (prop_types_default()).string,
4230
+ primary: prop_types_default().shape({
4231
+ id: (prop_types_default()).string.isRequired,
4232
+ type: prop_types_default().oneOf(['tool', 'action', 'toggle']).isRequired,
4233
+ uiType: (prop_types_default()).string
4234
+ }),
4235
+ secondary: prop_types_default().shape({
4236
+ id: (prop_types_default()).string,
4237
+ icon: (prop_types_default()).string.isRequired,
4238
+ label: (prop_types_default()).string,
4239
+ tooltip: (prop_types_default()).string.isRequired,
4240
+ isActive: (prop_types_default()).bool
4241
+ }),
4242
+ items: prop_types_default().arrayOf(prop_types_default().shape({
4243
+ id: (prop_types_default()).string.isRequired,
4244
+ type: prop_types_default().oneOf(['tool', 'action', 'toggle']).isRequired,
4245
+ icon: (prop_types_default()).string,
4246
+ label: (prop_types_default()).string,
4247
+ tooltip: (prop_types_default()).string
4248
+ })),
4249
+ renderer: (prop_types_default()).func,
4250
+ onInteraction: (prop_types_default()).func.isRequired,
4251
+ servicesManager: prop_types_default().shape({
4252
+ services: prop_types_default().shape({
4253
+ toolbarService: (prop_types_default()).object
4254
+ })
4255
+ })
4256
+ };
4257
+ ToolbarSplitButtonWithServices.defaultProps = {
4258
+ isRadio: false,
4259
+ isAction: false
4260
+ };
4261
+ /* harmony default export */ const Toolbar_ToolbarSplitButtonWithServices = (ToolbarSplitButtonWithServices);
4262
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/Toolbar/ToolbarButtonWithServices.tsx
4263
+ function ToolbarButtonWithServices_extends() { ToolbarButtonWithServices_extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return ToolbarButtonWithServices_extends.apply(this, arguments); }
3655
4264
 
3656
- /* harmony default export */ const ToolbarSplitButton = (ui_src/* SplitButton */.aW);
4265
+
4266
+
4267
+ function ToolbarButtonWithServices(_ref) {
4268
+ let {
4269
+ id,
4270
+ type,
4271
+ commands,
4272
+ onInteraction,
4273
+ servicesManager,
4274
+ ...props
4275
+ } = _ref;
4276
+ const {
4277
+ toolbarService
4278
+ } = servicesManager?.services || {};
4279
+ const [buttonsState, setButtonState] = (0,react.useState)({
4280
+ primaryToolId: '',
4281
+ toggles: {},
4282
+ groups: {}
4283
+ });
4284
+ const {
4285
+ primaryToolId
4286
+ } = buttonsState;
4287
+ const isActive = type === 'tool' && id === primaryToolId || type === 'toggle' && buttonsState.toggles[id] === true;
4288
+ (0,react.useEffect)(() => {
4289
+ const {
4290
+ unsubscribe
4291
+ } = toolbarService.subscribe(toolbarService.EVENTS.TOOL_BAR_STATE_MODIFIED, state => {
4292
+ setButtonState({
4293
+ ...state
4294
+ });
4295
+ });
4296
+ return () => {
4297
+ unsubscribe();
4298
+ };
4299
+ }, [toolbarService]);
4300
+ return /*#__PURE__*/react.createElement(ui_src/* ToolbarButton */.hA, ToolbarButtonWithServices_extends({
4301
+ commands: commands,
4302
+ id: id,
4303
+ type: type,
4304
+ isActive: isActive,
4305
+ onInteraction: onInteraction
4306
+ }, props));
4307
+ }
4308
+ ToolbarButtonWithServices.propTypes = {
4309
+ id: (prop_types_default()).string.isRequired,
4310
+ type: prop_types_default().oneOf(['tool', 'action', 'toggle']).isRequired,
4311
+ commands: prop_types_default().arrayOf(prop_types_default().shape({
4312
+ commandName: (prop_types_default()).string.isRequired,
4313
+ context: (prop_types_default()).string
4314
+ })),
4315
+ onInteraction: (prop_types_default()).func.isRequired,
4316
+ servicesManager: prop_types_default().shape({
4317
+ services: prop_types_default().shape({
4318
+ toolbarService: prop_types_default().shape({
4319
+ subscribe: (prop_types_default()).func.isRequired,
4320
+ state: prop_types_default().shape({
4321
+ primaryToolId: (prop_types_default()).string,
4322
+ toggles: prop_types_default().objectOf((prop_types_default()).bool),
4323
+ groups: prop_types_default().objectOf((prop_types_default()).object)
4324
+ }).isRequired
4325
+ }).isRequired
4326
+ }).isRequired
4327
+ }).isRequired
4328
+ };
4329
+ /* harmony default export */ const Toolbar_ToolbarButtonWithServices = (ToolbarButtonWithServices);
3657
4330
  ;// CONCATENATED MODULE: ../../../extensions/default/src/getToolbarModule.tsx
3658
4331
 
3659
4332
 
@@ -3670,15 +4343,15 @@ function getToolbarModule(_ref) {
3670
4343
  clickHandler: () => {}
3671
4344
  }, {
3672
4345
  name: 'ohif.action',
3673
- defaultComponent: ui_src/* ToolbarButton */.hA,
4346
+ defaultComponent: Toolbar_ToolbarButtonWithServices,
3674
4347
  clickHandler: () => {}
3675
4348
  }, {
3676
4349
  name: 'ohif.radioGroup',
3677
- defaultComponent: ui_src/* ToolbarButton */.hA,
4350
+ defaultComponent: Toolbar_ToolbarButtonWithServices,
3678
4351
  clickHandler: () => {}
3679
4352
  }, {
3680
4353
  name: 'ohif.splitButton',
3681
- defaultComponent: ToolbarSplitButton,
4354
+ defaultComponent: Toolbar_ToolbarSplitButtonWithServices,
3682
4355
  clickHandler: () => {}
3683
4356
  }, {
3684
4357
  name: 'ohif.layoutSelector',
@@ -3686,7 +4359,7 @@ function getToolbarModule(_ref) {
3686
4359
  clickHandler: (evt, clickedBtn, btnSectionName) => {}
3687
4360
  }, {
3688
4361
  name: 'ohif.toggle',
3689
- defaultComponent: ui_src/* ToolbarButton */.hA,
4362
+ defaultComponent: Toolbar_ToolbarButtonWithServices,
3690
4363
  clickHandler: () => {}
3691
4364
  }];
3692
4365
  }
@@ -3856,7 +4529,7 @@ function adaptItem(item, subProps) {
3856
4529
  return newItem;
3857
4530
  }
3858
4531
  // EXTERNAL MODULE: ../../ui/src/components/ContextMenu/ContextMenu.tsx
3859
- var ContextMenu = __webpack_require__(63021);
4532
+ var ContextMenu = __webpack_require__(5638);
3860
4533
  ;// CONCATENATED MODULE: ../../../extensions/default/src/CustomizableContextMenu/ContextMenuController.tsx
3861
4534
 
3862
4535
 
@@ -3917,8 +4590,8 @@ class ContextMenuController {
3917
4590
  defaultPosition: ContextMenuController._getDefaultPosition(defaultPointsPosition, event?.detail, viewportElement),
3918
4591
  event,
3919
4592
  content: ContextMenu/* default */.Z,
3920
- // This naming is part of hte uiDialogService convention
3921
- // Clicking outside simpy closes the dialog box.
4593
+ // This naming is part of the uiDialogService convention
4594
+ // Clicking outside simply closes the dialog box.
3922
4595
  onClickOutside: () => this.services.uiDialogService.dismiss({
3923
4596
  id: 'context-menu'
3924
4597
  }),
@@ -4065,10 +4738,10 @@ const defaultContextMenu = {
4065
4738
 
4066
4739
 
4067
4740
  // EXTERNAL MODULE: ../../../node_modules/moment/moment.js
4068
- var moment = __webpack_require__(53806);
4741
+ var moment = __webpack_require__(71271);
4069
4742
  var moment_default = /*#__PURE__*/__webpack_require__.n(moment);
4070
4743
  // EXTERNAL MODULE: ../../../node_modules/react-window/dist/index.esm.js
4071
- var index_esm = __webpack_require__(21767);
4744
+ var index_esm = __webpack_require__(94614);
4072
4745
  ;// CONCATENATED MODULE: ../../../extensions/default/src/DicomTagBrowser/DicomTagTable.tsx
4073
4746
 
4074
4747
 
@@ -4093,34 +4766,34 @@ function ColumnHeaders(_ref) {
4093
4766
  valueRef
4094
4767
  } = _ref;
4095
4768
  return /*#__PURE__*/react.createElement("div", {
4096
- className: classnames_default()('flex flex-row w-full bg-secondary-dark ohif-scrollbar overflow-y-scroll'),
4769
+ className: classnames_default()('bg-secondary-dark ohif-scrollbar flex w-full flex-row overflow-y-scroll'),
4097
4770
  style: rowVerticalPaddingStyle
4098
4771
  }, /*#__PURE__*/react.createElement("div", {
4099
- className: "px-3 w-4/24"
4772
+ className: "w-4/24 px-3"
4100
4773
  }, /*#__PURE__*/react.createElement("label", {
4101
4774
  ref: tagRef,
4102
- className: "flex flex-col flex-1 text-white text-lg pl-1 select-none"
4775
+ className: "flex flex-1 select-none flex-col pl-1 text-lg text-white"
4103
4776
  }, /*#__PURE__*/react.createElement("span", {
4104
4777
  className: "flex flex-row items-center focus:outline-none"
4105
4778
  }, "Tag"))), /*#__PURE__*/react.createElement("div", {
4106
- className: "px-3 w-2/24"
4779
+ className: "w-2/24 px-3"
4107
4780
  }, /*#__PURE__*/react.createElement("label", {
4108
4781
  ref: vrRef,
4109
- className: "flex flex-col flex-1 text-white text-lg pl-1 select-none"
4782
+ className: "flex flex-1 select-none flex-col pl-1 text-lg text-white"
4110
4783
  }, /*#__PURE__*/react.createElement("span", {
4111
4784
  className: "flex flex-row items-center focus:outline-none"
4112
4785
  }, "VR"))), /*#__PURE__*/react.createElement("div", {
4113
- className: "px-3 w-6/24"
4786
+ className: "w-6/24 px-3"
4114
4787
  }, /*#__PURE__*/react.createElement("label", {
4115
4788
  ref: keywordRef,
4116
- className: "flex flex-col flex-1 text-white text-lg pl-1 select-none"
4789
+ className: "flex flex-1 select-none flex-col pl-1 text-lg text-white"
4117
4790
  }, /*#__PURE__*/react.createElement("span", {
4118
4791
  className: "flex flex-row items-center focus:outline-none"
4119
4792
  }, "Keyword"))), /*#__PURE__*/react.createElement("div", {
4120
- className: "px-3 w-5/24 grow"
4793
+ className: "w-5/24 grow px-3"
4121
4794
  }, /*#__PURE__*/react.createElement("label", {
4122
4795
  ref: valueRef,
4123
- className: "flex flex-col flex-1 text-white text-lg pl-1 select-none"
4796
+ className: "flex flex-1 select-none flex-col pl-1 text-lg text-white"
4124
4797
  }, /*#__PURE__*/react.createElement("span", {
4125
4798
  className: "flex flex-row items-center focus:outline-none"
4126
4799
  }, "Value"))));
@@ -4193,16 +4866,16 @@ function DicomTagTable(_ref2) {
4193
4866
  ...style,
4194
4867
  ...rowStyle
4195
4868
  },
4196
- className: classnames_default()('hover:bg-secondary-main transition duration-300 bg-black flex flex-row w-full border-secondary-light items-center text-base break-all', lineHeightClassName),
4869
+ className: classnames_default()('hover:bg-secondary-main border-secondary-light flex w-full flex-row items-center break-all bg-black text-base transition duration-300', lineHeightClassName),
4197
4870
  key: `DICOMTagRow-${index}`
4198
4871
  }, /*#__PURE__*/react.createElement("div", {
4199
- className: "px-3 w-4/24"
4872
+ className: "w-4/24 px-3"
4200
4873
  }, row[0]), /*#__PURE__*/react.createElement("div", {
4201
- className: "px-3 w-2/24"
4874
+ className: "w-2/24 px-3"
4202
4875
  }, row[1]), /*#__PURE__*/react.createElement("div", {
4203
- className: "px-3 w-6/24"
4876
+ className: "w-6/24 px-3"
4204
4877
  }, row[2]), /*#__PURE__*/react.createElement("div", {
4205
- className: "px-3 w-5/24 grow"
4878
+ className: "w-5/24 grow px-3"
4206
4879
  }, row[3]));
4207
4880
  }, [rows]);
4208
4881
 
@@ -4240,7 +4913,7 @@ function DicomTagTable(_ref2) {
4240
4913
  keywordRef: keywordRef,
4241
4914
  valueRef: valueRef
4242
4915
  }), /*#__PURE__*/react.createElement("div", {
4243
- className: "m-auto relative border-2 border-black bg-black",
4916
+ className: "relative m-auto border-2 border-black bg-black",
4244
4917
  style: {
4245
4918
  height: '32rem'
4246
4919
  }
@@ -4266,7 +4939,6 @@ function DicomTagTable(_ref2) {
4266
4939
 
4267
4940
 
4268
4941
 
4269
-
4270
4942
  const {
4271
4943
  ImageSet: DicomTagBrowser_ImageSet
4272
4944
  } = src.classes;
@@ -4295,7 +4967,6 @@ const DicomTagBrowser = _ref => {
4295
4967
  setSelectedDisplaySetInstanceUID(value.value);
4296
4968
  setInstanceNumber(1);
4297
4969
  };
4298
- const searchInputRef = (0,react.useRef)(null);
4299
4970
  const activeDisplaySet = displaySets.find(ds => ds.displaySetInstanceUID === selectedDisplaySetInstanceUID);
4300
4971
  const isImageStack = _isImageStack(activeDisplaySet);
4301
4972
  const showInstanceList = isImageStack && activeDisplaySet.images.length > 1;
@@ -4361,14 +5032,14 @@ const DicomTagBrowser = _ref => {
4361
5032
  return /*#__PURE__*/react.createElement("div", {
4362
5033
  className: "dicom-tag-browser-content"
4363
5034
  }, /*#__PURE__*/react.createElement("div", {
4364
- className: "flex flex-row mb-6 items-center pl-1"
5035
+ className: "mb-6 flex flex-row items-center pl-1"
4365
5036
  }, /*#__PURE__*/react.createElement("div", {
4366
- className: "flex flex-row items-center w-1/2"
5037
+ className: "flex w-1/2 flex-row items-center"
4367
5038
  }, /*#__PURE__*/react.createElement(ui_src/* Typography */.ZT, {
4368
5039
  variant: "subtitle",
4369
5040
  className: "mr-4"
4370
5041
  }, "Series"), /*#__PURE__*/react.createElement("div", {
4371
- className: "grow mr-8"
5042
+ className: "mr-8 grow"
4372
5043
  }, /*#__PURE__*/react.createElement(ui_src/* Select */.Ph, {
4373
5044
  id: "display-set-selector",
4374
5045
  isClearable: false,
@@ -4377,7 +5048,7 @@ const DicomTagBrowser = _ref => {
4377
5048
  value: displaySetList.find(ds => ds.value === selectedDisplaySetInstanceUID),
4378
5049
  className: "text-white"
4379
5050
  }))), /*#__PURE__*/react.createElement("div", {
4380
- className: "flex flex-row items-center w-1/2"
5051
+ className: "flex w-1/2 flex-row items-center"
4381
5052
  }, showInstanceList && /*#__PURE__*/react.createElement(ui_src/* Typography */.ZT, {
4382
5053
  variant: "subtitle",
4383
5054
  className: "mr-4"
@@ -4396,32 +5067,14 @@ const DicomTagBrowser = _ref => {
4396
5067
  labelPosition: "left",
4397
5068
  trackColor: '#3a3f99'
4398
5069
  })))), /*#__PURE__*/react.createElement("div", {
4399
- className: "w-full h-1 bg-black"
5070
+ className: "h-1 w-full bg-black"
4400
5071
  }), /*#__PURE__*/react.createElement("div", {
4401
- className: "flex flex-row my-3 w-1/2"
4402
- }, /*#__PURE__*/react.createElement("label", {
4403
- className: "relative block w-full mr-8"
4404
- }, /*#__PURE__*/react.createElement("span", {
4405
- className: "absolute inset-y-0 left-0 flex items-center pl-2"
4406
- }, /*#__PURE__*/react.createElement(ui_src/* Icon */.JO, {
4407
- name: "icon-search"
4408
- })), /*#__PURE__*/react.createElement("input", {
4409
- ref: searchInputRef,
4410
- type: "text",
4411
- className: "block bg-black w-full shadow transition duration-300 appearance-none border border-inputfield-main focus:border-inputfield-focus focus:outline-none disabled:border-inputfield-disabled rounded w-full py-2 px-9 text-base leading-tight placeholder:text-inputfield-placeholder",
5072
+ className: "my-3 flex w-1/2 flex-row"
5073
+ }, /*#__PURE__*/react.createElement(ui_src/* InputFilterText */.Xe, {
5074
+ className: "mr-8 block w-full",
4412
5075
  placeholder: "Search metadata...",
4413
- onChange: event => debouncedSetFilterValue(event.target.value),
4414
- autoComplete: "off"
4415
- }), /*#__PURE__*/react.createElement("span", {
4416
- className: "absolute inset-y-0 right-0 flex items-center pr-2"
4417
- }, /*#__PURE__*/react.createElement(ui_src/* Icon */.JO, {
4418
- name: "icon-clear-field",
4419
- className: classnames_default()('cursor-pointer', filterValue ? '' : 'hidden'),
4420
- onClick: () => {
4421
- searchInputRef.current.value = '';
4422
- debouncedSetFilterValue('');
4423
- }
4424
- })))), /*#__PURE__*/react.createElement(DicomTagBrowser_DicomTagTable, {
5076
+ onDebounceChange: setFilterValue
5077
+ })), /*#__PURE__*/react.createElement(DicomTagBrowser_DicomTagTable, {
4425
5078
  rows: filteredRows
4426
5079
  }));
4427
5080
  };
@@ -4594,61 +5247,62 @@ function _sortTagList(tagList) {
4594
5247
  */
4595
5248
  const reuseCachedLayout = (state, hangingProtocolService, syncService) => {
4596
5249
  const {
4597
- activeViewportIndex,
4598
- viewports,
4599
- layout
5250
+ activeViewportId
4600
5251
  } = state;
5252
+ const {
5253
+ protocol
5254
+ } = hangingProtocolService.getActiveProtocol();
4601
5255
  const hpInfo = hangingProtocolService.getState();
4602
5256
  const {
4603
5257
  protocolId,
4604
5258
  stageIndex,
4605
5259
  activeStudyUID
4606
5260
  } = hpInfo;
4607
- const {
4608
- protocol
4609
- } = hangingProtocolService.getActiveProtocol();
4610
- const stage = protocol.stages[stageIndex];
4611
- const storeId = `${activeStudyUID}:${protocolId}:${stageIndex}`;
4612
5261
  const syncState = syncService.getState();
4613
- const cacheId = `${activeStudyUID}:${protocolId}`;
4614
5262
  const viewportGridStore = {
4615
5263
  ...syncState.viewportGridStore
4616
5264
  };
4617
- const hangingProtocolStageIndexMap = {
4618
- ...syncState.hangingProtocolStageIndexMap
4619
- };
4620
5265
  const displaySetSelectorMap = {
4621
5266
  ...syncState.displaySetSelectorMap
4622
5267
  };
5268
+ const stage = protocol.stages[stageIndex];
5269
+ const storeId = `${activeStudyUID}:${protocolId}:${stageIndex}`;
5270
+ const cacheId = `${activeStudyUID}:${protocolId}`;
5271
+ const hangingProtocolStageIndexMap = {
5272
+ ...syncState.hangingProtocolStageIndexMap
5273
+ };
4623
5274
  const {
4624
5275
  rows,
4625
5276
  columns
4626
5277
  } = stage.viewportStructure.properties;
4627
- const custom = stage.viewports.length !== state.viewports.length || state.layout.numRows !== rows || state.layout.numCols !== columns;
5278
+ const custom = stage.viewports.length !== state.viewports.size || state.layout.numRows !== rows || state.layout.numCols !== columns;
4628
5279
  hangingProtocolStageIndexMap[cacheId] = hpInfo;
4629
5280
  if (storeId && custom) {
4630
5281
  viewportGridStore[storeId] = {
4631
5282
  ...state
4632
5283
  };
4633
5284
  }
4634
- for (let idx = 0; idx < state.viewports.length; idx++) {
4635
- const viewport = state.viewports[idx];
5285
+ state.viewports.forEach((viewport, viewportId) => {
4636
5286
  const {
4637
5287
  displaySetOptions,
4638
5288
  displaySetInstanceUIDs
4639
5289
  } = viewport;
4640
- if (!displaySetOptions) continue;
5290
+ if (!displaySetOptions) {
5291
+ return;
5292
+ }
4641
5293
  for (let i = 0; i < displaySetOptions.length; i++) {
4642
5294
  const displaySetUID = displaySetInstanceUIDs[i];
4643
- if (!displaySetUID) continue;
4644
- if (idx === activeViewportIndex && i === 0) {
5295
+ if (!displaySetUID) {
5296
+ continue;
5297
+ }
5298
+ if (viewportId === activeViewportId && i === 0) {
4645
5299
  displaySetSelectorMap[`${activeStudyUID}:activeDisplaySet:0`] = displaySetUID;
4646
5300
  }
4647
5301
  if (displaySetOptions[i]?.id) {
4648
5302
  displaySetSelectorMap[`${activeStudyUID}:${displaySetOptions[i].id}:${displaySetOptions[i].matchedDisplaySetsIndex || 0}`] = displaySetUID;
4649
5303
  }
4650
5304
  }
4651
- }
5305
+ });
4652
5306
  return {
4653
5307
  hangingProtocolStageIndexMap,
4654
5308
  viewportGridStore,
@@ -4660,22 +5314,24 @@ const reuseCachedLayout = (state, hangingProtocolService, syncService) => {
4660
5314
  /**
4661
5315
  * This find or create viewport is paired with the reduce results from
4662
5316
  * below, and the action of this viewport is to look for previously filled
4663
- * viewports, and to re-use by position id. If there is no filled viewport,
5317
+ * viewports, and to reuse by position id. If there is no filled viewport,
4664
5318
  * then one can be re-used from the display set if it isn't going to be displayed.
4665
5319
  * @param hangingProtocolService - bound parameter supplied before using this
4666
5320
  * @param viewportsByPosition - bound parameter supplied before using this
4667
- * @param viewportIndex - the index to retrieve
5321
+ * @param position - the position in the grid to retrieve
4668
5322
  * @param positionId - the current position on screen to retrieve
4669
5323
  * @param options - the set of options used, so that subsequent calls can
4670
5324
  * store state that is reset by the setLayout.
4671
5325
  * This class uses the options to store the already viewed
4672
5326
  * display sets, filling it initially with the pre-existing viewports.
4673
5327
  */
4674
- const findViewportsByPosition_findOrCreateViewport = (hangingProtocolService, viewportsByPosition, viewportIndex, positionId, options) => {
5328
+ const findViewportsByPosition_findOrCreateViewport = (hangingProtocolService, viewportsByPosition, position, positionId, options) => {
4675
5329
  const byPositionViewport = viewportsByPosition?.[positionId];
4676
- if (byPositionViewport) return {
4677
- ...byPositionViewport
4678
- };
5330
+ if (byPositionViewport) {
5331
+ return {
5332
+ ...byPositionViewport
5333
+ };
5334
+ }
4679
5335
  const {
4680
5336
  protocolId,
4681
5337
  stageIndex
@@ -4723,7 +5379,7 @@ const findViewportsByPosition = (state, _ref, syncService) => {
4723
5379
  ...syncState.viewportsByPosition
4724
5380
  };
4725
5381
  const initialInDisplay = [];
4726
- for (const viewport of viewports) {
5382
+ viewports.forEach(viewport => {
4727
5383
  if (viewport.positionId) {
4728
5384
  const storedViewport = {
4729
5385
  ...viewport,
@@ -4732,16 +5388,11 @@ const findViewportsByPosition = (state, _ref, syncService) => {
4732
5388
  }
4733
5389
  };
4734
5390
  viewportsByPosition[viewport.positionId] = storedViewport;
4735
- // The cache doesn't store the viewport options - it is only useful
4736
- // for remembering the type of viewport and UIDs
4737
- delete storedViewport.viewportId;
4738
- delete storedViewport.viewportOptions.viewportId;
4739
5391
  }
4740
- }
5392
+ });
4741
5393
  for (let row = 0; row < numRows; row++) {
4742
5394
  for (let col = 0; col < numCols; col++) {
4743
- const pos = col + row * numCols;
4744
- const positionId = viewports?.[pos]?.positionId || `${col}-${row}`;
5395
+ const positionId = `${col}-${row}`;
4745
5396
  const viewport = viewportsByPosition[positionId];
4746
5397
  if (viewport?.displaySetInstanceUIDs) {
4747
5398
  initialInDisplay.push(...viewport.displaySetInstanceUIDs);
@@ -4756,8 +5407,8 @@ const findViewportsByPosition = (state, _ref, syncService) => {
4756
5407
  };
4757
5408
  };
4758
5409
  /* harmony default export */ const src_findViewportsByPosition = (findViewportsByPosition);
4759
- // EXTERNAL MODULE: ./index.js + 32 modules
4760
- var index = __webpack_require__(97837);
5410
+ // EXTERNAL MODULE: ./index.js + 33 modules
5411
+ var index = __webpack_require__(59754);
4761
5412
  ;// CONCATENATED MODULE: ../../../extensions/default/src/commandsModule.ts
4762
5413
 
4763
5414
 
@@ -4859,7 +5510,9 @@ const commandsModule = _ref => {
4859
5510
  stage
4860
5511
  } = hangingProtocolService.getActiveProtocol();
4861
5512
  const enableListener = button => {
4862
- if (!button.id) return;
5513
+ if (!button.id) {
5514
+ return;
5515
+ }
4863
5516
  const {
4864
5517
  commands,
4865
5518
  items
@@ -4868,14 +5521,16 @@ const commandsModule = _ref => {
4868
5521
  items.forEach(enableListener);
4869
5522
  }
4870
5523
  const hpCommand = commands?.find?.(isHangingProtocolCommand);
4871
- if (!hpCommand) return;
5524
+ if (!hpCommand) {
5525
+ return;
5526
+ }
4872
5527
  const {
4873
5528
  protocolId,
4874
5529
  stageIndex,
4875
5530
  stageId
4876
5531
  } = hpCommand.commandOptions;
4877
5532
  const isActive = (!protocolId || protocolId === protocol.id) && (stageIndex === undefined || stageIndex === toggleStageIndex) && (!stageId || stageId === stage.id);
4878
- toolbarService.setActive(button.id, isActive);
5533
+ toolbarService.setToggled(button.id, isActive);
4879
5534
  };
4880
5535
  Object.values(toolbarService.getButtons()).forEach(enableListener);
4881
5536
  },
@@ -4911,6 +5566,7 @@ const commandsModule = _ref => {
4911
5566
  stageIndex,
4912
5567
  reset = false
4913
5568
  } = _ref3;
5569
+ const primaryToolBeforeHPChange = toolbarService.getActivePrimaryTool();
4914
5570
  try {
4915
5571
  // Stores in the state the display set selector id to displaySetUID mapping
4916
5572
  // Pass in viewportId for the active viewport. This item will get set as
@@ -4927,7 +5583,7 @@ const commandsModule = _ref => {
4927
5583
  displaySetSelectorMap
4928
5584
  } = stateSyncReduce;
4929
5585
  if (!protocolId) {
4930
- // Re-use the previous protocol id, and optionally stage
5586
+ // Reuse the previous protocol id, and optionally stage
4931
5587
  protocolId = hpInfo.protocolId;
4932
5588
  if (stageId === undefined && stageIndex === undefined) {
4933
5589
  stageIndex = hpInfo.stageIndex;
@@ -4970,22 +5626,44 @@ const commandsModule = _ref => {
4970
5626
  delete displaySetSelectorMap[`${activeStudyUID || hpInfo.activeStudyUID}:activeDisplaySet:0`];
4971
5627
  stateSyncService.store(stateSyncReduce);
4972
5628
  // This is a default action applied
4973
- actions.toggleHpTools(hangingProtocolService.getActiveProtocol());
5629
+ const {
5630
+ protocol
5631
+ } = hangingProtocolService.getActiveProtocol();
5632
+ actions.toggleHpTools();
5633
+
5634
+ // try to use the same tool in the new hanging protocol stage
5635
+ const primaryButton = toolbarService.getButton(primaryToolBeforeHPChange);
5636
+ if (primaryButton) {
5637
+ // is there any type of interaction on this button, if not it might be in the
5638
+ // items. This is a bit of a hack, but it works for now.
5639
+
5640
+ let interactionType = primaryButton.props?.interactionType;
5641
+ if (!interactionType && primaryButton.props?.items) {
5642
+ const firstItem = primaryButton.props.items[0];
5643
+ interactionType = firstItem.props?.interactionType || firstItem.props?.type;
5644
+ }
5645
+ if (interactionType) {
5646
+ toolbarService.recordInteraction({
5647
+ interactionType,
5648
+ ...primaryButton.props
5649
+ });
5650
+ }
5651
+ }
5652
+
4974
5653
  // Send the notification about updating the state
4975
5654
  if (protocolId !== hpInfo.protocolId) {
4976
- const {
4977
- protocol
4978
- } = hangingProtocolService.getActiveProtocol();
4979
5655
  // The old protocol callbacks are used for turning off things
4980
5656
  // like crosshairs when moving to the new HP
4981
5657
  commandsManager.run(oldProtocol.callbacks?.onProtocolExit);
4982
5658
  // The new protocol callback is used for things like
4983
5659
  // activating modes etc.
4984
- commandsManager.run(protocol.callbacks?.onProtocolEnter);
4985
5660
  }
5661
+
5662
+ commandsManager.run(protocol.callbacks?.onProtocolEnter);
4986
5663
  return true;
4987
5664
  } catch (e) {
4988
- actions.toggleHpTools(hangingProtocolService.getActiveProtocol());
5665
+ console.error(e);
5666
+ actions.toggleHpTools();
4989
5667
  uiNotificationService.show({
4990
5668
  title: 'Apply Hanging Protocol',
4991
5669
  message: 'The hanging protocol could not be applied.',
@@ -5098,7 +5776,7 @@ const commandsModule = _ref => {
5098
5776
  toggleOneUp() {
5099
5777
  const viewportGridState = viewportGridService.getState();
5100
5778
  const {
5101
- activeViewportIndex,
5779
+ activeViewportId,
5102
5780
  viewports,
5103
5781
  layout
5104
5782
  } = viewportGridState;
@@ -5106,7 +5784,7 @@ const commandsModule = _ref => {
5106
5784
  displaySetInstanceUIDs,
5107
5785
  displaySetOptions,
5108
5786
  viewportOptions
5109
- } = viewports[activeViewportIndex];
5787
+ } = viewports.get(activeViewportId);
5110
5788
  if (layout.numCols === 1 && layout.numRows === 1) {
5111
5789
  // The viewer is in one-up. Check if there is a state to restore/toggle back to.
5112
5790
  const {
@@ -5117,24 +5795,33 @@ const commandsModule = _ref => {
5117
5795
  }
5118
5796
  // There is a state to toggle back to. The viewport that was
5119
5797
  // originally toggled to one up was the former active viewport.
5120
- const viewportIndexToUpdate = toggleOneUpViewportGridStore.activeViewportIndex;
5798
+ const viewportIdToUpdate = toggleOneUpViewportGridStore.activeViewportId;
5121
5799
 
5122
- // Determine which viewports need to be updated. This is particularly
5123
- // important when MPR is toggled to one up and a different reconstructable
5124
- // is swapped in. Note that currently HangingProtocolService.getViewportsRequireUpdate
5125
- // does not support viewport with multiple display sets.
5126
- const updatedViewports = displaySetInstanceUIDs.length > 1 ? [] : displaySetInstanceUIDs.map(displaySetInstanceUID => hangingProtocolService.getViewportsRequireUpdate(viewportIndexToUpdate, displaySetInstanceUID)).flat();
5800
+ // We are restoring the previous layout but taking into the account that
5801
+ // the current one up viewport might have a new displaySet dragged and dropped on it.
5802
+ // updatedViewportsViaHP below contains the viewports applicable to the HP that existed
5803
+ // prior to the toggle to one-up - including the updated viewports if a display
5804
+ // set swap were to have occurred.
5805
+ const updatedViewportsViaHP = displaySetInstanceUIDs.length > 1 ? [] : displaySetInstanceUIDs.map(displaySetInstanceUID => hangingProtocolService.getViewportsRequireUpdate(viewportIdToUpdate, displaySetInstanceUID)).flat();
5127
5806
 
5128
- // This findOrCreateViewport returns either one of the updatedViewports
5807
+ // findOrCreateViewport returns either one of the updatedViewportsViaHP
5129
5808
  // returned from the HP service OR if there is not one from the HP service then
5130
- // simply returns what was in the previous state.
5131
- const findOrCreateViewport = viewportIndex => {
5132
- const viewport = updatedViewports.find(viewport => viewport.viewportIndex === viewportIndex);
5133
- return viewport ? {
5809
+ // simply returns what was in the previous state for a given position in the layout.
5810
+ const findOrCreateViewport = (position, positionId) => {
5811
+ // Find the viewport for the given position prior to the toggle to one-up.
5812
+ const preOneUpViewport = Array.from(toggleOneUpViewportGridStore.viewports.values()).find(viewport => viewport.positionId === positionId);
5813
+
5814
+ // Use the viewport id from before the toggle to one-up to find any updates to the viewport.
5815
+ const viewport = updatedViewportsViaHP.find(viewport => viewport.viewportId === preOneUpViewport.viewportId);
5816
+ return viewport ?
5817
+ // Use the applicable viewport from the HP updated viewports
5818
+ {
5134
5819
  viewportOptions,
5135
5820
  displaySetOptions,
5136
5821
  ...viewport
5137
- } : toggleOneUpViewportGridStore.viewports[viewportIndex];
5822
+ } :
5823
+ // Use the previous viewport for the given position
5824
+ preOneUpViewport;
5138
5825
  };
5139
5826
  const layoutOptions = viewportGridService.getLayoutOptionsFromState(toggleOneUpViewportGridStore);
5140
5827
 
@@ -5142,7 +5829,7 @@ const commandsModule = _ref => {
5142
5829
  viewportGridService.setLayout({
5143
5830
  numRows: toggleOneUpViewportGridStore.layout.numRows,
5144
5831
  numCols: toggleOneUpViewportGridStore.layout.numCols,
5145
- activeViewportIndex: viewportIndexToUpdate,
5832
+ activeViewportId: viewportIdToUpdate,
5146
5833
  layoutOptions,
5147
5834
  findOrCreateViewport
5148
5835
  });
@@ -5207,10 +5894,10 @@ const commandsModule = _ref => {
5207
5894
  },
5208
5895
  openDICOMTagViewer() {
5209
5896
  const {
5210
- activeViewportIndex,
5897
+ activeViewportId,
5211
5898
  viewports
5212
5899
  } = viewportGridService.getState();
5213
- const activeViewportSpecificData = viewports[activeViewportIndex];
5900
+ const activeViewportSpecificData = viewports.get(activeViewportId);
5214
5901
  const {
5215
5902
  displaySetInstanceUIDs
5216
5903
  } = activeViewportSpecificData;
@@ -5242,13 +5929,10 @@ const commandsModule = _ref => {
5242
5929
  },
5243
5930
  scrollActiveThumbnailIntoView: () => {
5244
5931
  const {
5245
- activeViewportIndex,
5932
+ activeViewportId,
5246
5933
  viewports
5247
5934
  } = viewportGridService.getState();
5248
- if (!viewports || activeViewportIndex < 0 || activeViewportIndex > viewports.length - 1) {
5249
- return;
5250
- }
5251
- const activeViewport = viewports[activeViewportIndex];
5935
+ const activeViewport = viewports.get(activeViewportId);
5252
5936
  const activeDisplaySetInstanceUID = activeViewport.displaySetInstanceUIDs[0];
5253
5937
  const thumbnailList = document.querySelector('#ohif-thumbnail-list');
5254
5938
  if (!thumbnailList) {
@@ -5282,12 +5966,12 @@ const commandsModule = _ref => {
5282
5966
  const currentDisplaySets = [...displaySetService.activeDisplaySets];
5283
5967
  currentDisplaySets.sort(dsSortFn);
5284
5968
  const {
5285
- activeViewportIndex,
5969
+ activeViewportId,
5286
5970
  viewports
5287
5971
  } = viewportGridService.getState();
5288
5972
  const {
5289
5973
  displaySetInstanceUIDs
5290
- } = viewports[activeViewportIndex];
5974
+ } = viewports.get(activeViewportId);
5291
5975
  const activeDisplaySetIndex = currentDisplaySets.findIndex(displaySet => displaySetInstanceUIDs.includes(displaySet.displaySetInstanceUID));
5292
5976
  let displaySetIndexToShow;
5293
5977
  for (displaySetIndexToShow = activeDisplaySetIndex + direction; displaySetIndexToShow > -1 && displaySetIndexToShow < currentDisplaySets.length; displaySetIndexToShow += direction) {
@@ -5303,7 +5987,7 @@ const commandsModule = _ref => {
5303
5987
  } = currentDisplaySets[displaySetIndexToShow];
5304
5988
  let updatedViewports = [];
5305
5989
  try {
5306
- updatedViewports = hangingProtocolService.getViewportsRequireUpdate(activeViewportIndex, displaySetInstanceUID);
5990
+ updatedViewports = hangingProtocolService.getViewportsRequireUpdate(activeViewportId, displaySetInstanceUID);
5307
5991
  } catch (error) {
5308
5992
  console.warn(error);
5309
5993
  uiNotificationService.show({
@@ -5397,7 +6081,6 @@ const commandsModule = _ref => {
5397
6081
  * It is not included in the viewer mode by default.
5398
6082
  */
5399
6083
  const hpMN = {
5400
- hasUpdatedPriorsInformation: false,
5401
6084
  id: '@ohif/mnGrid',
5402
6085
  description: 'Has various hanging protocol grid layouts',
5403
6086
  name: '2x2',
@@ -5418,7 +6101,8 @@ const hpMN = {
5418
6101
  greaterThan: {
5419
6102
  value: 0
5420
6103
  }
5421
- }
6104
+ },
6105
+ required: true
5422
6106
  },
5423
6107
  // This display set will select the specified items by preference
5424
6108
  // It has no affect if nothing is specified in the URL.
@@ -5606,15 +6290,182 @@ const hpMN = {
5606
6290
  numberOfPriorsReferenced: -1
5607
6291
  };
5608
6292
  /* harmony default export */ const hpMNGrid = (hpMN);
6293
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/hpCompare.ts
6294
+ const defaultDisplaySetSelector = {
6295
+ studyMatchingRules: [{
6296
+ // The priorInstance is a study counter that indicates what position this study is in
6297
+ // and the value comes from the options parameter.
6298
+ attribute: 'studyInstanceUIDsIndex',
6299
+ from: 'options',
6300
+ required: true,
6301
+ constraint: {
6302
+ equals: {
6303
+ value: 0
6304
+ }
6305
+ }
6306
+ }],
6307
+ seriesMatchingRules: [{
6308
+ attribute: 'numImageFrames',
6309
+ constraint: {
6310
+ greaterThan: {
6311
+ value: 0
6312
+ }
6313
+ }
6314
+ },
6315
+ // This display set will select the specified items by preference
6316
+ // It has no affect if nothing is specified in the URL.
6317
+ {
6318
+ attribute: 'isDisplaySetFromUrl',
6319
+ weight: 10,
6320
+ constraint: {
6321
+ equals: true
6322
+ }
6323
+ }]
6324
+ };
6325
+ const priorDisplaySetSelector = {
6326
+ studyMatchingRules: [{
6327
+ // The priorInstance is a study counter that indicates what position this study is in
6328
+ // and the value comes from the options parameter.
6329
+ attribute: 'studyInstanceUIDsIndex',
6330
+ from: 'options',
6331
+ required: true,
6332
+ constraint: {
6333
+ equals: {
6334
+ value: 1
6335
+ }
6336
+ }
6337
+ }],
6338
+ seriesMatchingRules: [{
6339
+ attribute: 'numImageFrames',
6340
+ constraint: {
6341
+ greaterThan: {
6342
+ value: 0
6343
+ }
6344
+ }
6345
+ },
6346
+ // This display set will select the specified items by preference
6347
+ // It has no affect if nothing is specified in the URL.
6348
+ {
6349
+ attribute: 'isDisplaySetFromUrl',
6350
+ weight: 10,
6351
+ constraint: {
6352
+ equals: true
6353
+ }
6354
+ }]
6355
+ };
6356
+ const currentDisplaySet = {
6357
+ id: 'defaultDisplaySetId'
6358
+ };
6359
+ const priorDisplaySet = {
6360
+ id: 'priorDisplaySetId'
6361
+ };
6362
+ const currentViewport0 = {
6363
+ viewportOptions: {
6364
+ toolGroupId: 'default',
6365
+ allowUnmatchedView: true
6366
+ },
6367
+ displaySets: [currentDisplaySet]
6368
+ };
6369
+ const currentViewport1 = {
6370
+ ...currentViewport0,
6371
+ displaySets: [{
6372
+ ...currentDisplaySet,
6373
+ matchedDisplaySetsIndex: 1
6374
+ }]
6375
+ };
6376
+ const priorViewport0 = {
6377
+ ...currentViewport0,
6378
+ displaySets: [priorDisplaySet]
6379
+ };
6380
+ const priorViewport1 = {
6381
+ ...priorViewport0,
6382
+ displaySets: [{
6383
+ ...priorDisplaySet,
6384
+ matchedDisplaySetsIndex: 1
6385
+ }]
6386
+ };
6387
+
6388
+ /**
6389
+ * This hanging protocol can be activated on the primary mode by directly
6390
+ * referencing it in a URL or by directly including it within a mode, e.g.:
6391
+ * `&hangingProtocolId=@ohif/mnGrid` added to the viewer URL
6392
+ * It is not included in the viewer mode by default.
6393
+ */
6394
+ const hpMNCompare = {
6395
+ id: '@ohif/hpCompare',
6396
+ description: 'Compare two studies in various layouts',
6397
+ name: 'Compare Two Studies',
6398
+ numberOfPriorsReferenced: 1,
6399
+ protocolMatchingRules: [{
6400
+ id: 'Two Studies',
6401
+ weight: 1000,
6402
+ attribute: 'StudyInstanceUID',
6403
+ // The 'from' attribute says where to get the 'attribute' value from. In this case
6404
+ // prior means the second study in the study list.
6405
+ from: 'prior',
6406
+ required: true,
6407
+ constraint: {
6408
+ notNull: true
6409
+ }
6410
+ }],
6411
+ toolGroupIds: ['default'],
6412
+ displaySetSelectors: {
6413
+ defaultDisplaySetId: defaultDisplaySetSelector,
6414
+ priorDisplaySetId: priorDisplaySetSelector
6415
+ },
6416
+ defaultViewport: {
6417
+ viewportOptions: {
6418
+ viewportType: 'stack',
6419
+ toolGroupId: 'default',
6420
+ allowUnmatchedView: true
6421
+ },
6422
+ displaySets: [{
6423
+ id: 'defaultDisplaySetId',
6424
+ matchedDisplaySetsIndex: -1
6425
+ }]
6426
+ },
6427
+ stages: [{
6428
+ name: '2x2',
6429
+ stageActivation: {
6430
+ enabled: {
6431
+ minViewportsMatched: 4
6432
+ }
6433
+ },
6434
+ viewportStructure: {
6435
+ layoutType: 'grid',
6436
+ properties: {
6437
+ rows: 2,
6438
+ columns: 2
6439
+ }
6440
+ },
6441
+ viewports: [currentViewport0, priorViewport0, currentViewport1, priorViewport1]
6442
+ }, {
6443
+ name: '2x1',
6444
+ stageActivation: {
6445
+ enabled: {
6446
+ minViewportsMatched: 2
6447
+ }
6448
+ },
6449
+ viewportStructure: {
6450
+ layoutType: 'grid',
6451
+ properties: {
6452
+ rows: 1,
6453
+ columns: 2
6454
+ }
6455
+ },
6456
+ viewports: [currentViewport0, priorViewport0]
6457
+ }]
6458
+ };
6459
+ /* harmony default export */ const hpCompare = (hpMNCompare);
5609
6460
  ;// CONCATENATED MODULE: ../../../extensions/default/src/getHangingProtocolModule.js
5610
6461
 
6462
+
5611
6463
  const defaultProtocol = {
5612
6464
  id: 'default',
5613
6465
  locked: true,
5614
6466
  // Don't store this hanging protocol as it applies to the currently active
5615
6467
  // display set by default
5616
6468
  // cacheId: null,
5617
- hasUpdatedPriorsInformation: false,
5618
6469
  name: 'Default',
5619
6470
  createdDate: '2021-02-23T19:22:08.894Z',
5620
6471
  modifiedDate: '2023-04-01',
@@ -5679,6 +6530,7 @@ const defaultProtocol = {
5679
6530
  viewports: [{
5680
6531
  viewportOptions: {
5681
6532
  viewportType: 'stack',
6533
+ viewportId: 'default',
5682
6534
  toolGroupId: 'default',
5683
6535
  // This will specify the initial image options index if it matches in the URL
5684
6536
  // and will otherwise not specify anything.
@@ -5708,6 +6560,11 @@ function getHangingProtocolModule() {
5708
6560
  {
5709
6561
  name: hpMNGrid.id,
5710
6562
  protocol: hpMNGrid
6563
+ },
6564
+ // Create a MxN comparison hanging protocol available by default
6565
+ {
6566
+ name: hpCompare.id,
6567
+ protocol: hpCompare
5711
6568
  }];
5712
6569
  }
5713
6570
  /* harmony default export */ const src_getHangingProtocolModule = (getHangingProtocolModule);
@@ -5730,21 +6587,21 @@ function DataSourceSelector() {
5730
6587
  height: '100%'
5731
6588
  }
5732
6589
  }, /*#__PURE__*/react.createElement("div", {
5733
- className: "h-screen w-screen flex justify-center items-center "
6590
+ className: "flex h-screen w-screen items-center justify-center "
5734
6591
  }, /*#__PURE__*/react.createElement("div", {
5735
- className: "py-8 px-8 mx-auto bg-secondary-dark drop-shadow-md space-y-2 rounded-lg"
6592
+ className: "bg-secondary-dark mx-auto space-y-2 rounded-lg py-8 px-8 drop-shadow-md"
5736
6593
  }, /*#__PURE__*/react.createElement("img", {
5737
- className: "block mx-auto h-14",
6594
+ className: "mx-auto block h-14",
5738
6595
  src: "./ohif-logo.svg",
5739
6596
  alt: "OHIF"
5740
6597
  }), /*#__PURE__*/react.createElement("div", {
5741
- className: "text-center space-y-2 pt-4"
6598
+ className: "space-y-2 pt-4 text-center"
5742
6599
  }, dsConfigs.filter(it => it.sourceName !== 'dicomjson' && it.sourceName !== 'dicomlocal').map(ds => /*#__PURE__*/react.createElement("div", {
5743
6600
  key: ds.sourceName
5744
6601
  }, /*#__PURE__*/react.createElement("h1", {
5745
6602
  className: "text-white"
5746
- }, ds.friendlyName), /*#__PURE__*/react.createElement(ui_src/* Button */.zx, {
5747
- type: ui_src/* ButtonEnums.type */.LZ.U.primary,
6603
+ }, ds.configuration?.friendlyName || ds.friendlyName), /*#__PURE__*/react.createElement(ui_src/* Button */.zx, {
6604
+ type: ui_src/* ButtonEnums.type */.LZ.dt.primary,
5748
6605
  className: classnames_default()('ml-2'),
5749
6606
  onClick: () => {
5750
6607
  navigate({
@@ -5755,10 +6612,442 @@ function DataSourceSelector() {
5755
6612
  }, ds.sourceName), /*#__PURE__*/react.createElement("br", null)))))));
5756
6613
  }
5757
6614
  /* harmony default export */ const Panels_DataSourceSelector = (DataSourceSelector);
6615
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/Components/ItemListComponent.tsx
6616
+
6617
+
6618
+
6619
+
6620
+ function ItemListComponent(_ref) {
6621
+ let {
6622
+ itemLabel,
6623
+ itemList,
6624
+ onItemClicked
6625
+ } = _ref;
6626
+ const {
6627
+ t
6628
+ } = (0,es/* useTranslation */.$G)('DataSourceConfiguration');
6629
+ const [filterValue, setFilterValue] = (0,react.useState)('');
6630
+ (0,react.useEffect)(() => {
6631
+ setFilterValue('');
6632
+ }, [itemList]);
6633
+ return /*#__PURE__*/react.createElement("div", {
6634
+ className: "flex min-h-[1px] grow flex-col gap-4"
6635
+ }, /*#__PURE__*/react.createElement("div", {
6636
+ className: "flex items-center justify-between"
6637
+ }, /*#__PURE__*/react.createElement("div", {
6638
+ className: "text-primary-light text-[20px]"
6639
+ }, t(`Select ${itemLabel}`)), /*#__PURE__*/react.createElement(ui_src/* InputFilterText */.Xe, {
6640
+ className: "max-w-[40%] grow",
6641
+ value: filterValue,
6642
+ onDebounceChange: setFilterValue,
6643
+ placeholder: t(`Search ${itemLabel} list`)
6644
+ })), /*#__PURE__*/react.createElement("div", {
6645
+ className: "relative flex min-h-[1px] grow flex-col bg-black text-[14px]"
6646
+ }, itemList == null ? /*#__PURE__*/react.createElement(ui_src/* LoadingIndicatorProgress */.LE, {
6647
+ className: 'h-full w-full'
6648
+ }) : itemList.length === 0 ? /*#__PURE__*/react.createElement("div", {
6649
+ className: "text-primary-light flex h-full flex-col items-center justify-center px-6 py-4"
6650
+ }, /*#__PURE__*/react.createElement(ui_src/* Icon */.JO, {
6651
+ name: "magnifier",
6652
+ className: "mb-4"
6653
+ }), /*#__PURE__*/react.createElement("span", null, t(`No ${itemLabel} available`))) : /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement("div", {
6654
+ className: "bg-secondary-dark px-3 py-1.5 text-white"
6655
+ }, t(itemLabel)), /*#__PURE__*/react.createElement("div", {
6656
+ className: "ohif-scrollbar overflow-auto"
6657
+ }, itemList.filter(item => !filterValue || item.name.toLowerCase().includes(filterValue.toLowerCase())).map(item => {
6658
+ const border = 'rounded border-transparent border-b-secondary-light border-[1px] hover:border-primary-light';
6659
+ return /*#__PURE__*/react.createElement("div", {
6660
+ className: classnames_default()('hover:text-primary-light hover:bg-primary-dark group mx-2 flex items-center justify-between px-6 py-2', border),
6661
+ key: item.id
6662
+ }, /*#__PURE__*/react.createElement("div", null, item.name), /*#__PURE__*/react.createElement(ui_src/* Button */.zx, {
6663
+ onClick: () => onItemClicked(item),
6664
+ className: "invisible group-hover:visible",
6665
+ endIcon: /*#__PURE__*/react.createElement(ui_src/* Icon */.JO, {
6666
+ name: "arrow-left"
6667
+ })
6668
+ }, t('Select')));
6669
+ })))));
6670
+ }
6671
+ /* harmony default export */ const Components_ItemListComponent = (ItemListComponent);
6672
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/Components/DataSourceConfigurationModalComponent.tsx
6673
+
6674
+
6675
+
6676
+
6677
+
6678
+ const NO_WRAP_ELLIPSIS_CLASS_NAMES = 'text-ellipsis whitespace-nowrap overflow-hidden';
6679
+ function DataSourceConfigurationModalComponent(_ref) {
6680
+ let {
6681
+ configurationAPI,
6682
+ configuredItems,
6683
+ onHide
6684
+ } = _ref;
6685
+ const {
6686
+ t
6687
+ } = (0,es/* useTranslation */.$G)('DataSourceConfiguration');
6688
+ const [itemList, setItemList] = (0,react.useState)();
6689
+ const [selectedItems, setSelectedItems] = (0,react.useState)(configuredItems);
6690
+ const [errorMessage, setErrorMessage] = (0,react.useState)();
6691
+ const [itemLabels] = (0,react.useState)(configurationAPI.getItemLabels());
6692
+
6693
+ // Determines whether to show the full/existing configuration for the data source.
6694
+ // A full or complete configuration is one where the data source (path) has the
6695
+ // maximum/required number of path items. Anything less is considered not complete and
6696
+ // the configuration starts from scratch (i.e. as if no items are configured at all).
6697
+ // TODO: consider configuration starting from a partial (i.e. non-empty) configuration
6698
+ const [showFullConfig, setShowFullConfig] = (0,react.useState)(itemLabels.length === configuredItems.length);
6699
+
6700
+ /**
6701
+ * The index of the selected item that is considered current and for which
6702
+ * its sub-items should be displayed in the items list component. When the
6703
+ * full/existing configuration for a data source is to be shown, the current
6704
+ * selected item is the second to last in the `selectedItems` list.
6705
+ */
6706
+ const currentSelectedItemIndex = showFullConfig ? selectedItems.length - 2 : selectedItems.length - 1;
6707
+ (0,react.useEffect)(() => {
6708
+ let shouldUpdate = true;
6709
+ setErrorMessage(null);
6710
+
6711
+ // Clear out the former/old list while we fetch the next sub item list.
6712
+ setItemList(null);
6713
+ if (selectedItems.length === 0) {
6714
+ configurationAPI.initialize().then(items => {
6715
+ if (shouldUpdate) {
6716
+ setItemList(items);
6717
+ }
6718
+ }).catch(error => setErrorMessage(error.message));
6719
+ } else if (!showFullConfig && selectedItems.length === itemLabels.length) {
6720
+ // The last item to configure the data source (path) has been selected.
6721
+ configurationAPI.setCurrentItem(selectedItems[selectedItems.length - 1]);
6722
+ // We can hide the modal dialog now.
6723
+ onHide();
6724
+ } else {
6725
+ configurationAPI.setCurrentItem(selectedItems[currentSelectedItemIndex]).then(items => {
6726
+ if (shouldUpdate) {
6727
+ setItemList(items);
6728
+ }
6729
+ }).catch(error => setErrorMessage(error.message));
6730
+ }
6731
+ return () => {
6732
+ shouldUpdate = false;
6733
+ };
6734
+ }, [selectedItems, configurationAPI, onHide, itemLabels, showFullConfig, currentSelectedItemIndex]);
6735
+ const getSelectedItemCursorClasses = itemIndex => itemIndex !== itemLabels.length - 1 && itemIndex < selectedItems.length ? 'cursor-pointer' : 'cursor-auto';
6736
+ const getSelectedItemBackgroundClasses = itemIndex => itemIndex < selectedItems.length ? classnames_default()('bg-black/[.4]', itemIndex !== itemLabels.length - 1 ? 'hover:bg-transparent active:bg-secondary-dark' : '') : 'bg-transparent';
6737
+ const getSelectedItemBorderClasses = itemIndex => itemIndex === currentSelectedItemIndex + 1 ? classnames_default()('border-2', 'border-solid', 'border-primary-light') : itemIndex < selectedItems.length ? 'border border-solid border-primary-active hover:border-primary-light active:border-white' : 'border border-dashed border-secondary-light';
6738
+ const getSelectedItemTextClasses = itemIndex => itemIndex <= selectedItems.length ? 'text-primary-light' : 'text-primary-active';
6739
+ const getErrorComponent = () => {
6740
+ return /*#__PURE__*/react.createElement("div", {
6741
+ className: "flex min-h-[1px] grow flex-col gap-4"
6742
+ }, /*#__PURE__*/react.createElement("div", {
6743
+ className: "text-primary-light text-[20px]"
6744
+ }, t(`Error fetching ${itemLabels[selectedItems.length]} list`)), /*#__PURE__*/react.createElement("div", {
6745
+ className: "grow bg-black p-4 text-[14px]"
6746
+ }, errorMessage));
6747
+ };
6748
+ const getSelectedItemsComponent = () => {
6749
+ return /*#__PURE__*/react.createElement("div", {
6750
+ className: "flex gap-4"
6751
+ }, itemLabels.map((itemLabel, itemLabelIndex) => {
6752
+ return /*#__PURE__*/react.createElement("div", {
6753
+ key: itemLabel,
6754
+ className: classnames_default()('flex min-w-[1px] shrink basis-[200px] flex-col gap-1 rounded-md p-3.5', getSelectedItemCursorClasses(itemLabelIndex), getSelectedItemBackgroundClasses(itemLabelIndex), getSelectedItemBorderClasses(itemLabelIndex), getSelectedItemTextClasses(itemLabelIndex)),
6755
+ onClick: showFullConfig && itemLabelIndex < currentSelectedItemIndex || itemLabelIndex <= currentSelectedItemIndex ? () => {
6756
+ setShowFullConfig(false);
6757
+ setSelectedItems(theList => theList.slice(0, itemLabelIndex));
6758
+ } : undefined
6759
+ }, /*#__PURE__*/react.createElement("div", {
6760
+ className: "text- flex items-center gap-2"
6761
+ }, itemLabelIndex < selectedItems.length ? /*#__PURE__*/react.createElement(ui_src/* Icon */.JO, {
6762
+ name: "status-tracked"
6763
+ }) : /*#__PURE__*/react.createElement(ui_src/* Icon */.JO, {
6764
+ name: "status-untracked"
6765
+ }), /*#__PURE__*/react.createElement("div", {
6766
+ className: classnames_default()(NO_WRAP_ELLIPSIS_CLASS_NAMES)
6767
+ }, t(itemLabel))), itemLabelIndex < selectedItems.length ? /*#__PURE__*/react.createElement("div", {
6768
+ className: classnames_default()('text-[14px] text-white', NO_WRAP_ELLIPSIS_CLASS_NAMES)
6769
+ }, selectedItems[itemLabelIndex].name) : /*#__PURE__*/react.createElement("br", null));
6770
+ }));
6771
+ };
6772
+ return /*#__PURE__*/react.createElement("div", {
6773
+ className: "flex h-[calc(100vh-300px)] select-none flex-col gap-4 pt-0.5"
6774
+ }, getSelectedItemsComponent(), /*#__PURE__*/react.createElement("div", {
6775
+ className: "h-0.5 w-full shrink-0 bg-black"
6776
+ }), errorMessage ? getErrorComponent() : /*#__PURE__*/react.createElement(Components_ItemListComponent, {
6777
+ itemLabel: itemLabels[currentSelectedItemIndex + 1],
6778
+ itemList: itemList,
6779
+ onItemClicked: item => {
6780
+ setShowFullConfig(false);
6781
+ setSelectedItems(theList => [...theList.slice(0, currentSelectedItemIndex + 1), item]);
6782
+ }
6783
+ }));
6784
+ }
6785
+ /* harmony default export */ const Components_DataSourceConfigurationModalComponent = (DataSourceConfigurationModalComponent);
6786
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/Components/DataSourceConfigurationComponent.tsx
6787
+
6788
+
6789
+
6790
+
6791
+ function DataSourceConfigurationComponent(_ref) {
6792
+ let {
6793
+ servicesManager,
6794
+ extensionManager
6795
+ } = _ref;
6796
+ const {
6797
+ t
6798
+ } = (0,es/* useTranslation */.$G)('DataSourceConfiguration');
6799
+ const {
6800
+ show,
6801
+ hide
6802
+ } = (0,ui_src/* useModal */.dd)();
6803
+ const {
6804
+ customizationService
6805
+ } = servicesManager.services;
6806
+ const [configurationAPI, setConfigurationAPI] = (0,react.useState)();
6807
+ const [configuredItems, setConfiguredItems] = (0,react.useState)();
6808
+ (0,react.useEffect)(() => {
6809
+ let shouldUpdate = true;
6810
+ const dataSourceChangedCallback = async () => {
6811
+ const activeDataSourceDef = extensionManager.getActiveDataSourceDefinition();
6812
+ if (!activeDataSourceDef.configuration.configurationAPI) {
6813
+ return;
6814
+ }
6815
+ const {
6816
+ factory: configurationAPIFactory
6817
+ } = customizationService.get(activeDataSourceDef.configuration.configurationAPI) ?? {};
6818
+ if (!configurationAPIFactory) {
6819
+ return;
6820
+ }
6821
+ const configAPI = configurationAPIFactory(activeDataSourceDef.sourceName);
6822
+ setConfigurationAPI(configAPI);
6823
+
6824
+ // New configuration API means that the existing configured items must be cleared.
6825
+ setConfiguredItems(null);
6826
+ configAPI.getConfiguredItems().then(list => {
6827
+ if (shouldUpdate) {
6828
+ setConfiguredItems(list);
6829
+ }
6830
+ });
6831
+ };
6832
+ const sub = extensionManager.subscribe(extensionManager.EVENTS.ACTIVE_DATA_SOURCE_CHANGED, dataSourceChangedCallback);
6833
+ dataSourceChangedCallback();
6834
+ return () => {
6835
+ shouldUpdate = false;
6836
+ sub.unsubscribe();
6837
+ };
6838
+ }, []);
6839
+ const showConfigurationModal = (0,react.useCallback)(() => {
6840
+ show({
6841
+ content: Components_DataSourceConfigurationModalComponent,
6842
+ title: t('Configure Data Source'),
6843
+ contentProps: {
6844
+ configurationAPI,
6845
+ configuredItems,
6846
+ onHide: hide
6847
+ }
6848
+ });
6849
+ }, [configurationAPI, configuredItems]);
6850
+ (0,react.useEffect)(() => {
6851
+ if (!configurationAPI || !configuredItems) {
6852
+ return;
6853
+ }
6854
+ if (configuredItems.length !== configurationAPI.getItemLabels().length) {
6855
+ // Not the correct number of configured items, so show the modal to configure the data source.
6856
+ showConfigurationModal();
6857
+ }
6858
+ }, [configurationAPI, configuredItems, showConfigurationModal]);
6859
+ return configuredItems ? /*#__PURE__*/react.createElement("div", {
6860
+ className: "text-aqua-pale flex items-center overflow-hidden"
6861
+ }, /*#__PURE__*/react.createElement(ui_src/* Icon */.JO, {
6862
+ name: "settings",
6863
+ className: "mr-2.5 h-3.5 w-3.5 shrink-0 cursor-pointer",
6864
+ onClick: showConfigurationModal
6865
+ }), configuredItems.map((item, itemIndex) => {
6866
+ return /*#__PURE__*/react.createElement("div", {
6867
+ key: itemIndex,
6868
+ className: "flex overflow-hidden"
6869
+ }, /*#__PURE__*/react.createElement("div", {
6870
+ key: itemIndex,
6871
+ className: "overflow-hidden text-ellipsis whitespace-nowrap"
6872
+ }, item.name), itemIndex !== configuredItems.length - 1 && /*#__PURE__*/react.createElement("div", {
6873
+ className: "px-2.5"
6874
+ }, "|"));
6875
+ })) : /*#__PURE__*/react.createElement(react.Fragment, null);
6876
+ }
6877
+ /* harmony default export */ const Components_DataSourceConfigurationComponent = (DataSourceConfigurationComponent);
6878
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/DataSourceConfigurationAPI/GoogleCloudDataSourceConfigurationAPI.ts
6879
+ /**
6880
+ * This file contains the implementations of BaseDataSourceConfigurationAPIItem
6881
+ * and BaseDataSourceConfigurationAPI for the Google cloud healthcare API. To
6882
+ * better understand this implementation and/or to implement custom implementations,
6883
+ * see the platform\core\src\types\DataSourceConfigurationAPI.ts and its JS doc
6884
+ * comments as a guide.
6885
+ */
6886
+ /**
6887
+ * The various Google Cloud Healthcare path item types.
6888
+ */
6889
+ var ItemType = /*#__PURE__*/function (ItemType) {
6890
+ ItemType[ItemType["projects"] = 0] = "projects";
6891
+ ItemType[ItemType["locations"] = 1] = "locations";
6892
+ ItemType[ItemType["datasets"] = 2] = "datasets";
6893
+ ItemType[ItemType["dicomStores"] = 3] = "dicomStores";
6894
+ return ItemType;
6895
+ }(ItemType || {});
6896
+ const initialUrl = 'https://cloudresourcemanager.googleapis.com/v1';
6897
+ const baseHealthcareUrl = 'https://healthcare.googleapis.com/v1';
6898
+ class GoogleCloudDataSourceConfigurationAPIItem {
6899
+ constructor() {
6900
+ this.id = void 0;
6901
+ this.name = void 0;
6902
+ this.url = void 0;
6903
+ this.itemType = void 0;
6904
+ }
6905
+ }
6906
+ class GoogleCloudDataSourceConfigurationAPI {
6907
+ constructor(dataSourceName, servicesManager, extensionManager) {
6908
+ this._extensionManager = void 0;
6909
+ this._fetchOptions = void 0;
6910
+ this._dataSourceName = void 0;
6911
+ this.getItemLabels = () => ['Project', 'Location', 'Data set', 'DICOM store'];
6912
+ this._dataSourceName = dataSourceName;
6913
+ this._extensionManager = extensionManager;
6914
+ const userAuthenticationService = servicesManager.services.userAuthenticationService;
6915
+ this._fetchOptions = {
6916
+ method: 'GET',
6917
+ headers: userAuthenticationService.getAuthorizationHeader()
6918
+ };
6919
+ }
6920
+ async initialize() {
6921
+ const url = `${initialUrl}/projects`;
6922
+ const projects = await GoogleCloudDataSourceConfigurationAPI._doFetch(url, ItemType.projects, this._fetchOptions);
6923
+ if (!projects?.length) {
6924
+ return [];
6925
+ }
6926
+ const projectItems = projects.map(project => {
6927
+ return {
6928
+ id: project.projectId,
6929
+ name: project.name,
6930
+ itemType: ItemType.projects,
6931
+ url: `${baseHealthcareUrl}/projects/${project.projectId}`
6932
+ };
6933
+ });
6934
+ return projectItems;
6935
+ }
6936
+ async setCurrentItem(anItem) {
6937
+ const googleCloudItem = anItem;
6938
+ if (googleCloudItem.itemType === ItemType.dicomStores) {
6939
+ // Last configurable item, so update the data source configuration.
6940
+ const url = `${googleCloudItem.url}/dicomWeb`;
6941
+ const dataSourceDefCopy = JSON.parse(JSON.stringify(this._extensionManager.getDataSourceDefinition(this._dataSourceName)));
6942
+ dataSourceDefCopy.configuration = {
6943
+ ...dataSourceDefCopy.configuration,
6944
+ wadoUriRoot: url,
6945
+ qidoRoot: url,
6946
+ wadoRoot: url
6947
+ };
6948
+ this._extensionManager.updateDataSourceConfiguration(dataSourceDefCopy.sourceName, dataSourceDefCopy.configuration);
6949
+ return [];
6950
+ }
6951
+ const subItemType = googleCloudItem.itemType + 1;
6952
+ const subItemField = `${ItemType[subItemType]}`;
6953
+ const url = `${googleCloudItem.url}/${subItemField}`;
6954
+ const fetchedSubItems = await GoogleCloudDataSourceConfigurationAPI._doFetch(url, subItemType, this._fetchOptions);
6955
+ if (!fetchedSubItems?.length) {
6956
+ return [];
6957
+ }
6958
+ const subItems = fetchedSubItems.map(subItem => {
6959
+ const nameSplit = subItem.name.split('/');
6960
+ return {
6961
+ id: subItem.name,
6962
+ name: nameSplit[nameSplit.length - 1],
6963
+ itemType: subItemType,
6964
+ url: `${baseHealthcareUrl}/${subItem.name}`
6965
+ };
6966
+ });
6967
+ return subItems;
6968
+ }
6969
+ async getConfiguredItems() {
6970
+ const dataSourceDefinition = this._extensionManager.getDataSourceDefinition(this._dataSourceName);
6971
+ const url = dataSourceDefinition.configuration.wadoUriRoot;
6972
+ const projectsIndex = url.indexOf('projects');
6973
+ // Split the configured URL into (essentially) pairs (i.e. item type followed by item)
6974
+ // Explicitly: ['projects','aProject','locations','aLocation','datasets','aDataSet','dicomStores','aDicomStore']
6975
+ // Note that a partial configuration will have a subset of the above.
6976
+ const urlSplit = url.substring(projectsIndex).split('/');
6977
+ const configuredItems = [];
6978
+ for (let itemType = 0;
6979
+ // the number of configured items is either the max (4) or the number extracted from the url split
6980
+ itemType < 4 && (itemType + 1) * 2 < urlSplit.length; itemType += 1) {
6981
+ if (itemType === ItemType.projects) {
6982
+ const projectId = urlSplit[1];
6983
+ const projectUrl = `${initialUrl}/projects/${projectId}`;
6984
+ const data = await GoogleCloudDataSourceConfigurationAPI._doFetch(projectUrl, ItemType.projects, this._fetchOptions);
6985
+ const project = data[0];
6986
+ configuredItems.push({
6987
+ id: project.projectId,
6988
+ name: project.name,
6989
+ itemType: itemType,
6990
+ url: `${baseHealthcareUrl}/projects/${project.projectId}`
6991
+ });
6992
+ } else {
6993
+ const relativePath = urlSplit.slice(0, itemType * 2 + 2).join('/');
6994
+ configuredItems.push({
6995
+ id: relativePath,
6996
+ name: urlSplit[itemType * 2 + 1],
6997
+ itemType: itemType,
6998
+ url: `${baseHealthcareUrl}/${relativePath}`
6999
+ });
7000
+ }
7001
+ }
7002
+ return configuredItems;
7003
+ }
7004
+
7005
+ /**
7006
+ * Fetches an array of items the specified item type.
7007
+ * @param urlStr the fetch url
7008
+ * @param fetchItemType the type to fetch
7009
+ * @param fetchOptions the header options for the fetch (e.g. authorization header)
7010
+ * @param fetchSearchParams any search query params; currently only used for paging results
7011
+ * @returns an array of items of the specified type
7012
+ */
7013
+ static async _doFetch(urlStr, fetchItemType) {
7014
+ let fetchOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
7015
+ let fetchSearchParams = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
7016
+ try {
7017
+ const url = new URL(urlStr);
7018
+ url.search = new URLSearchParams(fetchSearchParams).toString();
7019
+ const response = await fetch(url, fetchOptions);
7020
+ const data = await response.json();
7021
+ if (response.status >= 200 && response.status < 300 && data != null) {
7022
+ if (data.nextPageToken != null) {
7023
+ fetchSearchParams.pageToken = data.nextPageToken;
7024
+ const subPageData = await this._doFetch(urlStr, fetchItemType, fetchOptions, fetchSearchParams);
7025
+ data[ItemType[fetchItemType]] = data[ItemType[fetchItemType]].concat(subPageData);
7026
+ }
7027
+ if (data[ItemType[fetchItemType]]) {
7028
+ return data[ItemType[fetchItemType]];
7029
+ } else if (data.name) {
7030
+ return [data];
7031
+ } else {
7032
+ return [];
7033
+ }
7034
+ } else {
7035
+ const message = data?.error?.message || `Error returned from Google Cloud Healthcare: ${response.status} - ${response.statusText}`;
7036
+ throw new Error(message);
7037
+ }
7038
+ } catch (err) {
7039
+ const message = err?.message || 'Error occurred during fetch request.';
7040
+ throw new Error(message);
7041
+ }
7042
+ }
7043
+ }
7044
+
5758
7045
  ;// CONCATENATED MODULE: ../../../extensions/default/src/getCustomizationModule.tsx
5759
7046
 
5760
7047
 
5761
7048
 
7049
+
7050
+
5762
7051
  /**
5763
7052
  *
5764
7053
  * Note: this is an example of how the customization module can be used
@@ -5768,7 +7057,11 @@ function DataSourceSelector() {
5768
7057
  * custom page for the user to view their profile, or to add a custom
5769
7058
  * page for login etc.
5770
7059
  */
5771
- function getCustomizationModule() {
7060
+ function getCustomizationModule(_ref) {
7061
+ let {
7062
+ servicesManager,
7063
+ extensionManager
7064
+ } = _ref;
5772
7065
  return [{
5773
7066
  name: 'helloPage',
5774
7067
  value: {
@@ -5835,12 +7128,16 @@ function getCustomizationModule() {
5835
7128
  {
5836
7129
  id: 'ohif.overlayItem',
5837
7130
  content: function (props) {
5838
- if (this.condition && !this.condition(props)) return null;
7131
+ if (this.condition && !this.condition(props)) {
7132
+ return null;
7133
+ }
5839
7134
  const {
5840
7135
  instance
5841
7136
  } = props;
5842
7137
  const value = instance && this.attribute ? instance[this.attribute] : this.contentF && typeof this.contentF === 'function' ? this.contentF(props) : null;
5843
- if (!value) return null;
7138
+ if (!value) {
7139
+ return null;
7140
+ }
5844
7141
  return /*#__PURE__*/react.createElement("span", {
5845
7142
  className: "overlay-item flex flex-row",
5846
7143
  style: {
@@ -5878,11 +7175,22 @@ function getCustomizationModule() {
5878
7175
  }
5879
7176
  return clonedObject;
5880
7177
  }
7178
+ }, {
7179
+ // the generic GUI component to configure a data source using an instance of a BaseDataSourceConfigurationAPI
7180
+ id: 'ohif.dataSourceConfigurationComponent',
7181
+ component: Components_DataSourceConfigurationComponent.bind(null, {
7182
+ servicesManager,
7183
+ extensionManager
7184
+ })
7185
+ }, {
7186
+ // The factory for creating an instance of a BaseDataSourceConfigurationAPI for Google Cloud Healthcare
7187
+ id: 'ohif.dataSourceConfigurationAPI.google',
7188
+ factory: dataSourceName => new GoogleCloudDataSourceConfigurationAPI(dataSourceName, servicesManager, extensionManager)
5881
7189
  }]
5882
7190
  }];
5883
7191
  }
5884
7192
  // EXTERNAL MODULE: ../../../node_modules/@cornerstonejs/calculate-suv/dist/calculate-suv.esm.js
5885
- var calculate_suv_esm = __webpack_require__(71251);
7193
+ var calculate_suv_esm = __webpack_require__(15747);
5886
7194
  ;// CONCATENATED MODULE: ../../../extensions/default/src/getPTImageIdInstanceMetadata.ts
5887
7195
 
5888
7196
  const getPTImageIdInstanceMetadata_metadataProvider = src["default"].classes.MetadataProvider;
@@ -5891,9 +7199,12 @@ function getPTImageIdInstanceMetadata(imageId) {
5891
7199
  if (!dicomMetaData) {
5892
7200
  throw new Error('dicom metadata are required');
5893
7201
  }
5894
- if (dicomMetaData.SeriesDate === undefined || dicomMetaData.SeriesTime === undefined || dicomMetaData.PatientWeight === undefined || dicomMetaData.CorrectedImage === undefined || dicomMetaData.Units === undefined || !dicomMetaData.RadiopharmaceuticalInformationSequence || dicomMetaData.RadiopharmaceuticalInformationSequence[0].RadionuclideHalfLife === undefined || dicomMetaData.RadiopharmaceuticalInformationSequence[0].RadionuclideTotalDose === undefined || dicomMetaData.DecayCorrection === undefined || dicomMetaData.AcquisitionDate === undefined || dicomMetaData.AcquisitionTime === undefined || dicomMetaData.RadiopharmaceuticalInformationSequence[0].RadiopharmaceuticalStartDateTime === undefined && dicomMetaData.RadiopharmaceuticalInformationSequence[0].RadiopharmaceuticalStartTime === undefined) {
7202
+ if (dicomMetaData.SeriesDate === undefined || dicomMetaData.SeriesTime === undefined || dicomMetaData.CorrectedImage === undefined || dicomMetaData.Units === undefined || !dicomMetaData.RadiopharmaceuticalInformationSequence || dicomMetaData.RadiopharmaceuticalInformationSequence[0].RadionuclideHalfLife === undefined || dicomMetaData.RadiopharmaceuticalInformationSequence[0].RadionuclideTotalDose === undefined || dicomMetaData.DecayCorrection === undefined || dicomMetaData.AcquisitionDate === undefined || dicomMetaData.AcquisitionTime === undefined || dicomMetaData.RadiopharmaceuticalInformationSequence[0].RadiopharmaceuticalStartDateTime === undefined && dicomMetaData.RadiopharmaceuticalInformationSequence[0].RadiopharmaceuticalStartTime === undefined) {
5895
7203
  throw new Error('required metadata are missing');
5896
7204
  }
7205
+ if (dicomMetaData.PatientWeight === undefined) {
7206
+ console.warn('PatientWeight missing from PT instance metadata');
7207
+ }
5897
7208
  const instanceMetadata = {
5898
7209
  CorrectedImage: dicomMetaData.CorrectedImage,
5899
7210
  Units: dicomMetaData.Units,
@@ -6064,6 +7375,8 @@ const handlePETImageMetadata = _ref2 => {
6064
7375
 
6065
7376
 
6066
7377
 
7378
+
7379
+
6067
7380
  const defaultExtension = {
6068
7381
  /**
6069
7382
  * Only required property. Should be a unique value across all extensions.