diff --git a/addOns/externals/devDependencies/package.json b/addOns/externals/devDependencies/package.json
index 2b6e996ac..5b26aa49e 100644
--- a/addOns/externals/devDependencies/package.json
+++ b/addOns/externals/devDependencies/package.json
@@ -76,7 +76,6 @@
"shader-loader": "^1.3.1",
"shx": "^0.3.3",
"source-map-loader": "^4.0.1",
- "start-server-and-test": "^1.10.0",
"style-loader": "^1.0.0",
"stylus": "^0.59.0",
"stylus-loader": "^7.1.3",
diff --git a/bun.lock b/bun.lock
index 1855c6374..7e8ee4e98 100644
--- a/bun.lock
+++ b/bun.lock
@@ -33,7 +33,7 @@
},
"addOns/externals/devDependencies": {
"name": "@externals/devDependencies",
- "version": "3.10.0-beta.150",
+ "version": "3.10.0-beta.152",
"dependencies": {
"@babel/runtime": "^7.20.13",
"@kitware/vtk.js": "32.12.0",
@@ -98,7 +98,6 @@
"shader-loader": "^1.3.1",
"shx": "^0.3.3",
"source-map-loader": "^4.0.1",
- "start-server-and-test": "^1.10.0",
"style-loader": "^1.0.0",
"stylus": "^0.59.0",
"stylus-loader": "^7.1.3",
@@ -121,14 +120,14 @@
},
"addOns/externals/dicom-microscopy-viewer": {
"name": "@externals/dicom-microscopy-viewer",
- "version": "3.10.0-beta.150",
+ "version": "3.10.0-beta.152",
"dependencies": {
"dicom-microscopy-viewer": "^0.46.1",
},
},
"extensions/cornerstone": {
"name": "@ohif/extension-cornerstone",
- "version": "3.10.0-beta.150",
+ "version": "3.10.0-beta.152",
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/adapters": "^3.9.1",
@@ -167,7 +166,7 @@
},
"extensions/cornerstone-dicom-pmap": {
"name": "@ohif/extension-cornerstone-dicom-pmap",
- "version": "3.10.0-beta.150",
+ "version": "3.10.0-beta.152",
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/adapters": "^3.9.1",
@@ -190,7 +189,7 @@
},
"extensions/cornerstone-dicom-rt": {
"name": "@ohif/extension-cornerstone-dicom-rt",
- "version": "3.10.0-beta.150",
+ "version": "3.10.0-beta.152",
"dependencies": {
"@babel/runtime": "^7.20.13",
"react-color": "^2.19.3",
@@ -210,7 +209,7 @@
},
"extensions/cornerstone-dicom-seg": {
"name": "@ohif/extension-cornerstone-dicom-seg",
- "version": "3.10.0-beta.150",
+ "version": "3.10.0-beta.152",
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/adapters": "^3.9.1",
@@ -233,7 +232,7 @@
},
"extensions/cornerstone-dicom-sr": {
"name": "@ohif/extension-cornerstone-dicom-sr",
- "version": "3.10.0-beta.150",
+ "version": "3.10.0-beta.152",
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/adapters": "^3.9.1",
@@ -255,7 +254,7 @@
},
"extensions/cornerstone-dynamic-volume": {
"name": "@ohif/extension-cornerstone-dynamic-volume",
- "version": "3.10.0-beta.150",
+ "version": "3.10.0-beta.152",
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/core": "^3.9.1",
@@ -277,7 +276,7 @@
},
"extensions/default": {
"name": "@ohif/extension-default",
- "version": "3.10.0-beta.150",
+ "version": "3.10.0-beta.152",
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/calculate-suv": "^1.1.0",
@@ -300,7 +299,7 @@
},
"extensions/dicom-microscopy": {
"name": "@ohif/extension-dicom-microscopy",
- "version": "3.10.0-beta.150",
+ "version": "3.10.0-beta.152",
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/codec-charls": "^1.2.3",
@@ -325,7 +324,7 @@
},
"extensions/dicom-pdf": {
"name": "@ohif/extension-dicom-pdf",
- "version": "3.10.0-beta.150",
+ "version": "3.10.0-beta.152",
"dependencies": {
"@babel/runtime": "^7.20.13",
"classnames": "^2.3.2",
@@ -342,7 +341,7 @@
},
"extensions/dicom-video": {
"name": "@ohif/extension-dicom-video",
- "version": "3.10.0-beta.150",
+ "version": "3.10.0-beta.152",
"dependencies": {
"@babel/runtime": "^7.20.13",
"classnames": "^2.3.2",
@@ -359,7 +358,7 @@
},
"extensions/measurement-tracking": {
"name": "@ohif/extension-measurement-tracking",
- "version": "3.10.0-beta.150",
+ "version": "3.10.0-beta.152",
"dependencies": {
"@babel/runtime": "^7.20.13",
"@ohif/ui": "3.10.0-beta.150",
@@ -385,7 +384,7 @@
},
"extensions/test-extension": {
"name": "@ohif/extension-test",
- "version": "3.10.0-beta.150",
+ "version": "3.10.0-beta.152",
"dependencies": {
"@babel/runtime": "^7.20.13",
"classnames": "^2.3.2",
@@ -402,7 +401,7 @@
},
"extensions/tmtv": {
"name": "@ohif/extension-tmtv",
- "version": "3.10.0-beta.150",
+ "version": "3.10.0-beta.152",
"dependencies": {
"@babel/runtime": "^7.20.13",
"classnames": "^2.3.2",
@@ -419,7 +418,7 @@
},
"modes/basic-dev-mode": {
"name": "@ohif/mode-basic-dev-mode",
- "version": "3.10.0-beta.150",
+ "version": "3.10.0-beta.152",
"dependencies": {
"@babel/runtime": "^7.20.13",
"i18next": "^17.0.3",
@@ -439,7 +438,7 @@
},
"modes/basic-test-mode": {
"name": "@ohif/mode-test",
- "version": "3.10.0-beta.150",
+ "version": "3.10.0-beta.152",
"dependencies": {
"@babel/runtime": "^7.20.13",
"i18next": "^17.0.3",
@@ -461,7 +460,7 @@
},
"modes/longitudinal": {
"name": "@ohif/mode-longitudinal",
- "version": "3.10.0-beta.150",
+ "version": "3.10.0-beta.152",
"dependencies": {
"@babel/runtime": "^7.20.13",
"i18next": "^17.0.3",
@@ -484,7 +483,7 @@
},
"modes/microscopy": {
"name": "@ohif/mode-microscopy",
- "version": "3.10.0-beta.150",
+ "version": "3.10.0-beta.152",
"dependencies": {
"@babel/runtime": "^7.20.13",
"i18next": "^17.0.3",
@@ -496,7 +495,7 @@
},
"modes/preclinical-4d": {
"name": "@ohif/mode-preclinical-4d",
- "version": "3.10.0-beta.150",
+ "version": "3.10.0-beta.152",
"dependencies": {
"@babel/runtime": "^7.20.13",
},
@@ -515,7 +514,7 @@
},
"modes/segmentation": {
"name": "@ohif/mode-segmentation",
- "version": "3.10.0-beta.150",
+ "version": "3.10.0-beta.152",
"dependencies": {
"@babel/runtime": "^7.20.13",
"i18next": "^17.0.3",
@@ -559,7 +558,7 @@
},
"modes/tmtv": {
"name": "@ohif/mode-tmtv",
- "version": "3.10.0-beta.150",
+ "version": "3.10.0-beta.152",
"dependencies": {
"@babel/runtime": "^7.20.13",
"i18next": "^17.0.3",
@@ -580,7 +579,7 @@
},
"platform/app": {
"name": "@ohif/app",
- "version": "3.10.0-beta.150",
+ "version": "3.10.0-beta.152",
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/codec-charls": "^1.2.3",
@@ -646,7 +645,7 @@
},
"platform/cli": {
"name": "@ohif/cli",
- "version": "3.10.0-beta.150",
+ "version": "3.10.0-beta.152",
"bin": {
"ohif-cli": "src/index.js",
},
@@ -670,7 +669,7 @@
},
"platform/core": {
"name": "@ohif/core",
- "version": "3.10.0-beta.150",
+ "version": "3.10.0-beta.152",
"dependencies": {
"@babel/runtime": "^7.20.13",
"dcmjs": "*",
@@ -703,7 +702,7 @@
},
"platform/i18n": {
"name": "@ohif/i18n",
- "version": "3.10.0-beta.150",
+ "version": "3.10.0-beta.152",
"dependencies": {
"@babel/runtime": "^7.20.13",
"i18next-locize-backend": "^2.0.0",
@@ -728,7 +727,7 @@
},
"platform/ui": {
"name": "@ohif/ui",
- "version": "3.10.0-beta.150",
+ "version": "3.10.0-beta.152",
"dependencies": {
"@testing-library/react": "^13.1.0",
"browser-detect": "^0.2.28",
@@ -788,7 +787,7 @@
},
"platform/ui-next": {
"name": "@ohif/ui-next",
- "version": "3.10.0-beta.150",
+ "version": "3.10.0-beta.152",
"dependencies": {
"@radix-ui/react-accordion": "^1.2.0",
"@radix-ui/react-checkbox": "^1.1.1",
@@ -1251,10 +1250,6 @@
"@gar/promisify": ["@gar/promisify@1.1.3", "", {}, "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw=="],
- "@hapi/hoek": ["@hapi/hoek@9.3.0", "", {}, "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ=="],
-
- "@hapi/topo": ["@hapi/topo@5.1.0", "", { "dependencies": { "@hapi/hoek": "^9.0.0" } }, "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg=="],
-
"@humanwhocodes/config-array": ["@humanwhocodes/config-array@0.13.0", "", { "dependencies": { "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", "minimatch": "^3.0.5" } }, "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw=="],
"@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="],
@@ -1695,12 +1690,6 @@
"@scarf/scarf": ["@scarf/scarf@1.4.0", "", {}, "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ=="],
- "@sideway/address": ["@sideway/address@4.1.5", "", { "dependencies": { "@hapi/hoek": "^9.0.0" } }, "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q=="],
-
- "@sideway/formula": ["@sideway/formula@3.0.1", "", {}, "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg=="],
-
- "@sideway/pinpoint": ["@sideway/pinpoint@2.0.0", "", {}, "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ=="],
-
"@sigstore/bundle": ["@sigstore/bundle@1.1.0", "", { "dependencies": { "@sigstore/protobuf-specs": "^0.2.0" } }, "sha512-PFutXEy0SmQxYI4texPw3dd2KewuNqv7OuK1ZFtY2fM754yhvG2KdgwIhRnoEE2uHdtdGNQ8s0lb94dW9sELog=="],
"@sigstore/protobuf-specs": ["@sigstore/protobuf-specs@0.2.1", "", {}, "sha512-XTWVxnWJu+c1oCshMLwnKvz8ZQJJDVOlciMfgpJBQbThVjKTCG8dwyhgLngBD2KN0ap9F/gOV8rFDEx8uh7R2A=="],
@@ -3021,8 +3010,6 @@
"etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="],
- "event-stream": ["event-stream@3.3.4", "", { "dependencies": { "duplexer": "~0.1.1", "from": "~0", "map-stream": "~0.1.0", "pause-stream": "0.0.11", "split": "0.3", "stream-combiner": "~0.0.4", "through": "~2.3.1" } }, "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g=="],
-
"event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="],
"eventemitter2": ["eventemitter2@6.4.7", "", {}, "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg=="],
@@ -3151,8 +3138,6 @@
"fresh": ["fresh@0.5.2", "", {}, "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="],
- "from": ["from@0.1.7", "", {}, "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g=="],
-
"fs-constants": ["fs-constants@1.0.0", "", {}, "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="],
"fs-extra": ["fs-extra@9.1.0", "", { "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ=="],
@@ -3645,8 +3630,6 @@
"jju": ["jju@1.4.0", "", {}, "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA=="],
- "joi": ["joi@17.13.3", "", { "dependencies": { "@hapi/hoek": "^9.3.0", "@hapi/topo": "^5.1.0", "@sideway/address": "^4.1.5", "@sideway/formula": "^3.0.1", "@sideway/pinpoint": "^2.0.0" } }, "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA=="],
-
"jpeg-lossless-decoder-js": ["jpeg-lossless-decoder-js@2.1.2", "", { "optionalDependencies": { "@rollup/rollup-linux-x64-gnu": "4.13.0" } }, "sha512-fYf/plymnuKwVw+s8gJ8O/BPw8y8OLaN11MBvEcT05kuy3m45o7BEC4J0JbxMNUYyO+MdK/I/jq0q1gkVYZm2Q=="],
"js-sha3": ["js-sha3@0.9.3", "", {}, "sha512-BcJPCQeLg6WjEx3FE591wVAevlli8lxsxm9/FzV4HXkV49TmBH38Yvrpce6fjbADGMKFrBMGTqrVz3qPIZ88Gg=="],
@@ -3829,8 +3812,6 @@
"map-or-similar": ["map-or-similar@1.5.0", "", {}, "sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg=="],
- "map-stream": ["map-stream@0.1.0", "", {}, "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g=="],
-
"mapbox-to-css-font": ["mapbox-to-css-font@2.4.5", "", {}, "sha512-VJ6nB8emkO9VODI0Fk+TQ/0zKBTqmf/Pkt8Xv0kHstoc0iXRajA00DAid4Kc3K5xeFIOoiZrVxijEzj0GLVO2w=="],
"markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="],
@@ -4241,8 +4222,6 @@
"pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
- "pause-stream": ["pause-stream@0.0.11", "", { "dependencies": { "through": "~2.3" } }, "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A=="],
-
"pbf": ["pbf@3.2.1", "", { "dependencies": { "ieee754": "^1.1.12", "resolve-protobuf-schema": "^2.1.0" }, "bin": { "pbf": "bin/pbf" } }, "sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ=="],
"pbkdf2": ["pbkdf2@3.1.2", "", { "dependencies": { "create-hash": "^1.1.2", "create-hmac": "^1.1.4", "ripemd160": "^2.0.1", "safe-buffer": "^5.0.1", "sha.js": "^2.4.8" } }, "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA=="],
@@ -4481,8 +4460,6 @@
"proxy-from-env": ["proxy-from-env@1.0.0", "", {}, "sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A=="],
- "ps-tree": ["ps-tree@1.2.0", "", { "dependencies": { "event-stream": "=3.3.4" }, "bin": { "ps-tree": "./bin/ps-tree.js" } }, "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA=="],
-
"pseudomap": ["pseudomap@1.0.2", "", {}, "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ=="],
"psl": ["psl@1.15.0", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w=="],
@@ -4881,8 +4858,6 @@
"stackframe": ["stackframe@1.3.4", "", {}, "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw=="],
- "start-server-and-test": ["start-server-and-test@1.15.5", "", { "dependencies": { "arg": "^5.0.2", "bluebird": "3.7.2", "check-more-types": "2.24.0", "debug": "4.3.4", "execa": "5.1.1", "lazy-ass": "1.6.0", "ps-tree": "1.2.0", "wait-on": "7.0.1" }, "bin": { "start-test": "src/bin/start.js", "server-test": "src/bin/start.js", "start-server-and-test": "src/bin/start.js" } }, "sha512-o3EmkX0++GV+qsvIJ/OKWm3w91fD8uS/bPQVPrh/7loaxkpXSuAIHdnmN/P/regQK9eNAK76aBJcHt+OSTk+nA=="],
-
"statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="],
"stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="],
@@ -4893,8 +4868,6 @@
"stream-browserify": ["stream-browserify@3.0.0", "", { "dependencies": { "inherits": "~2.0.4", "readable-stream": "^3.5.0" } }, "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA=="],
- "stream-combiner": ["stream-combiner@0.0.4", "", { "dependencies": { "duplexer": "~0.1.1" } }, "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw=="],
-
"stream-http": ["stream-http@3.2.0", "", { "dependencies": { "builtin-status-codes": "^3.0.0", "inherits": "^2.0.4", "readable-stream": "^3.6.0", "xtend": "^4.0.2" } }, "sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A=="],
"stream-shift": ["stream-shift@1.0.3", "", {}, "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ=="],
@@ -5219,8 +5192,6 @@
"w3c-xmlserializer": ["w3c-xmlserializer@4.0.0", "", { "dependencies": { "xml-name-validator": "^4.0.0" } }, "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw=="],
- "wait-on": ["wait-on@7.0.1", "", { "dependencies": { "axios": "^0.27.2", "joi": "^17.7.0", "lodash": "^4.17.21", "minimist": "^1.2.7", "rxjs": "^7.8.0" }, "bin": { "wait-on": "bin/wait-on" } }, "sha512-9AnJE9qTjRQOlTZIldAaf/da2eW0eSRSgcqq85mXQja/DW3MriHxkpODDSUEg+Gri/rKEcXUZHe+cevvYItaog=="],
-
"walker": ["walker@1.0.8", "", { "dependencies": { "makeerror": "1.0.12" } }, "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ=="],
"warning": ["warning@4.0.3", "", { "dependencies": { "loose-envify": "^1.0.0" } }, "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w=="],
@@ -5889,8 +5860,6 @@
"espree/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
- "event-stream/split": ["split@0.3.3", "", { "dependencies": { "through": "2" } }, "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA=="],
-
"executable/pify": ["pify@2.3.0", "", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="],
"expect/jest-get-type": ["jest-get-type@29.6.3", "", {}, "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw=="],
@@ -6501,10 +6470,6 @@
"stack-utils/escape-string-regexp": ["escape-string-regexp@2.0.0", "", {}, "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="],
- "start-server-and-test/debug": ["debug@4.3.4", "", { "dependencies": { "ms": "2.1.2" } }, "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ=="],
-
- "start-server-and-test/execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="],
-
"stream-browserify/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
"stream-http/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
@@ -6567,8 +6532,6 @@
"vfile/is-buffer": ["is-buffer@2.0.5", "", {}, "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ=="],
- "wait-on/axios": ["axios@0.27.2", "", { "dependencies": { "follow-redirects": "^1.14.9", "form-data": "^4.0.0" } }, "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ=="],
-
"webpack/eslint-scope": ["eslint-scope@5.1.1", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" } }, "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw=="],
"webpack/schema-utils": ["schema-utils@3.3.0", "", { "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", "ajv-keywords": "^3.5.2" } }, "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg=="],
@@ -7389,22 +7352,6 @@
"shader-loader/loader-utils/json5": ["json5@1.0.2", "", { "dependencies": { "minimist": "^1.2.0" }, "bin": { "json5": "lib/cli.js" } }, "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA=="],
- "start-server-and-test/debug/ms": ["ms@2.1.2", "", {}, "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="],
-
- "start-server-and-test/execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="],
-
- "start-server-and-test/execa/human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="],
-
- "start-server-and-test/execa/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
-
- "start-server-and-test/execa/npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="],
-
- "start-server-and-test/execa/onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="],
-
- "start-server-and-test/execa/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
-
- "start-server-and-test/execa/strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="],
-
"style-loader/schema-utils/ajv-keywords": ["ajv-keywords@3.5.2", "", { "peerDependencies": { "ajv": "^6.9.1" } }, "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ=="],
"temp/rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
@@ -7717,8 +7664,6 @@
"renderkid/css-select/domutils/dom-serializer": ["dom-serializer@1.4.1", "", { "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.2.0", "entities": "^2.0.0" } }, "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag=="],
- "start-server-and-test/execa/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
-
"tempy/del/rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
"unused-webpack-plugin/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="],
diff --git a/extensions/cornerstone-dicom-rt/src/utils/promptHydrateRT.ts b/extensions/cornerstone-dicom-rt/src/utils/promptHydrateRT.ts
index 8799aa4ad..781cf85dd 100644
--- a/extensions/cornerstone-dicom-rt/src/utils/promptHydrateRT.ts
+++ b/extensions/cornerstone-dicom-rt/src/utils/promptHydrateRT.ts
@@ -1,5 +1,3 @@
-import { ButtonEnums } from '@ohif/ui';
-
const RESPONSE = {
NO_NEVER: -1,
CANCEL: 0,
@@ -47,13 +45,13 @@ function _askHydrate(
const actions = [
{
id: 'no-hydrate',
- type: ButtonEnums.type.secondary,
+ type: 'secondary',
text: 'No',
value: RESPONSE.CANCEL,
},
{
id: 'yes-hydrate',
- type: ButtonEnums.type.primary,
+ type: 'primary',
text: 'Yes',
value: RESPONSE.HYDRATE_SEG,
},
diff --git a/extensions/cornerstone-dicom-rt/src/viewports/_getStatusComponent.tsx b/extensions/cornerstone-dicom-rt/src/viewports/_getStatusComponent.tsx
index b2dc2744f..d6158c75c 100644
--- a/extensions/cornerstone-dicom-rt/src/viewports/_getStatusComponent.tsx
+++ b/extensions/cornerstone-dicom-rt/src/viewports/_getStatusComponent.tsx
@@ -15,7 +15,7 @@ export default function _getStatusComponent({ isHydrated, onStatusClick }) {
case false:
StatusIcon = () => (
);
diff --git a/extensions/cornerstone-dicom-seg/src/utils/promptHydrateSEG.ts b/extensions/cornerstone-dicom-seg/src/utils/promptHydrateSEG.ts
index b6aa263d3..1e43e9fd8 100644
--- a/extensions/cornerstone-dicom-seg/src/utils/promptHydrateSEG.ts
+++ b/extensions/cornerstone-dicom-seg/src/utils/promptHydrateSEG.ts
@@ -1,5 +1,3 @@
-import { ButtonEnums } from '@ohif/ui';
-
const RESPONSE = {
NO_NEVER: -1,
CANCEL: 0,
@@ -49,13 +47,13 @@ function _askHydrate(
const actions = [
{
id: 'no-hydrate',
- type: ButtonEnums.type.secondary,
+ type: 'secondary',
text: 'No',
value: RESPONSE.CANCEL,
},
{
id: 'yes-hydrate',
- type: ButtonEnums.type.primary,
+ type: 'primary',
text: 'Yes',
value: RESPONSE.HYDRATE_SEG,
},
diff --git a/extensions/cornerstone-dicom-seg/src/viewports/_getStatusComponent.tsx b/extensions/cornerstone-dicom-seg/src/viewports/_getStatusComponent.tsx
index 003b75fb2..de321c647 100644
--- a/extensions/cornerstone-dicom-seg/src/viewports/_getStatusComponent.tsx
+++ b/extensions/cornerstone-dicom-seg/src/viewports/_getStatusComponent.tsx
@@ -15,7 +15,7 @@ export default function _getStatusComponent({ isHydrated, onStatusClick }) {
case false:
StatusIcon = () => (
);
diff --git a/extensions/cornerstone-dicom-sr/src/components/OHIFCornerstoneSRMeasurementViewport.tsx b/extensions/cornerstone-dicom-sr/src/components/OHIFCornerstoneSRMeasurementViewport.tsx
index a5f7a1ace..11ae7bcc9 100644
--- a/extensions/cornerstone-dicom-sr/src/components/OHIFCornerstoneSRMeasurementViewport.tsx
+++ b/extensions/cornerstone-dicom-sr/src/components/OHIFCornerstoneSRMeasurementViewport.tsx
@@ -427,7 +427,7 @@ function _getStatusComponent({
case 3:
StatusIcon = () => (
);
diff --git a/extensions/cornerstone-dynamic-volume/src/panels/GenerateVolume.tsx b/extensions/cornerstone-dynamic-volume/src/panels/GenerateVolume.tsx
deleted file mode 100644
index 516502a67..000000000
--- a/extensions/cornerstone-dynamic-volume/src/panels/GenerateVolume.tsx
+++ /dev/null
@@ -1,71 +0,0 @@
-import React from 'react';
-import { InputDoubleRange } from '@ohif/ui';
-import { Select } from '@ohif/ui';
-import { Button } from '@ohif/ui';
-import PropTypes from 'prop-types';
-
-const GenerateVolume = ({
- rangeValues,
- handleSliderChange,
- operationsUI,
- options,
- handleGenerateOptionsChange,
- onGenerateImage,
- returnTo4D,
- displayingComputedVolume,
-}) => {
- return (
- <>
-
-
Computed Image
-
- >
- );
-};
-
-GenerateVolume.propTypes = {
- rangeValues: PropTypes.array.isRequired,
- handleSliderChange: PropTypes.func.isRequired,
- operationsUI: PropTypes.array.isRequired,
- options: PropTypes.object.isRequired,
- handleGenerateOptionsChange: PropTypes.func.isRequired,
- onGenerateImage: PropTypes.func.isRequired,
- returnTo4D: PropTypes.func.isRequired,
-};
-
-export default GenerateVolume;
diff --git a/extensions/cornerstone/src/Viewport/OHIFCornerstoneViewport.tsx b/extensions/cornerstone/src/Viewport/OHIFCornerstoneViewport.tsx
index 1faf3ced8..9f270afd4 100644
--- a/extensions/cornerstone/src/Viewport/OHIFCornerstoneViewport.tsx
+++ b/extensions/cornerstone/src/Viewport/OHIFCornerstoneViewport.tsx
@@ -4,7 +4,7 @@ import * as cs3DTools from '@cornerstonejs/tools';
import { Enums, eventTarget, getEnabledElement } from '@cornerstonejs/core';
import { MeasurementService } from '@ohif/core';
import { AllInOneMenu } from '@ohif/ui-next';
-import { useViewportDialog } from '@ohif/ui';
+import { useViewportDialog } from '@ohif/ui-next';
import type { Types as csTypes } from '@cornerstonejs/core';
import { setEnabledElement } from '../state';
diff --git a/extensions/cornerstone/src/components/WindowLevelActionMenu/Colorbar.tsx b/extensions/cornerstone/src/components/WindowLevelActionMenu/Colorbar.tsx
index 8b42bd15b..503f17412 100644
--- a/extensions/cornerstone/src/components/WindowLevelActionMenu/Colorbar.tsx
+++ b/extensions/cornerstone/src/components/WindowLevelActionMenu/Colorbar.tsx
@@ -103,7 +103,8 @@ export function Colorbar({
return (
{
+ onClick={e => {
+ e.stopPropagation();
onSetColorbar();
}}
>
@@ -112,7 +113,8 @@ export function Colorbar({
{
+ onClick={e => {
+ e.stopPropagation();
onSetColorbar();
}}
/>
diff --git a/extensions/cornerstone/src/initMeasurementService.ts b/extensions/cornerstone/src/initMeasurementService.ts
index f1567e5f8..cec8cb106 100644
--- a/extensions/cornerstone/src/initMeasurementService.ts
+++ b/extensions/cornerstone/src/initMeasurementService.ts
@@ -360,6 +360,10 @@ const connectMeasurementServiceToTools = (
measurementService.subscribe(
MEASUREMENT_UPDATED,
({ source, measurement, notYetUpdatedAtSource }) => {
+ if (!source) {
+ return;
+ }
+
if (source.name !== CORNERSTONE_3D_TOOLS_SOURCE_NAME) {
return;
}
diff --git a/extensions/default/src/Components/LineChartViewport/LineChartViewport.tsx b/extensions/default/src/Components/LineChartViewport/LineChartViewport.tsx
index 4f6115695..23afb5f45 100644
--- a/extensions/default/src/Components/LineChartViewport/LineChartViewport.tsx
+++ b/extensions/default/src/Components/LineChartViewport/LineChartViewport.tsx
@@ -1,5 +1,5 @@
import React from 'react';
-import { LineChart } from '@ohif/ui';
+import { LineChart } from '@ohif/ui-next';
const LineChartViewport = ({ displaySets }) => {
const displaySet = displaySets[0];
diff --git a/extensions/default/src/Components/ProgressDropdownWithService.tsx b/extensions/default/src/Components/ProgressDropdownWithService.tsx
index d8720b4cc..aeed5b3f5 100644
--- a/extensions/default/src/Components/ProgressDropdownWithService.tsx
+++ b/extensions/default/src/Components/ProgressDropdownWithService.tsx
@@ -1,5 +1,5 @@
import React, { useEffect, useState, useCallback, ReactElement } from 'react';
-import { ProgressDropdown } from '@ohif/ui';
+import { ProgressDropdown } from '@ohif/ui-next';
const workflowStepsToDropdownOptions = (steps = []) =>
steps.map(step => ({
diff --git a/extensions/default/src/CustomizableContextMenu/ContextMenuItemsBuilder.ts b/extensions/default/src/CustomizableContextMenu/ContextMenuItemsBuilder.ts
index dc3162ff9..08a5ac66f 100644
--- a/extensions/default/src/CustomizableContextMenu/ContextMenuItemsBuilder.ts
+++ b/extensions/default/src/CustomizableContextMenu/ContextMenuItemsBuilder.ts
@@ -1,4 +1,4 @@
-import { Types } from '@ohif/ui';
+import { Types } from '@ohif/ui-next';
import { Menu, SelectorProps, MenuItem, ContextMenuProps } from './types';
type ContextMenuItem = Types.ContextMenuItem;
diff --git a/extensions/default/src/Panels/StudyBrowser/PanelStudyBrowser.tsx b/extensions/default/src/Panels/StudyBrowser/PanelStudyBrowser.tsx
index 59a04d477..cac2fa2e0 100644
--- a/extensions/default/src/Panels/StudyBrowser/PanelStudyBrowser.tsx
+++ b/extensions/default/src/Panels/StudyBrowser/PanelStudyBrowser.tsx
@@ -1,5 +1,5 @@
import React, { useState, useEffect, useCallback } from 'react';
-import { useImageViewer } from '@ohif/ui';
+import { useImageViewer } from '@ohif/ui-next';
import { useSystem, utils } from '@ohif/core';
import { useNavigate } from 'react-router-dom';
import { useViewportGrid, StudyBrowser, Separator } from '@ohif/ui-next';
diff --git a/extensions/default/src/Toolbar/LegacyLayoutSelector.tsx b/extensions/default/src/Toolbar/LegacyLayoutSelector.tsx
deleted file mode 100644
index e87b2e4de..000000000
--- a/extensions/default/src/Toolbar/LegacyLayoutSelector.tsx
+++ /dev/null
@@ -1,87 +0,0 @@
-import React, { useEffect, useState, useCallback } from 'react';
-import PropTypes from 'prop-types';
-import { LayoutSelector as OHIFLayoutSelector, ToolbarButton } from '@ohif/ui';
-
-function LegacyLayoutSelectorWithServices({
- servicesManager,
- rows = 3,
- columns = 3,
- onLayoutChange = () => {},
- ...props
-}) {
- const { toolbarService } = servicesManager.services;
-
- const onSelection = useCallback(
- props => {
- toolbarService.recordInteraction({
- interactionType: 'action',
- commands: [
- {
- commandName: 'setViewportGridLayout',
- commandOptions: { ...props },
- context: 'DEFAULT',
- },
- ],
- });
- },
- [toolbarService]
- );
-
- return (
-
- );
-}
-
-function LayoutSelector({ rows, columns, className, onSelection, ...rest }) {
- const [isOpen, setIsOpen] = useState(false);
-
- const closeOnOutsideClick = () => {
- if (isOpen) {
- setIsOpen(false);
- }
- };
-
- useEffect(() => {
- window.addEventListener('click', closeOnOutsideClick);
- return () => {
- window.removeEventListener('click', closeOnOutsideClick);
- };
- }, [isOpen]);
-
- const onInteractionHandler = () => setIsOpen(!isOpen);
- const DropdownContent = isOpen ? OHIFLayoutSelector : null;
-
- return (
-
- )
- }
- isActive={isOpen}
- type="toggle"
- />
- );
-}
-
-LayoutSelector.propTypes = {
- rows: PropTypes.number,
- columns: PropTypes.number,
- onLayoutChange: PropTypes.func,
- servicesManager: PropTypes.object.isRequired,
-};
-
-export default LegacyLayoutSelectorWithServices;
diff --git a/extensions/default/src/ViewerLayout/index.tsx b/extensions/default/src/ViewerLayout/index.tsx
index e8c5aeb62..c74451858 100644
--- a/extensions/default/src/ViewerLayout/index.tsx
+++ b/extensions/default/src/ViewerLayout/index.tsx
@@ -1,7 +1,7 @@
-import React, { useEffect, useState, useCallback, useRef } from 'react';
+import React, { useEffect, useState, useCallback } from 'react';
import PropTypes from 'prop-types';
-import { InvestigationalUseDialog } from '@ohif/ui';
+import { InvestigationalUseDialog } from '@ohif/ui-next';
import { HangingProtocolService, CommandsManager } from '@ohif/core';
import { useAppConfig } from '@state';
import ViewerHeader from './ViewerHeader';
diff --git a/extensions/default/src/customizations/labellingFlowCustomization.tsx b/extensions/default/src/customizations/labellingFlowCustomization.tsx
index 00f97f822..9f65b09f2 100644
--- a/extensions/default/src/customizations/labellingFlowCustomization.tsx
+++ b/extensions/default/src/customizations/labellingFlowCustomization.tsx
@@ -1,4 +1,4 @@
-import { LabellingFlow } from '@ohif/ui';
+import { LabellingFlow } from '@ohif/ui-next';
export default {
'ui.labellingComponent': LabellingFlow,
diff --git a/extensions/default/src/customizations/loadingIndicatorProgressCustomization.tsx b/extensions/default/src/customizations/loadingIndicatorProgressCustomization.tsx
index 9bba0cc75..360cfaacc 100644
--- a/extensions/default/src/customizations/loadingIndicatorProgressCustomization.tsx
+++ b/extensions/default/src/customizations/loadingIndicatorProgressCustomization.tsx
@@ -1,4 +1,4 @@
-import { LoadingIndicatorProgress } from '@ohif/ui';
+import { LoadingIndicatorProgress } from '@ohif/ui-next';
export default {
'ui.loadingIndicatorProgress': LoadingIndicatorProgress,
diff --git a/extensions/default/src/customizations/loadingIndicatorTotalPercentCustomization.tsx b/extensions/default/src/customizations/loadingIndicatorTotalPercentCustomization.tsx
index 368834f63..b206eaeb5 100644
--- a/extensions/default/src/customizations/loadingIndicatorTotalPercentCustomization.tsx
+++ b/extensions/default/src/customizations/loadingIndicatorTotalPercentCustomization.tsx
@@ -1,4 +1,4 @@
-import { LoadingIndicatorTotalPercent } from '@ohif/ui';
+import { LoadingIndicatorTotalPercent } from '@ohif/ui-next';
export default {
'ui.loadingIndicatorTotalPercent': LoadingIndicatorTotalPercent,
diff --git a/extensions/default/src/customizations/notificationCustomization.ts b/extensions/default/src/customizations/notificationCustomization.ts
index 360e1d267..18cf14ee9 100644
--- a/extensions/default/src/customizations/notificationCustomization.ts
+++ b/extensions/default/src/customizations/notificationCustomization.ts
@@ -1,5 +1,5 @@
import i18n from '@ohif/i18n';
-import { Notification } from '@ohif/ui';
+import { ViewportDialog } from '@ohif/ui-next';
const beginTrackingMessage = i18n.t('MeasurementTable:Track measurements for this series?');
const trackNewSeriesMessage = i18n.t('Do you want to add this measurement to the existing report?');
@@ -16,7 +16,7 @@ const hydrateSEGMessage = i18n.t('Do you want to open this Segmentation?');
const discardDirtyMessage = i18n.t('There are unsaved measurements. Do you want to save it?');
export default {
- 'ui.notificationComponent': Notification,
+ 'ui.notificationComponent': ViewportDialog,
'viewportNotification.beginTrackingMessage': beginTrackingMessage,
'viewportNotification.trackNewSeriesMessage': trackNewSeriesMessage,
'viewportNotification.discardSeriesMessage': discardSeriesMessage,
diff --git a/extensions/default/src/customizations/progressLoadingBarCustomization.tsx b/extensions/default/src/customizations/progressLoadingBarCustomization.tsx
index d4de5097c..874b6c9aa 100644
--- a/extensions/default/src/customizations/progressLoadingBarCustomization.tsx
+++ b/extensions/default/src/customizations/progressLoadingBarCustomization.tsx
@@ -1,4 +1,4 @@
-import { ProgressLoadingBar } from '@ohif/ui';
+import { ProgressLoadingBar } from '@ohif/ui-next';
export default {
'ui.progressLoadingBar': ProgressLoadingBar,
diff --git a/extensions/default/src/getToolbarModule.tsx b/extensions/default/src/getToolbarModule.tsx
index e0aaf6e41..eb52efb8b 100644
--- a/extensions/default/src/getToolbarModule.tsx
+++ b/extensions/default/src/getToolbarModule.tsx
@@ -33,7 +33,12 @@ export default function getToolbarModule({ commandsManager, servicesManager }: w
name: 'ohif.toolBoxButton',
defaultComponent: ToolBoxButtonWrapper,
},
- // legacy
+ // legacy will get removed in the future
+ // legacy will get removed in the future
+ // legacy will get removed in the future
+ // legacy will get removed in the future
+ // legacy will get removed in the future
+ // legacy will get removed in the future
{
name: 'ohif.radioGroup',
defaultComponent: ToolbarButtonLegacy,
diff --git a/extensions/default/src/utils/addIcon.ts b/extensions/default/src/utils/addIcon.ts
index 03bbf2c7e..b2d9967e7 100644
--- a/extensions/default/src/utils/addIcon.ts
+++ b/extensions/default/src/utils/addIcon.ts
@@ -1,8 +1,6 @@
-import { addIcon as addIconUI } from '@ohif/ui';
import { Icons } from '@ohif/ui-next';
/** Adds the icon to both ui and ui-next */
export function addIcon(name, icon) {
- addIconUI(name, icon);
Icons.addIcon(name, icon);
}
diff --git a/extensions/default/src/utils/callInputDialog.tsx b/extensions/default/src/utils/callInputDialog.tsx
index 65c2e41ac..e2aa55dbe 100644
--- a/extensions/default/src/utils/callInputDialog.tsx
+++ b/extensions/default/src/utils/callInputDialog.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import { setAnnotationLabel } from '@cornerstonejs/tools/utilities';
import { annotation } from '@cornerstonejs/tools';
-import { LabellingFlow } from '@ohif/ui';
+import { LabellingFlow } from '@ohif/ui-next';
import { InputDialog } from '@ohif/ui-next';
interface InputDialogDefaultProps {
diff --git a/extensions/dicom-microscopy/src/components/MicroscopyPanel/MicroscopyPanel.tsx b/extensions/dicom-microscopy/src/components/MicroscopyPanel/MicroscopyPanel.tsx
index 390296f4c..ed367456e 100644
--- a/extensions/dicom-microscopy/src/components/MicroscopyPanel/MicroscopyPanel.tsx
+++ b/extensions/dicom-microscopy/src/components/MicroscopyPanel/MicroscopyPanel.tsx
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import { callInputDialog } from '@ohif/extension-default';
import { ExtensionManager, CommandsManager, DicomMetadataStore } from '@ohif/core';
-import { MeasurementTable } from '@ohif/ui';
+import { DataRow } from '@ohif/ui-next';
import { withTranslation, WithTranslation } from 'react-i18next';
import { EVENTS as MicroscopyEvents } from '../../services/MicroscopyService';
import dcmjs from 'dcmjs';
@@ -328,14 +328,34 @@ function MicroscopyPanel(props: IMicroscopyPanelProps) {
className="ohif-scrollbar overflow-y-auto overflow-x-hidden"
data-cy={'measurements-panel'}
>
-
+
+ {data.map(item => (
+ onMeasurementItemClickHandler({ uid: item.uid })}
+ details={{
+ primary: item.displayText,
+ secondary: [],
+ }}
+ isVisible={true}
+ onToggleVisibility={() => {}}
+ isLocked={false}
+ onToggleLocked={() => {}}
+ onRename={() =>
+ onMeasurementItemEditHandler({ uid: item.uid, isActive: item.isActive })
+ }
+ onDelete={() =>
+ onMeasurementDeleteHandler({ uid: item.uid, isActive: item.isActive })
+ }
+ onColor={() => {}}
+ disableEditing={false}
+ description={item.displayText.join(', ')}
+ />
+ ))}
+
>
);
diff --git a/extensions/dicom-microscopy/src/customizations/panelMeasurementItem.tsx b/extensions/dicom-microscopy/src/customizations/panelMeasurementItem.tsx
deleted file mode 100644
index e971d7a6b..000000000
--- a/extensions/dicom-microscopy/src/customizations/panelMeasurementItem.tsx
+++ /dev/null
@@ -1,5 +0,0 @@
-import { MeasurementItem } from '@ohif/ui';
-
-export default {
- 'microscopyPanel.measurementItem': MeasurementItem,
-};
diff --git a/extensions/dicom-microscopy/src/getCustomizationModule.ts b/extensions/dicom-microscopy/src/getCustomizationModule.ts
index bee4fd087..4ba6f6b95 100644
--- a/extensions/dicom-microscopy/src/getCustomizationModule.ts
+++ b/extensions/dicom-microscopy/src/getCustomizationModule.ts
@@ -1,12 +1,3 @@
-import panelMeasurementItem from './customizations/panelMeasurementItem';
-
export default function getCustomizationModule() {
- return [
- {
- name: 'default',
- value: {
- ...panelMeasurementItem,
- },
- },
- ];
+ return [];
}
diff --git a/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/promptBeginTracking.js b/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/promptBeginTracking.js
index 756d642eb..bb1ebc58e 100644
--- a/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/promptBeginTracking.js
+++ b/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/promptBeginTracking.js
@@ -1,4 +1,3 @@
-import { ButtonEnums } from '@ohif/ui';
import i18n from 'i18next';
const RESPONSE = {
@@ -50,19 +49,19 @@ function _askTrackMeasurements(uiViewportDialogService, customizationService, vi
const actions = [
{
id: 'prompt-begin-tracking-cancel',
- type: ButtonEnums.type.secondary,
+ type: 'secondary',
text: i18n.t('Common:No'),
value: RESPONSE.CANCEL,
},
{
id: 'prompt-begin-tracking-no-do-not-ask-again',
- type: ButtonEnums.type.secondary,
+ type: 'secondary',
text: i18n.t('MeasurementTable:No, do not ask again'),
value: RESPONSE.NO_NEVER,
},
{
id: 'prompt-begin-tracking-yes',
- type: ButtonEnums.type.primary,
+ type: 'primary',
text: i18n.t('Common:Yes'),
value: RESPONSE.SET_STUDY_AND_SERIES,
},
diff --git a/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/promptHydrateStructuredReport.js b/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/promptHydrateStructuredReport.js
index 0c1ea2602..6503eae1a 100644
--- a/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/promptHydrateStructuredReport.js
+++ b/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/promptHydrateStructuredReport.js
@@ -1,5 +1,4 @@
import { hydrateStructuredReport } from '@ohif/extension-cornerstone-dicom-sr';
-import { ButtonEnums } from '@ohif/ui';
import { measurementTrackingMode } from './promptBeginTracking';
const RESPONSE = {
@@ -59,13 +58,13 @@ function _askTrackMeasurements(uiViewportDialogService, customizationService, vi
const actions = [
{
id: 'no-hydrate',
- type: ButtonEnums.type.secondary,
+ type: 'secondary',
text: 'No',
value: RESPONSE.CANCEL,
},
{
id: 'yes-hydrate',
- type: ButtonEnums.type.primary,
+ type: 'primary',
text: 'Yes',
value: RESPONSE.HYDRATE_REPORT,
},
diff --git a/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/promptTrackNewSeries.js b/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/promptTrackNewSeries.js
index 1a430549d..32b285b8b 100644
--- a/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/promptTrackNewSeries.js
+++ b/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/promptTrackNewSeries.js
@@ -1,4 +1,3 @@
-import { ButtonEnums } from '@ohif/ui';
import { measurementTrackingMode } from './promptBeginTracking';
const RESPONSE = {
@@ -47,17 +46,17 @@ function _askShouldAddMeasurements(uiViewportDialogService, customizationService
);
const actions = [
{
- type: ButtonEnums.type.secondary,
+ type: 'secondary',
text: 'Cancel',
value: RESPONSE.CANCEL,
},
{
- type: ButtonEnums.type.primary,
+ type: 'primary',
text: 'Create new report',
value: RESPONSE.CREATE_REPORT,
},
{
- type: ButtonEnums.type.primary,
+ type: 'primary',
text: 'Add to existing report',
value: RESPONSE.ADD_SERIES,
},
@@ -88,7 +87,7 @@ function _askSaveDiscardOrCancel(UIViewportDialogService, customizationService,
);
const actions = [
- { type: 'cancel', text: 'Cancel', value: RESPONSE.CANCEL },
+ { type: 'secondary', text: 'Cancel', value: RESPONSE.CANCEL },
{
type: 'secondary',
text: 'Save',
diff --git a/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking.tsx b/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking.tsx
index 956e6a37b..d392e43b2 100644
--- a/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking.tsx
+++ b/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking.tsx
@@ -1,5 +1,4 @@
import React from 'react';
-import { Dialog, ButtonEnums } from '@ohif/ui';
import { utils } from '@ohif/core';
import { AccordionTrigger, MeasurementTable, useViewportGrid } from '@ohif/ui-next';
import {
diff --git a/modes/basic-dev-mode/src/toolbarButtons.ts b/modes/basic-dev-mode/src/toolbarButtons.ts
index 6a8c1d5f1..4b4d7ad88 100644
--- a/modes/basic-dev-mode/src/toolbarButtons.ts
+++ b/modes/basic-dev-mode/src/toolbarButtons.ts
@@ -1,24 +1,5 @@
-import { WindowLevelMenuItem } from '@ohif/ui';
-import { defaults } from '@ohif/core';
import type { Button } from '@ohif/core/types';
-const { windowLevelPresets } = defaults;
-
-function _createWwwcPreset(preset, title, subtitle) {
- return {
- id: preset.toString(),
- title,
- subtitle,
- commands: {
- commandName: 'setWindowLevel',
- commandOptions: {
- ...windowLevelPresets[preset],
- },
- context: 'CORNERSTONE',
- },
- };
-}
-
export const setToolActiveToolbar = {
commandName: 'setToolActive',
commandOptions: {
diff --git a/modes/microscopy/src/index.tsx b/modes/microscopy/src/index.tsx
index b0d2ff71a..dd49efcdc 100644
--- a/modes/microscopy/src/index.tsx
+++ b/modes/microscopy/src/index.tsx
@@ -1,4 +1,3 @@
-import { hotkeys } from '@ohif/core';
import i18n from 'i18next';
import { id } from './id';
@@ -94,7 +93,7 @@ function modeFactory({ modeConfiguration }) {
leftPanels: [ohif.leftPanel],
leftPanelResizable: true,
leftPanelClosed: true, // we have problem with rendering thumbnails for microscopy images
- rightPanelClosed: true, // we do not have the save microscopy measurements yet
+ // rightPanelClosed: true, // we do not have the save microscopy measurements yet
rightPanels: [ohif.rightPanel],
rightPanelResizable: true,
viewports: [
diff --git a/modes/segmentation/src/toolbarButtons.ts b/modes/segmentation/src/toolbarButtons.ts
index 9dea55253..46d1e915a 100644
--- a/modes/segmentation/src/toolbarButtons.ts
+++ b/modes/segmentation/src/toolbarButtons.ts
@@ -308,6 +308,8 @@ const toolbarButtons: Button[] = [
props: {
icon: 'icon-tool-interpolation',
label: 'Interpolate Labelmap',
+ tooltip:
+ 'Automatically fill in missing slices between drawn segments. Use brush or threshold tools on at least two slices, then click to interpolate across slices. Works in any direction. Volume must be reconstructable.',
evaluate: [
'evaluate.cornerstone.segmentation',
{
@@ -324,7 +326,8 @@ const toolbarButtons: Button[] = [
props: {
icon: 'icon-tool-bidirectional-segment',
label: 'Segment Bidirectional',
- tooltip: 'Finding the largest bidirectional measurement per lesion',
+ tooltip:
+ 'Automatically detects the largest length and width across slices for the selected segment and displays a bidirectional measurement.',
evaluate: {
name: 'evaluate.cornerstone.segmentation',
disabledText: 'Create new segmentation to enable this tool.',
@@ -338,7 +341,8 @@ const toolbarButtons: Button[] = [
props: {
icon: 'icon-tool-click-segment',
label: 'One Click Segment',
- tooltip: 'One Click Segment',
+ tooltip:
+ 'Detects segmentable regions with one click. Hover for visual feedback—click when a plus sign appears to auto-segment the lesion.',
evaluate: {
name: 'evaluate.cornerstone.segmentation',
toolNames: ['RegionSegmentPlus'],
@@ -353,7 +357,8 @@ const toolbarButtons: Button[] = [
props: {
icon: 'icon-labelmap-slice-propagation',
label: 'Labelmap Assist',
- tooltip: 'Labelmap Assist Based on Previous / Next Slice Data',
+ tooltip:
+ 'Toggle AI assistance for segmenting nearby slices. After drawing on a slice, scroll to preview predictions. Press Enter to accept or Esc to skip.',
evaluate: [
'evaluate.cornerstoneTool.toggle',
{
@@ -376,7 +381,7 @@ const toolbarButtons: Button[] = [
icon: 'icon-marker-labelmap',
label: 'Marker Guided Labelmap',
tooltip:
- 'Use Positive and Negative Points to Guide Labelmap Creation, hit N to move to next slice',
+ 'Use include/exclude markers to guide AI (SAM) segmentation. Click to place markers, Enter to accept results, Esc to reject, and N to go to the next slice while keeping markers.',
evaluate: [
{
name: 'evaluate.cornerstone.segmentation',
diff --git a/platform/app/package.json b/platform/app/package.json
index d2617bddf..4fd77e578 100644
--- a/platform/app/package.json
+++ b/platform/app/package.json
@@ -38,7 +38,6 @@
"start": "yarn run dev",
"test:e2e": "cypress open",
"test:e2e:local": "cypress run --config video=false --browser chrome --spec 'cypress/integration/common/**/*,cypress/integration/pwa/**/*'",
- "test:e2e:dist": "start-server-and-test test:e2e:serve http://localhost:3000 test:e2e:ci",
"test:e2e:serve": "cross-env APP_CONFIG=config/e2e.js yarn start",
"test:unit": "jest --watchAll",
"test:unit:ci": "jest --ci --runInBand --collectCoverage",
diff --git a/platform/app/src/App.tsx b/platform/app/src/App.tsx
index 5dc1b439a..8f8727463 100644
--- a/platform/app/src/App.tsx
+++ b/platform/app/src/App.tsx
@@ -14,7 +14,6 @@ import {
ServiceProvidersManager,
SystemContextProvider,
} from '@ohif/core';
-import { ThemeWrapper, ViewportDialogProvider, UserAuthenticationProvider } from '@ohif/ui';
import {
ThemeWrapper as ThemeWrapperNext,
NotificationProvider,
@@ -25,6 +24,8 @@ import {
Modal as ModalNext,
ManagedDialog,
ModalProvider,
+ ViewportDialogProvider,
+ UserAuthenticationProvider,
} from '@ohif/ui-next';
// Viewer Project
// TODO: Should this influence study list?
@@ -110,7 +111,6 @@ function App({
[UserAuthenticationProvider, { service: userAuthenticationService }],
[I18nextProvider, { i18n }],
[ThemeWrapperNext],
- [ThemeWrapper],
[SystemContextProvider, { commandsManager, extensionManager, hotkeysManager, servicesManager }],
[ViewportGridProvider, { service: viewportGridService }],
[ViewportDialogProvider, { service: uiViewportDialogService }],
diff --git a/platform/app/src/routes/Mode/Mode.tsx b/platform/app/src/routes/Mode/Mode.tsx
index 86651aa0d..11efb98c6 100644
--- a/platform/app/src/routes/Mode/Mode.tsx
+++ b/platform/app/src/routes/Mode/Mode.tsx
@@ -2,7 +2,7 @@ import React, { useEffect, useState, useRef } from 'react';
import { useParams, useLocation, useNavigate } from 'react-router';
import PropTypes from 'prop-types';
import { utils } from '@ohif/core';
-import { DragAndDropProvider, ImageViewerProvider } from '@ohif/ui';
+import { ImageViewerProvider, DragAndDropProvider } from '@ohif/ui-next';
import { useSearchParams } from '@hooks';
import { useAppConfig } from '@state';
import ViewportGrid from '@components/ViewportGrid';
diff --git a/platform/app/src/routes/PrivateRoute.tsx b/platform/app/src/routes/PrivateRoute.tsx
index 0e9f9279e..d4b20a49d 100644
--- a/platform/app/src/routes/PrivateRoute.tsx
+++ b/platform/app/src/routes/PrivateRoute.tsx
@@ -1,4 +1,4 @@
-import { useUserAuthentication } from '@ohif/ui';
+import { useUserAuthentication } from '@ohif/ui-next';
export const PrivateRoute = ({ children, handleUnauthenticated }) => {
const [{ user, enabled }] = useUserAuthentication();
diff --git a/platform/app/src/routes/WorkList/WorkList.tsx b/platform/app/src/routes/WorkList/WorkList.tsx
index 619cb6de5..708e0b59f 100644
--- a/platform/app/src/routes/WorkList/WorkList.tsx
+++ b/platform/app/src/routes/WorkList/WorkList.tsx
@@ -11,7 +11,6 @@ import filtersMeta from './filtersMeta.js';
import { useAppConfig } from '@state';
import { useDebounce, useSearchParams } from '@hooks';
import { utils } from '@ohif/core';
-import { routerBasename } from '../../utils/publicUrl';
import {
StudyListExpandedRow,
diff --git a/platform/ui-next/src/components/Button/Button.tsx b/platform/ui-next/src/components/Button/Button.tsx
index 8a8de6346..3b7db1cef 100644
--- a/platform/ui-next/src/components/Button/Button.tsx
+++ b/platform/ui-next/src/components/Button/Button.tsx
@@ -40,8 +40,11 @@ export interface ButtonProps
const Button = React.forwardRef(
({ className, variant, size, asChild = false, ...props }, forwardRef) => {
const Comp = asChild ? Slot : 'button';
+ const dataCY = props.dataCY || `${props.name}-btn`;
+
return (
>(({ className, ...props }, ref) => (
-
+
((props, ref) => (
));
@@ -105,7 +105,7 @@ const CommandSeparator = React.forwardRef<
>(({ className, ...props }, ref) => (
));
@@ -118,7 +118,7 @@ const CommandItem = React.forwardRef<
- {isReturnEnabled && }
+ {isReturnEnabled && }
{WhiteLabeling?.createLogoComponentFn?.(React, props) || }
diff --git a/platform/ui-next/src/components/InvestigationalUseDialog/InvestigationalUseDialog.tsx b/platform/ui-next/src/components/InvestigationalUseDialog/InvestigationalUseDialog.tsx
new file mode 100644
index 000000000..6267a1a54
--- /dev/null
+++ b/platform/ui-next/src/components/InvestigationalUseDialog/InvestigationalUseDialog.tsx
@@ -0,0 +1,103 @@
+import React, { useState, useEffect } from 'react';
+import PropTypes from 'prop-types';
+import { Icons } from '@ohif/ui-next';
+import { Button } from '../Button';
+
+export enum showDialogOption {
+ NeverShowDialog = 'never',
+ AlwaysShowDialog = 'always',
+ ShowOnceAndConfigure = 'configure',
+}
+
+const InvestigationalUseDialog = ({
+ dialogConfiguration = {
+ option: showDialogOption.AlwaysShowDialog,
+ },
+}) => {
+ const { option, days } = dialogConfiguration;
+ const [isHidden, setIsHidden] = useState(true);
+
+ useEffect(() => {
+ const dialogLocalState = localStorage.getItem('investigationalUseDialog');
+ const dialogSessionState = sessionStorage.getItem('investigationalUseDialog');
+
+ switch (option) {
+ case showDialogOption.NeverShowDialog:
+ setIsHidden(true);
+ break;
+ case showDialogOption.AlwaysShowDialog:
+ setIsHidden(!!dialogSessionState);
+ break;
+ case showDialogOption.ShowOnceAndConfigure:
+ if (dialogLocalState) {
+ const { expiryDate } = JSON.parse(dialogLocalState);
+ const isExpired = new Date() > new Date(expiryDate);
+ setIsHidden(!isExpired);
+ } else {
+ setIsHidden(false);
+ }
+ break;
+ default:
+ setIsHidden(true);
+ }
+ }, [option, days]);
+
+ const handleConfirmAndHide = () => {
+ const expiryDate = new Date();
+
+ switch (option) {
+ case showDialogOption.ShowOnceAndConfigure:
+ expiryDate.setDate(expiryDate.getDate() + days);
+ localStorage.setItem('investigationalUseDialog', JSON.stringify({ expiryDate }));
+ break;
+ case showDialogOption.AlwaysShowDialog:
+ sessionStorage.setItem('investigationalUseDialog', 'hidden');
+ break;
+ }
+ setIsHidden(true);
+ };
+
+ if (isHidden) {
+ return null;
+ }
+
+ return (
+
+
+
+
+
+
+ OHIF Viewer is{' '}
+ for investigational use only
+
+
+ window.open('https://ohif.org/', '_blank')}
+ >
+ Learn more about OHIF Viewer
+
+
+
+
+
+
+
+ );
+};
+
+InvestigationalUseDialog.propTypes = {
+ dialogConfiguration: PropTypes.shape({
+ option: PropTypes.oneOf(Object.values(showDialogOption)).isRequired,
+ days: PropTypes.number,
+ }),
+};
+
+export default InvestigationalUseDialog;
diff --git a/platform/ui-next/src/components/InvestigationalUseDialog/index.ts b/platform/ui-next/src/components/InvestigationalUseDialog/index.ts
new file mode 100644
index 000000000..4d9c6d5ce
--- /dev/null
+++ b/platform/ui-next/src/components/InvestigationalUseDialog/index.ts
@@ -0,0 +1,3 @@
+import InvestigationalUseDialog from './InvestigationalUseDialog';
+
+export default InvestigationalUseDialog;
diff --git a/platform/ui-next/src/components/Labelling/LabellingFlow.tsx b/platform/ui-next/src/components/Labelling/LabellingFlow.tsx
new file mode 100644
index 000000000..152c7ad55
--- /dev/null
+++ b/platform/ui-next/src/components/Labelling/LabellingFlow.tsx
@@ -0,0 +1,92 @@
+import React, { useState, useEffect } from 'react';
+import { CheckIcon } from '@radix-ui/react-icons';
+import cloneDeep from 'lodash.clonedeep';
+import {
+ Command,
+ CommandEmpty,
+ CommandGroup,
+ CommandInput,
+ CommandItem,
+ CommandList,
+} from '../Command/Command'; // Assuming Command is in ../Command relative path
+import { cn } from '../../lib/utils'; // Assuming cn utility is here
+import { ScrollArea } from '../ScrollArea/ScrollArea'; // Import ScrollArea
+
+interface PropType {
+ labellingDoneCallback: (label: string) => void;
+ measurementData: any; // Keep any for now as per instructions
+ labelData: LabelInfo[]; // Use LabelInfo[] for better type safety
+ exclusive: boolean;
+ hide: () => void; // Add hide prop explicitly
+ placeholder?: string; // Optional placeholder for input
+ emptyMessage?: string; // Optional message for empty results
+ initialLabel?: string; // Optional initial selected label
+}
+
+export interface LabelInfo {
+ label: string;
+ value: string;
+}
+
+// Convert to Functional Component
+const LabellingFlow: React.FC = ({
+ labellingDoneCallback,
+ measurementData,
+ labelData,
+ exclusive,
+ hide,
+ placeholder = 'Type label or search...',
+ emptyMessage = 'No labels found.',
+ initialLabel,
+}) => {
+ // State for the selected value
+ const [value, setValue] = useState(initialLabel);
+ const [currentItems, setCurrentItems] = useState([]);
+
+ // Update items when labelData changes
+ useEffect(() => {
+ if (labelData) {
+ setCurrentItems(cloneDeep(labelData));
+ }
+ }, [labelData]);
+
+ // Original component used measurementData?.label in constructor,
+ // let's try to initialize based on that if initialLabel is not provided.
+ useEffect(() => {
+ if (!initialLabel && measurementData?.label) {
+ setValue(measurementData.label);
+ }
+ }, [initialLabel, measurementData]);
+
+ return (
+
+
+
+ {emptyMessage}
+
+
+ {currentItems.map(item => (
+ {
+ const newValue = currentValue === value ? '' : currentValue;
+ setValue(newValue);
+ labellingDoneCallback(newValue);
+ hide();
+ }}
+ >
+
+ {item.label}
+
+ ))}
+
+
+
+
+ );
+};
+
+export default LabellingFlow;
\ No newline at end of file
diff --git a/platform/ui-next/src/components/Labelling/index.ts b/platform/ui-next/src/components/Labelling/index.ts
new file mode 100644
index 000000000..77a5fe795
--- /dev/null
+++ b/platform/ui-next/src/components/Labelling/index.ts
@@ -0,0 +1,2 @@
+import LabellingFlow from './LabellingFlow';
+export default LabellingFlow;
diff --git a/platform/ui-next/src/components/LineChart/LineChart.css b/platform/ui-next/src/components/LineChart/LineChart.css
new file mode 100644
index 000000000..ea2f03129
--- /dev/null
+++ b/platform/ui-next/src/components/LineChart/LineChart.css
@@ -0,0 +1,40 @@
+.LineChart svg {
+ color: #ddd;
+ border: solid 1px #383f42;
+}
+
+.LineChart svg .background {
+ stroke: none;
+ fill: #151c20;
+}
+
+.LineChart svg .axis.label {
+ fill: #ddd;
+ font-size: 12px;
+}
+
+.LineChart svg .axis .tick line {
+ stroke: #ddd;
+}
+
+.LineChart svg .axis .tick text {
+ fill: #ddd;
+}
+
+.LineChart svg .axis.grid .domain {
+ stroke: none;
+}
+
+.LineChart svg .axis.grid .tick line {
+ stroke: #383f42;
+}
+
+.LineChart svg .line {
+ stroke-width: 1px;
+ fill: none;
+}
+
+.LineChart svg .legend .legend-background {
+ fill: none;
+ stroke: #383f42;
+}
diff --git a/platform/ui-next/src/components/LineChart/LineChart.tsx b/platform/ui-next/src/components/LineChart/LineChart.tsx
new file mode 100644
index 000000000..08fe3cfe0
--- /dev/null
+++ b/platform/ui-next/src/components/LineChart/LineChart.tsx
@@ -0,0 +1,121 @@
+import React, { useEffect, useLayoutEffect, useState, useRef } from 'react';
+import PropTypes from 'prop-types';
+import classnames from 'classnames';
+import * as d3Selection from 'd3-selection';
+import { lineChart } from './d3LineChart';
+import './LineChart.css';
+
+const LineChart = ({
+ width: widthProp,
+ height: heightProp,
+ axis,
+ series,
+ showAxisLabels = true,
+ showAxisGrid = true,
+ showLegend = false,
+ legendWidth = 120,
+ transparentChartBackground = false,
+ containerClassName,
+ chartContainerClassName,
+}: {
+ title: string;
+ width: number;
+ height: number;
+ showAxisGrid: boolean;
+ showAxisLabels: boolean;
+ showLegend: boolean;
+ legendWidth: number;
+ transparentChartBackground: boolean;
+ containerClassName: string;
+ chartContainerClassName: string;
+}): JSX.Element => {
+ const chartContainerRef = useRef(null);
+ const [d3SVGContainer, setD3SVGRef] = useState(null);
+ const [width, setWidth] = useState(0);
+ const [height, setHeight] = useState(0);
+
+ useLayoutEffect(() => {
+ const chartContainer = chartContainerRef.current;
+ const containerWidth = chartContainer.offsetWidth;
+ const containerHeight = chartContainer.offsetHeight;
+ const d3Container = d3Selection
+ .create('svg')
+ .attr('viewBox', [0, 0, containerWidth, containerHeight])
+ .style('max-width', `${containerWidth}px`)
+ .style('overflow', 'visible');
+
+ chartContainer.append(d3Container.node());
+
+ setD3SVGRef(d3Container);
+ setWidth(containerWidth);
+ setHeight(containerHeight);
+ }, [chartContainerRef]);
+
+ useEffect(() => {
+ if (!d3SVGContainer) {
+ return;
+ }
+
+ lineChart.addLineChartNode({
+ d3SVGRef: d3SVGContainer,
+ axis,
+ series,
+ width,
+ height,
+ showAxisLabels,
+ showAxisGrid,
+ showLegend,
+ legendWidth,
+ transparentChartBackground,
+ });
+ }, [
+ d3SVGContainer,
+ axis,
+ series,
+ width,
+ height,
+ showAxisLabels,
+ showAxisGrid,
+ transparentChartBackground,
+ showLegend,
+ legendWidth,
+ ]);
+
+ return (
+
+ );
+};
+
+LineChart.propTypes = {
+ title: PropTypes.string,
+ width: PropTypes.number,
+ height: PropTypes.number,
+ showAxisLabels: PropTypes.bool,
+ showAxisGrid: PropTypes.bool,
+ showLegend: PropTypes.bool,
+ legendWidth: PropTypes.number,
+ transparentChartBackground: PropTypes.bool,
+ containerClassName: PropTypes.string,
+ chartContainerClassName: PropTypes.string,
+};
+
+export default LineChart;
diff --git a/platform/ui-next/src/components/LineChart/d3LineChart/chart.ts b/platform/ui-next/src/components/LineChart/d3LineChart/chart.ts
new file mode 100644
index 000000000..a8757ffb6
--- /dev/null
+++ b/platform/ui-next/src/components/LineChart/d3LineChart/chart.ts
@@ -0,0 +1,365 @@
+/**
+ * It adds attributes to root element from passed attributes list
+ *
+ * @param {object} root svg element to be changed
+ * @param {object} attributes list of attributes to be added on root element
+ * @modifies {root}
+ *
+ */
+const _addAttributes = (root, attributes = []) => {
+ for (const attr of attributes) {
+ const { key, value } = attr;
+
+ if (key && value) {
+ if (key === 'class') {
+ root.classed(value, true);
+ } else {
+ root.attr(key, value);
+ }
+ }
+ }
+};
+
+/**
+ * It formats axis labels. It adds units if existing
+ *
+ * @param {Object} def
+ * @param {string} def.label string representing axis label
+ * @param {string} [def.unit] string representing axis unit
+ * @return {string} formatted label
+ */
+const _formatAxisLabel = ({ label, unit }) => {
+ let formattedLabel = label;
+
+ if (unit) {
+ formattedLabel += `(${unit})`;
+ }
+
+ return formattedLabel;
+};
+
+/**
+ * It appends chart svg element container to root element
+ *
+ * @param {object} root svg element to be changed
+ * @param {number} width width of container
+ * @param {number} height height of container
+ * @param {number} translateX value to translate on axis x
+ * @param {number} translateY value to translate on axis y
+ * @modifies {root}
+ *
+ * @return {object} appended chart container (svg element)
+ */
+const _addChartContainer = (root, width, height, translateX, translateY) => {
+ root.append('rect').attr('class', 'background').attr('width', width).attr('height', height);
+
+ return root
+ .append('g')
+ .attr('width', width)
+ .attr('height', height)
+ .append('g')
+ .attr('transform', 'translate(' + translateX + ',' + translateY + ')');
+};
+
+const _addLegend = (root, width, height, posX, posY) => {
+ const legendContainer = root
+ .append('g')
+ .attr('class', 'legend')
+ .attr('transform', 'translate(' + posX + ',' + posY + ')');
+
+ legendContainer
+ .append('rect')
+ .attr('class', 'legend-background')
+ .attr('width', width)
+ .attr('height', height);
+
+ return legendContainer;
+};
+
+const _setLegendLabels = (root, labels, colors, itemHeight, itemWidth, textEllipses) => {
+ const yOffset = itemHeight / 2;
+ const textLeft = 20;
+
+ // Add one dot in the legend for each
+ root
+ .selectAll('labelDots')
+ .data(labels)
+ .enter()
+ .append('circle')
+ .attr('cx', 10)
+ .attr('cy', (_d, i) => yOffset + i * itemHeight)
+ .attr('r', 5)
+ .style('fill', (_d, i) => colors[i]);
+
+ root
+ .selectAll('labelText')
+ .data(labels)
+ .enter()
+ .append('text')
+ .attr('x', textLeft)
+ .attr('y', (_d, i) => yOffset + i * itemHeight)
+ .style('fill', (_d, i) => colors[i])
+ .append('tspan')
+ .style('alignment-baseline', 'middle')
+ .text(d => d)
+ .each(textEllipses(itemWidth - textLeft, 1));
+};
+
+/**
+ * It appends axis svg element to root element
+ *
+ * @param {object} root svg element to be changed
+ * @param {TimecoursePointDef} axisDef definition of given axis
+ * @param {number} posX position X to place content
+ * @param {number} posY position Y to place content
+ * @param {number} axisExtraAttrs list of extra attributes to be added on content
+ * @param {function} axisBuilder callback function to create axis graphics
+ * @param {boolean} showAxisLabels flag to display labels or not
+ * @param {number} labelPosX position X to place label content
+ * @param {number} labelPosY position Y to place label content
+ * @param {object} labelExtraAttrs list of extra attributes to be added on label content
+ * @modifies {root}
+ *
+ * @return {object} axis svg element
+ */
+const _addAxis = (
+ root,
+ axisDef,
+ posX = 0,
+ posY = 0,
+ axisExtraAttrs = [],
+ axisBuilder,
+ showAxisLabels,
+ labelPosX,
+ labelPosY,
+ labelExtraAttrs = [],
+ showAxisGrid
+) => {
+ const { type } = axisDef;
+ const showAxisGridClass = showAxisGrid ? 'grid' : '';
+ const translateValue = `${posX}, ${posY}`;
+ const axisContent = root
+ .append('g')
+ .attr('class', `${type} ${showAxisGridClass} axis`)
+ .attr('transform', `translate(${translateValue})`);
+
+ _addAttributes(axisContent, axisExtraAttrs);
+ axisContent.call(axisBuilder());
+
+ if (showAxisLabels) {
+ const label = _formatAxisLabel(axisDef);
+ // text label for the x axis
+ if (label) {
+ const labelContent = root
+ .append('text')
+ .attr('class', 'label axis')
+ .attr('x', labelPosX)
+ .attr('y', labelPosY)
+ .style('text-anchor', 'middle')
+ .text(label);
+
+ _addAttributes(labelContent, labelExtraAttrs);
+ }
+ }
+
+ return axisContent;
+};
+
+/**
+ * It scales axis graphics
+ *
+ * @param {object} sourceGraphicsXAxis original d3 graphics to be proccessed (x Axis)
+ * @param {object} sourceGraphicsYAxis original d3 graphics to be proccessed (y Axis)
+ * @param {object} xAxisGenerator d3 X axis generator
+ * @param {object} yAxisGenerator d3 Y axis generator
+ * @param {object} newXAxisScale d3 continuos scale for X Axis
+ * @param {object} newYAxisScale d3 continuos scale for Y Axis
+ * @modifies {sourceGraphicsXAxis|sourceGraphicsYAxis}
+ */
+const _scaleAxisGraphics = (
+ sourceGraphicsXAxis,
+ sourceGraphicsYAxis,
+ xAxisGenerator,
+ yAxisGenerator,
+ newXAxisScale,
+ newYAxisScale
+) => {
+ const newXAxis = xAxisGenerator.scale(newXAxisScale);
+ const newYAxis = yAxisGenerator.scale(newYAxisScale);
+
+ sourceGraphicsXAxis.call(newXAxis);
+ sourceGraphicsYAxis.call(newYAxis);
+};
+
+/**
+ * It appends background svg element to root element and also a clip path for chart boundaries
+ *
+ * @param {object} root svg element to be changed
+ * @param {number} width content`s width
+ * @param {number} height content`s height
+ * @param {boolean} transparent background to be transparent or not
+ * @param {number} offset content`s clip offset
+ * @modifies {root}
+ *
+ */
+const _addChartClipPath = (root, width, height, transparent = false, offset = 6) => {
+ const translateOffset = -offset / 2;
+ root
+ .append('clipPath')
+ .attr('id', 'clip')
+ .append('rect')
+ .attr('transform', `translate(${translateOffset}, ${translateOffset})`)
+ .attr('width', width + offset)
+ .attr('height', height + offset);
+
+ root
+ .append('rect')
+ .attr('class', 'background')
+ .attr('width', width)
+ .attr('height', height)
+ .classed('transparent', transparent);
+};
+
+const _addSeries = (root, seriesIndex, style) => {
+ return root
+ .append('g')
+ .attr('id', `series_${seriesIndex}`)
+ .attr('class', 'series')
+ .attr('stroke', style.color || '#ffffff');
+};
+
+/**
+ * It appends line chart svg element to root element
+ *
+ * @param {object} root svg element to be changed
+ * @param {object} dataset dataset to tie to content
+ * @param {string} dAttrValue path d attribute
+ * @modifies {root}
+ *
+ * @return {object} svg line element line
+ */
+const _addLine = (root, dataset, dAttrValue) => {
+ return root
+ .append('path')
+ .attr('clip-path', 'url(#clip)')
+ .datum(dataset)
+ .attr('class', 'line')
+ .attr('d', dAttrValue);
+};
+
+/**
+ * It updates line chart svg element with new dataset and dAttrValue value
+ *
+ * @param {object} root svg element to be changed
+ * @param {object} dataset dataset to tie to content
+ * @param {string} dAttrValue path d attribute
+ * @modifies {root}
+ *
+ */
+const _updateLine = (root, dataset = [], dAttrValue) => {
+ const line = root.select('.line');
+
+ if (dataset && dataset.length > 0) {
+ line.datum(dataset);
+ }
+
+ line.attr('d', dAttrValue);
+};
+
+/**
+ * It appends points svg element to root element
+ *
+ * @param {object} root svg element to be changed
+ * @param {object} dataset dataset to tie to content
+ * @param {number} cxAttrValue cx attribute
+ * @param {number} cyAttrValue cy attribute
+ * @modifies {root}
+ *
+ * @return {object} points svg element
+ *
+ */
+const _addPoints = (root, dataset, cxAttrValue, cyAttrValue) => {
+ return root
+ .selectAll('.dot')
+ .data(dataset)
+ .enter()
+ .append('circle')
+ .attr('id', (point, index) => {
+ return `point-${index}`;
+ })
+ .attr('class', 'dot')
+ .attr('clip-path', 'url(#clip)')
+ .attr('cx', cxAttrValue)
+ .attr('cy', cyAttrValue)
+ .attr('r', 2);
+};
+
+/**
+ * It return points from root parent
+ *
+ * @param {object} root svg element to be changed
+ * @return {object} points svg element
+ *
+ */
+const _getPoints = root => {
+ return root.selectAll('.dot');
+};
+
+/**
+ * It updates points svg element with new dataset, cxAttrValue and cyAttrValue
+ *
+ * @param {object} root svg element to be changed
+ * @param {object} dataset dataset to tie to content
+ * @param {number} cxAttrValue cx attribute
+ * @param {number} cyAttrValue cy attribute
+ * @modifies {root}
+ *
+ */
+const _updatePoints = (root, dataset = [], cxAttrValue, cyAttrValue) => {
+ const dot = root.selectAll('.dot');
+
+ dot.data(dataset).attr('cx', cxAttrValue).attr('cy', cyAttrValue);
+};
+
+/**
+ * It removes children nodes from root param.
+ *
+ * @param {object} root svg element to remove children content
+ * @modifies {root}
+ *
+ */
+const _removeChartContents = root => {
+ root.selectAll('*').remove();
+};
+
+const chart = {
+ axis: {
+ addNode: _addAxis,
+ scaleGraphics: _scaleAxisGraphics,
+ },
+ series: {
+ addNode: _addSeries,
+ },
+ lines: {
+ addNode: _addLine,
+ updateNode: _updateLine,
+ },
+ points: {
+ addNode: _addPoints,
+ updateNode: _updatePoints,
+ getPoints: _getPoints,
+ },
+ background: {
+ addNode: _addChartClipPath,
+ },
+ container: {
+ addNode: _addChartContainer,
+ },
+ legend: {
+ addNode: _addLegend,
+ setLabels: _setLegendLabels,
+ },
+ removeContents: _removeChartContents,
+};
+
+export default chart;
diff --git a/platform/ui-next/src/components/LineChart/d3LineChart/events.ts b/platform/ui-next/src/components/LineChart/d3LineChart/events.ts
new file mode 100644
index 000000000..b4a01b032
--- /dev/null
+++ b/platform/ui-next/src/components/LineChart/d3LineChart/events.ts
@@ -0,0 +1,149 @@
+import * as d3Shape from 'd3-shape';
+import * as d3Zoom from 'd3-zoom';
+
+import chart from './chart';
+
+const d3ZoomNativeEvents = [
+ 'wheel.zoom',
+ 'mousedown.zoom',
+ 'dblclick.zoom',
+ 'touchstart.zoom',
+ 'touchmove.zoom',
+ 'touchend.zoom touchcancel.zoom',
+];
+/**
+ * Object to override native zoom events.
+ * It defines a specific callback for a event name.
+ *
+ * @type {Object} zoomEvents
+ */
+const zoomEvents = {
+ 'dblclick.zoom': (root, zoom) => {
+ _resetZoom(root, zoom);
+ },
+};
+
+/**
+ * It resets given root
+ *
+ * @param {object} root svg element to be reset
+ * @param {object} zoom d3 zoom object
+ *
+ * @modifies {root}
+ */
+const _resetZoom = (root, zoom) => {
+ if (root) {
+ root.transition().duration(750).call(zoom.transform, d3Zoom.zoomIdentity);
+ }
+};
+
+const DEFAULT_MAX_ZOOM_SCALE = 20;
+
+const _zoom = d3Zoom.zoom().scaleExtent([1, DEFAULT_MAX_ZOOM_SCALE]);
+
+const _bindZoom = (
+ root,
+ gX,
+ gY,
+ xAxisScale,
+ yAxisScale,
+ xAxisGenerator,
+ yAxisGenerator,
+ parseXPoint,
+ parseYPoint,
+ datasets
+) => {
+ _zoom.on('zoom', _zoomListener); // .filter(zoomEventsFilter);
+
+ _removeListeners();
+ _setListeners();
+
+ function _removeListeners() {
+ for (const eventName of d3ZoomNativeEvents) {
+ root.on(eventName, null);
+ }
+ }
+
+ function _setListeners() {
+ const keys = Object.keys(zoomEvents);
+
+ for (const key of keys) {
+ root.call(_zoom).on(key, zoomEvents[key].bind(undefined, root, _zoom));
+ }
+ }
+
+ function _zoomListener(event) {
+ const transformedXAxisScale = event.transform.rescaleX(xAxisScale);
+ const transformedYAxisScale = event.transform.rescaleY(yAxisScale);
+
+ chart.axis.scaleGraphics(
+ gX,
+ gY,
+ xAxisGenerator,
+ yAxisGenerator,
+ transformedXAxisScale,
+ transformedYAxisScale
+ );
+
+ datasets.forEach((dataset, seriesIndex) => {
+ const seriesContainer = root.select(`#series_${seriesIndex}`);
+ // create line
+ const line = d3Shape
+ .line()
+ .x(parseXPoint(transformedXAxisScale))
+ .y(parseYPoint(transformedYAxisScale));
+
+ chart.lines.updateNode(seriesContainer, dataset, line);
+
+ chart.points.updateNode(
+ seriesContainer,
+ dataset,
+ parseXPoint(transformedXAxisScale),
+ parseYPoint(transformedYAxisScale)
+ );
+ });
+ }
+
+ return _zoom;
+};
+
+const bindMouseEvents = (
+ root,
+ gX,
+ gY,
+ xAxisScale,
+ yAxisScale,
+ xAxisGenerator,
+ yAxisGenerator,
+ parseXPoint,
+ parseYPoint,
+ datasets
+) => {
+ if (!root) {
+ return;
+ }
+
+ _bindZoom(
+ root,
+ gX,
+ gY,
+ xAxisScale,
+ yAxisScale,
+ xAxisGenerator,
+ yAxisGenerator,
+ parseXPoint,
+ parseYPoint,
+ datasets
+ );
+};
+
+const events = {
+ bindMouseEvents,
+ external: {
+ resetZoom: root => {
+ _resetZoom(root, _zoom);
+ },
+ },
+};
+
+export default events;
diff --git a/platform/ui-next/src/components/LineChart/d3LineChart/index.ts b/platform/ui-next/src/components/LineChart/d3LineChart/index.ts
new file mode 100644
index 000000000..8cbc2f80f
--- /dev/null
+++ b/platform/ui-next/src/components/LineChart/d3LineChart/index.ts
@@ -0,0 +1,3 @@
+import * as lineChart from './lineChart';
+
+export { lineChart };
diff --git a/platform/ui-next/src/components/LineChart/d3LineChart/lineChart.ts b/platform/ui-next/src/components/LineChart/d3LineChart/lineChart.ts
new file mode 100644
index 000000000..fc2ecd8aa
--- /dev/null
+++ b/platform/ui-next/src/components/LineChart/d3LineChart/lineChart.ts
@@ -0,0 +1,272 @@
+import * as d3Array from 'd3-array';
+import * as d3Axis from 'd3-axis';
+import * as d3Scale from 'd3-scale';
+import * as d3ScaleChromatic from 'd3-scale-chromatic';
+import * as d3Selection from 'd3-selection';
+import * as d3Shape from 'd3-shape';
+
+import chart from './chart';
+import events from './events';
+
+const {
+ external: { resetZoom },
+} = events;
+
+/**
+ * @typedef TimecoursePoint It defines a tuple of timecourse value (time X intensity)
+ * @type {array}
+ * @property {number} 0 indicates x|y value on the pair (x,y)
+ * @property {number} 1 indicates x|y value on the pair (x,y)
+ */
+
+/**
+ * @typedef TimecoursePointDef It defines the shape of a given TimecoursePoint Axis
+ * @type {object}
+ * @property {string} label label for given TimecoursePoint Axis
+ * @property {string} [unit] unit for given TimecoursePoint Axis
+ * @property {string} type defines the type of given TimecoursePoint Axis
+ * @property {number} indexRef refers the index into given TimecoursePoint for the current Axis Definition
+ */
+
+/**
+ * It gets max value of an array, considering value of param index
+ *
+ * @param {TimecoursePoint[]} array array of items to be evaluated
+ * @param {number} index index of each array`s item to be evaluated
+ * @return {any} max value
+ */
+function _getMaxValue(array, index) {
+ return d3Array.max(array, arrayItem => {
+ return arrayItem[index];
+ });
+}
+
+function _getMinValue(array, index) {
+ return d3Array.min(array, arrayItem => {
+ return arrayItem[index];
+ });
+}
+
+const LEGEND = { width: 100, margin: 10 };
+const MARGIN = { top: 20, right: 20, bottom: 50, left: 50 };
+
+function _createAxisScale(domainBottom, domainUpper, rangeBottom, rangeUpper) {
+ return d3Scale
+ .scaleLinear()
+ .domain([domainBottom, domainUpper * 1.05])
+ .range([rangeBottom, rangeUpper]);
+}
+
+const _getSeriesColor = series => {
+ const seriesLabels = series.reduce((labels, series) => [...labels, series.label], []);
+
+ return d3Scale.scaleOrdinal().domain(seriesLabels).range(d3ScaleChromatic.schemeSet2);
+};
+
+const _updateSeriesColors = series => {
+ const seriesColor = _getSeriesColor(series);
+
+ return series.map(series => ({
+ ...series,
+ color: series.color ?? seriesColor(series.label),
+ }));
+};
+
+const _textEllipses = (width, padding = 0) => {
+ return function (...args) {
+ const self = d3Selection.select(this);
+ let textLength = self.node().getComputedTextLength();
+ let text = self.text();
+
+ while (textLength > width - 2 * padding && text.length > 0) {
+ text = text.slice(0, -1);
+ self.text(text + '...');
+ textLength = self.node().getComputedTextLength();
+ }
+ };
+};
+
+const _addLegend = (root, series, chartWidth, chartHeight, legendWidth) => {
+ const legendItemHeight = 25;
+ const legendHeight = legendItemHeight * series.length;
+
+ const legendContainer = chart.legend.addNode(
+ root,
+ legendWidth,
+ legendHeight,
+ chartWidth + LEGEND.margin,
+ chartHeight / 2 - legendHeight / 2
+ );
+
+ const seriesLabels = series.reduce((labels, series) => [...labels, series.label], []);
+
+ const seriesColors = series.reduce((colors, series) => [...colors, series.color], []);
+
+ chart.legend.setLabels(
+ legendContainer,
+ seriesLabels,
+ seriesColors,
+ legendItemHeight,
+ legendWidth,
+ _textEllipses
+ );
+};
+
+/**
+ * It creates a svg chart containing lines, dots, axis, labels
+ *
+ * @param {object} d3SVGRef svg content reference to append chart
+ * @param {Object} axis definition of axis
+ * @param {object} points list of points to be created
+ * @param {number} width width for whole content including lines, dots, axis, labels
+ * @param {number} height height for whole content including lines, dots, axis, labels
+ * @param {boolean} showAxisLabels flag to display labels or not
+ *
+ * @modifies {d3SVGRef}
+ */
+const addLineChartNode = ({
+ d3SVGRef,
+ axis,
+ series,
+ width,
+ height,
+ legendWidth = LEGEND.width,
+ showAxisLabels = true,
+ showAxisGrid = false,
+ showLegend = false,
+ transparentChartBackground = false,
+}) => {
+ const marginRight = showLegend ? legendWidth + 2 * LEGEND.margin : MARGIN.right;
+ const _width = width - MARGIN.left - marginRight;
+ const _height = height - MARGIN.top - MARGIN.bottom;
+ const { x: XAxis, y: YAxis } = axis;
+
+ series = _updateSeriesColors(series);
+
+ let maxX = -Infinity;
+ let minX = Infinity;
+ let maxY = -Infinity;
+ let minY = Infinity;
+
+ series.forEach(currentSeries => {
+ minX = Math.min(minX, _getMinValue(currentSeries.points, XAxis.indexRef));
+ maxX = Math.max(maxX, _getMaxValue(currentSeries.points, XAxis.indexRef));
+ minY = Math.min(minY, _getMinValue(currentSeries.points, YAxis.indexRef));
+ maxY = Math.max(maxY, _getMaxValue(currentSeries.points, YAxis.indexRef));
+ });
+
+ minX = axis?.x?.range?.min ?? minX;
+ maxX = axis?.x?.range?.max ?? maxX;
+ minY = axis?.y?.range?.min ?? minY;
+ maxY = axis?.y?.range?.max ?? maxY;
+
+ const xAxisScale = _createAxisScale(minX, maxX, 0, _width);
+ const yAxisScale = _createAxisScale(minY, maxY, _height, 0);
+
+ const parseXPoint = axisScale => point => {
+ return (axisScale || xAxisScale)(point.x);
+ };
+
+ const parseYPoint = axisScale => point => {
+ return (axisScale || yAxisScale)(point.y);
+ };
+
+ // Remove old D3 elements
+ chart.removeContents(d3SVGRef);
+
+ const chartWrapper = chart.container.addNode(d3SVGRef, width, height, MARGIN.left, MARGIN.top);
+
+ // add background
+ chart.background.addNode(chartWrapper, _width, _height, transparentChartBackground);
+
+ // call the x axis in a group tag
+ const xAxisGenerator = d3Axis.axisBottom(xAxisScale);
+
+ if (showAxisGrid) {
+ xAxisGenerator.tickSize(-_height).tickPadding(10);
+ }
+ const gXAxis = chart.axis.addNode(
+ chartWrapper,
+ XAxis,
+ undefined,
+ _height,
+ undefined,
+ () => xAxisGenerator,
+ showAxisLabels,
+ _width / 2,
+ _height + MARGIN.bottom / 2 + 10,
+ undefined,
+ showAxisGrid
+ );
+ const yAxisGenerator = d3Axis.axisLeft(yAxisScale);
+
+ if (showAxisGrid) {
+ yAxisGenerator.tickSize(-_width).tickPadding(10);
+ }
+ // add y axis
+ const gYAxis = chart.axis.addNode(
+ chartWrapper,
+ YAxis,
+ undefined,
+ undefined,
+ undefined,
+ () => yAxisGenerator,
+ showAxisLabels,
+ 0 - _height / 2,
+ 0 - MARGIN.left,
+ [
+ { key: 'transform', value: 'rotate(-90)' },
+ { key: 'dy', value: '1em' },
+ ],
+ showAxisGrid
+ );
+
+ const datasets = [];
+
+ series.forEach((currentSeries, seriesIndex) => {
+ const { points } = currentSeries;
+ const line = d3Shape.line().x(parseXPoint(xAxisScale)).y(parseYPoint(yAxisScale));
+
+ const dataset = points.map((point, pointIndex) => {
+ return { x: point[0], y: point[1], seriesIndex, pointIndex };
+ });
+
+ const seriesContainer = chart.series.addNode(chartWrapper, seriesIndex, {
+ color: currentSeries.color || '#00ff00',
+ });
+
+ chart.lines.addNode(seriesContainer, dataset, line);
+
+ // add chart points
+ chart.points.addNode(
+ seriesContainer,
+ dataset,
+ parseXPoint(xAxisScale),
+ parseYPoint(yAxisScale)
+ );
+
+ datasets.push(dataset);
+ });
+
+ if (showLegend) {
+ _addLegend(chartWrapper, series, _width, _height, legendWidth);
+ }
+
+ // bind events
+ events.bindMouseEvents(
+ chartWrapper,
+ gXAxis,
+ gYAxis,
+ xAxisScale,
+ yAxisScale,
+ xAxisGenerator,
+ yAxisGenerator,
+ parseXPoint,
+ parseYPoint,
+ datasets
+ );
+
+ return chartWrapper;
+};
+
+export { addLineChartNode, resetZoom };
diff --git a/platform/ui-next/src/components/LineChart/index.ts b/platform/ui-next/src/components/LineChart/index.ts
new file mode 100644
index 000000000..f87705103
--- /dev/null
+++ b/platform/ui-next/src/components/LineChart/index.ts
@@ -0,0 +1 @@
+export { default } from './LineChart';
diff --git a/platform/ui-next/src/components/LoadingIndicatorProgress/LoadingIndicatorProgress.tsx b/platform/ui-next/src/components/LoadingIndicatorProgress/LoadingIndicatorProgress.tsx
new file mode 100644
index 000000000..4b7b6970d
--- /dev/null
+++ b/platform/ui-next/src/components/LoadingIndicatorProgress/LoadingIndicatorProgress.tsx
@@ -0,0 +1,29 @@
+import React from 'react';
+import classNames from 'classnames';
+
+import ProgressLoadingBar from '../ProgressLoadingBar';
+import { Icons } from '../Icons';
+/**
+ * A React component that renders a loading indicator.
+ * if progress is not provided, it will render an infinite loading indicator
+ * if progress is provided, it will render a progress bar
+ * Optionally a textBlock can be provided to display a message
+ */
+function LoadingIndicatorProgress({ className, textBlock, progress }) {
+ return (
+
+ );
+}
+
+export default LoadingIndicatorProgress;
diff --git a/platform/ui-next/src/components/LoadingIndicatorProgress/index.ts b/platform/ui-next/src/components/LoadingIndicatorProgress/index.ts
new file mode 100644
index 000000000..f8a0fbe79
--- /dev/null
+++ b/platform/ui-next/src/components/LoadingIndicatorProgress/index.ts
@@ -0,0 +1,2 @@
+import LoadingIndicatorProgress from './LoadingIndicatorProgress';
+export default LoadingIndicatorProgress;
diff --git a/platform/ui-next/src/components/LoadingIndicatorTotalPercent/LoadingIndicatorTotalPercent.tsx b/platform/ui-next/src/components/LoadingIndicatorTotalPercent/LoadingIndicatorTotalPercent.tsx
new file mode 100644
index 000000000..199fb8ab6
--- /dev/null
+++ b/platform/ui-next/src/components/LoadingIndicatorTotalPercent/LoadingIndicatorTotalPercent.tsx
@@ -0,0 +1,49 @@
+import React from 'react';
+
+import LoadingIndicatorProgress from '../LoadingIndicatorProgress';
+
+interface Props {
+ className?: string;
+ totalNumbers: number | null;
+ percentComplete: number | null;
+ loadingText?: string;
+ targetText?: string;
+}
+
+/**
+ * A React component that renders a loading indicator but accepts a totalNumbers
+ * and percentComplete to display a more detailed message.
+ */
+function LoadingIndicatorTotalPercent({
+ className,
+ totalNumbers,
+ percentComplete,
+ loadingText = 'Loading...',
+ targetText = 'segments',
+}: Props): JSX.Element {
+ const progress = percentComplete;
+ const totalNumbersText = totalNumbers !== null ? `${totalNumbers}` : '';
+ const numTargetsLoadedText =
+ percentComplete !== null ? Math.floor((percentComplete * totalNumbers) / 100) : '';
+
+ const textBlock =
+ !totalNumbers && percentComplete === null ? (
+ {loadingText}
+ ) : !totalNumbers && percentComplete !== null ? (
+ Loaded {percentComplete}%
+ ) : (
+
+ Loaded {numTargetsLoadedText} of {totalNumbersText} {targetText}
+
+ );
+
+ return (
+
+ );
+}
+
+export default LoadingIndicatorTotalPercent;
diff --git a/platform/ui-next/src/components/LoadingIndicatorTotalPercent/index.ts b/platform/ui-next/src/components/LoadingIndicatorTotalPercent/index.ts
new file mode 100644
index 000000000..fc4e1fa10
--- /dev/null
+++ b/platform/ui-next/src/components/LoadingIndicatorTotalPercent/index.ts
@@ -0,0 +1,2 @@
+import LoadingIndicatorTotalPercent from './LoadingIndicatorTotalPercent';
+export default LoadingIndicatorTotalPercent;
diff --git a/platform/ui-next/src/components/ProgressDropdown/ProgressDiscreteBar.tsx b/platform/ui-next/src/components/ProgressDropdown/ProgressDiscreteBar.tsx
new file mode 100644
index 000000000..66ea3f15d
--- /dev/null
+++ b/platform/ui-next/src/components/ProgressDropdown/ProgressDiscreteBar.tsx
@@ -0,0 +1,27 @@
+import React, { ReactElement } from 'react';
+import PropTypes from 'prop-types';
+import classnames from 'classnames';
+import { ProgressDropdownOption, ProgressDropdownOptionPropType } from './types';
+
+const ProgressDiscreteBar = ({ options }: { options: ProgressDropdownOption[] }): ReactElement => {
+ return (
+
+ {options.map((option, i) => (
+
+ ))}
+
+ );
+};
+
+ProgressDiscreteBar.propTypes = {
+ options: PropTypes.arrayOf(ProgressDropdownOptionPropType).isRequired,
+};
+
+export default ProgressDiscreteBar;
diff --git a/platform/ui-next/src/components/ProgressDropdown/ProgressDropdown.tsx b/platform/ui-next/src/components/ProgressDropdown/ProgressDropdown.tsx
new file mode 100644
index 000000000..2da94099d
--- /dev/null
+++ b/platform/ui-next/src/components/ProgressDropdown/ProgressDropdown.tsx
@@ -0,0 +1,164 @@
+import React, { ReactNode, useEffect, useCallback, useState, useMemo, useRef } from 'react';
+import PropTypes from 'prop-types';
+import classnames from 'classnames';
+import ProgressDiscreteBar from './ProgressDiscreteBar';
+import ProgressItemDetail from './ProgressItemDetail';
+import ProgressItem from './ProgressItem';
+import { Icons } from '../Icons';
+import { ProgressDropdownOption, ProgressDropdownOptionPropType } from './types';
+
+const ProgressDropdown = ({
+ options: optionsProps,
+ value,
+ children,
+ dropDownWidth = '170',
+ onChange,
+}: {
+ options: ProgressDropdownOption[];
+ value?: string;
+ children?: ReactNode;
+ onChange?: ({ selectedOption }) => void;
+}): JSX.Element => {
+ const element = useRef(null);
+ const [open, setOpen] = useState(false);
+ const toggleOpen = () => setOpen(s => !s);
+ const [options, setOptions] = useState(optionsProps);
+ const [selectedOption, setSelectedOption] = useState(undefined);
+
+ const selectedOptionIndex = useMemo(
+ () => options.findIndex(option => option.value === selectedOption?.value),
+ [options, selectedOption]
+ );
+
+ const canMoveNext = useMemo(
+ () => selectedOptionIndex < options.length - 1,
+ [selectedOptionIndex, options]
+ );
+
+ const handleOptionSelected = useCallback(
+ (newSelectedOption?: ProgressDropdownOption): void => {
+ if (newSelectedOption?.value === selectedOption?.value) {
+ return;
+ }
+
+ setOpen(false);
+ setSelectedOption(newSelectedOption);
+
+ if (newSelectedOption) {
+ newSelectedOption.activated = true;
+ newSelectedOption.onSelect?.();
+ }
+
+ if (onChange) {
+ onChange({ selectedOption: newSelectedOption });
+ }
+ },
+ [selectedOption, onChange]
+ );
+
+ const handleNextButtonClick = useCallback(() => {
+ if (canMoveNext) {
+ handleOptionSelected(options[selectedOptionIndex + 1]);
+ }
+ }, [options, selectedOptionIndex, canMoveNext, handleOptionSelected]);
+
+ // Update the options in case the options from props has changed
+ useEffect(() => setOptions(optionsProps), [optionsProps]);
+
+ // Updates the selected item based on the value from props
+ useEffect(() => {
+ if (!value) {
+ return;
+ }
+
+ const newOption = value ? options.find(option => option.value === value) : undefined;
+
+ handleOptionSelected(newOption);
+ }, [value, options, handleOptionSelected]);
+
+ // Listen to any click event outside of the dropdown context to hide the options
+ useEffect(() => {
+ const handleDocumentClick = e => {
+ if (element.current && !element.current.contains(e.target)) {
+ setOpen(false);
+ }
+ };
+
+ document.addEventListener('click', handleDocumentClick);
+
+ if (!open) {
+ document.removeEventListener('click', handleDocumentClick);
+ }
+ }, [open]);
+
+ return (
+
+
+
+
+
+ {selectedOption &&
}
+
+ {!selectedOption &&
{children}
}
+
+
+
+
+
+
+ {options.map((option, index) => (
+
handleOptionSelected(option)}
+ />
+ ))}
+
+
+
+
+
+ );
+};
+
+ProgressDropdown.propTypes = {
+ options: PropTypes.arrayOf(ProgressDropdownOptionPropType).isRequired,
+ value: PropTypes.string,
+ onChange: PropTypes.func,
+ children: PropTypes.node,
+ dropDownWidth: PropTypes.string,
+};
+
+export default ProgressDropdown;
diff --git a/platform/ui-next/src/components/ProgressDropdown/ProgressItem.tsx b/platform/ui-next/src/components/ProgressDropdown/ProgressItem.tsx
new file mode 100644
index 000000000..e92dbff24
--- /dev/null
+++ b/platform/ui-next/src/components/ProgressDropdown/ProgressItem.tsx
@@ -0,0 +1,31 @@
+import React, { ReactElement } from 'react';
+import PropTypes from 'prop-types';
+import ProgressItemDetail from './ProgressItemDetail';
+import { ProgressDropdownOption, ProgressDropdownOptionPropType } from './types';
+
+const ProgressItem = ({
+ option,
+ onSelect,
+}: {
+ option: ProgressDropdownOption;
+ onSelect: (option: ProgressDropdownOption) => void;
+}): ReactElement => {
+ const { value } = option;
+
+ return (
+ onSelect(option)}
+ >
+
+
+ );
+};
+
+ProgressItem.propTypes = {
+ option: ProgressDropdownOptionPropType.isRequired,
+ onSelect: PropTypes.func,
+};
+
+export default ProgressItem;
diff --git a/platform/ui-next/src/components/ProgressDropdown/ProgressItemDetail.tsx b/platform/ui-next/src/components/ProgressDropdown/ProgressItemDetail.tsx
new file mode 100644
index 000000000..e88b81b38
--- /dev/null
+++ b/platform/ui-next/src/components/ProgressDropdown/ProgressItemDetail.tsx
@@ -0,0 +1,94 @@
+import React, { useState, useMemo, ReactElement } from 'react';
+import { Icons } from '../Icons';
+import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from '../Tooltip';
+import { ProgressDropdownOption, ProgressDropdownOptionPropType } from './types';
+
+const MAX_TOOLTIP_LENGTH = 150;
+const iconClassNames = 'grow-0 text-primary-light h-4 w-4 mt-1 mr-2 mb-0 ml-1';
+
+const ProgressItemDetail = ({ option }: { option: ProgressDropdownOption }): ReactElement => {
+ const { label, info, completed } = option;
+ const [truncate, setTruncate] = useState(true);
+ const handleOnHideTooltip = () => setTruncate(true);
+ let icon;
+
+ if (completed) {
+ icon = 'status-tracked';
+ } else if (info) {
+ icon = 'launch-info';
+ }
+
+ const tooltipText = useMemo(() => {
+ if (!truncate || !info || info.length <= MAX_TOOLTIP_LENGTH) {
+ return info;
+ }
+
+ const handleReadMoreClick = e => {
+ setTruncate(false);
+ e.stopPropagation();
+ e.preventDefault();
+ };
+
+ return (
+ <>
+ {info.slice(0, MAX_TOOLTIP_LENGTH)}
+
+ >
+ );
+ }, [info, truncate]);
+
+ const handleOpenChange = (open: boolean) => {
+ if (!open) {
+ handleOnHideTooltip();
+ }
+ };
+
+ const iconContent = (
+ <>
+ {icon && (
+
+
+
+ )}
+ {!icon && }
+ >
+ );
+
+ return (
+ <>
+ {info && (
+
+
+ {iconContent}
+
+ {tooltipText}
+
+
+
+ )}
+ {!info && iconContent}
+
+
+ {label}
+
+ >
+ );
+};
+
+ProgressItemDetail.propTypes = {
+ option: ProgressDropdownOptionPropType.isRequired,
+};
+
+export default ProgressItemDetail;
diff --git a/platform/ui-next/src/components/ProgressDropdown/index.ts b/platform/ui-next/src/components/ProgressDropdown/index.ts
new file mode 100644
index 000000000..abd294a17
--- /dev/null
+++ b/platform/ui-next/src/components/ProgressDropdown/index.ts
@@ -0,0 +1 @@
+export { default } from './ProgressDropdown';
diff --git a/platform/ui-next/src/components/ProgressDropdown/types.ts b/platform/ui-next/src/components/ProgressDropdown/types.ts
new file mode 100644
index 000000000..fb99e1bd2
--- /dev/null
+++ b/platform/ui-next/src/components/ProgressDropdown/types.ts
@@ -0,0 +1,19 @@
+import PropTypes from 'prop-types';
+
+export type ProgressDropdownOption = {
+ label: string;
+ value: string;
+ info?: string;
+ activated?: boolean;
+ completed?: boolean;
+ onSelect?: () => void;
+};
+
+export const ProgressDropdownOptionPropType = PropTypes.shape({
+ label: PropTypes.string.isRequired,
+ value: PropTypes.string.isRequired,
+ info: PropTypes.string,
+ activated: PropTypes.bool,
+ completed: PropTypes.bool,
+ onSelect: PropTypes.func,
+});
diff --git a/platform/ui-next/src/components/ProgressLoadingBar/ProgressLoadingBar.css b/platform/ui-next/src/components/ProgressLoadingBar/ProgressLoadingBar.css
new file mode 100644
index 000000000..f9addbe9e
--- /dev/null
+++ b/platform/ui-next/src/components/ProgressLoadingBar/ProgressLoadingBar.css
@@ -0,0 +1,26 @@
+.loading {
+ background-color: #091731;
+ height: 8px;
+ border-radius: 4px;
+ overflow: hidden;
+ position: relative;
+ width: 100%;
+}
+
+.infinite-loading-bar {
+ animation: side2side 2s ease-in-out infinite;
+ height: 100%;
+ position: absolute;
+ border-radius: 4px;
+ width: 50%;
+}
+
+@keyframes side2side {
+ 0%,
+ 100% {
+ transform: translateX(-50%);
+ }
+ 50% {
+ transform: translateX(150%);
+ }
+}
diff --git a/platform/ui-next/src/components/ProgressLoadingBar/ProgressLoadingBar.tsx b/platform/ui-next/src/components/ProgressLoadingBar/ProgressLoadingBar.tsx
new file mode 100644
index 000000000..62034dc46
--- /dev/null
+++ b/platform/ui-next/src/components/ProgressLoadingBar/ProgressLoadingBar.tsx
@@ -0,0 +1,32 @@
+import React, { ReactElement } from 'react';
+
+import './ProgressLoadingBar.css';
+
+export type ProgressLoadingBarProps = {
+ progress?: number;
+};
+/**
+ * A React component that renders a loading progress bar.
+ * If progress is not provided, it will render an infinite loading bar
+ * If progress is provided, it will render a progress bar
+ * The progress text can be optionally displayed to the left of the bar.
+ */
+function ProgressLoadingBar({ progress }: ProgressLoadingBarProps): ReactElement {
+ return (
+
+ {progress === undefined || progress === null ? (
+
+ ) : (
+
+ )}
+
+ );
+}
+
+export default ProgressLoadingBar;
diff --git a/platform/ui-next/src/components/ProgressLoadingBar/index.ts b/platform/ui-next/src/components/ProgressLoadingBar/index.ts
new file mode 100644
index 000000000..ab8869ffc
--- /dev/null
+++ b/platform/ui-next/src/components/ProgressLoadingBar/index.ts
@@ -0,0 +1,2 @@
+import ProgressLoadingBar from './ProgressLoadingBar';
+export default ProgressLoadingBar;
diff --git a/platform/ui-next/src/components/ToolButton/ToolButton.tsx b/platform/ui-next/src/components/ToolButton/ToolButton.tsx
index e7002701f..6e93779da 100644
--- a/platform/ui-next/src/components/ToolButton/ToolButton.tsx
+++ b/platform/ui-next/src/components/ToolButton/ToolButton.tsx
@@ -59,9 +59,11 @@ function ToolButton(props: ToolButtonProps) {
className
);
- const defaultTooltip = tooltip || label;
+ const defaultTooltip = label;
const disabledTooltip = disabled && disabledText ? disabledText : null;
- const hasTooltip = defaultTooltip || disabledTooltip;
+ const hasSecondaryTooltip = tooltip || disabledTooltip;
+
+ const showTooltip = hasSecondaryTooltip || defaultTooltip;
return (
@@ -85,7 +87,7 @@ function ToolButton(props: ToolButtonProps) {
}}
variant="ghost"
size="icon"
- aria-label={hasTooltip ? defaultTooltip : undefined}
+ aria-label={defaultTooltip}
disabled={disabled}
>
-
- {hasTooltip && (
- <>
- {defaultTooltip}
- {disabledTooltip && {disabledTooltip}
}
- >
+
+ {showTooltip && (
+
+ {defaultTooltip &&
{defaultTooltip}
}
+ {disabledTooltip ? (
+
{disabledTooltip}
+ ) : (
+ tooltip &&
{tooltip}
+ )}
+
)}
diff --git a/platform/ui-next/src/components/ViewportDialog/ViewportDialog.tsx b/platform/ui-next/src/components/ViewportDialog/ViewportDialog.tsx
new file mode 100644
index 000000000..b27244ab4
--- /dev/null
+++ b/platform/ui-next/src/components/ViewportDialog/ViewportDialog.tsx
@@ -0,0 +1,128 @@
+import React, { useEffect, useRef } from 'react';
+import classnames from 'classnames';
+
+import { Button } from '../Button';
+import { Icons } from '../Icons';
+
+type NotificationAction = {
+ id: string;
+ text: string;
+ value: any;
+ type?: 'primary' | 'secondary';
+ size?: 'sm' | 'md';
+};
+
+export type ViewportDialogProps = {
+ id: string;
+ type: 'error' | 'warning' | 'info' | 'success';
+ message: string | React.ReactNode;
+ actions: NotificationAction[];
+ onSubmit: (e: any) => void;
+ onOutsideClick?: () => void;
+ onKeyPress?: (e: React.KeyboardEvent) => void;
+};
+
+const ViewportDialog: React.FC = ({
+ id,
+ type = 'info',
+ message,
+ actions,
+ onSubmit,
+ onOutsideClick = () => {},
+ onKeyPress,
+}) => {
+ const notificationRef = useRef(null);
+
+ useEffect(() => {
+ const notificationElement = notificationRef.current;
+ const handleClick = function (event) {
+ const isClickInside = notificationElement.contains(event.target);
+
+ if (!isClickInside) {
+ onOutsideClick();
+ }
+ };
+
+ // Both a mouse down and up listeners are desired so as to avoid missing events
+ // from elements that have pointer-events:none (e.g. the active viewport).
+ document.addEventListener('mousedown', handleClick);
+ document.addEventListener('mouseup', handleClick);
+
+ return () => {
+ document.removeEventListener('mousedown', handleClick);
+ document.removeEventListener('mouseup', handleClick);
+ };
+ }, [onOutsideClick]);
+
+ useEffect(() => {
+ notificationRef.current.focus();
+ }, []);
+
+ const iconsByType = {
+ error: {
+ icon: 'info',
+ color: 'text-red-700',
+ },
+ warning: {
+ icon: 'notificationwarning-diamond',
+ color: 'text-yellow-500',
+ },
+ info: {
+ icon: 'notifications-info',
+ color: 'text-primary-main',
+ },
+ success: {
+ icon: 'info',
+ color: 'text-green-500',
+ },
+ };
+
+ const getIconData = () => {
+ return (
+ iconsByType[type] || {
+ icon: '',
+ color: '',
+ }
+ );
+ };
+
+ const { icon, color } = getIconData();
+
+ return (
+
+
+
+ {message}
+
+
+ {actions?.map((action, index) => {
+ return (
+
+ );
+ })}
+
+
+ );
+};
+
+export default ViewportDialog;
diff --git a/platform/ui-next/src/components/ViewportDialog/index.ts b/platform/ui-next/src/components/ViewportDialog/index.ts
new file mode 100644
index 000000000..f52a6b3a1
--- /dev/null
+++ b/platform/ui-next/src/components/ViewportDialog/index.ts
@@ -0,0 +1,2 @@
+import ViewportDialog from './ViewportDialog';
+export default ViewportDialog;
diff --git a/platform/ui-next/src/components/index.ts b/platform/ui-next/src/components/index.ts
index 5c83800ef..8c1eae9d8 100644
--- a/platform/ui-next/src/components/index.ts
+++ b/platform/ui-next/src/components/index.ts
@@ -62,7 +62,11 @@ import Modal from './Modal/Modal';
import { FooterAction } from './FooterAction';
import { InputFilter } from './InputFilter';
import { WindowLevel, WindowLevelHistogram } from './OHIFPanels';
-
+import ProgressDropdown from './ProgressDropdown';
+import LoadingIndicatorProgress from './LoadingIndicatorProgress';
+import LoadingIndicatorTotalPercent from './LoadingIndicatorTotalPercent';
+import ProgressLoadingBar from './ProgressLoadingBar';
+import ViewportDialog from './ViewportDialog';
import {
DropdownMenu,
DropdownMenuTrigger,
@@ -119,6 +123,9 @@ export { MeasurementTable } from './MeasurementTable';
export * from './ColorCircle';
export { default as AllInOneMenu } from './AllInOneMenu';
export * from './AllInOneMenu';
+export { default as LineChart } from './LineChart';
+export { default as InvestigationalUseDialog } from './InvestigationalUseDialog';
+export { default as LabellingFlow } from './Labelling';
// Segmentation Context Exports
export { useSegmentationTableContext, useSegmentationExpanded, useSegmentStatistics };
@@ -255,5 +262,10 @@ export {
InputFilter,
WindowLevel,
WindowLevelHistogram,
+ ProgressDropdown,
+ LoadingIndicatorProgress,
+ LoadingIndicatorTotalPercent,
+ ProgressLoadingBar,
+ ViewportDialog,
CinePlayer,
-};
\ No newline at end of file
+};
diff --git a/platform/ui-next/src/contextProviders/DragAndDropProvider.tsx b/platform/ui-next/src/contextProviders/DragAndDropProvider.tsx
new file mode 100644
index 000000000..68a134992
--- /dev/null
+++ b/platform/ui-next/src/contextProviders/DragAndDropProvider.tsx
@@ -0,0 +1,40 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { DndProvider } from 'react-dnd';
+import { HTML5Backend } from 'react-dnd-html5-backend';
+
+// import TouchBackend from 'react-dnd-touch-backend';
+
+// TODO: this is false when it should not be :thinking:
+const isTouchDevice =
+ typeof window !== `undefined` && !!('ontouchstart' in window || navigator.maxTouchPoints);
+
+/**
+ * Relevant:
+ * https://github.com/react-dnd/react-dnd/issues/186#issuecomment-335429067
+ * https://github.com/react-dnd/react-dnd/issues/186#issuecomment-282789420
+ *
+ * Docs:
+ * http://react-dnd.github.io/react-dnd/docs/api/drag-drop-context
+ */
+function DragAndDropProvider({ children }) {
+ const backend = HTML5Backend; // isTouchDevice ? TouchBackend : HTML5Backend;
+ const opts = {}; // isTouchDevice ? { enableMouseEvents: true } : {};
+
+ console.log('using... touch backend?', isTouchDevice);
+
+ return (
+
+ {children}
+
+ );
+}
+
+DragAndDropProvider.propTypes = {
+ children: PropTypes.any,
+};
+
+export default DragAndDropProvider;
diff --git a/platform/ui-next/src/contextProviders/ImageViewerProvider.tsx b/platform/ui-next/src/contextProviders/ImageViewerProvider.tsx
new file mode 100644
index 000000000..c00377892
--- /dev/null
+++ b/platform/ui-next/src/contextProviders/ImageViewerProvider.tsx
@@ -0,0 +1,17 @@
+import React, { ReactNode, createContext, useContext, useMemo } from 'react';
+
+interface ImageViewerProviderProps {
+ StudyInstanceUIDs: string[];
+ children: ReactNode;
+}
+
+export const ImageViewerContext = createContext(null);
+export const useImageViewer = () => useContext(ImageViewerContext);
+
+export function ImageViewerProvider({ StudyInstanceUIDs, children }: ImageViewerProviderProps) {
+ const value = useMemo(() => {
+ return { StudyInstanceUIDs };
+ }, [StudyInstanceUIDs]);
+
+ return {children};
+}
diff --git a/platform/ui-next/src/contextProviders/UserAuthenticationProvider.tsx b/platform/ui-next/src/contextProviders/UserAuthenticationProvider.tsx
new file mode 100644
index 000000000..185746fc8
--- /dev/null
+++ b/platform/ui-next/src/contextProviders/UserAuthenticationProvider.tsx
@@ -0,0 +1,133 @@
+import React, { createContext, useCallback, useContext, useEffect, useReducer } from 'react';
+import PropTypes from 'prop-types';
+
+const DEFAULT_STATE = {
+ user: null,
+ enabled: false,
+};
+
+export const UserAuthenticationContext = createContext(DEFAULT_STATE);
+
+export function UserAuthenticationProvider({ children, service }) {
+ const userAuthenticationReducer = (state, action) => {
+ switch (action.type) {
+ case 'SET_USER': {
+ return {
+ ...state,
+ ...{ user: action.payload.user },
+ };
+ }
+ case 'RESET': {
+ return {
+ user: null,
+ };
+ }
+
+ case 'SET': {
+ return {
+ ...state,
+ ...action.payload,
+ };
+ }
+
+ default:
+ return action.payload;
+ }
+ };
+
+ const [userAuthenticationState, dispatch] = useReducer(userAuthenticationReducer, DEFAULT_STATE);
+
+ const getState = useCallback(() => userAuthenticationState, [userAuthenticationState]);
+
+ const setUser = useCallback(
+ user =>
+ dispatch({
+ type: 'SET_USER',
+ payload: {
+ user,
+ },
+ }),
+ [dispatch]
+ );
+
+ const getUser = useCallback(() => userAuthenticationState.user, [userAuthenticationState]);
+
+ const reset = useCallback(
+ () =>
+ dispatch({
+ type: 'RESET',
+ payload: {},
+ }),
+ [dispatch]
+ );
+
+ const set = useCallback(
+ payload =>
+ dispatch({
+ type: 'SET',
+ payload,
+ }),
+ [dispatch]
+ );
+
+ /**
+ * Sets the implementation of the UserAuthenticationService that can be used by extensions.
+ *
+ * @returns void
+ */
+ // TODO: should this be a useEffect or not?
+ useEffect(() => {
+ if (service) {
+ service.setServiceImplementation({
+ getState,
+ setUser,
+ getUser,
+ reset,
+ set,
+ });
+ }
+ }, [getState, service, setUser, getUser, reset, set]);
+
+ // TODO: This may not be correct, but I think we need to set the implementation for the service
+ // immediately when this runs, since otherwise the authentication redirects will fail.
+ // (useEffect only runs after the child components - in this case, routing logic - has failed)
+ if (service) {
+ service.setServiceImplementation({
+ getState,
+ setUser,
+ getUser,
+ reset,
+ set,
+ });
+ }
+
+ const api = {
+ getState,
+ setUser,
+ getUser,
+ getAuthorizationHeader: service.getAuthorizationHeader,
+ handleUnauthenticated: service.handleUnauthenticated,
+ reset,
+ set,
+ };
+
+ return (
+
+ {children}
+
+ );
+}
+
+export default UserAuthenticationProvider;
+
+const UserAuthenticationConsumer = UserAuthenticationContext.Consumer;
+export { UserAuthenticationConsumer };
+
+UserAuthenticationProvider.propTypes = {
+ children: PropTypes.any,
+ service: PropTypes.shape({
+ setServiceImplementation: PropTypes.func,
+ }).isRequired,
+};
+
+export const useUserAuthentication = () => useContext(UserAuthenticationContext);
diff --git a/platform/ui-next/src/contextProviders/ViewportDialogProvider.tsx b/platform/ui-next/src/contextProviders/ViewportDialogProvider.tsx
new file mode 100644
index 000000000..333f42f61
--- /dev/null
+++ b/platform/ui-next/src/contextProviders/ViewportDialogProvider.tsx
@@ -0,0 +1,52 @@
+import React, { useState, createContext, useContext, useCallback, useEffect } from 'react';
+import PropTypes from 'prop-types';
+
+const DEFAULT_STATE = {
+ message: undefined,
+ type: 'info', // "error" | "warning" | "info" | "success"
+ actions: undefined, // array of { type, text, value }
+ onSubmit: () => {
+ console.log('btn value?');
+ },
+ onOutsideClick: () => {
+ console.warn('default: onOutsideClick');
+ },
+ onDismiss: () => {
+ console.log('dismiss? -1');
+ },
+ onKeyPress: () => {
+ console.log('key pressed?');
+ },
+};
+
+const ViewportDialogContext = createContext(null);
+const { Provider } = ViewportDialogContext;
+
+export const useViewportDialog = () => useContext(ViewportDialogContext);
+
+const ViewportDialogProvider = ({ children, service }) => {
+ const [viewportDialogState, setViewportDialogState] = useState(DEFAULT_STATE);
+ const show = useCallback(
+ params => setViewportDialogState({ ...viewportDialogState, ...params }),
+ [viewportDialogState]
+ );
+ const hide = useCallback(() => setViewportDialogState(DEFAULT_STATE), []);
+
+ useEffect(() => {
+ if (service) {
+ service.setServiceImplementation({ hide, show });
+ }
+ }, [hide, service, show]);
+
+ return {children};
+};
+
+ViewportDialogProvider.propTypes = {
+ /** Children that will be wrapped with Modal Context */
+ children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired,
+ service: PropTypes.shape({
+ setServiceImplementation: PropTypes.func,
+ }),
+};
+
+export default ViewportDialogProvider;
diff --git a/platform/ui-next/src/contextProviders/index.ts b/platform/ui-next/src/contextProviders/index.ts
index a14f89349..178f1d953 100644
--- a/platform/ui-next/src/contextProviders/index.ts
+++ b/platform/ui-next/src/contextProviders/index.ts
@@ -3,6 +3,11 @@ import { ViewportGridContext, ViewportGridProvider, useViewportGrid } from './Vi
import { ModalProvider, useModal } from './ModalProvider';
import { DialogProvider, useDialog } from './DialogProvider';
import ManagedDialog from './ManagedDialog';
+import ViewportDialogProvider from './ViewportDialogProvider';
+import { useViewportDialog } from './ViewportDialogProvider';
+import { UserAuthenticationProvider, useUserAuthentication } from './UserAuthenticationProvider';
+import { ImageViewerContext, ImageViewerProvider, useImageViewer } from './ImageViewerProvider';
+import DragAndDropProvider from './DragAndDropProvider';
import CineProvider, { useCine } from './CineProvider';
export { useNotification, NotificationProvider };
@@ -10,4 +15,8 @@ export { ViewportGridContext, ViewportGridProvider, useViewportGrid };
export { ModalProvider, useModal };
export { DialogProvider, useDialog };
export { ManagedDialog };
-export { CineProvider, useCine };
\ No newline at end of file
+export { ViewportDialogProvider, useViewportDialog };
+export { UserAuthenticationProvider, useUserAuthentication };
+export { ImageViewerContext, ImageViewerProvider, useImageViewer };
+export { DragAndDropProvider };
+export { CineProvider, useCine };
diff --git a/platform/ui-next/src/index.ts b/platform/ui-next/src/index.ts
index 73c4652e9..153888e27 100644
--- a/platform/ui-next/src/index.ts
+++ b/platform/ui-next/src/index.ts
@@ -1,5 +1,6 @@
import * as utils from './utils';
export * from './components';
export * from './contextProviders';
+export * as Types from './types';
export { utils };
diff --git a/platform/ui/src/components/MeasurementTable/MeasurementItem.tsx b/platform/ui/src/components/MeasurementTable/MeasurementItem.tsx
index f20f6aaa7..999031179 100644
--- a/platform/ui/src/components/MeasurementTable/MeasurementItem.tsx
+++ b/platform/ui/src/components/MeasurementTable/MeasurementItem.tsx
@@ -70,8 +70,8 @@ const MeasurementItem = ({
{index}
)}
-
-
{label}
+
+ {label}
{displayText.map((line, i) => (
{amount}
-
+ {/*
*/}
{data.length !== 0 &&
diff --git a/yarn.lock b/yarn.lock
index d6b285315..394d15c59 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1914,18 +1914,6 @@
resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6"
integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==
-"@hapi/hoek@^9.0.0", "@hapi/hoek@^9.3.0":
- version "9.3.0"
- resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb"
- integrity sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==
-
-"@hapi/topo@^5.1.0":
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012"
- integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==
- dependencies:
- "@hapi/hoek" "^9.0.0"
-
"@humanwhocodes/config-array@^0.13.0":
version "0.13.0"
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.13.0.tgz#fb907624df3256d04b9aa2df50d7aa97ec648748"
@@ -4186,23 +4174,6 @@
resolved "https://registry.yarnpkg.com/@scarf/scarf/-/scarf-1.3.0.tgz#f8c75560d0dace4452dee1e31995e6396e61f3ee"
integrity sha512-lHKK8M5CTcpFj2hZDB3wIjb0KAbEOgDmiJGDv1WBRfQgRm/a8/XMEkG/N1iM01xgbUDsPQwi42D+dFo1XPAKew==
-"@sideway/address@^4.1.5":
- version "4.1.5"
- resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.5.tgz#4bc149a0076623ced99ca8208ba780d65a99b9d5"
- integrity sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==
- dependencies:
- "@hapi/hoek" "^9.0.0"
-
-"@sideway/formula@^3.0.1":
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f"
- integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==
-
-"@sideway/pinpoint@^2.0.0":
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df"
- integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==
-
"@sigstore/bundle@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@sigstore/bundle/-/bundle-1.1.0.tgz#17f8d813b09348b16eeed66a8cf1c3d6bd3d04f1"
@@ -6721,14 +6692,6 @@ axe-core@^4.10.0:
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.10.0.tgz#d9e56ab0147278272739a000880196cdfe113b59"
integrity sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==
-axios@^0.27.2:
- version "0.27.2"
- resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972"
- integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==
- dependencies:
- follow-redirects "^1.14.9"
- form-data "^4.0.0"
-
axios@^1.0.0, axios@^1.4.0, axios@^1.6.2:
version "1.8.3"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.8.3.tgz#9ebccd71c98651d547162a018a1a95a4b4ed4de8"
@@ -7050,7 +7013,7 @@ blob-util@^2.0.2:
resolved "https://registry.yarnpkg.com/blob-util/-/blob-util-2.0.2.tgz#3b4e3c281111bb7f11128518006cdc60b403a1eb"
integrity sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==
-bluebird@3.7.2, bluebird@^3.7.2:
+bluebird@^3.7.2:
version "3.7.2"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
@@ -7629,7 +7592,7 @@ chardet@^0.7.0:
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
-check-more-types@2.24.0, check-more-types@^2.24.0:
+check-more-types@^2.24.0:
version "2.24.0"
resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600"
integrity sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==
@@ -8926,13 +8889,6 @@ debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, d
dependencies:
ms "^2.1.3"
-debug@4.3.4:
- version "4.3.4"
- resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
- integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
- dependencies:
- ms "2.1.2"
-
debug@^3.1.0, debug@^3.2.7:
version "3.2.7"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
@@ -9628,7 +9584,7 @@ dunder-proto@^1.0.1:
es-errors "^1.3.0"
gopd "^1.2.0"
-duplexer@^0.1.1, duplexer@^0.1.2, duplexer@~0.1.1:
+duplexer@^0.1.1, duplexer@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6"
integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==
@@ -10384,19 +10340,6 @@ etag@~1.8.1:
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
-event-stream@=3.3.4:
- version "3.3.4"
- resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571"
- integrity sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==
- dependencies:
- duplexer "~0.1.1"
- from "~0"
- map-stream "~0.1.0"
- pause-stream "0.0.11"
- split "0.3"
- stream-combiner "~0.0.4"
- through "~2.3.1"
-
event-target-shim@^5.0.0:
version "5.0.1"
resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
@@ -10460,21 +10403,6 @@ execa@5.0.0:
signal-exit "^3.0.3"
strip-final-newline "^2.0.0"
-execa@5.1.1, execa@^5.0.0, execa@^5.1.1:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
- integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==
- dependencies:
- cross-spawn "^7.0.3"
- get-stream "^6.0.0"
- human-signals "^2.1.0"
- is-stream "^2.0.0"
- merge-stream "^2.0.0"
- npm-run-path "^4.0.1"
- onetime "^5.1.2"
- signal-exit "^3.0.3"
- strip-final-newline "^2.0.0"
-
execa@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8"
@@ -10503,6 +10431,21 @@ execa@^2.0.3:
signal-exit "^3.0.2"
strip-final-newline "^2.0.0"
+execa@^5.0.0, execa@^5.1.1:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
+ integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==
+ dependencies:
+ cross-spawn "^7.0.3"
+ get-stream "^6.0.0"
+ human-signals "^2.1.0"
+ is-stream "^2.0.0"
+ merge-stream "^2.0.0"
+ npm-run-path "^4.0.1"
+ onetime "^5.1.2"
+ signal-exit "^3.0.3"
+ strip-final-newline "^2.0.0"
+
execa@^8.0.1:
version "8.0.1"
resolved "https://registry.yarnpkg.com/execa/-/execa-8.0.1.tgz#51f6a5943b580f963c3ca9c6321796db8cc39b8c"
@@ -10985,7 +10928,7 @@ flow-parser@0.*:
resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.247.1.tgz#4a512c882cd08126825fe00b1ee452637ef44371"
integrity sha512-DHwcm06fWbn2Z6uFD3NaBZ5lMOoABIQ4asrVA80IWvYjjT5WdbghkUOL1wIcbLcagnFTdCZYOlSNnKNp/xnRZQ==
-follow-redirects@^1.0.0, follow-redirects@^1.14.9, follow-redirects@^1.15.6:
+follow-redirects@^1.0.0, follow-redirects@^1.15.6:
version "1.15.6"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b"
integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==
@@ -11089,11 +11032,6 @@ fresh@0.5.2:
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==
-from@~0:
- version "0.1.7"
- resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe"
- integrity sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==
-
fs-constants@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
@@ -13530,17 +13468,6 @@ jju@~1.4.0:
resolved "https://registry.yarnpkg.com/jju/-/jju-1.4.0.tgz#a3abe2718af241a2b2904f84a625970f389ae32a"
integrity sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==
-joi@^17.7.0:
- version "17.13.1"
- resolved "https://registry.yarnpkg.com/joi/-/joi-17.13.1.tgz#9c7b53dc3b44dd9ae200255cc3b398874918a6ca"
- integrity sha512-vaBlIKCyo4FCUtCm7Eu4QZd/q02bWcxfUO6YSXAZOWF6gzcLBeba8kwotUdYJjDLW8Cz8RywsSOqiNJZW0mNvg==
- dependencies:
- "@hapi/hoek" "^9.3.0"
- "@hapi/topo" "^5.1.0"
- "@sideway/address" "^4.1.5"
- "@sideway/formula" "^3.0.1"
- "@sideway/pinpoint" "^2.0.0"
-
jpeg-lossless-decoder-js@^2.1.0:
version "2.1.2"
resolved "https://registry.yarnpkg.com/jpeg-lossless-decoder-js/-/jpeg-lossless-decoder-js-2.1.2.tgz#9ff0ecfcafa6acaee6097c532720bd526278aa3d"
@@ -13840,7 +13767,7 @@ last-call-webpack-plugin@^3.0.0:
lodash "^4.17.5"
webpack-sources "^1.1.0"
-lazy-ass@1.6.0, lazy-ass@^1.6.0:
+lazy-ass@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513"
integrity sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==
@@ -14488,11 +14415,6 @@ map-or-similar@^1.5.0:
resolved "https://registry.yarnpkg.com/map-or-similar/-/map-or-similar-1.5.0.tgz#6de2653174adfb5d9edc33c69d3e92a1b76faf08"
integrity sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg==
-map-stream@~0.1.0:
- version "0.1.0"
- resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194"
- integrity sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==
-
mapbox-to-css-font@^2.4.1:
version "2.4.5"
resolved "https://registry.yarnpkg.com/mapbox-to-css-font/-/mapbox-to-css-font-2.4.5.tgz#b10a7a33af3e1a9a1369e4d5e8285492a7943c46"
@@ -15197,7 +15119,7 @@ minimist-options@4.1.0:
is-plain-obj "^1.1.0"
kind-of "^6.0.3"
-minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5, minimist@^1.2.6, minimist@^1.2.7, minimist@^1.2.8:
+minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5, minimist@^1.2.6, minimist@^1.2.8:
version "1.2.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
@@ -15354,11 +15276,6 @@ ms@2.0.0:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==
-ms@2.1.2:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
- integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
-
ms@2.1.3, ms@^2.0.0, ms@^2.1.1, ms@^2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
@@ -16554,13 +16471,6 @@ pathe@^1.1.2:
resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec"
integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==
-pause-stream@0.0.11:
- version "0.0.11"
- resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445"
- integrity sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==
- dependencies:
- through "~2.3"
-
pbf@3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/pbf/-/pbf-3.2.1.tgz#b4c1b9e72af966cd82c6531691115cc0409ffe2a"
@@ -17551,13 +17461,6 @@ proxy-from-env@^1.0.0, proxy-from-env@^1.1.0:
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
-ps-tree@1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.2.0.tgz#5e7425b89508736cdd4f2224d028f7bb3f722ebd"
- integrity sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==
- dependencies:
- event-stream "=3.3.4"
-
pseudomap@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
@@ -18705,7 +18608,7 @@ rxjs@^6.3.3:
dependencies:
tslib "^1.9.0"
-rxjs@^7.5.1, rxjs@^7.5.5, rxjs@^7.8.0:
+rxjs@^7.5.1, rxjs@^7.5.5:
version "7.8.1"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543"
integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==
@@ -19437,13 +19340,6 @@ split2@^3.2.2:
dependencies:
readable-stream "^3.0.0"
-split@0.3:
- version "0.3.3"
- resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f"
- integrity sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==
- dependencies:
- through "2"
-
split@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9"
@@ -19512,20 +19408,6 @@ stackframe@^1.3.4:
resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310"
integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==
-start-server-and-test@^1.10.0:
- version "1.15.5"
- resolved "https://registry.yarnpkg.com/start-server-and-test/-/start-server-and-test-1.15.5.tgz#5c9103bd87c06678fc62658fbe97d09501714011"
- integrity sha512-o3EmkX0++GV+qsvIJ/OKWm3w91fD8uS/bPQVPrh/7loaxkpXSuAIHdnmN/P/regQK9eNAK76aBJcHt+OSTk+nA==
- dependencies:
- arg "^5.0.2"
- bluebird "3.7.2"
- check-more-types "2.24.0"
- debug "4.3.4"
- execa "5.1.1"
- lazy-ass "1.6.0"
- ps-tree "1.2.0"
- wait-on "7.0.1"
-
statuses@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
@@ -19563,13 +19445,6 @@ stream-browserify@3.0.0, stream-browserify@^3.0.0:
inherits "~2.0.4"
readable-stream "^3.5.0"
-stream-combiner@~0.0.4:
- version "0.0.4"
- resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14"
- integrity sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==
- dependencies:
- duplexer "~0.1.1"
-
stream-http@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-3.2.0.tgz#1872dfcf24cb15752677e40e5c3f9cc1926028b5"
@@ -20244,7 +20119,7 @@ through2@^2.0.0, through2@^2.0.3:
readable-stream "~2.3.6"
xtend "~4.0.1"
-through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6, through@^2.3.8, through@~2.3, through@~2.3.1:
+through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6, through@^2.3.8:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==
@@ -21081,17 +20956,6 @@ w3c-xmlserializer@^4.0.0:
dependencies:
xml-name-validator "^4.0.0"
-wait-on@7.0.1:
- version "7.0.1"
- resolved "https://registry.yarnpkg.com/wait-on/-/wait-on-7.0.1.tgz#5cff9f8427e94f4deacbc2762e6b0a489b19eae9"
- integrity sha512-9AnJE9qTjRQOlTZIldAaf/da2eW0eSRSgcqq85mXQja/DW3MriHxkpODDSUEg+Gri/rKEcXUZHe+cevvYItaog==
- dependencies:
- axios "^0.27.2"
- joi "^17.7.0"
- lodash "^4.17.21"
- minimist "^1.2.7"
- rxjs "^7.8.0"
-
walker@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f"