refactor(ui): Migrate components to ui-next (#4930)

Co-authored-by: Dan Rukas <dan.rukas@gmail.com>
This commit is contained in:
Alireza 2025-04-08 12:06:07 -04:00 committed by GitHub
parent 1b8729dded
commit 2bdf4afa4d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
80 changed files with 2196 additions and 521 deletions

View File

@ -76,7 +76,6 @@
"shader-loader": "^1.3.1", "shader-loader": "^1.3.1",
"shx": "^0.3.3", "shx": "^0.3.3",
"source-map-loader": "^4.0.1", "source-map-loader": "^4.0.1",
"start-server-and-test": "^1.10.0",
"style-loader": "^1.0.0", "style-loader": "^1.0.0",
"stylus": "^0.59.0", "stylus": "^0.59.0",
"stylus-loader": "^7.1.3", "stylus-loader": "^7.1.3",

111
bun.lock
View File

@ -33,7 +33,7 @@
}, },
"addOns/externals/devDependencies": { "addOns/externals/devDependencies": {
"name": "@externals/devDependencies", "name": "@externals/devDependencies",
"version": "3.10.0-beta.150", "version": "3.10.0-beta.152",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.20.13", "@babel/runtime": "^7.20.13",
"@kitware/vtk.js": "32.12.0", "@kitware/vtk.js": "32.12.0",
@ -98,7 +98,6 @@
"shader-loader": "^1.3.1", "shader-loader": "^1.3.1",
"shx": "^0.3.3", "shx": "^0.3.3",
"source-map-loader": "^4.0.1", "source-map-loader": "^4.0.1",
"start-server-and-test": "^1.10.0",
"style-loader": "^1.0.0", "style-loader": "^1.0.0",
"stylus": "^0.59.0", "stylus": "^0.59.0",
"stylus-loader": "^7.1.3", "stylus-loader": "^7.1.3",
@ -121,14 +120,14 @@
}, },
"addOns/externals/dicom-microscopy-viewer": { "addOns/externals/dicom-microscopy-viewer": {
"name": "@externals/dicom-microscopy-viewer", "name": "@externals/dicom-microscopy-viewer",
"version": "3.10.0-beta.150", "version": "3.10.0-beta.152",
"dependencies": { "dependencies": {
"dicom-microscopy-viewer": "^0.46.1", "dicom-microscopy-viewer": "^0.46.1",
}, },
}, },
"extensions/cornerstone": { "extensions/cornerstone": {
"name": "@ohif/extension-cornerstone", "name": "@ohif/extension-cornerstone",
"version": "3.10.0-beta.150", "version": "3.10.0-beta.152",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.20.13", "@babel/runtime": "^7.20.13",
"@cornerstonejs/adapters": "^3.9.1", "@cornerstonejs/adapters": "^3.9.1",
@ -167,7 +166,7 @@
}, },
"extensions/cornerstone-dicom-pmap": { "extensions/cornerstone-dicom-pmap": {
"name": "@ohif/extension-cornerstone-dicom-pmap", "name": "@ohif/extension-cornerstone-dicom-pmap",
"version": "3.10.0-beta.150", "version": "3.10.0-beta.152",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.20.13", "@babel/runtime": "^7.20.13",
"@cornerstonejs/adapters": "^3.9.1", "@cornerstonejs/adapters": "^3.9.1",
@ -190,7 +189,7 @@
}, },
"extensions/cornerstone-dicom-rt": { "extensions/cornerstone-dicom-rt": {
"name": "@ohif/extension-cornerstone-dicom-rt", "name": "@ohif/extension-cornerstone-dicom-rt",
"version": "3.10.0-beta.150", "version": "3.10.0-beta.152",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.20.13", "@babel/runtime": "^7.20.13",
"react-color": "^2.19.3", "react-color": "^2.19.3",
@ -210,7 +209,7 @@
}, },
"extensions/cornerstone-dicom-seg": { "extensions/cornerstone-dicom-seg": {
"name": "@ohif/extension-cornerstone-dicom-seg", "name": "@ohif/extension-cornerstone-dicom-seg",
"version": "3.10.0-beta.150", "version": "3.10.0-beta.152",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.20.13", "@babel/runtime": "^7.20.13",
"@cornerstonejs/adapters": "^3.9.1", "@cornerstonejs/adapters": "^3.9.1",
@ -233,7 +232,7 @@
}, },
"extensions/cornerstone-dicom-sr": { "extensions/cornerstone-dicom-sr": {
"name": "@ohif/extension-cornerstone-dicom-sr", "name": "@ohif/extension-cornerstone-dicom-sr",
"version": "3.10.0-beta.150", "version": "3.10.0-beta.152",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.20.13", "@babel/runtime": "^7.20.13",
"@cornerstonejs/adapters": "^3.9.1", "@cornerstonejs/adapters": "^3.9.1",
@ -255,7 +254,7 @@
}, },
"extensions/cornerstone-dynamic-volume": { "extensions/cornerstone-dynamic-volume": {
"name": "@ohif/extension-cornerstone-dynamic-volume", "name": "@ohif/extension-cornerstone-dynamic-volume",
"version": "3.10.0-beta.150", "version": "3.10.0-beta.152",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.20.13", "@babel/runtime": "^7.20.13",
"@cornerstonejs/core": "^3.9.1", "@cornerstonejs/core": "^3.9.1",
@ -277,7 +276,7 @@
}, },
"extensions/default": { "extensions/default": {
"name": "@ohif/extension-default", "name": "@ohif/extension-default",
"version": "3.10.0-beta.150", "version": "3.10.0-beta.152",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.20.13", "@babel/runtime": "^7.20.13",
"@cornerstonejs/calculate-suv": "^1.1.0", "@cornerstonejs/calculate-suv": "^1.1.0",
@ -300,7 +299,7 @@
}, },
"extensions/dicom-microscopy": { "extensions/dicom-microscopy": {
"name": "@ohif/extension-dicom-microscopy", "name": "@ohif/extension-dicom-microscopy",
"version": "3.10.0-beta.150", "version": "3.10.0-beta.152",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.20.13", "@babel/runtime": "^7.20.13",
"@cornerstonejs/codec-charls": "^1.2.3", "@cornerstonejs/codec-charls": "^1.2.3",
@ -325,7 +324,7 @@
}, },
"extensions/dicom-pdf": { "extensions/dicom-pdf": {
"name": "@ohif/extension-dicom-pdf", "name": "@ohif/extension-dicom-pdf",
"version": "3.10.0-beta.150", "version": "3.10.0-beta.152",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.20.13", "@babel/runtime": "^7.20.13",
"classnames": "^2.3.2", "classnames": "^2.3.2",
@ -342,7 +341,7 @@
}, },
"extensions/dicom-video": { "extensions/dicom-video": {
"name": "@ohif/extension-dicom-video", "name": "@ohif/extension-dicom-video",
"version": "3.10.0-beta.150", "version": "3.10.0-beta.152",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.20.13", "@babel/runtime": "^7.20.13",
"classnames": "^2.3.2", "classnames": "^2.3.2",
@ -359,7 +358,7 @@
}, },
"extensions/measurement-tracking": { "extensions/measurement-tracking": {
"name": "@ohif/extension-measurement-tracking", "name": "@ohif/extension-measurement-tracking",
"version": "3.10.0-beta.150", "version": "3.10.0-beta.152",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.20.13", "@babel/runtime": "^7.20.13",
"@ohif/ui": "3.10.0-beta.150", "@ohif/ui": "3.10.0-beta.150",
@ -385,7 +384,7 @@
}, },
"extensions/test-extension": { "extensions/test-extension": {
"name": "@ohif/extension-test", "name": "@ohif/extension-test",
"version": "3.10.0-beta.150", "version": "3.10.0-beta.152",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.20.13", "@babel/runtime": "^7.20.13",
"classnames": "^2.3.2", "classnames": "^2.3.2",
@ -402,7 +401,7 @@
}, },
"extensions/tmtv": { "extensions/tmtv": {
"name": "@ohif/extension-tmtv", "name": "@ohif/extension-tmtv",
"version": "3.10.0-beta.150", "version": "3.10.0-beta.152",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.20.13", "@babel/runtime": "^7.20.13",
"classnames": "^2.3.2", "classnames": "^2.3.2",
@ -419,7 +418,7 @@
}, },
"modes/basic-dev-mode": { "modes/basic-dev-mode": {
"name": "@ohif/mode-basic-dev-mode", "name": "@ohif/mode-basic-dev-mode",
"version": "3.10.0-beta.150", "version": "3.10.0-beta.152",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.20.13", "@babel/runtime": "^7.20.13",
"i18next": "^17.0.3", "i18next": "^17.0.3",
@ -439,7 +438,7 @@
}, },
"modes/basic-test-mode": { "modes/basic-test-mode": {
"name": "@ohif/mode-test", "name": "@ohif/mode-test",
"version": "3.10.0-beta.150", "version": "3.10.0-beta.152",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.20.13", "@babel/runtime": "^7.20.13",
"i18next": "^17.0.3", "i18next": "^17.0.3",
@ -461,7 +460,7 @@
}, },
"modes/longitudinal": { "modes/longitudinal": {
"name": "@ohif/mode-longitudinal", "name": "@ohif/mode-longitudinal",
"version": "3.10.0-beta.150", "version": "3.10.0-beta.152",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.20.13", "@babel/runtime": "^7.20.13",
"i18next": "^17.0.3", "i18next": "^17.0.3",
@ -484,7 +483,7 @@
}, },
"modes/microscopy": { "modes/microscopy": {
"name": "@ohif/mode-microscopy", "name": "@ohif/mode-microscopy",
"version": "3.10.0-beta.150", "version": "3.10.0-beta.152",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.20.13", "@babel/runtime": "^7.20.13",
"i18next": "^17.0.3", "i18next": "^17.0.3",
@ -496,7 +495,7 @@
}, },
"modes/preclinical-4d": { "modes/preclinical-4d": {
"name": "@ohif/mode-preclinical-4d", "name": "@ohif/mode-preclinical-4d",
"version": "3.10.0-beta.150", "version": "3.10.0-beta.152",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.20.13", "@babel/runtime": "^7.20.13",
}, },
@ -515,7 +514,7 @@
}, },
"modes/segmentation": { "modes/segmentation": {
"name": "@ohif/mode-segmentation", "name": "@ohif/mode-segmentation",
"version": "3.10.0-beta.150", "version": "3.10.0-beta.152",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.20.13", "@babel/runtime": "^7.20.13",
"i18next": "^17.0.3", "i18next": "^17.0.3",
@ -559,7 +558,7 @@
}, },
"modes/tmtv": { "modes/tmtv": {
"name": "@ohif/mode-tmtv", "name": "@ohif/mode-tmtv",
"version": "3.10.0-beta.150", "version": "3.10.0-beta.152",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.20.13", "@babel/runtime": "^7.20.13",
"i18next": "^17.0.3", "i18next": "^17.0.3",
@ -580,7 +579,7 @@
}, },
"platform/app": { "platform/app": {
"name": "@ohif/app", "name": "@ohif/app",
"version": "3.10.0-beta.150", "version": "3.10.0-beta.152",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.20.13", "@babel/runtime": "^7.20.13",
"@cornerstonejs/codec-charls": "^1.2.3", "@cornerstonejs/codec-charls": "^1.2.3",
@ -646,7 +645,7 @@
}, },
"platform/cli": { "platform/cli": {
"name": "@ohif/cli", "name": "@ohif/cli",
"version": "3.10.0-beta.150", "version": "3.10.0-beta.152",
"bin": { "bin": {
"ohif-cli": "src/index.js", "ohif-cli": "src/index.js",
}, },
@ -670,7 +669,7 @@
}, },
"platform/core": { "platform/core": {
"name": "@ohif/core", "name": "@ohif/core",
"version": "3.10.0-beta.150", "version": "3.10.0-beta.152",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.20.13", "@babel/runtime": "^7.20.13",
"dcmjs": "*", "dcmjs": "*",
@ -703,7 +702,7 @@
}, },
"platform/i18n": { "platform/i18n": {
"name": "@ohif/i18n", "name": "@ohif/i18n",
"version": "3.10.0-beta.150", "version": "3.10.0-beta.152",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.20.13", "@babel/runtime": "^7.20.13",
"i18next-locize-backend": "^2.0.0", "i18next-locize-backend": "^2.0.0",
@ -728,7 +727,7 @@
}, },
"platform/ui": { "platform/ui": {
"name": "@ohif/ui", "name": "@ohif/ui",
"version": "3.10.0-beta.150", "version": "3.10.0-beta.152",
"dependencies": { "dependencies": {
"@testing-library/react": "^13.1.0", "@testing-library/react": "^13.1.0",
"browser-detect": "^0.2.28", "browser-detect": "^0.2.28",
@ -788,7 +787,7 @@
}, },
"platform/ui-next": { "platform/ui-next": {
"name": "@ohif/ui-next", "name": "@ohif/ui-next",
"version": "3.10.0-beta.150", "version": "3.10.0-beta.152",
"dependencies": { "dependencies": {
"@radix-ui/react-accordion": "^1.2.0", "@radix-ui/react-accordion": "^1.2.0",
"@radix-ui/react-checkbox": "^1.1.1", "@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=="], "@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/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=="], "@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=="], "@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/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=="], "@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=="], "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=="], "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=="], "eventemitter2": ["eventemitter2@6.4.7", "", {}, "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg=="],
@ -3151,8 +3138,6 @@
"fresh": ["fresh@0.5.2", "", {}, "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="], "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-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=="], "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=="], "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=="], "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=="], "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-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=="], "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=="], "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=="], "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=="], "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=="], "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=="], "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=="], "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=="], "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=="], "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=="], "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=="], "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-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-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=="], "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=="], "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=="], "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=="], "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=="], "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=="], "executable/pify": ["pify@2.3.0", "", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="],
"expect/jest-get-type": ["jest-get-type@29.6.3", "", {}, "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw=="], "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=="], "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-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=="], "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=="], "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/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=="], "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=="], "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=="], "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=="], "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=="], "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=="], "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=="], "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=="],

View File

@ -1,5 +1,3 @@
import { ButtonEnums } from '@ohif/ui';
const RESPONSE = { const RESPONSE = {
NO_NEVER: -1, NO_NEVER: -1,
CANCEL: 0, CANCEL: 0,
@ -47,13 +45,13 @@ function _askHydrate(
const actions = [ const actions = [
{ {
id: 'no-hydrate', id: 'no-hydrate',
type: ButtonEnums.type.secondary, type: 'secondary',
text: 'No', text: 'No',
value: RESPONSE.CANCEL, value: RESPONSE.CANCEL,
}, },
{ {
id: 'yes-hydrate', id: 'yes-hydrate',
type: ButtonEnums.type.primary, type: 'primary',
text: 'Yes', text: 'Yes',
value: RESPONSE.HYDRATE_SEG, value: RESPONSE.HYDRATE_SEG,
}, },

View File

@ -15,7 +15,7 @@ export default function _getStatusComponent({ isHydrated, onStatusClick }) {
case false: case false:
StatusIcon = () => ( StatusIcon = () => (
<Icons.ByName <Icons.ByName
className="text-aqua-pale" className="text-muted-foreground h-4 w-4"
name="status-untracked" name="status-untracked"
/> />
); );

View File

@ -1,5 +1,3 @@
import { ButtonEnums } from '@ohif/ui';
const RESPONSE = { const RESPONSE = {
NO_NEVER: -1, NO_NEVER: -1,
CANCEL: 0, CANCEL: 0,
@ -49,13 +47,13 @@ function _askHydrate(
const actions = [ const actions = [
{ {
id: 'no-hydrate', id: 'no-hydrate',
type: ButtonEnums.type.secondary, type: 'secondary',
text: 'No', text: 'No',
value: RESPONSE.CANCEL, value: RESPONSE.CANCEL,
}, },
{ {
id: 'yes-hydrate', id: 'yes-hydrate',
type: ButtonEnums.type.primary, type: 'primary',
text: 'Yes', text: 'Yes',
value: RESPONSE.HYDRATE_SEG, value: RESPONSE.HYDRATE_SEG,
}, },

View File

@ -15,7 +15,7 @@ export default function _getStatusComponent({ isHydrated, onStatusClick }) {
case false: case false:
StatusIcon = () => ( StatusIcon = () => (
<Icons.ByName <Icons.ByName
className="text-aqua-pale" className="text-muted-foreground h-4 w-4"
name="status-untracked" name="status-untracked"
/> />
); );

View File

@ -427,7 +427,7 @@ function _getStatusComponent({
case 3: case 3:
StatusIcon = () => ( StatusIcon = () => (
<Icons.ByName <Icons.ByName
className="text-aqua-pale h-4 w-4" className="text-muted-foreground h-4 w-4"
name="status-untracked" name="status-untracked"
/> />
); );

View File

@ -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 (
<>
<div>
<div className="mb-2 text-white">Computed Image</div>
<Select
closeMenuOnSelect={true}
className="border-primary-main mr-2 bg-black text-white "
options={operationsUI}
placeholder={operationsUI.find(option => option.value === options.Operation).placeHolder}
value={options.Operation}
onChange={({ value }) => {
handleGenerateOptionsChange({
Operation: value,
});
}}
/>
<InputDoubleRange
values={rangeValues}
onChange={handleSliderChange}
minValue={rangeValues[0] || 1}
maxValue={rangeValues[1] || 2}
showLabel={true}
step={1}
/>
<div className="flex space-x-2">
<Button
onClick={onGenerateImage}
className="w-1/2"
>
Generate
</Button>
<Button
onClick={returnTo4D}
disabled={!displayingComputedVolume}
className="w-1/2"
>
Return To 4D
</Button>
</div>
</div>
</>
);
};
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;

View File

@ -4,7 +4,7 @@ import * as cs3DTools from '@cornerstonejs/tools';
import { Enums, eventTarget, getEnabledElement } from '@cornerstonejs/core'; import { Enums, eventTarget, getEnabledElement } from '@cornerstonejs/core';
import { MeasurementService } from '@ohif/core'; import { MeasurementService } from '@ohif/core';
import { AllInOneMenu } from '@ohif/ui-next'; 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 type { Types as csTypes } from '@cornerstonejs/core';
import { setEnabledElement } from '../state'; import { setEnabledElement } from '../state';

View File

@ -103,7 +103,8 @@ export function Colorbar({
return ( return (
<div <div
className="hover:bg-accent flex h-8 w-full flex-shrink-0 cursor-pointer items-center px-2 text-base hover:rounded" className="hover:bg-accent flex h-8 w-full flex-shrink-0 cursor-pointer items-center px-2 text-base hover:rounded"
onClick={() => { onClick={e => {
e.stopPropagation();
onSetColorbar(); onSetColorbar();
}} }}
> >
@ -112,7 +113,8 @@ export function Colorbar({
<Switch <Switch
className="ml-2 flex-shrink-0" className="ml-2 flex-shrink-0"
checked={showColorbar} checked={showColorbar}
onCheckedChange={() => { onClick={e => {
e.stopPropagation();
onSetColorbar(); onSetColorbar();
}} }}
/> />

View File

@ -360,6 +360,10 @@ const connectMeasurementServiceToTools = (
measurementService.subscribe( measurementService.subscribe(
MEASUREMENT_UPDATED, MEASUREMENT_UPDATED,
({ source, measurement, notYetUpdatedAtSource }) => { ({ source, measurement, notYetUpdatedAtSource }) => {
if (!source) {
return;
}
if (source.name !== CORNERSTONE_3D_TOOLS_SOURCE_NAME) { if (source.name !== CORNERSTONE_3D_TOOLS_SOURCE_NAME) {
return; return;
} }

View File

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { LineChart } from '@ohif/ui'; import { LineChart } from '@ohif/ui-next';
const LineChartViewport = ({ displaySets }) => { const LineChartViewport = ({ displaySets }) => {
const displaySet = displaySets[0]; const displaySet = displaySets[0];

View File

@ -1,5 +1,5 @@
import React, { useEffect, useState, useCallback, ReactElement } from 'react'; import React, { useEffect, useState, useCallback, ReactElement } from 'react';
import { ProgressDropdown } from '@ohif/ui'; import { ProgressDropdown } from '@ohif/ui-next';
const workflowStepsToDropdownOptions = (steps = []) => const workflowStepsToDropdownOptions = (steps = []) =>
steps.map(step => ({ steps.map(step => ({

View File

@ -1,4 +1,4 @@
import { Types } from '@ohif/ui'; import { Types } from '@ohif/ui-next';
import { Menu, SelectorProps, MenuItem, ContextMenuProps } from './types'; import { Menu, SelectorProps, MenuItem, ContextMenuProps } from './types';
type ContextMenuItem = Types.ContextMenuItem; type ContextMenuItem = Types.ContextMenuItem;

View File

@ -1,5 +1,5 @@
import React, { useState, useEffect, useCallback } from 'react'; 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 { useSystem, utils } from '@ohif/core';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { useViewportGrid, StudyBrowser, Separator } from '@ohif/ui-next'; import { useViewportGrid, StudyBrowser, Separator } from '@ohif/ui-next';

View File

@ -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 (
<LayoutSelector
{...props}
onSelection={onSelection}
/>
);
}
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 (
<ToolbarButton
id="Layout"
label="Grid Layout"
icon="tool-layout"
onInteraction={onInteractionHandler}
className={className}
rounded={rest.rounded}
dropdownContent={
DropdownContent !== null && (
<DropdownContent
rows={rows}
columns={columns}
onSelection={onSelection}
/>
)
}
isActive={isOpen}
type="toggle"
/>
);
}
LayoutSelector.propTypes = {
rows: PropTypes.number,
columns: PropTypes.number,
onLayoutChange: PropTypes.func,
servicesManager: PropTypes.object.isRequired,
};
export default LegacyLayoutSelectorWithServices;

View File

@ -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 PropTypes from 'prop-types';
import { InvestigationalUseDialog } from '@ohif/ui'; import { InvestigationalUseDialog } from '@ohif/ui-next';
import { HangingProtocolService, CommandsManager } from '@ohif/core'; import { HangingProtocolService, CommandsManager } from '@ohif/core';
import { useAppConfig } from '@state'; import { useAppConfig } from '@state';
import ViewerHeader from './ViewerHeader'; import ViewerHeader from './ViewerHeader';

View File

@ -1,4 +1,4 @@
import { LabellingFlow } from '@ohif/ui'; import { LabellingFlow } from '@ohif/ui-next';
export default { export default {
'ui.labellingComponent': LabellingFlow, 'ui.labellingComponent': LabellingFlow,

View File

@ -1,4 +1,4 @@
import { LoadingIndicatorProgress } from '@ohif/ui'; import { LoadingIndicatorProgress } from '@ohif/ui-next';
export default { export default {
'ui.loadingIndicatorProgress': LoadingIndicatorProgress, 'ui.loadingIndicatorProgress': LoadingIndicatorProgress,

View File

@ -1,4 +1,4 @@
import { LoadingIndicatorTotalPercent } from '@ohif/ui'; import { LoadingIndicatorTotalPercent } from '@ohif/ui-next';
export default { export default {
'ui.loadingIndicatorTotalPercent': LoadingIndicatorTotalPercent, 'ui.loadingIndicatorTotalPercent': LoadingIndicatorTotalPercent,

View File

@ -1,5 +1,5 @@
import i18n from '@ohif/i18n'; 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 beginTrackingMessage = i18n.t('MeasurementTable:Track measurements for this series?');
const trackNewSeriesMessage = i18n.t('Do you want to add this measurement to the existing report?'); 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?'); const discardDirtyMessage = i18n.t('There are unsaved measurements. Do you want to save it?');
export default { export default {
'ui.notificationComponent': Notification, 'ui.notificationComponent': ViewportDialog,
'viewportNotification.beginTrackingMessage': beginTrackingMessage, 'viewportNotification.beginTrackingMessage': beginTrackingMessage,
'viewportNotification.trackNewSeriesMessage': trackNewSeriesMessage, 'viewportNotification.trackNewSeriesMessage': trackNewSeriesMessage,
'viewportNotification.discardSeriesMessage': discardSeriesMessage, 'viewportNotification.discardSeriesMessage': discardSeriesMessage,

View File

@ -1,4 +1,4 @@
import { ProgressLoadingBar } from '@ohif/ui'; import { ProgressLoadingBar } from '@ohif/ui-next';
export default { export default {
'ui.progressLoadingBar': ProgressLoadingBar, 'ui.progressLoadingBar': ProgressLoadingBar,

View File

@ -33,7 +33,12 @@ export default function getToolbarModule({ commandsManager, servicesManager }: w
name: 'ohif.toolBoxButton', name: 'ohif.toolBoxButton',
defaultComponent: ToolBoxButtonWrapper, 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', name: 'ohif.radioGroup',
defaultComponent: ToolbarButtonLegacy, defaultComponent: ToolbarButtonLegacy,

View File

@ -1,8 +1,6 @@
import { addIcon as addIconUI } from '@ohif/ui';
import { Icons } from '@ohif/ui-next'; import { Icons } from '@ohif/ui-next';
/** Adds the icon to both ui and ui-next */ /** Adds the icon to both ui and ui-next */
export function addIcon(name, icon) { export function addIcon(name, icon) {
addIconUI(name, icon);
Icons.addIcon(name, icon); Icons.addIcon(name, icon);
} }

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { setAnnotationLabel } from '@cornerstonejs/tools/utilities'; import { setAnnotationLabel } from '@cornerstonejs/tools/utilities';
import { annotation } from '@cornerstonejs/tools'; import { annotation } from '@cornerstonejs/tools';
import { LabellingFlow } from '@ohif/ui'; import { LabellingFlow } from '@ohif/ui-next';
import { InputDialog } from '@ohif/ui-next'; import { InputDialog } from '@ohif/ui-next';
interface InputDialogDefaultProps { interface InputDialogDefaultProps {

View File

@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { callInputDialog } from '@ohif/extension-default'; import { callInputDialog } from '@ohif/extension-default';
import { ExtensionManager, CommandsManager, DicomMetadataStore } from '@ohif/core'; 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 { withTranslation, WithTranslation } from 'react-i18next';
import { EVENTS as MicroscopyEvents } from '../../services/MicroscopyService'; import { EVENTS as MicroscopyEvents } from '../../services/MicroscopyService';
import dcmjs from 'dcmjs'; import dcmjs from 'dcmjs';
@ -328,14 +328,34 @@ function MicroscopyPanel(props: IMicroscopyPanelProps) {
className="ohif-scrollbar overflow-y-auto overflow-x-hidden" className="ohif-scrollbar overflow-y-auto overflow-x-hidden"
data-cy={'measurements-panel'} data-cy={'measurements-panel'}
> >
<MeasurementTable <div className="flex flex-col">
title="Measurements" {data.map(item => (
servicesManager={props.servicesManager} <DataRow
data={data} key={item.uid}
onClick={onMeasurementItemClickHandler} number={item.index + 1}
onEdit={onMeasurementItemEditHandler} title={item.label}
onDelete={onMeasurementDeleteHandler} isSelected={item.isActive}
/> onSelect={() => 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(', ')}
/>
))}
</div>
</div> </div>
</> </>
); );

View File

@ -1,5 +0,0 @@
import { MeasurementItem } from '@ohif/ui';
export default {
'microscopyPanel.measurementItem': MeasurementItem,
};

View File

@ -1,12 +1,3 @@
import panelMeasurementItem from './customizations/panelMeasurementItem';
export default function getCustomizationModule() { export default function getCustomizationModule() {
return [ return [];
{
name: 'default',
value: {
...panelMeasurementItem,
},
},
];
} }

View File

@ -1,4 +1,3 @@
import { ButtonEnums } from '@ohif/ui';
import i18n from 'i18next'; import i18n from 'i18next';
const RESPONSE = { const RESPONSE = {
@ -50,19 +49,19 @@ function _askTrackMeasurements(uiViewportDialogService, customizationService, vi
const actions = [ const actions = [
{ {
id: 'prompt-begin-tracking-cancel', id: 'prompt-begin-tracking-cancel',
type: ButtonEnums.type.secondary, type: 'secondary',
text: i18n.t('Common:No'), text: i18n.t('Common:No'),
value: RESPONSE.CANCEL, value: RESPONSE.CANCEL,
}, },
{ {
id: 'prompt-begin-tracking-no-do-not-ask-again', id: 'prompt-begin-tracking-no-do-not-ask-again',
type: ButtonEnums.type.secondary, type: 'secondary',
text: i18n.t('MeasurementTable:No, do not ask again'), text: i18n.t('MeasurementTable:No, do not ask again'),
value: RESPONSE.NO_NEVER, value: RESPONSE.NO_NEVER,
}, },
{ {
id: 'prompt-begin-tracking-yes', id: 'prompt-begin-tracking-yes',
type: ButtonEnums.type.primary, type: 'primary',
text: i18n.t('Common:Yes'), text: i18n.t('Common:Yes'),
value: RESPONSE.SET_STUDY_AND_SERIES, value: RESPONSE.SET_STUDY_AND_SERIES,
}, },

View File

@ -1,5 +1,4 @@
import { hydrateStructuredReport } from '@ohif/extension-cornerstone-dicom-sr'; import { hydrateStructuredReport } from '@ohif/extension-cornerstone-dicom-sr';
import { ButtonEnums } from '@ohif/ui';
import { measurementTrackingMode } from './promptBeginTracking'; import { measurementTrackingMode } from './promptBeginTracking';
const RESPONSE = { const RESPONSE = {
@ -59,13 +58,13 @@ function _askTrackMeasurements(uiViewportDialogService, customizationService, vi
const actions = [ const actions = [
{ {
id: 'no-hydrate', id: 'no-hydrate',
type: ButtonEnums.type.secondary, type: 'secondary',
text: 'No', text: 'No',
value: RESPONSE.CANCEL, value: RESPONSE.CANCEL,
}, },
{ {
id: 'yes-hydrate', id: 'yes-hydrate',
type: ButtonEnums.type.primary, type: 'primary',
text: 'Yes', text: 'Yes',
value: RESPONSE.HYDRATE_REPORT, value: RESPONSE.HYDRATE_REPORT,
}, },

View File

@ -1,4 +1,3 @@
import { ButtonEnums } from '@ohif/ui';
import { measurementTrackingMode } from './promptBeginTracking'; import { measurementTrackingMode } from './promptBeginTracking';
const RESPONSE = { const RESPONSE = {
@ -47,17 +46,17 @@ function _askShouldAddMeasurements(uiViewportDialogService, customizationService
); );
const actions = [ const actions = [
{ {
type: ButtonEnums.type.secondary, type: 'secondary',
text: 'Cancel', text: 'Cancel',
value: RESPONSE.CANCEL, value: RESPONSE.CANCEL,
}, },
{ {
type: ButtonEnums.type.primary, type: 'primary',
text: 'Create new report', text: 'Create new report',
value: RESPONSE.CREATE_REPORT, value: RESPONSE.CREATE_REPORT,
}, },
{ {
type: ButtonEnums.type.primary, type: 'primary',
text: 'Add to existing report', text: 'Add to existing report',
value: RESPONSE.ADD_SERIES, value: RESPONSE.ADD_SERIES,
}, },
@ -88,7 +87,7 @@ function _askSaveDiscardOrCancel(UIViewportDialogService, customizationService,
); );
const actions = [ const actions = [
{ type: 'cancel', text: 'Cancel', value: RESPONSE.CANCEL }, { type: 'secondary', text: 'Cancel', value: RESPONSE.CANCEL },
{ {
type: 'secondary', type: 'secondary',
text: 'Save', text: 'Save',

View File

@ -1,5 +1,4 @@
import React from 'react'; import React from 'react';
import { Dialog, ButtonEnums } from '@ohif/ui';
import { utils } from '@ohif/core'; import { utils } from '@ohif/core';
import { AccordionTrigger, MeasurementTable, useViewportGrid } from '@ohif/ui-next'; import { AccordionTrigger, MeasurementTable, useViewportGrid } from '@ohif/ui-next';
import { import {

View File

@ -1,24 +1,5 @@
import { WindowLevelMenuItem } from '@ohif/ui';
import { defaults } from '@ohif/core';
import type { Button } from '@ohif/core/types'; 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 = { export const setToolActiveToolbar = {
commandName: 'setToolActive', commandName: 'setToolActive',
commandOptions: { commandOptions: {

View File

@ -1,4 +1,3 @@
import { hotkeys } from '@ohif/core';
import i18n from 'i18next'; import i18n from 'i18next';
import { id } from './id'; import { id } from './id';
@ -94,7 +93,7 @@ function modeFactory({ modeConfiguration }) {
leftPanels: [ohif.leftPanel], leftPanels: [ohif.leftPanel],
leftPanelResizable: true, leftPanelResizable: true,
leftPanelClosed: true, // we have problem with rendering thumbnails for microscopy images 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], rightPanels: [ohif.rightPanel],
rightPanelResizable: true, rightPanelResizable: true,
viewports: [ viewports: [

View File

@ -308,6 +308,8 @@ const toolbarButtons: Button[] = [
props: { props: {
icon: 'icon-tool-interpolation', icon: 'icon-tool-interpolation',
label: 'Interpolate Labelmap', 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: [
'evaluate.cornerstone.segmentation', 'evaluate.cornerstone.segmentation',
{ {
@ -324,7 +326,8 @@ const toolbarButtons: Button[] = [
props: { props: {
icon: 'icon-tool-bidirectional-segment', icon: 'icon-tool-bidirectional-segment',
label: 'Segment Bidirectional', 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: { evaluate: {
name: 'evaluate.cornerstone.segmentation', name: 'evaluate.cornerstone.segmentation',
disabledText: 'Create new segmentation to enable this tool.', disabledText: 'Create new segmentation to enable this tool.',
@ -338,7 +341,8 @@ const toolbarButtons: Button[] = [
props: { props: {
icon: 'icon-tool-click-segment', icon: 'icon-tool-click-segment',
label: 'One 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: { evaluate: {
name: 'evaluate.cornerstone.segmentation', name: 'evaluate.cornerstone.segmentation',
toolNames: ['RegionSegmentPlus'], toolNames: ['RegionSegmentPlus'],
@ -353,7 +357,8 @@ const toolbarButtons: Button[] = [
props: { props: {
icon: 'icon-labelmap-slice-propagation', icon: 'icon-labelmap-slice-propagation',
label: 'Labelmap Assist', 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: [
'evaluate.cornerstoneTool.toggle', 'evaluate.cornerstoneTool.toggle',
{ {
@ -376,7 +381,7 @@ const toolbarButtons: Button[] = [
icon: 'icon-marker-labelmap', icon: 'icon-marker-labelmap',
label: 'Marker Guided Labelmap', label: 'Marker Guided Labelmap',
tooltip: 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: [ evaluate: [
{ {
name: 'evaluate.cornerstone.segmentation', name: 'evaluate.cornerstone.segmentation',

View File

@ -38,7 +38,6 @@
"start": "yarn run dev", "start": "yarn run dev",
"test:e2e": "cypress open", "test:e2e": "cypress open",
"test:e2e:local": "cypress run --config video=false --browser chrome --spec 'cypress/integration/common/**/*,cypress/integration/pwa/**/*'", "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:e2e:serve": "cross-env APP_CONFIG=config/e2e.js yarn start",
"test:unit": "jest --watchAll", "test:unit": "jest --watchAll",
"test:unit:ci": "jest --ci --runInBand --collectCoverage", "test:unit:ci": "jest --ci --runInBand --collectCoverage",

View File

@ -14,7 +14,6 @@ import {
ServiceProvidersManager, ServiceProvidersManager,
SystemContextProvider, SystemContextProvider,
} from '@ohif/core'; } from '@ohif/core';
import { ThemeWrapper, ViewportDialogProvider, UserAuthenticationProvider } from '@ohif/ui';
import { import {
ThemeWrapper as ThemeWrapperNext, ThemeWrapper as ThemeWrapperNext,
NotificationProvider, NotificationProvider,
@ -25,6 +24,8 @@ import {
Modal as ModalNext, Modal as ModalNext,
ManagedDialog, ManagedDialog,
ModalProvider, ModalProvider,
ViewportDialogProvider,
UserAuthenticationProvider,
} from '@ohif/ui-next'; } from '@ohif/ui-next';
// Viewer Project // Viewer Project
// TODO: Should this influence study list? // TODO: Should this influence study list?
@ -110,7 +111,6 @@ function App({
[UserAuthenticationProvider, { service: userAuthenticationService }], [UserAuthenticationProvider, { service: userAuthenticationService }],
[I18nextProvider, { i18n }], [I18nextProvider, { i18n }],
[ThemeWrapperNext], [ThemeWrapperNext],
[ThemeWrapper],
[SystemContextProvider, { commandsManager, extensionManager, hotkeysManager, servicesManager }], [SystemContextProvider, { commandsManager, extensionManager, hotkeysManager, servicesManager }],
[ViewportGridProvider, { service: viewportGridService }], [ViewportGridProvider, { service: viewportGridService }],
[ViewportDialogProvider, { service: uiViewportDialogService }], [ViewportDialogProvider, { service: uiViewportDialogService }],

View File

@ -2,7 +2,7 @@ import React, { useEffect, useState, useRef } from 'react';
import { useParams, useLocation, useNavigate } from 'react-router'; import { useParams, useLocation, useNavigate } from 'react-router';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { utils } from '@ohif/core'; import { utils } from '@ohif/core';
import { DragAndDropProvider, ImageViewerProvider } from '@ohif/ui'; import { ImageViewerProvider, DragAndDropProvider } from '@ohif/ui-next';
import { useSearchParams } from '@hooks'; import { useSearchParams } from '@hooks';
import { useAppConfig } from '@state'; import { useAppConfig } from '@state';
import ViewportGrid from '@components/ViewportGrid'; import ViewportGrid from '@components/ViewportGrid';

View File

@ -1,4 +1,4 @@
import { useUserAuthentication } from '@ohif/ui'; import { useUserAuthentication } from '@ohif/ui-next';
export const PrivateRoute = ({ children, handleUnauthenticated }) => { export const PrivateRoute = ({ children, handleUnauthenticated }) => {
const [{ user, enabled }] = useUserAuthentication(); const [{ user, enabled }] = useUserAuthentication();

View File

@ -11,7 +11,6 @@ import filtersMeta from './filtersMeta.js';
import { useAppConfig } from '@state'; import { useAppConfig } from '@state';
import { useDebounce, useSearchParams } from '@hooks'; import { useDebounce, useSearchParams } from '@hooks';
import { utils } from '@ohif/core'; import { utils } from '@ohif/core';
import { routerBasename } from '../../utils/publicUrl';
import { import {
StudyListExpandedRow, StudyListExpandedRow,

View File

@ -40,8 +40,11 @@ export interface ButtonProps
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>( const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, forwardRef) => { ({ className, variant, size, asChild = false, ...props }, forwardRef) => {
const Comp = asChild ? Slot : 'button'; const Comp = asChild ? Slot : 'button';
const dataCY = props.dataCY || `${props.name}-btn`;
return ( return (
<Comp <Comp
data-cy={dataCY}
className={cn(buttonVariants({ variant, size }), className)} className={cn(buttonVariants({ variant, size }), className)}
ref={forwardRef} ref={forwardRef}
{...props} {...props}

View File

@ -13,7 +13,7 @@ const Command = React.forwardRef<
<CommandPrimitive <CommandPrimitive
ref={ref} ref={ref}
className={cn( className={cn(
'bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md', 'bg-background text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md',
className className
)} )}
{...props} {...props}
@ -40,10 +40,10 @@ const CommandInput = React.forwardRef<
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input> React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<div <div
className="flex items-center border-b px-3" className="border-input flex items-center border-b px-3"
cmdk-input-wrapper="" cmdk-input-wrapper=""
> >
<MagnifyingGlassIcon className="mr-2 h-4 w-4 shrink-0 opacity-50" /> <MagnifyingGlassIcon className="text-primary mr-2 h-4 w-4 shrink-0" />
<CommandPrimitive.Input <CommandPrimitive.Input
ref={ref} ref={ref}
className={cn( className={cn(
@ -76,7 +76,7 @@ const CommandEmpty = React.forwardRef<
>((props, ref) => ( >((props, ref) => (
<CommandPrimitive.Empty <CommandPrimitive.Empty
ref={ref} ref={ref}
className="py-6 text-center text-base" className="py-5 text-center text-base"
{...props} {...props}
/> />
)); ));
@ -105,7 +105,7 @@ const CommandSeparator = React.forwardRef<
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<CommandPrimitive.Separator <CommandPrimitive.Separator
ref={ref} ref={ref}
className={cn('bg-border -mx-1 h-px', className)} className={cn('bg-input -mx-1 h-px', className)}
{...props} {...props}
/> />
)); ));
@ -118,7 +118,7 @@ const CommandItem = React.forwardRef<
<CommandPrimitive.Item <CommandPrimitive.Item
ref={ref} ref={ref}
className={cn( className={cn(
'aria-selected:bg-accent aria-selected:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-base outline-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50', 'aria-selected:bg-primary/30 aria-selected:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-base outline-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50',
className className
)} )}
{...props} {...props}

View File

@ -65,7 +65,7 @@ function Header({
onClick={onClickReturn} onClick={onClickReturn}
data-cy="return-to-work-list" data-cy="return-to-work-list"
> >
{isReturnEnabled && <Icons.ArrowLeft className="text-primary w-8" />} {isReturnEnabled && <Icons.ArrowLeft className="text-primary ml-1 h-7 w-7" />}
<div className="ml-1"> <div className="ml-1">
{WhiteLabeling?.createLogoComponentFn?.(React, props) || <Icons.OHIFLogo />} {WhiteLabeling?.createLogoComponentFn?.(React, props) || <Icons.OHIFLogo />}
</div> </div>

View File

@ -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 (
<div className="fixed bottom-2 z-50 flex h-[86px] w-full justify-center">
<div className="bg-secondary-dark border-primary-dark flex w-[90%] items-center justify-between rounded-lg border-2 pl-[22px] pr-[22px] pt-[10px] pb-[10px] shadow-lg">
<div className="flex items-center gap-4">
<Icons.InvestigationalUse className="h-18 w-18" />
<div className="flex flex-col">
<div className="text-[19px] text-white">
OHIF Viewer is{' '}
<span className="text-primary-light">for investigational use only</span>
</div>
<div className="text-[13px] text-white">
<span
className="text-primary-active cursor-pointer"
onClick={() => window.open('https://ohif.org/', '_blank')}
>
Learn more about OHIF Viewer
</span>
</div>
</div>
</div>
<Button
onClick={handleConfirmAndHide}
className="bg-primary-main"
dataCY="confirm-and-hide-button"
>
Confirm and Hide
</Button>
</div>
</div>
);
};
InvestigationalUseDialog.propTypes = {
dialogConfiguration: PropTypes.shape({
option: PropTypes.oneOf(Object.values(showDialogOption)).isRequired,
days: PropTypes.number,
}),
};
export default InvestigationalUseDialog;

View File

@ -0,0 +1,3 @@
import InvestigationalUseDialog from './InvestigationalUseDialog';
export default InvestigationalUseDialog;

View File

@ -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<PropType> = ({
labellingDoneCallback,
measurementData,
labelData,
exclusive,
hide,
placeholder = 'Type label or search...',
emptyMessage = 'No labels found.',
initialLabel,
}) => {
// State for the selected value
const [value, setValue] = useState<string | undefined>(initialLabel);
const [currentItems, setCurrentItems] = useState<LabelInfo[]>([]);
// 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 (
<Command className="border-input border">
<CommandInput placeholder={placeholder} />
<CommandList>
<CommandEmpty>{emptyMessage}</CommandEmpty>
<ScrollArea className="h-[300px]">
<CommandGroup>
{currentItems.map(item => (
<CommandItem
key={item.value}
value={item.value}
onSelect={currentValue => {
const newValue = currentValue === value ? '' : currentValue;
setValue(newValue);
labellingDoneCallback(newValue);
hide();
}}
>
<CheckIcon
className={cn('mr-2 h-4 w-4', value === item.value ? 'opacity-100' : 'opacity-0')}
/>
{item.label}
</CommandItem>
))}
</CommandGroup>
</ScrollArea>
</CommandList>
</Command>
);
};
export default LabellingFlow;

View File

@ -0,0 +1,2 @@
import LabellingFlow from './LabellingFlow';
export default LabellingFlow;

View File

@ -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;
}

View File

@ -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 (
<div
className={classnames(
'LineChart text-white',
{
[`w-[${widthProp}px]`]: !!widthProp,
[`h-[${heightProp}px]`]: !!heightProp,
},
{
'w-full': !widthProp,
'h-full': !heightProp,
},
containerClassName
)}
>
<div
id="chartContainer"
ref={chartContainerRef}
className={classnames('h-full w-full', chartContainerClassName)}
></div>
</div>
);
};
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;

View File

@ -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;

View File

@ -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<string, function>} zoomEvents <zoom d3 event name, callback method>
*/
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;

View File

@ -0,0 +1,3 @@
import * as lineChart from './lineChart';
export { lineChart };

View File

@ -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<string, TimecoursePointDef>} 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 };

View File

@ -0,0 +1 @@
export { default } from './LineChart';

View File

@ -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 (
<div
className={classNames(
'absolute top-0 left-0 z-50 flex flex-col items-center justify-center space-y-5',
className
)}
>
<Icons.LoadingOHIFMark className="h-12 w-12 text-white" />
<div className="w-48">
<ProgressLoadingBar progress={progress} />
</div>
{textBlock}
</div>
);
}
export default LoadingIndicatorProgress;

View File

@ -0,0 +1,2 @@
import LoadingIndicatorProgress from './LoadingIndicatorProgress';
export default LoadingIndicatorProgress;

View File

@ -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 ? (
<div className="text-sm text-white">{loadingText}</div>
) : !totalNumbers && percentComplete !== null ? (
<div className="text-sm text-white">Loaded {percentComplete}%</div>
) : (
<div className="text-sm text-white">
Loaded {numTargetsLoadedText} of {totalNumbersText} {targetText}
</div>
);
return (
<LoadingIndicatorProgress
className={className}
progress={progress}
textBlock={textBlock}
/>
);
}
export default LoadingIndicatorTotalPercent;

View File

@ -0,0 +1,2 @@
import LoadingIndicatorTotalPercent from './LoadingIndicatorTotalPercent';
export default LoadingIndicatorTotalPercent;

View File

@ -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 (
<div className="flex">
{options.map((option, i) => (
<div
key={i}
className={classnames('mr-1 h-1 grow first:rounded-l-sm last:mr-0 last:rounded-r-sm', {
'bg-black': !option.activated && !option.completed,
'bg-primary-main': option.activated && !option.completed,
'bg-primary-light': option.completed,
})}
/>
))}
</div>
);
};
ProgressDiscreteBar.propTypes = {
options: PropTypes.arrayOf(ProgressDropdownOptionPropType).isRequired,
};
export default ProgressDiscreteBar;

View File

@ -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 (
<div
ref={element}
className="relative grow select-none text-[0px] text-white"
>
<div>
<div className="mb-1.5 flex h-[26px]">
<div
className="bg-secondary-dark border-primary-main flex grow cursor-pointer rounded border"
style={{ width: `${dropDownWidth}px` }}
onClick={toggleOpen}
>
<div className="flex grow">
{selectedOption && <ProgressItemDetail option={selectedOption} />}
{!selectedOption && <div className="ml-1 grow text-base leading-6">{children}</div>}
</div>
<Icons.ChevronDown className="text-primary-active mt-1.5 ml-1 mr-2" />
</div>
<button
className={classnames('ml-1.5 w-[26px] rounded text-base', {
'bg-primary-main': canMoveNext,
'bg-primary-dark pointer-events-none': !canMoveNext,
})}
>
<Icons.ArrowRight
className={classnames('relative left-0.5 h-6 w-6 text-white', {
'text-white': canMoveNext,
'text-secondary-light': !canMoveNext,
})}
onClick={handleNextButtonClick}
/>
</button>
</div>
<div
className={classnames(
'absolute top-7 left-0 right-8 z-10 mt-0.5 origin-top',
'bg-primary-dark overflow-hidden transition-[max-height] duration-300',
'border-secondary-main rounded border shadow',
{
hidden: !open,
'max-h-[500px]': open,
}
)}
>
{options.map((option, index) => (
<ProgressItem
key={index}
option={option}
onSelect={() => handleOptionSelected(option)}
/>
))}
</div>
<div>
<ProgressDiscreteBar options={options} />
</div>
</div>
</div>
);
};
ProgressDropdown.propTypes = {
options: PropTypes.arrayOf(ProgressDropdownOptionPropType).isRequired,
value: PropTypes.string,
onChange: PropTypes.func,
children: PropTypes.node,
dropDownWidth: PropTypes.string,
};
export default ProgressDropdown;

View File

@ -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 (
<div
key={value}
className={'hover:bg-secondary-main flex cursor-pointer py-1 transition duration-1000'}
onClick={() => onSelect(option)}
>
<ProgressItemDetail option={option} />
</div>
);
};
ProgressItem.propTypes = {
option: ProgressDropdownOptionPropType.isRequired,
onSelect: PropTypes.func,
};
export default ProgressItem;

View File

@ -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)}
<button
className="text-primary-active font-bold"
onClick={handleReadMoreClick}
>
&nbsp;Read more...
</button>
</>
);
}, [info, truncate]);
const handleOpenChange = (open: boolean) => {
if (!open) {
handleOnHideTooltip();
}
};
const iconContent = (
<>
{icon && (
<div>
<Icons.ByName
name={icon}
className={iconClassNames}
/>
</div>
)}
{!icon && <div className={iconClassNames} />}
</>
);
return (
<>
{info && (
<TooltipProvider delayDuration={200}>
<Tooltip onOpenChange={handleOpenChange}>
<TooltipTrigger asChild>{iconContent}</TooltipTrigger>
<TooltipContent
side="bottom"
align="start"
className={'max-w-xs'}
>
{tooltipText}
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
{!info && iconContent}
<div className="grow overflow-hidden text-ellipsis whitespace-nowrap text-base leading-6">
{label}
</div>
</>
);
};
ProgressItemDetail.propTypes = {
option: ProgressDropdownOptionPropType.isRequired,
};
export default ProgressItemDetail;

View File

@ -0,0 +1 @@
export { default } from './ProgressDropdown';

View File

@ -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,
});

View File

@ -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%);
}
}

View File

@ -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 (
<div className="loading">
{progress === undefined || progress === null ? (
<div className="infinite-loading-bar bg-primary-light"></div>
) : (
<div
className="bg-primary-light"
style={{
width: `${progress}%`,
height: '8px',
}}
></div>
)}
</div>
);
}
export default ProgressLoadingBar;

View File

@ -0,0 +1,2 @@
import ProgressLoadingBar from './ProgressLoadingBar';
export default ProgressLoadingBar;

View File

@ -59,9 +59,11 @@ function ToolButton(props: ToolButtonProps) {
className className
); );
const defaultTooltip = tooltip || label; const defaultTooltip = label;
const disabledTooltip = disabled && disabledText ? disabledText : null; const disabledTooltip = disabled && disabledText ? disabledText : null;
const hasTooltip = defaultTooltip || disabledTooltip; const hasSecondaryTooltip = tooltip || disabledTooltip;
const showTooltip = hasSecondaryTooltip || defaultTooltip;
return ( return (
<Tooltip> <Tooltip>
@ -85,7 +87,7 @@ function ToolButton(props: ToolButtonProps) {
}} }}
variant="ghost" variant="ghost"
size="icon" size="icon"
aria-label={hasTooltip ? defaultTooltip : undefined} aria-label={defaultTooltip}
disabled={disabled} disabled={disabled}
> >
<Icons.ByName <Icons.ByName
@ -95,12 +97,19 @@ function ToolButton(props: ToolButtonProps) {
</Button> </Button>
</span> </span>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent side="bottom"> <TooltipContent
{hasTooltip && ( side="bottom"
<> className="text-wrap w-auto max-w-sm whitespace-normal break-words"
<div>{defaultTooltip}</div> >
{disabledTooltip && <div className="text-muted-foreground">{disabledTooltip}</div>} {showTooltip && (
</> <div className="space-y-1">
{defaultTooltip && <div className="text-sm">{defaultTooltip}</div>}
{disabledTooltip ? (
<div className="text-muted-foreground text-xs">{disabledTooltip}</div>
) : (
tooltip && <div className="text-muted-foreground text-xs">{tooltip}</div>
)}
</div>
)} )}
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>

View File

@ -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<ViewportDialogProps> = ({
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 (
<div
ref={notificationRef}
className="border-input bg-muted mx-2 mt-2 flex flex-col rounded-md border p-2 outline-none"
data-cy={id}
onKeyDown={onKeyPress}
tabIndex={0}
>
<div className="flex grow items-center">
<Icons.ByName
name={icon}
className={classnames('h-5 w-5', color)}
/>
<span className="text-foreground ml-2 text-base">{message}</span>
</div>
<div className="mt-2 flex flex-wrap justify-end gap-2">
{actions?.map((action, index) => {
return (
<Button
name={action.id}
key={index}
variant={action.type === 'secondary' ? 'secondary' : 'default'}
size={action.size === 'sm' ? 'sm' : 'default'}
onClick={() => {
onSubmit(action.value);
}}
className="min-w-16"
>
{action.text}
</Button>
);
})}
</div>
</div>
);
};
export default ViewportDialog;

View File

@ -0,0 +1,2 @@
import ViewportDialog from './ViewportDialog';
export default ViewportDialog;

View File

@ -62,7 +62,11 @@ import Modal from './Modal/Modal';
import { FooterAction } from './FooterAction'; import { FooterAction } from './FooterAction';
import { InputFilter } from './InputFilter'; import { InputFilter } from './InputFilter';
import { WindowLevel, WindowLevelHistogram } from './OHIFPanels'; 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 { import {
DropdownMenu, DropdownMenu,
DropdownMenuTrigger, DropdownMenuTrigger,
@ -119,6 +123,9 @@ export { MeasurementTable } from './MeasurementTable';
export * from './ColorCircle'; export * from './ColorCircle';
export { default as AllInOneMenu } from './AllInOneMenu'; export { default as AllInOneMenu } from './AllInOneMenu';
export * 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 // Segmentation Context Exports
export { useSegmentationTableContext, useSegmentationExpanded, useSegmentStatistics }; export { useSegmentationTableContext, useSegmentationExpanded, useSegmentStatistics };
@ -255,5 +262,10 @@ export {
InputFilter, InputFilter,
WindowLevel, WindowLevel,
WindowLevelHistogram, WindowLevelHistogram,
ProgressDropdown,
LoadingIndicatorProgress,
LoadingIndicatorTotalPercent,
ProgressLoadingBar,
ViewportDialog,
CinePlayer, CinePlayer,
}; };

View File

@ -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 (
<DndProvider
backend={backend}
opts={opts}
>
{children}
</DndProvider>
);
}
DragAndDropProvider.propTypes = {
children: PropTypes.any,
};
export default DragAndDropProvider;

View File

@ -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 <ImageViewerContext.Provider value={value}>{children}</ImageViewerContext.Provider>;
}

View File

@ -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 (
<UserAuthenticationContext.Provider value={[userAuthenticationState, api]}>
{children}
</UserAuthenticationContext.Provider>
);
}
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);

View File

@ -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 <Provider value={[viewportDialogState, { show, hide }]}>{children}</Provider>;
};
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;

View File

@ -3,6 +3,11 @@ import { ViewportGridContext, ViewportGridProvider, useViewportGrid } from './Vi
import { ModalProvider, useModal } from './ModalProvider'; import { ModalProvider, useModal } from './ModalProvider';
import { DialogProvider, useDialog } from './DialogProvider'; import { DialogProvider, useDialog } from './DialogProvider';
import ManagedDialog from './ManagedDialog'; 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'; import CineProvider, { useCine } from './CineProvider';
export { useNotification, NotificationProvider }; export { useNotification, NotificationProvider };
@ -10,4 +15,8 @@ export { ViewportGridContext, ViewportGridProvider, useViewportGrid };
export { ModalProvider, useModal }; export { ModalProvider, useModal };
export { DialogProvider, useDialog }; export { DialogProvider, useDialog };
export { ManagedDialog }; export { ManagedDialog };
export { CineProvider, useCine }; export { ViewportDialogProvider, useViewportDialog };
export { UserAuthenticationProvider, useUserAuthentication };
export { ImageViewerContext, ImageViewerProvider, useImageViewer };
export { DragAndDropProvider };
export { CineProvider, useCine };

View File

@ -1,5 +1,6 @@
import * as utils from './utils'; import * as utils from './utils';
export * from './components'; export * from './components';
export * from './contextProviders'; export * from './contextProviders';
export * as Types from './types';
export { utils }; export { utils };

View File

@ -70,8 +70,8 @@ const MeasurementItem = ({
<span>{index}</span> <span>{index}</span>
)} )}
</div> </div>
<div className="relative flex flex-1 flex-col py-1 pl-2 pr-1"> <div className="relative flex flex-1 flex-col pl-2 pr-1">
<span className="text-primary-light mb-1 text-base">{label}</span> <span className="text-primary-light text-base">{label}</span>
{displayText.map((line, i) => ( {displayText.map((line, i) => (
<span <span
key={i} key={i}

View File

@ -39,7 +39,7 @@ const MeasurementTable = ({
</span> </span>
<span className="text-base font-bold text-white">&nbsp; {amount}</span> <span className="text-base font-bold text-white">&nbsp; {amount}</span>
</div> </div>
<CustomizedToolbar servicesManager={servicesManager} /> {/* <CustomizedToolbar servicesManager={servicesManager} /> */}
</div> </div>
<div className="ohif-scrollbar max-h-112 overflow-hidden"> <div className="ohif-scrollbar max-h-112 overflow-hidden">
{data.length !== 0 && {data.length !== 0 &&

182
yarn.lock
View File

@ -1914,18 +1914,6 @@
resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6"
integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== 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": "@humanwhocodes/config-array@^0.13.0":
version "0.13.0" version "0.13.0"
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.13.0.tgz#fb907624df3256d04b9aa2df50d7aa97ec648748" 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" resolved "https://registry.yarnpkg.com/@scarf/scarf/-/scarf-1.3.0.tgz#f8c75560d0dace4452dee1e31995e6396e61f3ee"
integrity sha512-lHKK8M5CTcpFj2hZDB3wIjb0KAbEOgDmiJGDv1WBRfQgRm/a8/XMEkG/N1iM01xgbUDsPQwi42D+dFo1XPAKew== 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": "@sigstore/bundle@^1.1.0":
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/@sigstore/bundle/-/bundle-1.1.0.tgz#17f8d813b09348b16eeed66a8cf1c3d6bd3d04f1" 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" resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.10.0.tgz#d9e56ab0147278272739a000880196cdfe113b59"
integrity sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g== 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: axios@^1.0.0, axios@^1.4.0, axios@^1.6.2:
version "1.8.3" version "1.8.3"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.8.3.tgz#9ebccd71c98651d547162a018a1a95a4b4ed4de8" 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" resolved "https://registry.yarnpkg.com/blob-util/-/blob-util-2.0.2.tgz#3b4e3c281111bb7f11128518006cdc60b403a1eb"
integrity sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ== integrity sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==
bluebird@3.7.2, bluebird@^3.7.2: bluebird@^3.7.2:
version "3.7.2" version "3.7.2"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== 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" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== 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" version "2.24.0"
resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600" resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600"
integrity sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA== 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: dependencies:
ms "^2.1.3" 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: debug@^3.1.0, debug@^3.2.7:
version "3.2.7" version "3.2.7"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" 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" es-errors "^1.3.0"
gopd "^1.2.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" version "0.1.2"
resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6"
integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== 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" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== 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: event-target-shim@^5.0.0:
version "5.0.1" version "5.0.1"
resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" 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" signal-exit "^3.0.3"
strip-final-newline "^2.0.0" 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: execa@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" 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" signal-exit "^3.0.2"
strip-final-newline "^2.0.0" 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: execa@^8.0.1:
version "8.0.1" version "8.0.1"
resolved "https://registry.yarnpkg.com/execa/-/execa-8.0.1.tgz#51f6a5943b580f963c3ca9c6321796db8cc39b8c" 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" resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.247.1.tgz#4a512c882cd08126825fe00b1ee452637ef44371"
integrity sha512-DHwcm06fWbn2Z6uFD3NaBZ5lMOoABIQ4asrVA80IWvYjjT5WdbghkUOL1wIcbLcagnFTdCZYOlSNnKNp/xnRZQ== 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" version "1.15.6"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b"
integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== 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" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== 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: fs-constants@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" 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" resolved "https://registry.yarnpkg.com/jju/-/jju-1.4.0.tgz#a3abe2718af241a2b2904f84a625970f389ae32a"
integrity sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA== 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: jpeg-lossless-decoder-js@^2.1.0:
version "2.1.2" version "2.1.2"
resolved "https://registry.yarnpkg.com/jpeg-lossless-decoder-js/-/jpeg-lossless-decoder-js-2.1.2.tgz#9ff0ecfcafa6acaee6097c532720bd526278aa3d" 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" lodash "^4.17.5"
webpack-sources "^1.1.0" webpack-sources "^1.1.0"
lazy-ass@1.6.0, lazy-ass@^1.6.0: lazy-ass@^1.6.0:
version "1.6.0" version "1.6.0"
resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513"
integrity sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw== 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" resolved "https://registry.yarnpkg.com/map-or-similar/-/map-or-similar-1.5.0.tgz#6de2653174adfb5d9edc33c69d3e92a1b76faf08"
integrity sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg== 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: mapbox-to-css-font@^2.4.1:
version "2.4.5" version "2.4.5"
resolved "https://registry.yarnpkg.com/mapbox-to-css-font/-/mapbox-to-css-font-2.4.5.tgz#b10a7a33af3e1a9a1369e4d5e8285492a7943c46" 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" is-plain-obj "^1.1.0"
kind-of "^6.0.3" 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" version "1.2.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
@ -15354,11 +15276,6 @@ ms@2.0.0:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== 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: ms@2.1.3, ms@^2.0.0, ms@^2.1.1, ms@^2.1.3:
version "2.1.3" version "2.1.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" 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" resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec"
integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ== 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: pbf@3.2.1:
version "3.2.1" version "3.2.1"
resolved "https://registry.yarnpkg.com/pbf/-/pbf-3.2.1.tgz#b4c1b9e72af966cd82c6531691115cc0409ffe2a" 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" resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== 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: pseudomap@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
@ -18705,7 +18608,7 @@ rxjs@^6.3.3:
dependencies: dependencies:
tslib "^1.9.0" 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" version "7.8.1"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543"
integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==
@ -19437,13 +19340,6 @@ split2@^3.2.2:
dependencies: dependencies:
readable-stream "^3.0.0" 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: split@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9" 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" resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310"
integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw== 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: statuses@2.0.1:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" 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" inherits "~2.0.4"
readable-stream "^3.5.0" 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: stream-http@^3.2.0:
version "3.2.0" version "3.2.0"
resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-3.2.0.tgz#1872dfcf24cb15752677e40e5c3f9cc1926028b5" 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" readable-stream "~2.3.6"
xtend "~4.0.1" 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" version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==
@ -21081,17 +20956,6 @@ w3c-xmlserializer@^4.0.0:
dependencies: dependencies:
xml-name-validator "^4.0.0" 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: walker@^1.0.8:
version "1.0.8" version "1.0.8"
resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f"