@ohif/app 3.7.0-beta.9 → 3.7.0-beta.90

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.0edb40e9d9467dd3a189.js → 12.bundle.c51f9611deb347508909.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.73fc96c6b3ab1fabedc8.js} +226 -204
  4. package/dist/{351.css → 181.css} +1 -1
  5. package/dist/{744.bundle.c459c690581bc8a522d8.js → 19.bundle.a858382f8b2b4ba8d3cb.js} +240 -375
  6. package/dist/{606.bundle.5d876f5f3dd8287f0a28.js → 202.bundle.96bbb4547a346fe3921f.js} +1420 -750
  7. package/dist/{926.bundle.dbc9d0e591cb9217fda2.js → 220.bundle.f7e1c96c94245e70f2be.js} +990 -400
  8. package/dist/221.bundle.e2afcda7523b858d7edb.js +1723 -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.3f8bfb620791f4508420.js → 236.bundle.80a95257cee3c60edce1.js} +88 -104
  12. package/dist/{55.bundle.550a823e75eb608e8d5e.js → 250.bundle.4bebed43526c7e06344f.js} +52 -36
  13. package/dist/{973.bundle.5aa91607481865ead93f.js → 281.bundle.1d024348a9e68cabcb75.js} +18 -14
  14. package/dist/{82.bundle.978be6f7595202cd342b.js → 342.bundle.4949499cddc0e73aed86.js} +1765 -476
  15. package/dist/{404.bundle.83acdec604ed84f4b772.js → 359.bundle.293ba004301607ab27ff.js} +46 -131
  16. package/dist/{192.bundle.655fc9c5aeff41110aa9.js → 370.bundle.28d7737cee0832695236.js} +113 -99
  17. package/dist/{790.bundle.08e37fd3b64af8dd8e78.js → 410.bundle.492283c5b53922d55610.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.1c714bfb8b66d3a5adfb.js} +86 -106
  20. package/dist/{581.bundle.dc6197189f7c88c27d4c.js → 471.bundle.4aaec34d87b0c93687c1.js} +78 -99
  21. package/dist/{199.bundle.251f86c6e2eee85c49a5.js → 506.bundle.0e2f4377f64c0c78e5f9.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.b5c524ec95748332cb1d.js +532 -0
  26. package/dist/{984.bundle.0c8b7d8388a662ad5ebc.js → 663.bundle.5bed0c9a4ac30d4be7e9.js} +68 -38
  27. package/dist/{205.bundle.b5a473c200dcf2bbcdb4.js → 686.bundle.dccef1f36e4bc79bcc48.js} +6 -6
  28. package/dist/{50.bundle.bec52570fe00c2ccced8.js → 687.bundle.aefdf23ca61906e42117.js} +218 -9
  29. package/dist/{331.bundle.bd0c13931a21d53086c9.js → 743.bundle.4bfe6e562ffb2c22708f.js} +26281 -21326
  30. package/dist/{728.bundle.d13856835357400fef82.js → 774.bundle.7528cba56a1407357144.js} +95 -64
  31. package/dist/{381.bundle.0905e683605fcbc0895f.js → 775.bundle.2285e7e0e67878948c0d.js} +16 -16
  32. package/dist/{283.bundle.b43e001c27e02b0199aa.js → 782.bundle.98fa888b2e3f8ad7d37e.js} +117 -67
  33. package/dist/{642.bundle.1ab1e9ea67caeaedb189.js → 814.bundle.5ab8b6dbbade2e65975f.js} +6 -6
  34. package/dist/{799.bundle.758558e64147e5aad612.js → 822.bundle.00de6455c18be0307b41.js} +81 -34
  35. package/dist/831.bundle.83658f62fcc769043605.js +16700 -0
  36. package/dist/{707.bundle.9622c314b0ea3488d69a.js → 877.bundle.896122a750e45a7719ae.js} +1022 -708
  37. package/dist/{953.bundle.3b0189ebc11cf0946f18.js → 886.bundle.7324d84913daffb6a4c4.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.8c09a01840ab8aa32734.js} +7093 -987
  41. package/dist/{208.bundle.05451122c341d80d3c22.js → 99.bundle.eff869422ff7114f5403.js} +85 -104
  42. package/dist/_redirects +1 -1
  43. package/dist/app-config.js +35 -17
  44. package/dist/{app.bundle.d2ebd2fcc8b88864ebeb.js → app.bundle.ccda811fb90f522f9ec2.js} +71937 -66734
  45. package/dist/app.bundle.css +13 -12
  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 +1 -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 +20 -22
  61. package/dist/616.bundle.d0581701281977bea39b.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 → 877.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 + 64 modules
35
+ var src = __webpack_require__(67869);
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,11 +820,17 @@ 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;
831
+ if (!valueElem) {
832
+ return false;
833
+ }
810
834
  if (valueElem.vr == 'DA') {
811
835
  return this.compareDateRange(testValue, valueElem.Value[0]);
812
836
  }
@@ -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 + 99 modules
2066
+ var i18n_src = __webpack_require__(34708);
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.9";
2147
- const commitHash = "d75305e70d031962528bcd1aacaa064bc4fb4599";
2169
+ const versionNumber = "3.7.0-beta.90";
2170
+ const commitHash = "eb22328fc05d06fc4411805e7a30f826659d796a";
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');
@@ -2404,9 +2511,9 @@ function PanelStudyBrowser(_ref) {
2404
2511
  const isMounted = (0,react.useRef)(true);
2405
2512
  const onDoubleClickThumbnailHandler = displaySetInstanceUID => {
2406
2513
  let updatedViewports = [];
2407
- const viewportIndex = activeViewportIndex;
2514
+ const viewportId = activeViewportId;
2408
2515
  try {
2409
- updatedViewports = hangingProtocolService.getViewportsRequireUpdate(viewportIndex, displaySetInstanceUID);
2516
+ updatedViewports = hangingProtocolService.getViewportsRequireUpdate(viewportId, displaySetInstanceUID);
2410
2517
  } catch (error) {
2411
2518
  console.warn(error);
2412
2519
  uiNotificationService.show({
@@ -2427,6 +2534,10 @@ function PanelStudyBrowser(_ref) {
2427
2534
  const qidoForStudyUID = await dataSource.query.studies.search({
2428
2535
  studyInstanceUid: StudyInstanceUID
2429
2536
  });
2537
+ if (!qidoForStudyUID?.length) {
2538
+ navigate('/notfoundstudy', '_self');
2539
+ throw new Error('Invalid study URL');
2540
+ }
2430
2541
  let qidoStudiesForPatient = qidoForStudyUID;
2431
2542
 
2432
2543
  // try to fetch the prior studies based on the patientID if the
@@ -2457,8 +2568,7 @@ function PanelStudyBrowser(_ref) {
2457
2568
  });
2458
2569
  }
2459
2570
  StudyInstanceUIDs.forEach(sid => fetchStudiesForPatient(sid));
2460
- // eslint-disable-next-line react-hooks/exhaustive-deps
2461
- }, [StudyInstanceUIDs, getStudiesForPatientByMRN]);
2571
+ }, [StudyInstanceUIDs, dataSource, getStudiesForPatientByMRN, navigate]);
2462
2572
 
2463
2573
  // // ~~ Initial Thumbnails
2464
2574
  (0,react.useEffect)(() => {
@@ -2470,24 +2580,25 @@ function PanelStudyBrowser(_ref) {
2470
2580
  const imageId = imageIds[Math.floor(imageIds.length / 2)];
2471
2581
 
2472
2582
  // 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
- }
2583
+ if (!imageId || displaySet?.unsupported) {
2584
+ return;
2484
2585
  }
2586
+ // When the image arrives, render it and store the result in the thumbnailImgSrcMap
2587
+ newImageSrcEntry[dSet.displaySetInstanceUID] = await getImageSrc(imageId);
2588
+ if (!isMounted.current) {
2589
+ return;
2590
+ }
2591
+ setThumbnailImageSrcMap(prevState => {
2592
+ return {
2593
+ ...prevState,
2594
+ ...newImageSrcEntry
2595
+ };
2596
+ });
2485
2597
  });
2486
2598
  return () => {
2487
2599
  isMounted.current = false;
2488
2600
  };
2489
- // eslint-disable-next-line react-hooks/exhaustive-deps
2490
- }, []);
2601
+ }, [StudyInstanceUIDs, dataSource, displaySetService, getImageSrc]);
2491
2602
 
2492
2603
  // ~~ displaySets
2493
2604
  (0,react.useEffect)(() => {
@@ -2496,50 +2607,59 @@ function PanelStudyBrowser(_ref) {
2496
2607
  const mappedDisplaySets = _mapDisplaySets(currentDisplaySets, thumbnailImageSrcMap);
2497
2608
  sortStudyInstances(mappedDisplaySets);
2498
2609
  setDisplaySets(mappedDisplaySets);
2499
- // eslint-disable-next-line react-hooks/exhaustive-deps
2500
- }, [thumbnailImageSrcMap]);
2610
+ }, [StudyInstanceUIDs, thumbnailImageSrcMap, displaySetService]);
2501
2611
 
2502
2612
  // ~~ subscriptions --> displaySets
2503
2613
  (0,react.useEffect)(() => {
2504
2614
  // DISPLAY_SETS_ADDED returns an array of DisplaySets that were added
2505
2615
  const SubscriptionDisplaySetsAdded = displaySetService.subscribe(displaySetService.EVENTS.DISPLAY_SETS_ADDED, data => {
2506
2616
  const {
2507
- displaySetsAdded
2617
+ displaySetsAdded,
2618
+ options
2508
2619
  } = data;
2509
2620
  displaySetsAdded.forEach(async dSet => {
2510
2621
  const newImageSrcEntry = {};
2511
2622
  const displaySet = displaySetService.getDisplaySetByUID(dSet.displaySetInstanceUID);
2623
+ if (displaySet?.unsupported) {
2624
+ return;
2625
+ }
2512
2626
  const imageIds = dataSource.getImageIdsForDisplaySet(displaySet);
2513
2627
  const imageId = imageIds[Math.floor(imageIds.length / 2)];
2514
2628
 
2515
2629
  // 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
- }
2630
+ if (!imageId) {
2631
+ return;
2527
2632
  }
2528
- });
2529
- });
2530
-
2633
+ // When the image arrives, render it and store the result in the thumbnailImgSrcMap
2634
+ newImageSrcEntry[dSet.displaySetInstanceUID] = await getImageSrc(imageId, dSet.initialViewport);
2635
+ setThumbnailImageSrcMap(prevState => {
2636
+ return {
2637
+ ...prevState,
2638
+ ...newImageSrcEntry
2639
+ };
2640
+ });
2641
+ });
2642
+ });
2643
+ return () => {
2644
+ SubscriptionDisplaySetsAdded.unsubscribe();
2645
+ };
2646
+ }, [getImageSrc, dataSource, displaySetService]);
2647
+ (0,react.useEffect)(() => {
2531
2648
  // TODO: Will this always hold _all_ the displaySets we care about?
2532
2649
  // DISPLAY_SETS_CHANGED returns `DisplaySerService.activeDisplaySets`
2533
2650
  const SubscriptionDisplaySetsChanged = displaySetService.subscribe(displaySetService.EVENTS.DISPLAY_SETS_CHANGED, changedDisplaySets => {
2534
2651
  const mappedDisplaySets = _mapDisplaySets(changedDisplaySets, thumbnailImageSrcMap);
2535
2652
  setDisplaySets(mappedDisplaySets);
2536
2653
  });
2654
+ const SubscriptionDisplaySetMetaDataInvalidated = displaySetService.subscribe(displaySetService.EVENTS.DISPLAY_SET_SERIES_METADATA_INVALIDATED, () => {
2655
+ const mappedDisplaySets = _mapDisplaySets(displaySetService.getActiveDisplaySets(), thumbnailImageSrcMap);
2656
+ setDisplaySets(mappedDisplaySets);
2657
+ });
2537
2658
  return () => {
2538
- SubscriptionDisplaySetsAdded.unsubscribe();
2539
2659
  SubscriptionDisplaySetsChanged.unsubscribe();
2660
+ SubscriptionDisplaySetMetaDataInvalidated.unsubscribe();
2540
2661
  };
2541
- // eslint-disable-next-line react-hooks/exhaustive-deps
2542
- }, []);
2662
+ }, [StudyInstanceUIDs, thumbnailImageSrcMap, displaySetService]);
2543
2663
  const tabs = _createStudyBrowserTabs(StudyInstanceUIDs, studyDisplayList, displaySets);
2544
2664
 
2545
2665
  // TODO: Should not fire this on "close"
@@ -2554,7 +2674,7 @@ function PanelStudyBrowser(_ref) {
2554
2674
  requestDisplaySetCreationForStudy(displaySetService, StudyInstanceUID, madeInClient);
2555
2675
  }
2556
2676
  }
2557
- const activeDisplaySetInstanceUIDs = viewports[activeViewportIndex]?.displaySetInstanceUIDs;
2677
+ const activeDisplaySetInstanceUIDs = viewports.get(activeViewportId)?.displaySetInstanceUIDs;
2558
2678
  return /*#__PURE__*/react.createElement(ui_src/* StudyBrowser */.eX, {
2559
2679
  tabs: tabs,
2560
2680
  servicesManager: servicesManager,
@@ -2605,7 +2725,7 @@ function _mapDisplaySets(displaySets, thumbnailImageSrcMap) {
2605
2725
  const thumbnailNoImageDisplaySets = [];
2606
2726
  displaySets.filter(ds => !ds.excludeFromThumbnailBrowser).forEach(ds => {
2607
2727
  const imageSrc = thumbnailImageSrcMap[ds.displaySetInstanceUID];
2608
- const componentType = _getComponentType(ds.Modality);
2728
+ const componentType = _getComponentType(ds);
2609
2729
  const array = componentType === 'thumbnail' ? thumbnailDisplaySets : thumbnailNoImageDisplaySets;
2610
2730
  array.push({
2611
2731
  displaySetInstanceUID: ds.displaySetInstanceUID,
@@ -2617,21 +2737,23 @@ function _mapDisplaySets(displaySets, thumbnailImageSrcMap) {
2617
2737
  numInstances: ds.numImageFrames,
2618
2738
  countIcon: ds.countIcon,
2619
2739
  StudyInstanceUID: ds.StudyInstanceUID,
2740
+ messages: ds.messages,
2620
2741
  componentType,
2621
2742
  imageSrc,
2622
2743
  dragData: {
2623
2744
  type: 'displayset',
2624
2745
  displaySetInstanceUID: ds.displaySetInstanceUID
2625
2746
  // .. Any other data to pass
2626
- }
2747
+ },
2748
+
2749
+ isHydratedForDerivedDisplaySet: ds.isHydrated
2627
2750
  });
2628
2751
  });
2629
-
2630
2752
  return [...thumbnailDisplaySets, ...thumbnailNoImageDisplaySets];
2631
2753
  }
2632
2754
  const thumbnailNoImageModalities = ['SR', 'SEG', 'SM', 'RTSTRUCT', 'RTPLAN', 'RTDOSE'];
2633
- function _getComponentType(Modality) {
2634
- if (thumbnailNoImageModalities.includes(Modality)) {
2755
+ function _getComponentType(ds) {
2756
+ if (thumbnailNoImageModalities.includes(ds.Modality) || ds?.unsupported) {
2635
2757
  // TODO probably others.
2636
2758
  return 'thumbnailNoImage';
2637
2759
  }
@@ -2798,7 +2920,7 @@ function ActionButtons(_ref) {
2798
2920
  const {
2799
2921
  t
2800
2922
  } = (0,es/* useTranslation */.$G)('MeasurementTable');
2801
- return /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement(ui_src/* ButtonGroup */.hE, {
2923
+ return /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement(ui_src/* LegacyButtonGroup */.HO, {
2802
2924
  color: "black",
2803
2925
  size: "inherit"
2804
2926
  }, /*#__PURE__*/react.createElement(ui_src/* LegacyButton */.mN, {
@@ -2819,7 +2941,7 @@ ActionButtons.defaultProps = {
2819
2941
  };
2820
2942
  /* harmony default export */ const Panels_ActionButtons = (ActionButtons);
2821
2943
  // EXTERNAL MODULE: ../../../node_modules/lodash.debounce/index.js
2822
- var lodash_debounce = __webpack_require__(40001);
2944
+ var lodash_debounce = __webpack_require__(8324);
2823
2945
  var lodash_debounce_default = /*#__PURE__*/__webpack_require__.n(lodash_debounce);
2824
2946
  ;// CONCATENATED MODULE: ../../../extensions/default/src/Panels/createReportDialogPrompt.tsx
2825
2947
  /* eslint-disable react/display-name */
@@ -2906,11 +3028,11 @@ function createReportDialogPrompt(uiDialogService, _ref) {
2906
3028
  actions: [{
2907
3029
  id: 'cancel',
2908
3030
  text: 'Cancel',
2909
- type: ui_src/* ButtonEnums.type */.LZ.U.secondary
3031
+ type: ui_src/* ButtonEnums.type */.LZ.dt.secondary
2910
3032
  }, {
2911
3033
  id: 'save',
2912
3034
  text: 'Save',
2913
- type: ui_src/* ButtonEnums.type */.LZ.U.primary
3035
+ type: ui_src/* ButtonEnums.type */.LZ.dt.primary
2914
3036
  }],
2915
3037
  // TODO: Should be on button press...
2916
3038
  onSubmit: _handleFormSubmit,
@@ -2939,7 +3061,7 @@ function createReportDialogPrompt(uiDialogService, _ref) {
2939
3061
  };
2940
3062
  return /*#__PURE__*/react.createElement(react.Fragment, null, dataSourcesOpts.length > 1 && /*#__PURE__*/react.createElement(ui_src/* Select */.Ph, {
2941
3063
  closeMenuOnSelect: true,
2942
- className: "mr-2 bg-black border-primary-main",
3064
+ className: "border-primary-main mr-2 bg-black",
2943
3065
  options: dataSourcesOpts,
2944
3066
  placeholder: dataSourcesOpts.find(option => option.value === value.dataSourceName).placeHolder,
2945
3067
  value: value.dataSourceName,
@@ -2954,7 +3076,7 @@ function createReportDialogPrompt(uiDialogService, _ref) {
2954
3076
  autoFocus: true,
2955
3077
  label: "Enter the report name",
2956
3078
  labelClassName: "text-white text-[14px] leading-[1.2]",
2957
- className: "bg-black border-primary-main",
3079
+ className: "border-primary-main bg-black",
2958
3080
  type: "text",
2959
3081
  value: value.label,
2960
3082
  onChange: onChangeHandler,
@@ -2973,12 +3095,13 @@ function createReportDialogPrompt(uiDialogService, _ref) {
2973
3095
  /**
2974
3096
  *
2975
3097
  * @param {*} servicesManager
2976
- * @param {*} dataSource
2977
- * @param {*} measurements
2978
- * @param {*} options
2979
- * @returns {string[]} displaySetInstanceUIDs
2980
3098
  */
2981
- async function createReportAsync(servicesManager, commandsManager, dataSource, measurements, options) {
3099
+ async function createReportAsync(_ref) {
3100
+ let {
3101
+ servicesManager,
3102
+ getReport,
3103
+ reportType = 'measurement'
3104
+ } = _ref;
2982
3105
  const {
2983
3106
  displaySetService,
2984
3107
  uiNotificationService,
@@ -2988,32 +3111,27 @@ async function createReportAsync(servicesManager, commandsManager, dataSource, m
2988
3111
  showOverlay: true,
2989
3112
  isDraggable: false,
2990
3113
  centralize: true,
2991
- // TODO: Create a loading indicator component + zeplin design?
2992
3114
  content: Loading
2993
3115
  });
2994
3116
  try {
2995
- const naturalizedReport = await commandsManager.runCommand('storeMeasurements', {
2996
- measurementData: measurements,
2997
- dataSource,
2998
- additionalFindingTypes: ['ArrowAnnotate'],
2999
- options
3000
- }, 'CORNERSTONE_STRUCTURED_REPORT');
3117
+ const naturalizedReport = await getReport();
3001
3118
 
3002
3119
  // The "Mode" route listens for DicomMetadataStore changes
3003
3120
  // When a new instance is added, it listens and
3004
3121
  // automatically calls makeDisplaySets
3005
3122
  src.DicomMetadataStore.addInstances([naturalizedReport], true);
3006
- const displaySetInstanceUID = displaySetService.getMostRecentDisplaySet();
3123
+ const displaySet = displaySetService.getMostRecentDisplaySet();
3124
+ const displaySetInstanceUID = displaySet.displaySetInstanceUID;
3007
3125
  uiNotificationService.show({
3008
3126
  title: 'Create Report',
3009
- message: 'Measurements saved successfully',
3127
+ message: `${reportType} saved successfully`,
3010
3128
  type: 'success'
3011
3129
  });
3012
3130
  return [displaySetInstanceUID];
3013
3131
  } catch (error) {
3014
3132
  uiNotificationService.show({
3015
3133
  title: 'Create Report',
3016
- message: error.message || 'Failed to store measurements',
3134
+ message: error.message || `Failed to store ${reportType}`,
3017
3135
  type: 'error'
3018
3136
  });
3019
3137
  } finally {
@@ -3105,7 +3223,7 @@ function PanelMeasurementTable(_ref) {
3105
3223
  } = _ref;
3106
3224
  const [viewportGrid, viewportGridService] = (0,ui_src/* useViewportGrid */.O_)();
3107
3225
  const {
3108
- activeViewportIndex,
3226
+ activeViewportId,
3109
3227
  viewports
3110
3228
  } = viewportGrid;
3111
3229
  const {
@@ -3148,7 +3266,7 @@ function PanelMeasurementTable(_ref) {
3148
3266
  }
3149
3267
  async function createReport() {
3150
3268
  // filter measurements that are added to the active study
3151
- const activeViewport = viewports[activeViewportIndex];
3269
+ const activeViewport = viewports.get(activeViewportId);
3152
3270
  const measurements = measurementService.getMeasurements();
3153
3271
  const displaySet = displaySetService.getDisplaySetByUID(activeViewport.displaySetInstanceUIDs[0]);
3154
3272
  const trackedMeasurements = measurements.filter(m => displaySet.StudyInstanceUID === m.referenceStudyUID);
@@ -3172,10 +3290,21 @@ function PanelMeasurementTable(_ref) {
3172
3290
  promptResult.value === undefined || promptResult.value === '' ? 'Research Derived Series' // default
3173
3291
  : promptResult.value; // provided value
3174
3292
 
3175
- // Re-use an existing series having the same series description to avoid
3293
+ // Reuse an existing series having the same series description to avoid
3176
3294
  // creating too many series instances.
3177
3295
  const options = findSRWithSameSeriesDescription(SeriesDescription, displaySetService);
3178
- return Actions_createReportAsync(servicesManager, commandsManager, dataSource, trackedMeasurements, options);
3296
+ const getReport = async () => {
3297
+ return commandsManager.runCommand('storeMeasurements', {
3298
+ measurementData: trackedMeasurements,
3299
+ dataSource,
3300
+ additionalFindingTypes: ['ArrowAnnotate'],
3301
+ options
3302
+ }, 'CORNERSTONE_STRUCTURED_REPORT');
3303
+ };
3304
+ return Actions_createReportAsync({
3305
+ servicesManager,
3306
+ getReport
3307
+ });
3179
3308
  }
3180
3309
  }
3181
3310
  const jumpToImage = _ref2 => {
@@ -3183,7 +3312,7 @@ function PanelMeasurementTable(_ref) {
3183
3312
  uid,
3184
3313
  isActive
3185
3314
  } = _ref2;
3186
- measurementService.jumpToMeasurement(viewportGrid.activeViewportIndex, uid);
3315
+ measurementService.jumpToMeasurement(viewportGrid.activeViewportId, uid);
3187
3316
  onMeasurementItemClickHandler({
3188
3317
  uid,
3189
3318
  isActive
@@ -3255,7 +3384,7 @@ function PanelMeasurementTable(_ref) {
3255
3384
  labelClassName: "text-white text-[14px] leading-[1.2]",
3256
3385
  autoFocus: true,
3257
3386
  id: "annotation",
3258
- className: "bg-black border-primary-main",
3387
+ className: "border-primary-main bg-black",
3259
3388
  type: "text",
3260
3389
  value: value.label,
3261
3390
  onChange: onChangeHandler,
@@ -3265,11 +3394,11 @@ function PanelMeasurementTable(_ref) {
3265
3394
  actions: [{
3266
3395
  id: 'cancel',
3267
3396
  text: 'Cancel',
3268
- type: ui_src/* ButtonEnums.type */.LZ.U.secondary
3397
+ type: ui_src/* ButtonEnums.type */.LZ.dt.secondary
3269
3398
  }, {
3270
3399
  id: 'save',
3271
3400
  text: 'Save',
3272
- type: ui_src/* ButtonEnums.type */.LZ.U.primary
3401
+ type: ui_src/* ButtonEnums.type */.LZ.dt.primary
3273
3402
  }],
3274
3403
  onSubmit: onSubmitHandler
3275
3404
  }
@@ -3289,7 +3418,7 @@ function PanelMeasurementTable(_ref) {
3289
3418
  }
3290
3419
  };
3291
3420
  return /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement("div", {
3292
- className: "overflow-x-hidden overflow-y-auto ohif-scrollbar",
3421
+ className: "ohif-scrollbar overflow-y-auto overflow-x-hidden",
3293
3422
  "data-cy": 'measurements-panel'
3294
3423
  }, /*#__PURE__*/react.createElement(ui_src/* MeasurementTable */.wt, {
3295
3424
  title: "Measurements",
@@ -3316,7 +3445,7 @@ function _getMappedMeasurements(measurementService) {
3316
3445
 
3317
3446
  /**
3318
3447
  * Map the measurements to the display text.
3319
- * Adds finding and site inforamtion to the displayText and/or label,
3448
+ * Adds finding and site information to the displayText and/or label,
3320
3449
  * and provides as 'displayText' and 'label', while providing the original
3321
3450
  * values as baseDisplayText and baseLabel
3322
3451
  */
@@ -3336,7 +3465,9 @@ function _mapMeasurementToDisplay(measurement, index, types) {
3336
3465
  if (findingSites) {
3337
3466
  const siteText = [];
3338
3467
  findingSites.forEach(site => {
3339
- if (site?.text !== label) siteText.push(site.text);
3468
+ if (site?.text !== label) {
3469
+ siteText.push(site.text);
3470
+ }
3340
3471
  });
3341
3472
  displayText = [...siteText, ...displayText];
3342
3473
  }
@@ -3360,6 +3491,7 @@ function _mapMeasurementToDisplay(measurement, index, types) {
3360
3491
 
3361
3492
 
3362
3493
 
3494
+
3363
3495
  ;// CONCATENATED MODULE: ../../../extensions/default/src/getPanelModule.tsx
3364
3496
 
3365
3497
 
@@ -3384,7 +3516,7 @@ function getPanelModule(_ref) {
3384
3516
  };
3385
3517
  return [{
3386
3518
  name: 'seriesList',
3387
- iconName: 'group-layers',
3519
+ iconName: 'tab-studies',
3388
3520
  iconLabel: 'Studies',
3389
3521
  label: 'Studies',
3390
3522
  component: Panels_WrappedPanelStudyBrowser.bind(null, {
@@ -3403,25 +3535,335 @@ function getPanelModule(_ref) {
3403
3535
  }
3404
3536
  /* harmony default export */ const src_getPanelModule = (getPanelModule);
3405
3537
  // EXTERNAL MODULE: ../../core/src/utils/isImage.js
3406
- var isImage = __webpack_require__(81596);
3538
+ var isImage = __webpack_require__(11835);
3407
3539
  // EXTERNAL MODULE: ../../core/src/utils/sopClassDictionary.js
3408
- var sopClassDictionary = __webpack_require__(32746);
3540
+ var sopClassDictionary = __webpack_require__(24369);
3409
3541
  // EXTERNAL MODULE: ../../core/src/classes/ImageSet.ts
3410
- var ImageSet = __webpack_require__(4797);
3542
+ var ImageSet = __webpack_require__(13950);
3411
3543
  // EXTERNAL MODULE: ../../core/src/utils/isDisplaySetReconstructable.js
3412
- var isDisplaySetReconstructable = __webpack_require__(51227);
3544
+ var isDisplaySetReconstructable = __webpack_require__(89359);
3413
3545
  ;// CONCATENATED MODULE: ../../../extensions/default/package.json
3414
3546
  const package_namespaceObject = JSON.parse('{"u2":"@ohif/extension-default"}');
3415
3547
  ;// CONCATENATED MODULE: ../../../extensions/default/src/id.js
3416
3548
 
3417
3549
  const id = package_namespaceObject.u2;
3418
3550
 
3551
+ // EXTERNAL MODULE: ../../core/src/utils/sortInstancesByPosition.ts
3552
+ var sortInstancesByPosition = __webpack_require__(87425);
3553
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/utils/validations/checkMultiframe.ts
3554
+
3555
+
3556
+
3557
+ /**
3558
+ * Check various multi frame issues. It calls OHIF core functions
3559
+ * @param {*} multiFrameInstance
3560
+ * @param {*} warnings
3561
+ */
3562
+ function checkMultiFrame(multiFrameInstance, messages) {
3563
+ if (!(0,isDisplaySetReconstructable/* hasPixelMeasurements */.hu)(multiFrameInstance)) {
3564
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.MULTIFRAME_NO_PIXEL_MEASUREMENTS);
3565
+ }
3566
+ if (!(0,isDisplaySetReconstructable/* hasOrientation */.sb)(multiFrameInstance)) {
3567
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.MULTIFRAME_NO_ORIENTATION);
3568
+ }
3569
+ if (!(0,isDisplaySetReconstructable/* hasPosition */.kN)(multiFrameInstance)) {
3570
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.MULTIFRAME_NO_POSITION_INFORMATION);
3571
+ }
3572
+ }
3573
+ // EXTERNAL MODULE: ../../core/src/utils/toNumber.js
3574
+ var toNumber = __webpack_require__(94972);
3575
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/utils/validations/areAllImageDimensionsEqual.ts
3576
+
3577
+
3578
+ /**
3579
+ * Check if the frames in a series has different dimensions
3580
+ * @param {*} instances
3581
+ * @returns
3582
+ */
3583
+ function areAllImageDimensionsEqual(instances) {
3584
+ if (!instances?.length) {
3585
+ return false;
3586
+ }
3587
+ const firstImage = instances[0];
3588
+ const firstImageRows = (0,toNumber/* default */.Z)(firstImage.Rows);
3589
+ const firstImageColumns = (0,toNumber/* default */.Z)(firstImage.Columns);
3590
+ for (let i = 1; i < instances.length; i++) {
3591
+ const instance = instances[i];
3592
+ const {
3593
+ Rows,
3594
+ Columns
3595
+ } = instance;
3596
+ if (Rows !== firstImageRows || Columns !== firstImageColumns) {
3597
+ return false;
3598
+ }
3599
+ }
3600
+ return true;
3601
+ }
3602
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/utils/validations/areAllImageComponentsEqual.ts
3603
+
3604
+
3605
+ /**
3606
+ * Check if all voxels in series images has same number of components (samplesPerPixel)
3607
+ * @param {*} instances
3608
+ * @returns
3609
+ */
3610
+ function areAllImageComponentsEqual(instances) {
3611
+ if (!instances?.length) {
3612
+ return false;
3613
+ }
3614
+ const firstImage = instances[0];
3615
+ const firstImageSamplesPerPixel = (0,toNumber/* default */.Z)(firstImage.SamplesPerPixel);
3616
+ for (let i = 1; i < instances.length; i++) {
3617
+ const instance = instances[i];
3618
+ const {
3619
+ SamplesPerPixel
3620
+ } = instance;
3621
+ if (SamplesPerPixel !== firstImageSamplesPerPixel) {
3622
+ return false;
3623
+ }
3624
+ }
3625
+ return true;
3626
+ }
3627
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/utils/validations/areAllImageOrientationsEqual.ts
3628
+
3629
+
3630
+
3631
+ /**
3632
+ * Check is the series has frames with different orientations
3633
+ * @param {*} instances
3634
+ * @returns
3635
+ */
3636
+ function areAllImageOrientationsEqual(instances) {
3637
+ if (!instances?.length) {
3638
+ return false;
3639
+ }
3640
+ const firstImage = instances[0];
3641
+ const firstImageOrientationPatient = (0,toNumber/* default */.Z)(firstImage.ImageOrientationPatient);
3642
+ for (let i = 1; i < instances.length; i++) {
3643
+ const instance = instances[i];
3644
+ const imageOrientationPatient = (0,toNumber/* default */.Z)(instance.ImageOrientationPatient);
3645
+ if (!(0,isDisplaySetReconstructable/* _isSameOrientation */.NB)(imageOrientationPatient, firstImageOrientationPatient)) {
3646
+ return false;
3647
+ }
3648
+ }
3649
+ return true;
3650
+ }
3651
+ // EXTERNAL MODULE: ../../../node_modules/gl-matrix/esm/index.js + 10 modules
3652
+ var esm = __webpack_require__(45451);
3653
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/utils/calculateScanAxisNormal.ts
3654
+
3655
+
3656
+ /**
3657
+ * Calculates the scanAxisNormal based on a image orientation vector extract from a frame
3658
+ * @param {*} imageOrientation
3659
+ * @returns
3660
+ */
3661
+ function calculateScanAxisNormal(imageOrientation) {
3662
+ const rowCosineVec = esm/* vec3.fromValues */.R3.fromValues(imageOrientation[0], imageOrientation[1], imageOrientation[2]);
3663
+ const colCosineVec = esm/* vec3.fromValues */.R3.fromValues(imageOrientation[3], imageOrientation[4], imageOrientation[5]);
3664
+ return esm/* vec3.cross */.R3.cross(esm/* vec3.create */.R3.create(), rowCosineVec, colCosineVec);
3665
+ }
3666
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/utils/validations/areAllImagePositionsEqual.ts
3667
+
3668
+
3669
+
3670
+
3671
+
3672
+ /**
3673
+ * Checks if there is a position shift between consecutive frames
3674
+ * @param {*} previousPosition
3675
+ * @param {*} actualPosition
3676
+ * @param {*} scanAxisNormal
3677
+ * @param {*} averageSpacingBetweenFrames
3678
+ * @returns
3679
+ */
3680
+ function _checkSeriesPositionShift(previousPosition, actualPosition, scanAxisNormal, averageSpacingBetweenFrames) {
3681
+ // predicted position should be the previous position added by the multiplication
3682
+ // of the scanAxisNormal and the average spacing between frames
3683
+ const predictedPosition = esm/* vec3.scaleAndAdd */.R3.scaleAndAdd(esm/* vec3.create */.R3.create(), previousPosition, scanAxisNormal, averageSpacingBetweenFrames);
3684
+ return esm/* vec3.distance */.R3.distance(actualPosition, predictedPosition) > averageSpacingBetweenFrames;
3685
+ }
3686
+
3687
+ /**
3688
+ * Checks if a series has position shifts between consecutive frames
3689
+ * @param {*} instances
3690
+ * @returns
3691
+ */
3692
+ function areAllImagePositionsEqual(instances) {
3693
+ if (!instances?.length) {
3694
+ return false;
3695
+ }
3696
+ const firstImageOrientationPatient = (0,toNumber/* default */.Z)(instances[0].ImageOrientationPatient);
3697
+ if (!firstImageOrientationPatient) {
3698
+ return false;
3699
+ }
3700
+ const scanAxisNormal = calculateScanAxisNormal(firstImageOrientationPatient);
3701
+ const firstImagePositionPatient = (0,toNumber/* default */.Z)(instances[0].ImagePositionPatient);
3702
+ const lastIpp = (0,toNumber/* default */.Z)(instances[instances.length - 1].ImagePositionPatient);
3703
+ const averageSpacingBetweenFrames = (0,isDisplaySetReconstructable/* _getPerpendicularDistance */.Xn)(firstImagePositionPatient, lastIpp) / (instances.length - 1);
3704
+ let previousImagePositionPatient = firstImagePositionPatient;
3705
+ for (let i = 1; i < instances.length; i++) {
3706
+ const instance = instances[i];
3707
+ const imagePositionPatient = (0,toNumber/* default */.Z)(instance.ImagePositionPatient);
3708
+ if (_checkSeriesPositionShift(previousImagePositionPatient, imagePositionPatient, scanAxisNormal, averageSpacingBetweenFrames)) {
3709
+ return false;
3710
+ }
3711
+ previousImagePositionPatient = imagePositionPatient;
3712
+ }
3713
+ return true;
3714
+ }
3715
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/utils/validations/areAllImageSpacingEqual.ts
3716
+
3717
+
3718
+
3719
+ /**
3720
+ * Checks if series has spacing issues
3721
+ * @param {*} instances
3722
+ * @param {*} warnings
3723
+ */
3724
+ function areAllImageSpacingEqual(instances, messages) {
3725
+ if (!instances?.length) {
3726
+ return;
3727
+ }
3728
+ const firstImagePositionPatient = (0,toNumber/* default */.Z)(instances[0].ImagePositionPatient);
3729
+ if (!firstImagePositionPatient) {
3730
+ return;
3731
+ }
3732
+ const lastIpp = (0,toNumber/* default */.Z)(instances[instances.length - 1].ImagePositionPatient);
3733
+ const averageSpacingBetweenFrames = (0,isDisplaySetReconstructable/* _getPerpendicularDistance */.Xn)(firstImagePositionPatient, lastIpp) / (instances.length - 1);
3734
+ let previousImagePositionPatient = firstImagePositionPatient;
3735
+ const issuesFound = [];
3736
+ for (let i = 1; i < instances.length; i++) {
3737
+ const instance = instances[i];
3738
+ const imagePositionPatient = (0,toNumber/* default */.Z)(instance.ImagePositionPatient);
3739
+ const spacingBetweenFrames = (0,isDisplaySetReconstructable/* _getPerpendicularDistance */.Xn)(imagePositionPatient, previousImagePositionPatient);
3740
+ const spacingIssue = (0,isDisplaySetReconstructable/* _getSpacingIssue */.bg)(spacingBetweenFrames, averageSpacingBetweenFrames);
3741
+ if (spacingIssue) {
3742
+ const issue = spacingIssue.issue;
3743
+
3744
+ // avoid multiple warning of the same thing
3745
+ if (!issuesFound.includes(issue)) {
3746
+ issuesFound.push(issue);
3747
+ if (issue === isDisplaySetReconstructable/* reconstructionIssues */.e1.MISSING_FRAMES) {
3748
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.MISSING_FRAMES);
3749
+ } else if (issue === isDisplaySetReconstructable/* reconstructionIssues */.e1.IRREGULAR_SPACING) {
3750
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.IRREGULAR_SPACING);
3751
+ }
3752
+ }
3753
+ // we just want to find issues not how many
3754
+ if (issuesFound.length > 1) {
3755
+ break;
3756
+ }
3757
+ }
3758
+ previousImagePositionPatient = imagePositionPatient;
3759
+ }
3760
+ }
3761
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/utils/validations/checkSingleFrames.ts
3762
+
3763
+
3764
+
3765
+
3766
+
3767
+
3768
+
3769
+ /**
3770
+ * Runs various checks in a single frame series
3771
+ * @param {*} instances
3772
+ * @param {*} warnings
3773
+ */
3774
+ function checkSingleFrames(instances, messages) {
3775
+ if (instances.length > 2) {
3776
+ if (!areAllImageDimensionsEqual(instances)) {
3777
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.INCONSISTENT_DIMENSIONS);
3778
+ }
3779
+ if (!areAllImageComponentsEqual(instances)) {
3780
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.INCONSISTENT_COMPONENTS);
3781
+ }
3782
+ if (!areAllImageOrientationsEqual(instances)) {
3783
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.INCONSISTENT_ORIENTATIONS);
3784
+ }
3785
+ if (!areAllImagePositionsEqual(instances)) {
3786
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.INCONSISTENT_POSITION_INFORMATION);
3787
+ }
3788
+ areAllImageSpacingEqual(instances, messages);
3789
+ }
3790
+ }
3791
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/getDisplaySetMessages.ts
3792
+
3793
+
3794
+
3795
+
3796
+
3797
+ /**
3798
+ * Checks if a series is reconstructable to a 3D volume.
3799
+ *
3800
+ * @param {Object[]} instances An array of `OHIFInstanceMetadata` objects.
3801
+ */
3802
+ function getDisplaySetMessages(instances, isReconstructable) {
3803
+ const messages = new src/* DisplaySetMessageList */.iK();
3804
+ if (!instances.length) {
3805
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.NO_VALID_INSTANCES);
3806
+ }
3807
+ const firstInstance = instances[0];
3808
+ // Due to current requirements, LOCALIZER series doesn't have any messages
3809
+ if (firstInstance?.ImageType?.includes('LOCALIZER')) {
3810
+ return messages;
3811
+ }
3812
+ const Modality = firstInstance.Modality;
3813
+ if (!isDisplaySetReconstructable/* constructableModalities */.M6.includes(Modality)) {
3814
+ return messages;
3815
+ }
3816
+ const isMultiframe = firstInstance.NumberOfFrames > 1;
3817
+ // Can't reconstruct if all instances don't have the ImagePositionPatient.
3818
+ if (!isMultiframe && !instances.every(instance => instance.ImagePositionPatient)) {
3819
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.NO_POSITION_INFORMATION);
3820
+ }
3821
+ const sortedInstances = (0,sortInstancesByPosition/* default */.Z)(instances);
3822
+ isMultiframe ? checkMultiFrame(sortedInstances[0], messages) : checkSingleFrames(sortedInstances, messages);
3823
+ if (!isReconstructable) {
3824
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.NOT_RECONSTRUCTABLE);
3825
+ }
3826
+ return messages;
3827
+ }
3828
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/getDisplaySetsFromUnsupportedSeries.js
3829
+
3830
+
3831
+ /**
3832
+ * Default handler for a instance list with an unsupported sopClassUID
3833
+ */
3834
+ function getDisplaySetsFromUnsupportedSeries(instances) {
3835
+ const imageSet = new ImageSet/* default */.Z(instances);
3836
+ const messages = new src/* DisplaySetMessageList */.iK();
3837
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.UNSUPPORTED_DISPLAYSET);
3838
+ const instance = instances[0];
3839
+ imageSet.setAttributes({
3840
+ displaySetInstanceUID: imageSet.uid,
3841
+ // create a local alias for the imageSet UID
3842
+ SeriesDate: instance.SeriesDate,
3843
+ SeriesTime: instance.SeriesTime,
3844
+ SeriesInstanceUID: instance.SeriesInstanceUID,
3845
+ StudyInstanceUID: instance.StudyInstanceUID,
3846
+ SeriesNumber: instance.SeriesNumber || 0,
3847
+ FrameRate: instance.FrameTime,
3848
+ SOPClassUID: instance.SOPClassUID,
3849
+ SeriesDescription: instance.SeriesDescription || '',
3850
+ Modality: instance.Modality,
3851
+ numImageFrames: instances.length,
3852
+ unsupported: true,
3853
+ SOPClassHandlerId: 'unsupported',
3854
+ isReconstructable: false,
3855
+ messages
3856
+ });
3857
+ return [imageSet];
3858
+ }
3419
3859
  ;// CONCATENATED MODULE: ../../../extensions/default/src/getSopClassHandlerModule.js
3420
3860
 
3421
3861
 
3422
3862
 
3423
3863
 
3424
3864
 
3865
+
3866
+
3425
3867
  const sopClassHandlerName = 'stack';
3426
3868
  const isMultiFrame = instance => {
3427
3869
  return instance.NumberOfFrames > 1;
@@ -3432,9 +3874,9 @@ const makeDisplaySet = instances => {
3432
3874
  const {
3433
3875
  value: isReconstructable,
3434
3876
  averageSpacingBetweenFrames
3435
- } = (0,isDisplaySetReconstructable/* default */.Z)(instances);
3436
-
3877
+ } = (0,isDisplaySetReconstructable/* default */.ZP)(instances);
3437
3878
  // set appropriate attributes to image set...
3879
+ const messages = getDisplaySetMessages(instances, isReconstructable);
3438
3880
  imageSet.setAttributes({
3439
3881
  displaySetInstanceUID: imageSet.uid,
3440
3882
  // create a local alias for the imageSet UID
@@ -3452,6 +3894,7 @@ const makeDisplaySet = instances => {
3452
3894
  numImageFrames: instances.length,
3453
3895
  SOPClassHandlerId: `${id}.sopClassHandlerModule.${sopClassHandlerName}`,
3454
3896
  isReconstructable,
3897
+ messages,
3455
3898
  averageSpacingBetweenFrames: averageSpacingBetweenFrames || null
3456
3899
  });
3457
3900
 
@@ -3558,6 +4001,10 @@ function getSopClassHandlerModule() {
3558
4001
  name: sopClassHandlerName,
3559
4002
  sopClassUids,
3560
4003
  getDisplaySetsFromSeries
4004
+ }, {
4005
+ name: 'not-supported-display-sets-handler',
4006
+ sopClassUids: [],
4007
+ getDisplaySetsFromSeries: getDisplaySetsFromUnsupportedSeries
3561
4008
  }];
3562
4009
  }
3563
4010
  /* harmony default export */ const src_getSopClassHandlerModule = (getSopClassHandlerModule);
@@ -3565,44 +4012,53 @@ function getSopClassHandlerModule() {
3565
4012
 
3566
4013
  function ToolbarDivider() {
3567
4014
  return /*#__PURE__*/react.createElement("span", {
3568
- className: "self-center w-4 h-8 mx-2 border-l border-common-dark"
4015
+ className: "border-common-dark mx-2 h-8 w-4 self-center border-l"
3569
4016
  });
3570
4017
  }
3571
4018
  ;// CONCATENATED MODULE: ../../../extensions/default/src/Toolbar/ToolbarLayoutSelector.tsx
4019
+ 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
4020
 
3573
4021
 
3574
4022
 
3575
4023
 
3576
- function LayoutSelector(_ref) {
4024
+ function ToolbarLayoutSelectorWithServices(_ref) {
3577
4025
  let {
3578
- rows,
3579
- columns,
3580
- className,
3581
4026
  servicesManager,
3582
- ...rest
4027
+ ...props
3583
4028
  } = _ref;
3584
- const [isOpen, setIsOpen] = (0,react.useState)(false);
3585
4029
  const {
3586
- hangingProtocolService,
3587
4030
  toolbarService
3588
4031
  } = servicesManager.services;
4032
+ const onSelection = (0,react.useCallback)(props => {
4033
+ toolbarService.recordInteraction({
4034
+ interactionType: 'action',
4035
+ commands: [{
4036
+ commandName: 'setViewportGridLayout',
4037
+ commandOptions: {
4038
+ ...props
4039
+ },
4040
+ context: 'DEFAULT'
4041
+ }]
4042
+ });
4043
+ }, [toolbarService]);
4044
+ return /*#__PURE__*/react.createElement(LayoutSelector, ToolbarLayoutSelector_extends({}, props, {
4045
+ onSelection: onSelection
4046
+ }));
4047
+ }
4048
+ function LayoutSelector(_ref2) {
4049
+ let {
4050
+ rows,
4051
+ columns,
4052
+ className,
4053
+ onSelection,
4054
+ ...rest
4055
+ } = _ref2;
4056
+ const [isOpen, setIsOpen] = (0,react.useState)(false);
3589
4057
  const closeOnOutsideClick = () => {
3590
4058
  if (isOpen) {
3591
4059
  setIsOpen(false);
3592
4060
  }
3593
4061
  };
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
4062
  (0,react.useEffect)(() => {
3607
4063
  window.addEventListener('click', closeOnOutsideClick);
3608
4064
  return () => {
@@ -3611,18 +4067,6 @@ function LayoutSelector(_ref) {
3611
4067
  }, [isOpen]);
3612
4068
  const onInteractionHandler = () => setIsOpen(!isOpen);
3613
4069
  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
4070
  return /*#__PURE__*/react.createElement(ui_src/* ToolbarButton */.hA, {
3627
4071
  id: "Layout",
3628
4072
  label: "Grid Layout",
@@ -3633,7 +4077,7 @@ function LayoutSelector(_ref) {
3633
4077
  dropdownContent: DropdownContent !== null && /*#__PURE__*/react.createElement(DropdownContent, {
3634
4078
  rows: rows,
3635
4079
  columns: columns,
3636
- onSelection: onSelectionHandler
4080
+ onSelection: onSelection
3637
4081
  }),
3638
4082
  isActive: isOpen,
3639
4083
  type: "toggle"
@@ -3650,10 +4094,238 @@ LayoutSelector.defaultProps = {
3650
4094
  columns: 3,
3651
4095
  onLayoutChange: () => {}
3652
4096
  };
3653
- /* harmony default export */ const ToolbarLayoutSelector = (LayoutSelector);
3654
- ;// CONCATENATED MODULE: ../../../extensions/default/src/Toolbar/ToolbarSplitButton.tsx
4097
+ /* harmony default export */ const ToolbarLayoutSelector = (ToolbarLayoutSelectorWithServices);
4098
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/Toolbar/ToolbarSplitButtonWithServices.tsx
4099
+ 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); }
4100
+
3655
4101
 
3656
- /* harmony default export */ const ToolbarSplitButton = (ui_src/* SplitButton */.aW);
4102
+
4103
+
4104
+ function ToolbarSplitButtonWithServices(_ref) {
4105
+ let {
4106
+ isRadio,
4107
+ isAction,
4108
+ groupId,
4109
+ primary,
4110
+ secondary,
4111
+ items,
4112
+ renderer,
4113
+ onInteraction,
4114
+ servicesManager
4115
+ } = _ref;
4116
+ const {
4117
+ toolbarService
4118
+ } = servicesManager?.services;
4119
+ const handleItemClick = (item, index) => {
4120
+ const {
4121
+ id,
4122
+ type,
4123
+ commands
4124
+ } = item;
4125
+ onInteraction({
4126
+ groupId,
4127
+ itemId: id,
4128
+ interactionType: type,
4129
+ commands
4130
+ });
4131
+ setState(state => ({
4132
+ ...state,
4133
+ primary: !isAction && isRadio ? {
4134
+ ...item,
4135
+ index
4136
+ } : state.primary,
4137
+ isExpanded: false,
4138
+ items: getSplitButtonItems(items).filter(item => isRadio && !isAction ? item.index !== index : true)
4139
+ }));
4140
+ };
4141
+
4142
+ /* Bubbles up individual item clicks */
4143
+ const getSplitButtonItems = items => items.map((item, index) => ({
4144
+ ...item,
4145
+ index,
4146
+ onClick: () => handleItemClick(item, index)
4147
+ }));
4148
+ const [buttonsState, setButtonState] = (0,react.useState)({
4149
+ primaryToolId: '',
4150
+ toggles: {},
4151
+ groups: {}
4152
+ });
4153
+ const [state, setState] = (0,react.useState)({
4154
+ primary,
4155
+ items: getSplitButtonItems(items).filter(item => isRadio && !isAction ? item.id !== primary.id : true)
4156
+ });
4157
+ const {
4158
+ primaryToolId,
4159
+ toggles
4160
+ } = buttonsState;
4161
+ const isPrimaryToggle = state.primary.type === 'toggle';
4162
+ const isPrimaryActive = state.primary.type === 'tool' && primaryToolId === state.primary.id || isPrimaryToggle && toggles[state.primary.id] === true;
4163
+ const PrimaryButtonComponent = toolbarService?.getButtonComponentForUIType(state.primary.uiType) ?? ui_src/* ToolbarButton */.hA;
4164
+ (0,react.useEffect)(() => {
4165
+ const {
4166
+ unsubscribe
4167
+ } = toolbarService.subscribe(toolbarService.EVENTS.TOOL_BAR_STATE_MODIFIED, state => {
4168
+ setButtonState({
4169
+ ...state
4170
+ });
4171
+ });
4172
+ return () => {
4173
+ unsubscribe();
4174
+ };
4175
+ }, [toolbarService]);
4176
+ const updatedItems = state.items.map(item => {
4177
+ const isActive = item.type === 'tool' && primaryToolId === item.id;
4178
+
4179
+ // We could have added the
4180
+ // item.type === 'toggle' && toggles[item.id] === true
4181
+ // too but that makes the button active when the toggle is active under it
4182
+ // which feels weird
4183
+ return {
4184
+ ...item,
4185
+ isActive
4186
+ };
4187
+ });
4188
+ const DefaultListItemRenderer = _ref2 => {
4189
+ let {
4190
+ type,
4191
+ icon,
4192
+ label,
4193
+ t,
4194
+ id
4195
+ } = _ref2;
4196
+ const isActive = type === 'toggle' && toggles[id] === true;
4197
+ return /*#__PURE__*/react.createElement("div", {
4198
+ 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')
4199
+ }, icon && /*#__PURE__*/react.createElement("span", {
4200
+ className: "mr-4"
4201
+ }, /*#__PURE__*/react.createElement(ui_src/* Icon */.JO, {
4202
+ name: icon,
4203
+ className: "h-5 w-5"
4204
+ })), /*#__PURE__*/react.createElement("span", {
4205
+ className: "mr-5"
4206
+ }, t(label)));
4207
+ };
4208
+ const listItemRenderer = renderer || DefaultListItemRenderer;
4209
+ return /*#__PURE__*/react.createElement(ui_src/* SplitButton */.aW, {
4210
+ isRadio: isRadio,
4211
+ isAction: isAction,
4212
+ primary: state.primary,
4213
+ secondary: secondary,
4214
+ items: updatedItems,
4215
+ groupId: groupId,
4216
+ renderer: listItemRenderer,
4217
+ isActive: isPrimaryActive || updatedItems.some(item => item.isActive),
4218
+ isToggle: isPrimaryToggle,
4219
+ onInteraction: onInteraction,
4220
+ Component: props => /*#__PURE__*/react.createElement(PrimaryButtonComponent, ToolbarSplitButtonWithServices_extends({}, props, {
4221
+ servicesManager: servicesManager
4222
+ }))
4223
+ });
4224
+ }
4225
+ ToolbarSplitButtonWithServices.propTypes = {
4226
+ isRadio: (prop_types_default()).bool,
4227
+ isAction: (prop_types_default()).bool,
4228
+ groupId: (prop_types_default()).string,
4229
+ primary: prop_types_default().shape({
4230
+ id: (prop_types_default()).string.isRequired,
4231
+ type: prop_types_default().oneOf(['tool', 'action', 'toggle']).isRequired,
4232
+ uiType: (prop_types_default()).string
4233
+ }),
4234
+ secondary: prop_types_default().shape({
4235
+ id: (prop_types_default()).string,
4236
+ icon: (prop_types_default()).string.isRequired,
4237
+ label: (prop_types_default()).string,
4238
+ tooltip: (prop_types_default()).string.isRequired,
4239
+ isActive: (prop_types_default()).bool
4240
+ }),
4241
+ items: prop_types_default().arrayOf(prop_types_default().shape({
4242
+ id: (prop_types_default()).string.isRequired,
4243
+ type: prop_types_default().oneOf(['tool', 'action', 'toggle']).isRequired,
4244
+ icon: (prop_types_default()).string,
4245
+ label: (prop_types_default()).string,
4246
+ tooltip: (prop_types_default()).string
4247
+ })),
4248
+ renderer: (prop_types_default()).func,
4249
+ onInteraction: (prop_types_default()).func.isRequired,
4250
+ servicesManager: prop_types_default().shape({
4251
+ services: prop_types_default().shape({
4252
+ toolbarService: (prop_types_default()).object
4253
+ })
4254
+ })
4255
+ };
4256
+ ToolbarSplitButtonWithServices.defaultProps = {
4257
+ isRadio: false,
4258
+ isAction: false
4259
+ };
4260
+ /* harmony default export */ const Toolbar_ToolbarSplitButtonWithServices = (ToolbarSplitButtonWithServices);
4261
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/Toolbar/ToolbarButtonWithServices.tsx
4262
+ 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); }
4263
+
4264
+
4265
+
4266
+ function ToolbarButtonWithServices(_ref) {
4267
+ let {
4268
+ id,
4269
+ type,
4270
+ commands,
4271
+ onInteraction,
4272
+ servicesManager,
4273
+ ...props
4274
+ } = _ref;
4275
+ const {
4276
+ toolbarService
4277
+ } = servicesManager?.services || {};
4278
+ const [buttonsState, setButtonState] = (0,react.useState)({
4279
+ primaryToolId: '',
4280
+ toggles: {},
4281
+ groups: {}
4282
+ });
4283
+ const {
4284
+ primaryToolId
4285
+ } = buttonsState;
4286
+ const isActive = type === 'tool' && id === primaryToolId || type === 'toggle' && buttonsState.toggles[id] === true;
4287
+ (0,react.useEffect)(() => {
4288
+ const {
4289
+ unsubscribe
4290
+ } = toolbarService.subscribe(toolbarService.EVENTS.TOOL_BAR_STATE_MODIFIED, state => {
4291
+ setButtonState({
4292
+ ...state
4293
+ });
4294
+ });
4295
+ return () => {
4296
+ unsubscribe();
4297
+ };
4298
+ }, [toolbarService]);
4299
+ return /*#__PURE__*/react.createElement(ui_src/* ToolbarButton */.hA, ToolbarButtonWithServices_extends({
4300
+ commands: commands,
4301
+ id: id,
4302
+ type: type,
4303
+ isActive: isActive,
4304
+ onInteraction: onInteraction
4305
+ }, props));
4306
+ }
4307
+ ToolbarButtonWithServices.propTypes = {
4308
+ id: (prop_types_default()).string.isRequired,
4309
+ type: prop_types_default().oneOf(['tool', 'action', 'toggle']).isRequired,
4310
+ commands: prop_types_default().arrayOf(prop_types_default().shape({
4311
+ commandName: (prop_types_default()).string.isRequired,
4312
+ context: (prop_types_default()).string
4313
+ })),
4314
+ onInteraction: (prop_types_default()).func.isRequired,
4315
+ servicesManager: prop_types_default().shape({
4316
+ services: prop_types_default().shape({
4317
+ toolbarService: prop_types_default().shape({
4318
+ subscribe: (prop_types_default()).func.isRequired,
4319
+ state: prop_types_default().shape({
4320
+ primaryToolId: (prop_types_default()).string,
4321
+ toggles: prop_types_default().objectOf((prop_types_default()).bool),
4322
+ groups: prop_types_default().objectOf((prop_types_default()).object)
4323
+ }).isRequired
4324
+ }).isRequired
4325
+ }).isRequired
4326
+ }).isRequired
4327
+ };
4328
+ /* harmony default export */ const Toolbar_ToolbarButtonWithServices = (ToolbarButtonWithServices);
3657
4329
  ;// CONCATENATED MODULE: ../../../extensions/default/src/getToolbarModule.tsx
3658
4330
 
3659
4331
 
@@ -3670,15 +4342,15 @@ function getToolbarModule(_ref) {
3670
4342
  clickHandler: () => {}
3671
4343
  }, {
3672
4344
  name: 'ohif.action',
3673
- defaultComponent: ui_src/* ToolbarButton */.hA,
4345
+ defaultComponent: Toolbar_ToolbarButtonWithServices,
3674
4346
  clickHandler: () => {}
3675
4347
  }, {
3676
4348
  name: 'ohif.radioGroup',
3677
- defaultComponent: ui_src/* ToolbarButton */.hA,
4349
+ defaultComponent: Toolbar_ToolbarButtonWithServices,
3678
4350
  clickHandler: () => {}
3679
4351
  }, {
3680
4352
  name: 'ohif.splitButton',
3681
- defaultComponent: ToolbarSplitButton,
4353
+ defaultComponent: Toolbar_ToolbarSplitButtonWithServices,
3682
4354
  clickHandler: () => {}
3683
4355
  }, {
3684
4356
  name: 'ohif.layoutSelector',
@@ -3686,7 +4358,7 @@ function getToolbarModule(_ref) {
3686
4358
  clickHandler: (evt, clickedBtn, btnSectionName) => {}
3687
4359
  }, {
3688
4360
  name: 'ohif.toggle',
3689
- defaultComponent: ui_src/* ToolbarButton */.hA,
4361
+ defaultComponent: Toolbar_ToolbarButtonWithServices,
3690
4362
  clickHandler: () => {}
3691
4363
  }];
3692
4364
  }
@@ -3856,7 +4528,7 @@ function adaptItem(item, subProps) {
3856
4528
  return newItem;
3857
4529
  }
3858
4530
  // EXTERNAL MODULE: ../../ui/src/components/ContextMenu/ContextMenu.tsx
3859
- var ContextMenu = __webpack_require__(63021);
4531
+ var ContextMenu = __webpack_require__(5638);
3860
4532
  ;// CONCATENATED MODULE: ../../../extensions/default/src/CustomizableContextMenu/ContextMenuController.tsx
3861
4533
 
3862
4534
 
@@ -3917,8 +4589,8 @@ class ContextMenuController {
3917
4589
  defaultPosition: ContextMenuController._getDefaultPosition(defaultPointsPosition, event?.detail, viewportElement),
3918
4590
  event,
3919
4591
  content: ContextMenu/* default */.Z,
3920
- // This naming is part of hte uiDialogService convention
3921
- // Clicking outside simpy closes the dialog box.
4592
+ // This naming is part of the uiDialogService convention
4593
+ // Clicking outside simply closes the dialog box.
3922
4594
  onClickOutside: () => this.services.uiDialogService.dismiss({
3923
4595
  id: 'context-menu'
3924
4596
  }),
@@ -4065,10 +4737,10 @@ const defaultContextMenu = {
4065
4737
 
4066
4738
 
4067
4739
  // EXTERNAL MODULE: ../../../node_modules/moment/moment.js
4068
- var moment = __webpack_require__(53806);
4740
+ var moment = __webpack_require__(71271);
4069
4741
  var moment_default = /*#__PURE__*/__webpack_require__.n(moment);
4070
4742
  // EXTERNAL MODULE: ../../../node_modules/react-window/dist/index.esm.js
4071
- var index_esm = __webpack_require__(21767);
4743
+ var index_esm = __webpack_require__(94614);
4072
4744
  ;// CONCATENATED MODULE: ../../../extensions/default/src/DicomTagBrowser/DicomTagTable.tsx
4073
4745
 
4074
4746
 
@@ -4093,34 +4765,34 @@ function ColumnHeaders(_ref) {
4093
4765
  valueRef
4094
4766
  } = _ref;
4095
4767
  return /*#__PURE__*/react.createElement("div", {
4096
- className: classnames_default()('flex flex-row w-full bg-secondary-dark ohif-scrollbar overflow-y-scroll'),
4768
+ className: classnames_default()('bg-secondary-dark ohif-scrollbar flex w-full flex-row overflow-y-scroll'),
4097
4769
  style: rowVerticalPaddingStyle
4098
4770
  }, /*#__PURE__*/react.createElement("div", {
4099
- className: "px-3 w-4/24"
4771
+ className: "w-4/24 px-3"
4100
4772
  }, /*#__PURE__*/react.createElement("label", {
4101
4773
  ref: tagRef,
4102
- className: "flex flex-col flex-1 text-white text-lg pl-1 select-none"
4774
+ className: "flex flex-1 select-none flex-col pl-1 text-lg text-white"
4103
4775
  }, /*#__PURE__*/react.createElement("span", {
4104
4776
  className: "flex flex-row items-center focus:outline-none"
4105
4777
  }, "Tag"))), /*#__PURE__*/react.createElement("div", {
4106
- className: "px-3 w-2/24"
4778
+ className: "w-2/24 px-3"
4107
4779
  }, /*#__PURE__*/react.createElement("label", {
4108
4780
  ref: vrRef,
4109
- className: "flex flex-col flex-1 text-white text-lg pl-1 select-none"
4781
+ className: "flex flex-1 select-none flex-col pl-1 text-lg text-white"
4110
4782
  }, /*#__PURE__*/react.createElement("span", {
4111
4783
  className: "flex flex-row items-center focus:outline-none"
4112
4784
  }, "VR"))), /*#__PURE__*/react.createElement("div", {
4113
- className: "px-3 w-6/24"
4785
+ className: "w-6/24 px-3"
4114
4786
  }, /*#__PURE__*/react.createElement("label", {
4115
4787
  ref: keywordRef,
4116
- className: "flex flex-col flex-1 text-white text-lg pl-1 select-none"
4788
+ className: "flex flex-1 select-none flex-col pl-1 text-lg text-white"
4117
4789
  }, /*#__PURE__*/react.createElement("span", {
4118
4790
  className: "flex flex-row items-center focus:outline-none"
4119
4791
  }, "Keyword"))), /*#__PURE__*/react.createElement("div", {
4120
- className: "px-3 w-5/24 grow"
4792
+ className: "w-5/24 grow px-3"
4121
4793
  }, /*#__PURE__*/react.createElement("label", {
4122
4794
  ref: valueRef,
4123
- className: "flex flex-col flex-1 text-white text-lg pl-1 select-none"
4795
+ className: "flex flex-1 select-none flex-col pl-1 text-lg text-white"
4124
4796
  }, /*#__PURE__*/react.createElement("span", {
4125
4797
  className: "flex flex-row items-center focus:outline-none"
4126
4798
  }, "Value"))));
@@ -4193,16 +4865,16 @@ function DicomTagTable(_ref2) {
4193
4865
  ...style,
4194
4866
  ...rowStyle
4195
4867
  },
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),
4868
+ 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
4869
  key: `DICOMTagRow-${index}`
4198
4870
  }, /*#__PURE__*/react.createElement("div", {
4199
- className: "px-3 w-4/24"
4871
+ className: "w-4/24 px-3"
4200
4872
  }, row[0]), /*#__PURE__*/react.createElement("div", {
4201
- className: "px-3 w-2/24"
4873
+ className: "w-2/24 px-3"
4202
4874
  }, row[1]), /*#__PURE__*/react.createElement("div", {
4203
- className: "px-3 w-6/24"
4875
+ className: "w-6/24 px-3"
4204
4876
  }, row[2]), /*#__PURE__*/react.createElement("div", {
4205
- className: "px-3 w-5/24 grow"
4877
+ className: "w-5/24 grow px-3"
4206
4878
  }, row[3]));
4207
4879
  }, [rows]);
4208
4880
 
@@ -4240,7 +4912,7 @@ function DicomTagTable(_ref2) {
4240
4912
  keywordRef: keywordRef,
4241
4913
  valueRef: valueRef
4242
4914
  }), /*#__PURE__*/react.createElement("div", {
4243
- className: "m-auto relative border-2 border-black bg-black",
4915
+ className: "relative m-auto border-2 border-black bg-black",
4244
4916
  style: {
4245
4917
  height: '32rem'
4246
4918
  }
@@ -4266,7 +4938,6 @@ function DicomTagTable(_ref2) {
4266
4938
 
4267
4939
 
4268
4940
 
4269
-
4270
4941
  const {
4271
4942
  ImageSet: DicomTagBrowser_ImageSet
4272
4943
  } = src.classes;
@@ -4295,7 +4966,6 @@ const DicomTagBrowser = _ref => {
4295
4966
  setSelectedDisplaySetInstanceUID(value.value);
4296
4967
  setInstanceNumber(1);
4297
4968
  };
4298
- const searchInputRef = (0,react.useRef)(null);
4299
4969
  const activeDisplaySet = displaySets.find(ds => ds.displaySetInstanceUID === selectedDisplaySetInstanceUID);
4300
4970
  const isImageStack = _isImageStack(activeDisplaySet);
4301
4971
  const showInstanceList = isImageStack && activeDisplaySet.images.length > 1;
@@ -4361,14 +5031,14 @@ const DicomTagBrowser = _ref => {
4361
5031
  return /*#__PURE__*/react.createElement("div", {
4362
5032
  className: "dicom-tag-browser-content"
4363
5033
  }, /*#__PURE__*/react.createElement("div", {
4364
- className: "flex flex-row mb-6 items-center pl-1"
5034
+ className: "mb-6 flex flex-row items-center pl-1"
4365
5035
  }, /*#__PURE__*/react.createElement("div", {
4366
- className: "flex flex-row items-center w-1/2"
5036
+ className: "flex w-1/2 flex-row items-center"
4367
5037
  }, /*#__PURE__*/react.createElement(ui_src/* Typography */.ZT, {
4368
5038
  variant: "subtitle",
4369
5039
  className: "mr-4"
4370
5040
  }, "Series"), /*#__PURE__*/react.createElement("div", {
4371
- className: "grow mr-8"
5041
+ className: "mr-8 grow"
4372
5042
  }, /*#__PURE__*/react.createElement(ui_src/* Select */.Ph, {
4373
5043
  id: "display-set-selector",
4374
5044
  isClearable: false,
@@ -4377,7 +5047,7 @@ const DicomTagBrowser = _ref => {
4377
5047
  value: displaySetList.find(ds => ds.value === selectedDisplaySetInstanceUID),
4378
5048
  className: "text-white"
4379
5049
  }))), /*#__PURE__*/react.createElement("div", {
4380
- className: "flex flex-row items-center w-1/2"
5050
+ className: "flex w-1/2 flex-row items-center"
4381
5051
  }, showInstanceList && /*#__PURE__*/react.createElement(ui_src/* Typography */.ZT, {
4382
5052
  variant: "subtitle",
4383
5053
  className: "mr-4"
@@ -4396,32 +5066,14 @@ const DicomTagBrowser = _ref => {
4396
5066
  labelPosition: "left",
4397
5067
  trackColor: '#3a3f99'
4398
5068
  })))), /*#__PURE__*/react.createElement("div", {
4399
- className: "w-full h-1 bg-black"
5069
+ className: "h-1 w-full bg-black"
4400
5070
  }), /*#__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",
5071
+ className: "my-3 flex w-1/2 flex-row"
5072
+ }, /*#__PURE__*/react.createElement(ui_src/* InputFilterText */.Xe, {
5073
+ className: "mr-8 block w-full",
4412
5074
  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, {
5075
+ onDebounceChange: setFilterValue
5076
+ })), /*#__PURE__*/react.createElement(DicomTagBrowser_DicomTagTable, {
4425
5077
  rows: filteredRows
4426
5078
  }));
4427
5079
  };
@@ -4594,61 +5246,62 @@ function _sortTagList(tagList) {
4594
5246
  */
4595
5247
  const reuseCachedLayout = (state, hangingProtocolService, syncService) => {
4596
5248
  const {
4597
- activeViewportIndex,
4598
- viewports,
4599
- layout
5249
+ activeViewportId
4600
5250
  } = state;
5251
+ const {
5252
+ protocol
5253
+ } = hangingProtocolService.getActiveProtocol();
4601
5254
  const hpInfo = hangingProtocolService.getState();
4602
5255
  const {
4603
5256
  protocolId,
4604
5257
  stageIndex,
4605
5258
  activeStudyUID
4606
5259
  } = hpInfo;
4607
- const {
4608
- protocol
4609
- } = hangingProtocolService.getActiveProtocol();
4610
- const stage = protocol.stages[stageIndex];
4611
- const storeId = `${activeStudyUID}:${protocolId}:${stageIndex}`;
4612
5260
  const syncState = syncService.getState();
4613
- const cacheId = `${activeStudyUID}:${protocolId}`;
4614
5261
  const viewportGridStore = {
4615
5262
  ...syncState.viewportGridStore
4616
5263
  };
5264
+ const displaySetSelectorMap = {
5265
+ ...syncState.displaySetSelectorMap
5266
+ };
5267
+ const stage = protocol.stages[stageIndex];
5268
+ const storeId = `${activeStudyUID}:${protocolId}:${stageIndex}`;
5269
+ const cacheId = `${activeStudyUID}:${protocolId}`;
4617
5270
  const hangingProtocolStageIndexMap = {
4618
5271
  ...syncState.hangingProtocolStageIndexMap
4619
5272
  };
4620
- const displaySetSelectorMap = {
4621
- ...syncState.displaySetSelectorMap
4622
- };
4623
5273
  const {
4624
5274
  rows,
4625
5275
  columns
4626
5276
  } = stage.viewportStructure.properties;
4627
- const custom = stage.viewports.length !== state.viewports.length || state.layout.numRows !== rows || state.layout.numCols !== columns;
5277
+ const custom = stage.viewports.length !== state.viewports.size || state.layout.numRows !== rows || state.layout.numCols !== columns;
4628
5278
  hangingProtocolStageIndexMap[cacheId] = hpInfo;
4629
5279
  if (storeId && custom) {
4630
5280
  viewportGridStore[storeId] = {
4631
5281
  ...state
4632
5282
  };
4633
5283
  }
4634
- for (let idx = 0; idx < state.viewports.length; idx++) {
4635
- const viewport = state.viewports[idx];
5284
+ state.viewports.forEach((viewport, viewportId) => {
4636
5285
  const {
4637
5286
  displaySetOptions,
4638
5287
  displaySetInstanceUIDs
4639
5288
  } = viewport;
4640
- if (!displaySetOptions) continue;
5289
+ if (!displaySetOptions) {
5290
+ return;
5291
+ }
4641
5292
  for (let i = 0; i < displaySetOptions.length; i++) {
4642
5293
  const displaySetUID = displaySetInstanceUIDs[i];
4643
- if (!displaySetUID) continue;
4644
- if (idx === activeViewportIndex && i === 0) {
5294
+ if (!displaySetUID) {
5295
+ continue;
5296
+ }
5297
+ if (viewportId === activeViewportId && i === 0) {
4645
5298
  displaySetSelectorMap[`${activeStudyUID}:activeDisplaySet:0`] = displaySetUID;
4646
5299
  }
4647
5300
  if (displaySetOptions[i]?.id) {
4648
5301
  displaySetSelectorMap[`${activeStudyUID}:${displaySetOptions[i].id}:${displaySetOptions[i].matchedDisplaySetsIndex || 0}`] = displaySetUID;
4649
5302
  }
4650
5303
  }
4651
- }
5304
+ });
4652
5305
  return {
4653
5306
  hangingProtocolStageIndexMap,
4654
5307
  viewportGridStore,
@@ -4660,22 +5313,24 @@ const reuseCachedLayout = (state, hangingProtocolService, syncService) => {
4660
5313
  /**
4661
5314
  * This find or create viewport is paired with the reduce results from
4662
5315
  * 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,
5316
+ * viewports, and to reuse by position id. If there is no filled viewport,
4664
5317
  * then one can be re-used from the display set if it isn't going to be displayed.
4665
5318
  * @param hangingProtocolService - bound parameter supplied before using this
4666
5319
  * @param viewportsByPosition - bound parameter supplied before using this
4667
- * @param viewportIndex - the index to retrieve
5320
+ * @param position - the position in the grid to retrieve
4668
5321
  * @param positionId - the current position on screen to retrieve
4669
5322
  * @param options - the set of options used, so that subsequent calls can
4670
5323
  * store state that is reset by the setLayout.
4671
5324
  * This class uses the options to store the already viewed
4672
5325
  * display sets, filling it initially with the pre-existing viewports.
4673
5326
  */
4674
- const findViewportsByPosition_findOrCreateViewport = (hangingProtocolService, viewportsByPosition, viewportIndex, positionId, options) => {
5327
+ const findViewportsByPosition_findOrCreateViewport = (hangingProtocolService, viewportsByPosition, position, positionId, options) => {
4675
5328
  const byPositionViewport = viewportsByPosition?.[positionId];
4676
- if (byPositionViewport) return {
4677
- ...byPositionViewport
4678
- };
5329
+ if (byPositionViewport) {
5330
+ return {
5331
+ ...byPositionViewport
5332
+ };
5333
+ }
4679
5334
  const {
4680
5335
  protocolId,
4681
5336
  stageIndex
@@ -4723,7 +5378,7 @@ const findViewportsByPosition = (state, _ref, syncService) => {
4723
5378
  ...syncState.viewportsByPosition
4724
5379
  };
4725
5380
  const initialInDisplay = [];
4726
- for (const viewport of viewports) {
5381
+ viewports.forEach(viewport => {
4727
5382
  if (viewport.positionId) {
4728
5383
  const storedViewport = {
4729
5384
  ...viewport,
@@ -4732,16 +5387,11 @@ const findViewportsByPosition = (state, _ref, syncService) => {
4732
5387
  }
4733
5388
  };
4734
5389
  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
5390
  }
4740
- }
5391
+ });
4741
5392
  for (let row = 0; row < numRows; row++) {
4742
5393
  for (let col = 0; col < numCols; col++) {
4743
- const pos = col + row * numCols;
4744
- const positionId = viewports?.[pos]?.positionId || `${col}-${row}`;
5394
+ const positionId = `${col}-${row}`;
4745
5395
  const viewport = viewportsByPosition[positionId];
4746
5396
  if (viewport?.displaySetInstanceUIDs) {
4747
5397
  initialInDisplay.push(...viewport.displaySetInstanceUIDs);
@@ -4756,8 +5406,8 @@ const findViewportsByPosition = (state, _ref, syncService) => {
4756
5406
  };
4757
5407
  };
4758
5408
  /* harmony default export */ const src_findViewportsByPosition = (findViewportsByPosition);
4759
- // EXTERNAL MODULE: ./index.js + 32 modules
4760
- var index = __webpack_require__(97837);
5409
+ // EXTERNAL MODULE: ./index.js + 33 modules
5410
+ var index = __webpack_require__(59754);
4761
5411
  ;// CONCATENATED MODULE: ../../../extensions/default/src/commandsModule.ts
4762
5412
 
4763
5413
 
@@ -4859,7 +5509,9 @@ const commandsModule = _ref => {
4859
5509
  stage
4860
5510
  } = hangingProtocolService.getActiveProtocol();
4861
5511
  const enableListener = button => {
4862
- if (!button.id) return;
5512
+ if (!button.id) {
5513
+ return;
5514
+ }
4863
5515
  const {
4864
5516
  commands,
4865
5517
  items
@@ -4868,14 +5520,16 @@ const commandsModule = _ref => {
4868
5520
  items.forEach(enableListener);
4869
5521
  }
4870
5522
  const hpCommand = commands?.find?.(isHangingProtocolCommand);
4871
- if (!hpCommand) return;
5523
+ if (!hpCommand) {
5524
+ return;
5525
+ }
4872
5526
  const {
4873
5527
  protocolId,
4874
5528
  stageIndex,
4875
5529
  stageId
4876
5530
  } = hpCommand.commandOptions;
4877
5531
  const isActive = (!protocolId || protocolId === protocol.id) && (stageIndex === undefined || stageIndex === toggleStageIndex) && (!stageId || stageId === stage.id);
4878
- toolbarService.setActive(button.id, isActive);
5532
+ toolbarService.setToggled(button.id, isActive);
4879
5533
  };
4880
5534
  Object.values(toolbarService.getButtons()).forEach(enableListener);
4881
5535
  },
@@ -4927,7 +5581,7 @@ const commandsModule = _ref => {
4927
5581
  displaySetSelectorMap
4928
5582
  } = stateSyncReduce;
4929
5583
  if (!protocolId) {
4930
- // Re-use the previous protocol id, and optionally stage
5584
+ // Reuse the previous protocol id, and optionally stage
4931
5585
  protocolId = hpInfo.protocolId;
4932
5586
  if (stageId === undefined && stageIndex === undefined) {
4933
5587
  stageIndex = hpInfo.stageIndex;
@@ -4970,19 +5624,20 @@ const commandsModule = _ref => {
4970
5624
  delete displaySetSelectorMap[`${activeStudyUID || hpInfo.activeStudyUID}:activeDisplaySet:0`];
4971
5625
  stateSyncService.store(stateSyncReduce);
4972
5626
  // This is a default action applied
4973
- actions.toggleHpTools(hangingProtocolService.getActiveProtocol());
5627
+ const {
5628
+ protocol
5629
+ } = hangingProtocolService.getActiveProtocol();
5630
+ actions.toggleHpTools(protocol);
4974
5631
  // Send the notification about updating the state
4975
5632
  if (protocolId !== hpInfo.protocolId) {
4976
- const {
4977
- protocol
4978
- } = hangingProtocolService.getActiveProtocol();
4979
5633
  // The old protocol callbacks are used for turning off things
4980
5634
  // like crosshairs when moving to the new HP
4981
5635
  commandsManager.run(oldProtocol.callbacks?.onProtocolExit);
4982
5636
  // The new protocol callback is used for things like
4983
5637
  // activating modes etc.
4984
- commandsManager.run(protocol.callbacks?.onProtocolEnter);
4985
5638
  }
5639
+
5640
+ commandsManager.run(protocol.callbacks?.onProtocolEnter);
4986
5641
  return true;
4987
5642
  } catch (e) {
4988
5643
  actions.toggleHpTools(hangingProtocolService.getActiveProtocol());
@@ -5098,7 +5753,7 @@ const commandsModule = _ref => {
5098
5753
  toggleOneUp() {
5099
5754
  const viewportGridState = viewportGridService.getState();
5100
5755
  const {
5101
- activeViewportIndex,
5756
+ activeViewportId,
5102
5757
  viewports,
5103
5758
  layout
5104
5759
  } = viewportGridState;
@@ -5106,7 +5761,7 @@ const commandsModule = _ref => {
5106
5761
  displaySetInstanceUIDs,
5107
5762
  displaySetOptions,
5108
5763
  viewportOptions
5109
- } = viewports[activeViewportIndex];
5764
+ } = viewports.get(activeViewportId);
5110
5765
  if (layout.numCols === 1 && layout.numRows === 1) {
5111
5766
  // The viewer is in one-up. Check if there is a state to restore/toggle back to.
5112
5767
  const {
@@ -5117,24 +5772,33 @@ const commandsModule = _ref => {
5117
5772
  }
5118
5773
  // There is a state to toggle back to. The viewport that was
5119
5774
  // originally toggled to one up was the former active viewport.
5120
- const viewportIndexToUpdate = toggleOneUpViewportGridStore.activeViewportIndex;
5775
+ const viewportIdToUpdate = toggleOneUpViewportGridStore.activeViewportId;
5121
5776
 
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();
5777
+ // We are restoring the previous layout but taking into the account that
5778
+ // the current one up viewport might have a new displaySet dragged and dropped on it.
5779
+ // updatedViewportsViaHP below contains the viewports applicable to the HP that existed
5780
+ // prior to the toggle to one-up - including the updated viewports if a display
5781
+ // set swap were to have occurred.
5782
+ const updatedViewportsViaHP = displaySetInstanceUIDs.length > 1 ? [] : displaySetInstanceUIDs.map(displaySetInstanceUID => hangingProtocolService.getViewportsRequireUpdate(viewportIdToUpdate, displaySetInstanceUID)).flat();
5127
5783
 
5128
- // This findOrCreateViewport returns either one of the updatedViewports
5784
+ // findOrCreateViewport returns either one of the updatedViewportsViaHP
5129
5785
  // 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 ? {
5786
+ // simply returns what was in the previous state for a given position in the layout.
5787
+ const findOrCreateViewport = (position, positionId) => {
5788
+ // Find the viewport for the given position prior to the toggle to one-up.
5789
+ const preOneUpViewport = Array.from(toggleOneUpViewportGridStore.viewports.values()).find(viewport => viewport.positionId === positionId);
5790
+
5791
+ // Use the viewport id from before the toggle to one-up to find any updates to the viewport.
5792
+ const viewport = updatedViewportsViaHP.find(viewport => viewport.viewportId === preOneUpViewport.viewportId);
5793
+ return viewport ?
5794
+ // Use the applicable viewport from the HP updated viewports
5795
+ {
5134
5796
  viewportOptions,
5135
5797
  displaySetOptions,
5136
5798
  ...viewport
5137
- } : toggleOneUpViewportGridStore.viewports[viewportIndex];
5799
+ } :
5800
+ // Use the previous viewport for the given position
5801
+ preOneUpViewport;
5138
5802
  };
5139
5803
  const layoutOptions = viewportGridService.getLayoutOptionsFromState(toggleOneUpViewportGridStore);
5140
5804
 
@@ -5142,7 +5806,7 @@ const commandsModule = _ref => {
5142
5806
  viewportGridService.setLayout({
5143
5807
  numRows: toggleOneUpViewportGridStore.layout.numRows,
5144
5808
  numCols: toggleOneUpViewportGridStore.layout.numCols,
5145
- activeViewportIndex: viewportIndexToUpdate,
5809
+ activeViewportId: viewportIdToUpdate,
5146
5810
  layoutOptions,
5147
5811
  findOrCreateViewport
5148
5812
  });
@@ -5207,10 +5871,10 @@ const commandsModule = _ref => {
5207
5871
  },
5208
5872
  openDICOMTagViewer() {
5209
5873
  const {
5210
- activeViewportIndex,
5874
+ activeViewportId,
5211
5875
  viewports
5212
5876
  } = viewportGridService.getState();
5213
- const activeViewportSpecificData = viewports[activeViewportIndex];
5877
+ const activeViewportSpecificData = viewports.get(activeViewportId);
5214
5878
  const {
5215
5879
  displaySetInstanceUIDs
5216
5880
  } = activeViewportSpecificData;
@@ -5242,13 +5906,10 @@ const commandsModule = _ref => {
5242
5906
  },
5243
5907
  scrollActiveThumbnailIntoView: () => {
5244
5908
  const {
5245
- activeViewportIndex,
5909
+ activeViewportId,
5246
5910
  viewports
5247
5911
  } = viewportGridService.getState();
5248
- if (!viewports || activeViewportIndex < 0 || activeViewportIndex > viewports.length - 1) {
5249
- return;
5250
- }
5251
- const activeViewport = viewports[activeViewportIndex];
5912
+ const activeViewport = viewports.get(activeViewportId);
5252
5913
  const activeDisplaySetInstanceUID = activeViewport.displaySetInstanceUIDs[0];
5253
5914
  const thumbnailList = document.querySelector('#ohif-thumbnail-list');
5254
5915
  if (!thumbnailList) {
@@ -5282,12 +5943,12 @@ const commandsModule = _ref => {
5282
5943
  const currentDisplaySets = [...displaySetService.activeDisplaySets];
5283
5944
  currentDisplaySets.sort(dsSortFn);
5284
5945
  const {
5285
- activeViewportIndex,
5946
+ activeViewportId,
5286
5947
  viewports
5287
5948
  } = viewportGridService.getState();
5288
5949
  const {
5289
5950
  displaySetInstanceUIDs
5290
- } = viewports[activeViewportIndex];
5951
+ } = viewports.get(activeViewportId);
5291
5952
  const activeDisplaySetIndex = currentDisplaySets.findIndex(displaySet => displaySetInstanceUIDs.includes(displaySet.displaySetInstanceUID));
5292
5953
  let displaySetIndexToShow;
5293
5954
  for (displaySetIndexToShow = activeDisplaySetIndex + direction; displaySetIndexToShow > -1 && displaySetIndexToShow < currentDisplaySets.length; displaySetIndexToShow += direction) {
@@ -5303,7 +5964,7 @@ const commandsModule = _ref => {
5303
5964
  } = currentDisplaySets[displaySetIndexToShow];
5304
5965
  let updatedViewports = [];
5305
5966
  try {
5306
- updatedViewports = hangingProtocolService.getViewportsRequireUpdate(activeViewportIndex, displaySetInstanceUID);
5967
+ updatedViewports = hangingProtocolService.getViewportsRequireUpdate(activeViewportId, displaySetInstanceUID);
5307
5968
  } catch (error) {
5308
5969
  console.warn(error);
5309
5970
  uiNotificationService.show({
@@ -5397,7 +6058,6 @@ const commandsModule = _ref => {
5397
6058
  * It is not included in the viewer mode by default.
5398
6059
  */
5399
6060
  const hpMN = {
5400
- hasUpdatedPriorsInformation: false,
5401
6061
  id: '@ohif/mnGrid',
5402
6062
  description: 'Has various hanging protocol grid layouts',
5403
6063
  name: '2x2',
@@ -5606,15 +6266,182 @@ const hpMN = {
5606
6266
  numberOfPriorsReferenced: -1
5607
6267
  };
5608
6268
  /* harmony default export */ const hpMNGrid = (hpMN);
6269
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/hpCompare.ts
6270
+ const defaultDisplaySetSelector = {
6271
+ studyMatchingRules: [{
6272
+ // The priorInstance is a study counter that indicates what position this study is in
6273
+ // and the value comes from the options parameter.
6274
+ attribute: 'studyInstanceUIDsIndex',
6275
+ from: 'options',
6276
+ required: true,
6277
+ constraint: {
6278
+ equals: {
6279
+ value: 0
6280
+ }
6281
+ }
6282
+ }],
6283
+ seriesMatchingRules: [{
6284
+ attribute: 'numImageFrames',
6285
+ constraint: {
6286
+ greaterThan: {
6287
+ value: 0
6288
+ }
6289
+ }
6290
+ },
6291
+ // This display set will select the specified items by preference
6292
+ // It has no affect if nothing is specified in the URL.
6293
+ {
6294
+ attribute: 'isDisplaySetFromUrl',
6295
+ weight: 10,
6296
+ constraint: {
6297
+ equals: true
6298
+ }
6299
+ }]
6300
+ };
6301
+ const priorDisplaySetSelector = {
6302
+ studyMatchingRules: [{
6303
+ // The priorInstance is a study counter that indicates what position this study is in
6304
+ // and the value comes from the options parameter.
6305
+ attribute: 'studyInstanceUIDsIndex',
6306
+ from: 'options',
6307
+ required: true,
6308
+ constraint: {
6309
+ equals: {
6310
+ value: 1
6311
+ }
6312
+ }
6313
+ }],
6314
+ seriesMatchingRules: [{
6315
+ attribute: 'numImageFrames',
6316
+ constraint: {
6317
+ greaterThan: {
6318
+ value: 0
6319
+ }
6320
+ }
6321
+ },
6322
+ // This display set will select the specified items by preference
6323
+ // It has no affect if nothing is specified in the URL.
6324
+ {
6325
+ attribute: 'isDisplaySetFromUrl',
6326
+ weight: 10,
6327
+ constraint: {
6328
+ equals: true
6329
+ }
6330
+ }]
6331
+ };
6332
+ const currentDisplaySet = {
6333
+ id: 'defaultDisplaySetId'
6334
+ };
6335
+ const priorDisplaySet = {
6336
+ id: 'priorDisplaySetId'
6337
+ };
6338
+ const currentViewport0 = {
6339
+ viewportOptions: {
6340
+ toolGroupId: 'default',
6341
+ allowUnmatchedView: true
6342
+ },
6343
+ displaySets: [currentDisplaySet]
6344
+ };
6345
+ const currentViewport1 = {
6346
+ ...currentViewport0,
6347
+ displaySets: [{
6348
+ ...currentDisplaySet,
6349
+ matchedDisplaySetsIndex: 1
6350
+ }]
6351
+ };
6352
+ const priorViewport0 = {
6353
+ ...currentViewport0,
6354
+ displaySets: [priorDisplaySet]
6355
+ };
6356
+ const priorViewport1 = {
6357
+ ...priorViewport0,
6358
+ displaySets: [{
6359
+ ...priorDisplaySet,
6360
+ matchedDisplaySetsIndex: 1
6361
+ }]
6362
+ };
6363
+
6364
+ /**
6365
+ * This hanging protocol can be activated on the primary mode by directly
6366
+ * referencing it in a URL or by directly including it within a mode, e.g.:
6367
+ * `&hangingProtocolId=@ohif/mnGrid` added to the viewer URL
6368
+ * It is not included in the viewer mode by default.
6369
+ */
6370
+ const hpMNCompare = {
6371
+ id: '@ohif/hpCompare',
6372
+ description: 'Compare two studies in various layouts',
6373
+ name: 'Compare Two Studies',
6374
+ numberOfPriorsReferenced: 1,
6375
+ protocolMatchingRules: [{
6376
+ id: 'Two Studies',
6377
+ weight: 1000,
6378
+ attribute: 'StudyInstanceUID',
6379
+ // The 'from' attribute says where to get the 'attribute' value from. In this case
6380
+ // prior means the second study in the study list.
6381
+ from: 'prior',
6382
+ required: true,
6383
+ constraint: {
6384
+ notNull: true
6385
+ }
6386
+ }],
6387
+ toolGroupIds: ['default'],
6388
+ displaySetSelectors: {
6389
+ defaultDisplaySetId: defaultDisplaySetSelector,
6390
+ priorDisplaySetId: priorDisplaySetSelector
6391
+ },
6392
+ defaultViewport: {
6393
+ viewportOptions: {
6394
+ viewportType: 'stack',
6395
+ toolGroupId: 'default',
6396
+ allowUnmatchedView: true
6397
+ },
6398
+ displaySets: [{
6399
+ id: 'defaultDisplaySetId',
6400
+ matchedDisplaySetsIndex: -1
6401
+ }]
6402
+ },
6403
+ stages: [{
6404
+ name: '2x2',
6405
+ stageActivation: {
6406
+ enabled: {
6407
+ minViewportsMatched: 4
6408
+ }
6409
+ },
6410
+ viewportStructure: {
6411
+ layoutType: 'grid',
6412
+ properties: {
6413
+ rows: 2,
6414
+ columns: 2
6415
+ }
6416
+ },
6417
+ viewports: [currentViewport0, priorViewport0, currentViewport1, priorViewport1]
6418
+ }, {
6419
+ name: '2x1',
6420
+ stageActivation: {
6421
+ enabled: {
6422
+ minViewportsMatched: 2
6423
+ }
6424
+ },
6425
+ viewportStructure: {
6426
+ layoutType: 'grid',
6427
+ properties: {
6428
+ rows: 1,
6429
+ columns: 2
6430
+ }
6431
+ },
6432
+ viewports: [currentViewport0, priorViewport0]
6433
+ }]
6434
+ };
6435
+ /* harmony default export */ const hpCompare = (hpMNCompare);
5609
6436
  ;// CONCATENATED MODULE: ../../../extensions/default/src/getHangingProtocolModule.js
5610
6437
 
6438
+
5611
6439
  const defaultProtocol = {
5612
6440
  id: 'default',
5613
6441
  locked: true,
5614
6442
  // Don't store this hanging protocol as it applies to the currently active
5615
6443
  // display set by default
5616
6444
  // cacheId: null,
5617
- hasUpdatedPriorsInformation: false,
5618
6445
  name: 'Default',
5619
6446
  createdDate: '2021-02-23T19:22:08.894Z',
5620
6447
  modifiedDate: '2023-04-01',
@@ -5679,6 +6506,7 @@ const defaultProtocol = {
5679
6506
  viewports: [{
5680
6507
  viewportOptions: {
5681
6508
  viewportType: 'stack',
6509
+ viewportId: 'default',
5682
6510
  toolGroupId: 'default',
5683
6511
  // This will specify the initial image options index if it matches in the URL
5684
6512
  // and will otherwise not specify anything.
@@ -5708,6 +6536,11 @@ function getHangingProtocolModule() {
5708
6536
  {
5709
6537
  name: hpMNGrid.id,
5710
6538
  protocol: hpMNGrid
6539
+ },
6540
+ // Create a MxN comparison hanging protocol available by default
6541
+ {
6542
+ name: hpCompare.id,
6543
+ protocol: hpCompare
5711
6544
  }];
5712
6545
  }
5713
6546
  /* harmony default export */ const src_getHangingProtocolModule = (getHangingProtocolModule);
@@ -5730,21 +6563,21 @@ function DataSourceSelector() {
5730
6563
  height: '100%'
5731
6564
  }
5732
6565
  }, /*#__PURE__*/react.createElement("div", {
5733
- className: "h-screen w-screen flex justify-center items-center "
6566
+ className: "flex h-screen w-screen items-center justify-center "
5734
6567
  }, /*#__PURE__*/react.createElement("div", {
5735
- className: "py-8 px-8 mx-auto bg-secondary-dark drop-shadow-md space-y-2 rounded-lg"
6568
+ className: "bg-secondary-dark mx-auto space-y-2 rounded-lg py-8 px-8 drop-shadow-md"
5736
6569
  }, /*#__PURE__*/react.createElement("img", {
5737
- className: "block mx-auto h-14",
6570
+ className: "mx-auto block h-14",
5738
6571
  src: "./ohif-logo.svg",
5739
6572
  alt: "OHIF"
5740
6573
  }), /*#__PURE__*/react.createElement("div", {
5741
- className: "text-center space-y-2 pt-4"
6574
+ className: "space-y-2 pt-4 text-center"
5742
6575
  }, dsConfigs.filter(it => it.sourceName !== 'dicomjson' && it.sourceName !== 'dicomlocal').map(ds => /*#__PURE__*/react.createElement("div", {
5743
6576
  key: ds.sourceName
5744
6577
  }, /*#__PURE__*/react.createElement("h1", {
5745
6578
  className: "text-white"
5746
- }, ds.friendlyName), /*#__PURE__*/react.createElement(ui_src/* Button */.zx, {
5747
- type: ui_src/* ButtonEnums.type */.LZ.U.primary,
6579
+ }, ds.configuration?.friendlyName || ds.friendlyName), /*#__PURE__*/react.createElement(ui_src/* Button */.zx, {
6580
+ type: ui_src/* ButtonEnums.type */.LZ.dt.primary,
5748
6581
  className: classnames_default()('ml-2'),
5749
6582
  onClick: () => {
5750
6583
  navigate({
@@ -5755,10 +6588,442 @@ function DataSourceSelector() {
5755
6588
  }, ds.sourceName), /*#__PURE__*/react.createElement("br", null)))))));
5756
6589
  }
5757
6590
  /* harmony default export */ const Panels_DataSourceSelector = (DataSourceSelector);
6591
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/Components/ItemListComponent.tsx
6592
+
6593
+
6594
+
6595
+
6596
+ function ItemListComponent(_ref) {
6597
+ let {
6598
+ itemLabel,
6599
+ itemList,
6600
+ onItemClicked
6601
+ } = _ref;
6602
+ const {
6603
+ t
6604
+ } = (0,es/* useTranslation */.$G)('DataSourceConfiguration');
6605
+ const [filterValue, setFilterValue] = (0,react.useState)('');
6606
+ (0,react.useEffect)(() => {
6607
+ setFilterValue('');
6608
+ }, [itemList]);
6609
+ return /*#__PURE__*/react.createElement("div", {
6610
+ className: "flex min-h-[1px] grow flex-col gap-4"
6611
+ }, /*#__PURE__*/react.createElement("div", {
6612
+ className: "flex items-center justify-between"
6613
+ }, /*#__PURE__*/react.createElement("div", {
6614
+ className: "text-primary-light text-[20px]"
6615
+ }, t(`Select ${itemLabel}`)), /*#__PURE__*/react.createElement(ui_src/* InputFilterText */.Xe, {
6616
+ className: "max-w-[40%] grow",
6617
+ value: filterValue,
6618
+ onDebounceChange: setFilterValue,
6619
+ placeholder: t(`Search ${itemLabel} list`)
6620
+ })), /*#__PURE__*/react.createElement("div", {
6621
+ className: "relative flex min-h-[1px] grow flex-col bg-black text-[14px]"
6622
+ }, itemList == null ? /*#__PURE__*/react.createElement(ui_src/* LoadingIndicatorProgress */.LE, {
6623
+ className: 'h-full w-full'
6624
+ }) : itemList.length === 0 ? /*#__PURE__*/react.createElement("div", {
6625
+ className: "text-primary-light flex h-full flex-col items-center justify-center px-6 py-4"
6626
+ }, /*#__PURE__*/react.createElement(ui_src/* Icon */.JO, {
6627
+ name: "magnifier",
6628
+ className: "mb-4"
6629
+ }), /*#__PURE__*/react.createElement("span", null, t(`No ${itemLabel} available`))) : /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement("div", {
6630
+ className: "bg-secondary-dark px-3 py-1.5 text-white"
6631
+ }, t(itemLabel)), /*#__PURE__*/react.createElement("div", {
6632
+ className: "ohif-scrollbar overflow-auto"
6633
+ }, itemList.filter(item => !filterValue || item.name.toLowerCase().includes(filterValue.toLowerCase())).map(item => {
6634
+ const border = 'rounded border-transparent border-b-secondary-light border-[1px] hover:border-primary-light';
6635
+ return /*#__PURE__*/react.createElement("div", {
6636
+ className: classnames_default()('hover:text-primary-light hover:bg-primary-dark group mx-2 flex items-center justify-between px-6 py-2', border),
6637
+ key: item.id
6638
+ }, /*#__PURE__*/react.createElement("div", null, item.name), /*#__PURE__*/react.createElement(ui_src/* Button */.zx, {
6639
+ onClick: () => onItemClicked(item),
6640
+ className: "invisible group-hover:visible",
6641
+ endIcon: /*#__PURE__*/react.createElement(ui_src/* Icon */.JO, {
6642
+ name: "arrow-left"
6643
+ })
6644
+ }, t('Select')));
6645
+ })))));
6646
+ }
6647
+ /* harmony default export */ const Components_ItemListComponent = (ItemListComponent);
6648
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/Components/DataSourceConfigurationModalComponent.tsx
6649
+
6650
+
6651
+
6652
+
6653
+
6654
+ const NO_WRAP_ELLIPSIS_CLASS_NAMES = 'text-ellipsis whitespace-nowrap overflow-hidden';
6655
+ function DataSourceConfigurationModalComponent(_ref) {
6656
+ let {
6657
+ configurationAPI,
6658
+ configuredItems,
6659
+ onHide
6660
+ } = _ref;
6661
+ const {
6662
+ t
6663
+ } = (0,es/* useTranslation */.$G)('DataSourceConfiguration');
6664
+ const [itemList, setItemList] = (0,react.useState)();
6665
+ const [selectedItems, setSelectedItems] = (0,react.useState)(configuredItems);
6666
+ const [errorMessage, setErrorMessage] = (0,react.useState)();
6667
+ const [itemLabels] = (0,react.useState)(configurationAPI.getItemLabels());
6668
+
6669
+ // Determines whether to show the full/existing configuration for the data source.
6670
+ // A full or complete configuration is one where the data source (path) has the
6671
+ // maximum/required number of path items. Anything less is considered not complete and
6672
+ // the configuration starts from scratch (i.e. as if no items are configured at all).
6673
+ // TODO: consider configuration starting from a partial (i.e. non-empty) configuration
6674
+ const [showFullConfig, setShowFullConfig] = (0,react.useState)(itemLabels.length === configuredItems.length);
6675
+
6676
+ /**
6677
+ * The index of the selected item that is considered current and for which
6678
+ * its sub-items should be displayed in the items list component. When the
6679
+ * full/existing configuration for a data source is to be shown, the current
6680
+ * selected item is the second to last in the `selectedItems` list.
6681
+ */
6682
+ const currentSelectedItemIndex = showFullConfig ? selectedItems.length - 2 : selectedItems.length - 1;
6683
+ (0,react.useEffect)(() => {
6684
+ let shouldUpdate = true;
6685
+ setErrorMessage(null);
6686
+
6687
+ // Clear out the former/old list while we fetch the next sub item list.
6688
+ setItemList(null);
6689
+ if (selectedItems.length === 0) {
6690
+ configurationAPI.initialize().then(items => {
6691
+ if (shouldUpdate) {
6692
+ setItemList(items);
6693
+ }
6694
+ }).catch(error => setErrorMessage(error.message));
6695
+ } else if (!showFullConfig && selectedItems.length === itemLabels.length) {
6696
+ // The last item to configure the data source (path) has been selected.
6697
+ configurationAPI.setCurrentItem(selectedItems[selectedItems.length - 1]);
6698
+ // We can hide the modal dialog now.
6699
+ onHide();
6700
+ } else {
6701
+ configurationAPI.setCurrentItem(selectedItems[currentSelectedItemIndex]).then(items => {
6702
+ if (shouldUpdate) {
6703
+ setItemList(items);
6704
+ }
6705
+ }).catch(error => setErrorMessage(error.message));
6706
+ }
6707
+ return () => {
6708
+ shouldUpdate = false;
6709
+ };
6710
+ }, [selectedItems, configurationAPI, onHide, itemLabels, showFullConfig, currentSelectedItemIndex]);
6711
+ const getSelectedItemCursorClasses = itemIndex => itemIndex !== itemLabels.length - 1 && itemIndex < selectedItems.length ? 'cursor-pointer' : 'cursor-auto';
6712
+ const getSelectedItemBackgroundClasses = itemIndex => itemIndex < selectedItems.length ? classnames_default()('bg-black/[.4]', itemIndex !== itemLabels.length - 1 ? 'hover:bg-transparent active:bg-secondary-dark' : '') : 'bg-transparent';
6713
+ 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';
6714
+ const getSelectedItemTextClasses = itemIndex => itemIndex <= selectedItems.length ? 'text-primary-light' : 'text-primary-active';
6715
+ const getErrorComponent = () => {
6716
+ return /*#__PURE__*/react.createElement("div", {
6717
+ className: "flex min-h-[1px] grow flex-col gap-4"
6718
+ }, /*#__PURE__*/react.createElement("div", {
6719
+ className: "text-primary-light text-[20px]"
6720
+ }, t(`Error fetching ${itemLabels[selectedItems.length]} list`)), /*#__PURE__*/react.createElement("div", {
6721
+ className: "grow bg-black p-4 text-[14px]"
6722
+ }, errorMessage));
6723
+ };
6724
+ const getSelectedItemsComponent = () => {
6725
+ return /*#__PURE__*/react.createElement("div", {
6726
+ className: "flex gap-4"
6727
+ }, itemLabels.map((itemLabel, itemLabelIndex) => {
6728
+ return /*#__PURE__*/react.createElement("div", {
6729
+ key: itemLabel,
6730
+ 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)),
6731
+ onClick: showFullConfig && itemLabelIndex < currentSelectedItemIndex || itemLabelIndex <= currentSelectedItemIndex ? () => {
6732
+ setShowFullConfig(false);
6733
+ setSelectedItems(theList => theList.slice(0, itemLabelIndex));
6734
+ } : undefined
6735
+ }, /*#__PURE__*/react.createElement("div", {
6736
+ className: "text- flex items-center gap-2"
6737
+ }, itemLabelIndex < selectedItems.length ? /*#__PURE__*/react.createElement(ui_src/* Icon */.JO, {
6738
+ name: "status-tracked"
6739
+ }) : /*#__PURE__*/react.createElement(ui_src/* Icon */.JO, {
6740
+ name: "status-untracked"
6741
+ }), /*#__PURE__*/react.createElement("div", {
6742
+ className: classnames_default()(NO_WRAP_ELLIPSIS_CLASS_NAMES)
6743
+ }, t(itemLabel))), itemLabelIndex < selectedItems.length ? /*#__PURE__*/react.createElement("div", {
6744
+ className: classnames_default()('text-[14px] text-white', NO_WRAP_ELLIPSIS_CLASS_NAMES)
6745
+ }, selectedItems[itemLabelIndex].name) : /*#__PURE__*/react.createElement("br", null));
6746
+ }));
6747
+ };
6748
+ return /*#__PURE__*/react.createElement("div", {
6749
+ className: "flex h-[calc(100vh-300px)] select-none flex-col gap-4 pt-0.5"
6750
+ }, getSelectedItemsComponent(), /*#__PURE__*/react.createElement("div", {
6751
+ className: "h-0.5 w-full shrink-0 bg-black"
6752
+ }), errorMessage ? getErrorComponent() : /*#__PURE__*/react.createElement(Components_ItemListComponent, {
6753
+ itemLabel: itemLabels[currentSelectedItemIndex + 1],
6754
+ itemList: itemList,
6755
+ onItemClicked: item => {
6756
+ setShowFullConfig(false);
6757
+ setSelectedItems(theList => [...theList.slice(0, currentSelectedItemIndex + 1), item]);
6758
+ }
6759
+ }));
6760
+ }
6761
+ /* harmony default export */ const Components_DataSourceConfigurationModalComponent = (DataSourceConfigurationModalComponent);
6762
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/Components/DataSourceConfigurationComponent.tsx
6763
+
6764
+
6765
+
6766
+
6767
+ function DataSourceConfigurationComponent(_ref) {
6768
+ let {
6769
+ servicesManager,
6770
+ extensionManager
6771
+ } = _ref;
6772
+ const {
6773
+ t
6774
+ } = (0,es/* useTranslation */.$G)('DataSourceConfiguration');
6775
+ const {
6776
+ show,
6777
+ hide
6778
+ } = (0,ui_src/* useModal */.dd)();
6779
+ const {
6780
+ customizationService
6781
+ } = servicesManager.services;
6782
+ const [configurationAPI, setConfigurationAPI] = (0,react.useState)();
6783
+ const [configuredItems, setConfiguredItems] = (0,react.useState)();
6784
+ (0,react.useEffect)(() => {
6785
+ let shouldUpdate = true;
6786
+ const dataSourceChangedCallback = async () => {
6787
+ const activeDataSourceDef = extensionManager.getActiveDataSourceDefinition();
6788
+ if (!activeDataSourceDef.configuration.configurationAPI) {
6789
+ return;
6790
+ }
6791
+ const {
6792
+ factory: configurationAPIFactory
6793
+ } = customizationService.get(activeDataSourceDef.configuration.configurationAPI) ?? {};
6794
+ if (!configurationAPIFactory) {
6795
+ return;
6796
+ }
6797
+ const configAPI = configurationAPIFactory(activeDataSourceDef.sourceName);
6798
+ setConfigurationAPI(configAPI);
6799
+
6800
+ // New configuration API means that the existing configured items must be cleared.
6801
+ setConfiguredItems(null);
6802
+ configAPI.getConfiguredItems().then(list => {
6803
+ if (shouldUpdate) {
6804
+ setConfiguredItems(list);
6805
+ }
6806
+ });
6807
+ };
6808
+ const sub = extensionManager.subscribe(extensionManager.EVENTS.ACTIVE_DATA_SOURCE_CHANGED, dataSourceChangedCallback);
6809
+ dataSourceChangedCallback();
6810
+ return () => {
6811
+ shouldUpdate = false;
6812
+ sub.unsubscribe();
6813
+ };
6814
+ }, []);
6815
+ const showConfigurationModal = (0,react.useCallback)(() => {
6816
+ show({
6817
+ content: Components_DataSourceConfigurationModalComponent,
6818
+ title: t('Configure Data Source'),
6819
+ contentProps: {
6820
+ configurationAPI,
6821
+ configuredItems,
6822
+ onHide: hide
6823
+ }
6824
+ });
6825
+ }, [configurationAPI, configuredItems]);
6826
+ (0,react.useEffect)(() => {
6827
+ if (!configurationAPI || !configuredItems) {
6828
+ return;
6829
+ }
6830
+ if (configuredItems.length !== configurationAPI.getItemLabels().length) {
6831
+ // Not the correct number of configured items, so show the modal to configure the data source.
6832
+ showConfigurationModal();
6833
+ }
6834
+ }, [configurationAPI, configuredItems, showConfigurationModal]);
6835
+ return configuredItems ? /*#__PURE__*/react.createElement("div", {
6836
+ className: "text-aqua-pale flex items-center overflow-hidden"
6837
+ }, /*#__PURE__*/react.createElement(ui_src/* Icon */.JO, {
6838
+ name: "settings",
6839
+ className: "mr-2.5 h-3.5 w-3.5 shrink-0 cursor-pointer",
6840
+ onClick: showConfigurationModal
6841
+ }), configuredItems.map((item, itemIndex) => {
6842
+ return /*#__PURE__*/react.createElement("div", {
6843
+ key: itemIndex,
6844
+ className: "flex overflow-hidden"
6845
+ }, /*#__PURE__*/react.createElement("div", {
6846
+ key: itemIndex,
6847
+ className: "overflow-hidden text-ellipsis whitespace-nowrap"
6848
+ }, item.name), itemIndex !== configuredItems.length - 1 && /*#__PURE__*/react.createElement("div", {
6849
+ className: "px-2.5"
6850
+ }, "|"));
6851
+ })) : /*#__PURE__*/react.createElement(react.Fragment, null);
6852
+ }
6853
+ /* harmony default export */ const Components_DataSourceConfigurationComponent = (DataSourceConfigurationComponent);
6854
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/DataSourceConfigurationAPI/GoogleCloudDataSourceConfigurationAPI.ts
6855
+ /**
6856
+ * This file contains the implementations of BaseDataSourceConfigurationAPIItem
6857
+ * and BaseDataSourceConfigurationAPI for the Google cloud healthcare API. To
6858
+ * better understand this implementation and/or to implement custom implementations,
6859
+ * see the platform\core\src\types\DataSourceConfigurationAPI.ts and its JS doc
6860
+ * comments as a guide.
6861
+ */
6862
+ /**
6863
+ * The various Google Cloud Healthcare path item types.
6864
+ */
6865
+ var ItemType = /*#__PURE__*/function (ItemType) {
6866
+ ItemType[ItemType["projects"] = 0] = "projects";
6867
+ ItemType[ItemType["locations"] = 1] = "locations";
6868
+ ItemType[ItemType["datasets"] = 2] = "datasets";
6869
+ ItemType[ItemType["dicomStores"] = 3] = "dicomStores";
6870
+ return ItemType;
6871
+ }(ItemType || {});
6872
+ const initialUrl = 'https://cloudresourcemanager.googleapis.com/v1';
6873
+ const baseHealthcareUrl = 'https://healthcare.googleapis.com/v1';
6874
+ class GoogleCloudDataSourceConfigurationAPIItem {
6875
+ constructor() {
6876
+ this.id = void 0;
6877
+ this.name = void 0;
6878
+ this.url = void 0;
6879
+ this.itemType = void 0;
6880
+ }
6881
+ }
6882
+ class GoogleCloudDataSourceConfigurationAPI {
6883
+ constructor(dataSourceName, servicesManager, extensionManager) {
6884
+ this._extensionManager = void 0;
6885
+ this._fetchOptions = void 0;
6886
+ this._dataSourceName = void 0;
6887
+ this.getItemLabels = () => ['Project', 'Location', 'Data set', 'DICOM store'];
6888
+ this._dataSourceName = dataSourceName;
6889
+ this._extensionManager = extensionManager;
6890
+ const userAuthenticationService = servicesManager.services.userAuthenticationService;
6891
+ this._fetchOptions = {
6892
+ method: 'GET',
6893
+ headers: userAuthenticationService.getAuthorizationHeader()
6894
+ };
6895
+ }
6896
+ async initialize() {
6897
+ const url = `${initialUrl}/projects`;
6898
+ const projects = await GoogleCloudDataSourceConfigurationAPI._doFetch(url, ItemType.projects, this._fetchOptions);
6899
+ if (!projects?.length) {
6900
+ return [];
6901
+ }
6902
+ const projectItems = projects.map(project => {
6903
+ return {
6904
+ id: project.projectId,
6905
+ name: project.name,
6906
+ itemType: ItemType.projects,
6907
+ url: `${baseHealthcareUrl}/projects/${project.projectId}`
6908
+ };
6909
+ });
6910
+ return projectItems;
6911
+ }
6912
+ async setCurrentItem(anItem) {
6913
+ const googleCloudItem = anItem;
6914
+ if (googleCloudItem.itemType === ItemType.dicomStores) {
6915
+ // Last configurable item, so update the data source configuration.
6916
+ const url = `${googleCloudItem.url}/dicomWeb`;
6917
+ const dataSourceDefCopy = JSON.parse(JSON.stringify(this._extensionManager.getDataSourceDefinition(this._dataSourceName)));
6918
+ dataSourceDefCopy.configuration = {
6919
+ ...dataSourceDefCopy.configuration,
6920
+ wadoUriRoot: url,
6921
+ qidoRoot: url,
6922
+ wadoRoot: url
6923
+ };
6924
+ this._extensionManager.updateDataSourceConfiguration(dataSourceDefCopy.sourceName, dataSourceDefCopy.configuration);
6925
+ return [];
6926
+ }
6927
+ const subItemType = googleCloudItem.itemType + 1;
6928
+ const subItemField = `${ItemType[subItemType]}`;
6929
+ const url = `${googleCloudItem.url}/${subItemField}`;
6930
+ const fetchedSubItems = await GoogleCloudDataSourceConfigurationAPI._doFetch(url, subItemType, this._fetchOptions);
6931
+ if (!fetchedSubItems?.length) {
6932
+ return [];
6933
+ }
6934
+ const subItems = fetchedSubItems.map(subItem => {
6935
+ const nameSplit = subItem.name.split('/');
6936
+ return {
6937
+ id: subItem.name,
6938
+ name: nameSplit[nameSplit.length - 1],
6939
+ itemType: subItemType,
6940
+ url: `${baseHealthcareUrl}/${subItem.name}`
6941
+ };
6942
+ });
6943
+ return subItems;
6944
+ }
6945
+ async getConfiguredItems() {
6946
+ const dataSourceDefinition = this._extensionManager.getDataSourceDefinition(this._dataSourceName);
6947
+ const url = dataSourceDefinition.configuration.wadoUriRoot;
6948
+ const projectsIndex = url.indexOf('projects');
6949
+ // Split the configured URL into (essentially) pairs (i.e. item type followed by item)
6950
+ // Explicitly: ['projects','aProject','locations','aLocation','datasets','aDataSet','dicomStores','aDicomStore']
6951
+ // Note that a partial configuration will have a subset of the above.
6952
+ const urlSplit = url.substring(projectsIndex).split('/');
6953
+ const configuredItems = [];
6954
+ for (let itemType = 0;
6955
+ // the number of configured items is either the max (4) or the number extracted from the url split
6956
+ itemType < 4 && (itemType + 1) * 2 < urlSplit.length; itemType += 1) {
6957
+ if (itemType === ItemType.projects) {
6958
+ const projectId = urlSplit[1];
6959
+ const projectUrl = `${initialUrl}/projects/${projectId}`;
6960
+ const data = await GoogleCloudDataSourceConfigurationAPI._doFetch(projectUrl, ItemType.projects, this._fetchOptions);
6961
+ const project = data[0];
6962
+ configuredItems.push({
6963
+ id: project.projectId,
6964
+ name: project.name,
6965
+ itemType: itemType,
6966
+ url: `${baseHealthcareUrl}/projects/${project.projectId}`
6967
+ });
6968
+ } else {
6969
+ const relativePath = urlSplit.slice(0, itemType * 2 + 2).join('/');
6970
+ configuredItems.push({
6971
+ id: relativePath,
6972
+ name: urlSplit[itemType * 2 + 1],
6973
+ itemType: itemType,
6974
+ url: `${baseHealthcareUrl}/${relativePath}`
6975
+ });
6976
+ }
6977
+ }
6978
+ return configuredItems;
6979
+ }
6980
+
6981
+ /**
6982
+ * Fetches an array of items the specified item type.
6983
+ * @param urlStr the fetch url
6984
+ * @param fetchItemType the type to fetch
6985
+ * @param fetchOptions the header options for the fetch (e.g. authorization header)
6986
+ * @param fetchSearchParams any search query params; currently only used for paging results
6987
+ * @returns an array of items of the specified type
6988
+ */
6989
+ static async _doFetch(urlStr, fetchItemType) {
6990
+ let fetchOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
6991
+ let fetchSearchParams = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
6992
+ try {
6993
+ const url = new URL(urlStr);
6994
+ url.search = new URLSearchParams(fetchSearchParams).toString();
6995
+ const response = await fetch(url, fetchOptions);
6996
+ const data = await response.json();
6997
+ if (response.status >= 200 && response.status < 300 && data != null) {
6998
+ if (data.nextPageToken != null) {
6999
+ fetchSearchParams.pageToken = data.nextPageToken;
7000
+ const subPageData = await this._doFetch(urlStr, fetchItemType, fetchOptions, fetchSearchParams);
7001
+ data[ItemType[fetchItemType]] = data[ItemType[fetchItemType]].concat(subPageData);
7002
+ }
7003
+ if (data[ItemType[fetchItemType]]) {
7004
+ return data[ItemType[fetchItemType]];
7005
+ } else if (data.name) {
7006
+ return [data];
7007
+ } else {
7008
+ return [];
7009
+ }
7010
+ } else {
7011
+ const message = data?.error?.message || `Error returned from Google Cloud Healthcare: ${response.status} - ${response.statusText}`;
7012
+ throw new Error(message);
7013
+ }
7014
+ } catch (err) {
7015
+ const message = err?.message || 'Error occurred during fetch request.';
7016
+ throw new Error(message);
7017
+ }
7018
+ }
7019
+ }
7020
+
5758
7021
  ;// CONCATENATED MODULE: ../../../extensions/default/src/getCustomizationModule.tsx
5759
7022
 
5760
7023
 
5761
7024
 
7025
+
7026
+
5762
7027
  /**
5763
7028
  *
5764
7029
  * Note: this is an example of how the customization module can be used
@@ -5768,7 +7033,11 @@ function DataSourceSelector() {
5768
7033
  * custom page for the user to view their profile, or to add a custom
5769
7034
  * page for login etc.
5770
7035
  */
5771
- function getCustomizationModule() {
7036
+ function getCustomizationModule(_ref) {
7037
+ let {
7038
+ servicesManager,
7039
+ extensionManager
7040
+ } = _ref;
5772
7041
  return [{
5773
7042
  name: 'helloPage',
5774
7043
  value: {
@@ -5835,12 +7104,16 @@ function getCustomizationModule() {
5835
7104
  {
5836
7105
  id: 'ohif.overlayItem',
5837
7106
  content: function (props) {
5838
- if (this.condition && !this.condition(props)) return null;
7107
+ if (this.condition && !this.condition(props)) {
7108
+ return null;
7109
+ }
5839
7110
  const {
5840
7111
  instance
5841
7112
  } = props;
5842
7113
  const value = instance && this.attribute ? instance[this.attribute] : this.contentF && typeof this.contentF === 'function' ? this.contentF(props) : null;
5843
- if (!value) return null;
7114
+ if (!value) {
7115
+ return null;
7116
+ }
5844
7117
  return /*#__PURE__*/react.createElement("span", {
5845
7118
  className: "overlay-item flex flex-row",
5846
7119
  style: {
@@ -5878,11 +7151,22 @@ function getCustomizationModule() {
5878
7151
  }
5879
7152
  return clonedObject;
5880
7153
  }
7154
+ }, {
7155
+ // the generic GUI component to configure a data source using an instance of a BaseDataSourceConfigurationAPI
7156
+ id: 'ohif.dataSourceConfigurationComponent',
7157
+ component: Components_DataSourceConfigurationComponent.bind(null, {
7158
+ servicesManager,
7159
+ extensionManager
7160
+ })
7161
+ }, {
7162
+ // The factory for creating an instance of a BaseDataSourceConfigurationAPI for Google Cloud Healthcare
7163
+ id: 'ohif.dataSourceConfigurationAPI.google',
7164
+ factory: dataSourceName => new GoogleCloudDataSourceConfigurationAPI(dataSourceName, servicesManager, extensionManager)
5881
7165
  }]
5882
7166
  }];
5883
7167
  }
5884
7168
  // EXTERNAL MODULE: ../../../node_modules/@cornerstonejs/calculate-suv/dist/calculate-suv.esm.js
5885
- var calculate_suv_esm = __webpack_require__(71251);
7169
+ var calculate_suv_esm = __webpack_require__(15747);
5886
7170
  ;// CONCATENATED MODULE: ../../../extensions/default/src/getPTImageIdInstanceMetadata.ts
5887
7171
 
5888
7172
  const getPTImageIdInstanceMetadata_metadataProvider = src["default"].classes.MetadataProvider;
@@ -5891,9 +7175,12 @@ function getPTImageIdInstanceMetadata(imageId) {
5891
7175
  if (!dicomMetaData) {
5892
7176
  throw new Error('dicom metadata are required');
5893
7177
  }
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) {
7178
+ 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
7179
  throw new Error('required metadata are missing');
5896
7180
  }
7181
+ if (dicomMetaData.PatientWeight === undefined) {
7182
+ console.warn('PatientWeight missing from PT instance metadata');
7183
+ }
5897
7184
  const instanceMetadata = {
5898
7185
  CorrectedImage: dicomMetaData.CorrectedImage,
5899
7186
  Units: dicomMetaData.Units,
@@ -6064,6 +7351,8 @@ const handlePETImageMetadata = _ref2 => {
6064
7351
 
6065
7352
 
6066
7353
 
7354
+
7355
+
6067
7356
  const defaultExtension = {
6068
7357
  /**
6069
7358
  * Only required property. Should be a unique value across all extensions.