Compare commits
64
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02c732aa15 | ||
|
|
c5f532159b | ||
|
|
b1df019c49 | ||
|
|
54b70e5fa4 | ||
|
|
5802cf18eb | ||
|
|
210c2e8d2c | ||
|
|
da318c9c8c | ||
|
|
903c41d26f | ||
|
|
ba261a0d54 | ||
|
|
3398a894a3 | ||
|
|
5a13194203 | ||
|
|
a2cc477a1a | ||
|
|
2e7a59f7f8 | ||
|
|
83d7d59954 | ||
|
|
163fee8941 | ||
|
|
f547fdf832 | ||
|
|
ac347849d0 | ||
|
|
54e0e32e22 | ||
|
|
690156d4b6 | ||
|
|
3b5fdb678a | ||
|
|
e7604c4418 | ||
|
|
71f11a4856 | ||
|
|
ae5d0587db | ||
|
|
fbc06329e2 | ||
|
|
86732c8186 | ||
|
|
a34d47c850 | ||
|
|
f758741950 | ||
|
|
29a470103a | ||
|
|
104343aad5 | ||
|
|
d8b4a4d4c0 | ||
|
|
2fd60f3109 | ||
|
|
08ce30e69c | ||
|
|
546f6bfe68 | ||
|
|
8e1f15e031 | ||
|
|
0d5d804741 | ||
|
|
ca735fa070 | ||
|
|
3d425f54cc | ||
|
|
7bc9b45853 | ||
|
|
5bd3af0cca | ||
|
|
d4aee36542 | ||
|
|
2a281da19f | ||
|
|
4631218dcb | ||
|
|
8b3e5dffb1 | ||
|
|
ef6819cf40 | ||
|
|
2f104b5e91 | ||
|
|
f256ac5a7d | ||
|
|
3a4ee9e346 | ||
|
|
1a9cfea365 | ||
|
|
9055f19fb5 | ||
|
|
bcbc5e4bbf | ||
|
|
408ba36f4f | ||
|
|
091cab49e0 | ||
|
|
4a09f33446 | ||
|
|
4095629310 | ||
|
|
91d2d31846 | ||
|
|
ff2c1cb2f1 | ||
|
|
727c0d8ba6 | ||
|
|
4b3994e788 | ||
|
|
571dd5848f | ||
|
|
2d48d0aa37 | ||
|
|
562d63fa74 | ||
|
|
1a6246f99b | ||
|
|
ce1a62a961 | ||
|
|
dd6acb2d03 |
No files matched your search
@@ -87,6 +87,15 @@
|
||||
"contributions": [
|
||||
"test"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "pavertomato",
|
||||
"name": "Egor Lezhnin",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/878990?v=4",
|
||||
"profile": "http://egor.lezhn.in",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
||||
@@ -75,6 +75,7 @@ jobs:
|
||||
# Could do a check here, if it doesn't exist, install cypress?
|
||||
- run: yarn install
|
||||
- run: yarn run test:e2e:ci
|
||||
- run: yarn run test:e2e:scriptTag
|
||||
|
||||
npm_publish:
|
||||
<<: *defaults
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# Output
|
||||
dist/
|
||||
build/
|
||||
|
||||
# Dependencies
|
||||
node_modules/
|
||||
@@ -7,6 +8,7 @@ node_modules/
|
||||
# Root
|
||||
README.md
|
||||
Dockerfile
|
||||
dockerfile
|
||||
|
||||
# Misc. Config
|
||||
.git
|
||||
@@ -14,3 +16,8 @@ Dockerfile
|
||||
.gitignore
|
||||
.vscode
|
||||
.circleci
|
||||
|
||||
# Unnecessary things to pull into container
|
||||
extensions
|
||||
docs
|
||||
cypress
|
||||
+2
-1
@@ -36,4 +36,5 @@
|
||||
*.pdf binary
|
||||
*.ez binary
|
||||
*.bz2 binary
|
||||
*.swp binary
|
||||
*.swp binary
|
||||
*.fig binary
|
||||
@@ -28,3 +28,6 @@ docker/dcm4che/dcm4che-arc
|
||||
|
||||
# Cypress test results
|
||||
cypress/videos/
|
||||
|
||||
# Locize settings
|
||||
.locize
|
||||
+16
-4
@@ -27,22 +27,34 @@ FROM node:11.2.0-slim as builder
|
||||
RUN mkdir /usr/src/app
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
ENV PATH /usr/src/app/node_modules/.bin:$PATH
|
||||
ENV GENERATE_SOURCEMAP=false
|
||||
|
||||
COPY package.json /usr/src/app/package.json
|
||||
COPY yarn.lock /usr/src/app/yarn.lock
|
||||
|
||||
ADD . /usr/src/app/
|
||||
# Run the install before copying the rest of the files
|
||||
RUN yarn install
|
||||
|
||||
ENV PATH /usr/src/app/node_modules/.bin:$PATH
|
||||
ENV GENERATE_SOURCEMAP=false
|
||||
ENV REACT_APP_CONFIG=config/default.js
|
||||
|
||||
# White list instead of copying the whole directory
|
||||
COPY src /usr/src/app/src
|
||||
COPY public /usr/src/app/public
|
||||
COPY .babelrc /usr/src/app/.babelrc
|
||||
COPY .eslintrc /usr/src/app/.eslintrc
|
||||
|
||||
RUN yarn run build:web
|
||||
|
||||
# Stage 2: Bundle the built application into a Docker container
|
||||
# which runs Nginx using Alpine Linux
|
||||
FROM nginx:1.15.5-alpine
|
||||
RUN apk add --no-cache bash
|
||||
RUN rm -rf /etc/nginx/conf.d
|
||||
COPY docker/Viewer-v2.x /etc/nginx/conf.d
|
||||
COPY docker/Viewer-v2.x/entrypoint.sh /usr/src/
|
||||
RUN chmod 777 /usr/src/entrypoint.sh
|
||||
COPY --from=builder /usr/src/app/build /usr/share/nginx/html
|
||||
EXPOSE 80
|
||||
EXPOSE 443
|
||||
ENTRYPOINT ["/usr/src/entrypoint.sh"]
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
[![CircleCI][circleci-image]][circleci-url]
|
||||
[![codecov][codecov-image]][codecov-url]
|
||||
[](#contributors)
|
||||
[](#contributors)
|
||||
[![code style: prettier][prettier-image]][prettier-url]
|
||||
[![semantic-release][semantic-image]][semantic-url]
|
||||
|
||||
@@ -220,7 +220,22 @@ Thanks goes to these wonderful people
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
||||
<!-- prettier-ignore -->
|
||||
<table><tr><td align="center"><a href="https://github.com/swederik"><img src="https://avatars3.githubusercontent.com/u/607793?v=4" width="100px;" alt="Erik Ziegler"/><br /><sub><b>Erik Ziegler</b></sub></a><br /><a href="https://github.com/OHIF/Viewers/commits?author=swederik" title="Code">💻</a> <a href="#infra-swederik" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td><td align="center"><a href="https://github.com/evren217"><img src="https://avatars1.githubusercontent.com/u/4920551?v=4" width="100px;" alt="Evren Ozkan"/><br /><sub><b>Evren Ozkan</b></sub></a><br /><a href="https://github.com/OHIF/Viewers/commits?author=evren217" title="Code">💻</a></td><td align="center"><a href="https://github.com/galelis"><img src="https://avatars3.githubusercontent.com/u/2378326?v=4" width="100px;" alt="Gustavo André Lelis"/><br /><sub><b>Gustavo André Lelis</b></sub></a><br /><a href="https://github.com/OHIF/Viewers/commits?author=galelis" title="Code">💻</a></td><td align="center"><a href="http://dannyrb.com/"><img src="https://avatars1.githubusercontent.com/u/5797588?v=4" width="100px;" alt="Danny Brown"/><br /><sub><b>Danny Brown</b></sub></a><br /><a href="https://github.com/OHIF/Viewers/commits?author=dannyrb" title="Code">💻</a> <a href="#infra-dannyrb" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td><td align="center"><a href="https://github.com/all-contributors/all-contributors-bot"><img src="https://avatars3.githubusercontent.com/u/46843839?v=4" width="100px;" alt="allcontributors[bot]"/><br /><sub><b>allcontributors[bot]</b></sub></a><br /><a href="https://github.com/OHIF/Viewers/commits?author=allcontributors" title="Documentation">📖</a></td><td align="center"><a href="https://www.linkedin.com/in/siliconvalleynextgeneration/"><img src="https://avatars0.githubusercontent.com/u/1230575?v=4" width="100px;" alt="Esref Durna"/><br /><sub><b>Esref Durna</b></sub></a><br /><a href="#question-EsrefDurna" title="Answering Questions">💬</a></td><td align="center"><a href="https://github.com/diego0020"><img src="https://avatars3.githubusercontent.com/u/7297450?v=4" width="100px;" alt="diego0020"/><br /><sub><b>diego0020</b></sub></a><br /><a href="https://github.com/OHIF/Viewers/commits?author=diego0020" title="Code">💻</a></td></tr><tr><td align="center"><a href="https://github.com/dlwire"><img src="https://avatars3.githubusercontent.com/u/1167291?v=4" width="100px;" alt="David Wire"/><br /><sub><b>David Wire</b></sub></a><br /><a href="https://github.com/OHIF/Viewers/commits?author=dlwire" title="Code">💻</a></td><td align="center"><a href="https://github.com/jfmedeiros1820"><img src="https://avatars1.githubusercontent.com/u/2211708?v=4" width="100px;" alt="João Felipe de Medeiros Moreira"/><br /><sub><b>João Felipe de Medeiros Moreira</b></sub></a><br /><a href="https://github.com/OHIF/Viewers/commits?author=jfmedeiros1820" title="Tests">⚠️</a></td></tr></table>
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/swederik"><img src="https://avatars3.githubusercontent.com/u/607793?v=4" width="100px;" alt="Erik Ziegler"/><br /><sub><b>Erik Ziegler</b></sub></a><br /><a href="https://github.com/OHIF/Viewers/commits?author=swederik" title="Code">💻</a> <a href="#infra-swederik" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
|
||||
<td align="center"><a href="https://github.com/evren217"><img src="https://avatars1.githubusercontent.com/u/4920551?v=4" width="100px;" alt="Evren Ozkan"/><br /><sub><b>Evren Ozkan</b></sub></a><br /><a href="https://github.com/OHIF/Viewers/commits?author=evren217" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/galelis"><img src="https://avatars3.githubusercontent.com/u/2378326?v=4" width="100px;" alt="Gustavo André Lelis"/><br /><sub><b>Gustavo André Lelis</b></sub></a><br /><a href="https://github.com/OHIF/Viewers/commits?author=galelis" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://dannyrb.com/"><img src="https://avatars1.githubusercontent.com/u/5797588?v=4" width="100px;" alt="Danny Brown"/><br /><sub><b>Danny Brown</b></sub></a><br /><a href="https://github.com/OHIF/Viewers/commits?author=dannyrb" title="Code">💻</a> <a href="#infra-dannyrb" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
|
||||
<td align="center"><a href="https://github.com/all-contributors/all-contributors-bot"><img src="https://avatars3.githubusercontent.com/u/46843839?v=4" width="100px;" alt="allcontributors[bot]"/><br /><sub><b>allcontributors[bot]</b></sub></a><br /><a href="https://github.com/OHIF/Viewers/commits?author=allcontributors" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://www.linkedin.com/in/siliconvalleynextgeneration/"><img src="https://avatars0.githubusercontent.com/u/1230575?v=4" width="100px;" alt="Esref Durna"/><br /><sub><b>Esref Durna</b></sub></a><br /><a href="#question-EsrefDurna" title="Answering Questions">💬</a></td>
|
||||
<td align="center"><a href="https://github.com/diego0020"><img src="https://avatars3.githubusercontent.com/u/7297450?v=4" width="100px;" alt="diego0020"/><br /><sub><b>diego0020</b></sub></a><br /><a href="https://github.com/OHIF/Viewers/commits?author=diego0020" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/dlwire"><img src="https://avatars3.githubusercontent.com/u/1167291?v=4" width="100px;" alt="David Wire"/><br /><sub><b>David Wire</b></sub></a><br /><a href="https://github.com/OHIF/Viewers/commits?author=dlwire" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/jfmedeiros1820"><img src="https://avatars1.githubusercontent.com/u/2211708?v=4" width="100px;" alt="João Felipe de Medeiros Moreira"/><br /><sub><b>João Felipe de Medeiros Moreira</b></sub></a><br /><a href="https://github.com/OHIF/Viewers/commits?author=jfmedeiros1820" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="http://egor.lezhn.in"><img src="https://avatars0.githubusercontent.com/u/878990?v=4" width="100px;" alt="Egor Lezhnin"/><br /><sub><b>Egor Lezhnin</b></sub></a><br /><a href="https://github.com/OHIF/Viewers/commits?author=pavertomato" title="Code">💻</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
|
||||
<meta name="description" content="Open Health Imaging Foundation DICOM Viewer" />
|
||||
<meta name="viewport"
|
||||
content="width=device-width,initial-scale=1.0,minimum-scale=1,maximum-scale=1,user-scalable=no" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta http-equiv="cleartype" content="on" />
|
||||
<meta name="MobileOptimized" content="320" />
|
||||
<meta name="HandheldFriendly" content="True" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
|
||||
<!-- Latest compiled and minified CSS -->
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
|
||||
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous" />
|
||||
|
||||
<title>OHIF Standalone Viewer</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<noscript> You need to enable JavaScript to run this app. </noscript>
|
||||
|
||||
<div id="root"></div>
|
||||
|
||||
<script src="/dist/index.umd.js" crossorigin></script>
|
||||
<script>
|
||||
window.config = {
|
||||
// default: '/'
|
||||
routerBasename: '/',
|
||||
servers: {
|
||||
dicomWeb: [
|
||||
{
|
||||
name: 'DCM4CHEE',
|
||||
wadoUriRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/wado',
|
||||
qidoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs',
|
||||
wadoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs',
|
||||
qidoSupportsIncludeField: true,
|
||||
imageRendering: 'wadors',
|
||||
thumbnailRendering: 'wadors',
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
window.OHIFStandaloneViewer.installViewer(window.config);
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
|
||||
# If CLIENT_ID is specified, use the google.js configuration with the modified ID
|
||||
if [ ! -z "$CLIENT_ID" ]
|
||||
then
|
||||
echo "Google Cloud Healthcare $CLIENT_ID has been provided: "
|
||||
echo $CLIENT_ID
|
||||
echo "Updating config..."
|
||||
|
||||
# - Use SED to replace the CLIENT_ID that is currently in public/config/google.js
|
||||
sed -i -e "s/YOURCLIENTID.apps.googleusercontent.com/$CLIENT_ID/g" /usr/share/nginx/html/config/google.js
|
||||
|
||||
# - Copy public/config/google.js to overwrite public/config/default.js
|
||||
cp /usr/share/nginx/html/config/google.js /usr/share/nginx/html/config/default.js
|
||||
fi
|
||||
|
||||
echo "Starting Nginx to serve the OHIF Viewer..."
|
||||
|
||||
exec "$@"
|
||||
@@ -1,11 +1,5 @@
|
||||
# Google Cloud Healthcare
|
||||
|
||||
> ATTENTION: The original documentation for this integration lives in the legacy
|
||||
> `version 1` Meteor documentation. You can
|
||||
> [find it here](/history/v1/connecting-to-image-archives/google-cloud-healthcare.html).
|
||||
> These docs will mirror the Meteor documentation until our `React`
|
||||
> implementation has been updated to work with Google Cloud Healthcare.
|
||||
|
||||
> The [Google Cloud Healthcare API](https://cloud.google.com/healthcare/) is a
|
||||
> powerful option for storing medical imaging data in the cloud.
|
||||
|
||||
@@ -16,10 +10,7 @@ store their data in the cloud. It offers an
|
||||
[almost-entirely complete DICOMWeb API](https://cloud.google.com/healthcare/docs/dicom)
|
||||
which requires tokens generated via the
|
||||
[OAuth 2.0 Sign In flow](https://developers.google.com/identity/sign-in/web/sign-in).
|
||||
Images can even be transcoded on the fly if this is desired. The Cloud
|
||||
Healthcare API is a very attractive option because it allows us to avoid
|
||||
deploying the Meteor server entirely. We can just deploy OHIF as a client-only
|
||||
static site application.
|
||||
Images can even be transcoded on the fly if this is desired.
|
||||
|
||||
## Setup a Google Cloud Healthcare Project
|
||||
|
||||
@@ -44,10 +35,10 @@ static site application.
|
||||
- Choose the "Web Application" type
|
||||
- Set up an
|
||||
[OAuth 2.0 Client ID](https://support.google.com/cloud/answer/6158849?hl=en)
|
||||
- Add your domain (e.g. `http://localhost:3000`) to Authorized JavaScript
|
||||
- Add your domain (e.g. `http://localhost:5000`) to Authorized JavaScript
|
||||
origins.
|
||||
- Add your domain, plus `_oauth/google` (e.g.
|
||||
`http://localhost:3000/_oauth/google`) to Authorized Redirect URIs.
|
||||
- Add your domain, plus `callback` (e.g.
|
||||
`http://localhost:5000/callback`) to Authorized Redirect URIs.
|
||||
- Save your Client ID for later.
|
||||
|
||||
- (Optional): Enable Public Datasets that are being hosted by Google:
|
||||
@@ -55,70 +46,26 @@ static site application.
|
||||
|
||||
## Run the viewer with your OAuth Client ID
|
||||
|
||||
1. Open the `config/oidc-googleCloud.json` file and change `YOURCLIENTID` to
|
||||
1. Open the `config/google.js` file and change `YOURCLIENTID` to
|
||||
your Client ID value.
|
||||
1. Run the OHIF Viewer using the oidc-googleCloud.json configuration file
|
||||
1. Run the OHIF Viewer using the config/google.js configuration file
|
||||
|
||||
```bash
|
||||
cd OHIFViewer
|
||||
METEOR_PACKAGE_DIRS="../Packages" meteor npm install
|
||||
METEOR_PACKAGE_DIRS="../Packages" meteor --settings ../config/oidc-googleCloud.json
|
||||
yarn install
|
||||
REACT_APP_CONFIG=config/google.js yarn run dev
|
||||
```
|
||||
|
||||
## Running via Docker
|
||||
|
||||
OHIF is also providing a Docker container which can connect to Google Cloud
|
||||
Healthcare with a Client ID which is provided at runtime. This is a very simple
|
||||
method to get up and running. Internally, the container is running
|
||||
[Nginx](https://nginx.org/) to serve the
|
||||
[Standalone Viewer](../standalone-viewer/usage.md).
|
||||
The OHIF Viewer Docker container can be connected to Google Cloud
|
||||
Healthcare by providing a Client ID at runtime. This is a very simple
|
||||
method to get up and running.
|
||||
|
||||
1. Install Docker (https://www.docker.com/)
|
||||
1. Run the Docker container, providing a Client ID as an environment variable.
|
||||
Client IDs look like `xyz.apps.googleusercontent.com`.
|
||||
|
||||
```bash
|
||||
docker run --env CLIENT_ID=$CLIENT_ID --publish 3000:80 ohif/viewer-google-cloud:latest
|
||||
```
|
||||
|
||||
## Building the ohif/viewer-google-cloud Docker Image
|
||||
|
||||
The
|
||||
[ohif/viewer-google-cloud](https://cloud.docker.com/u/ohif/repository/docker/ohif/viewer-google-cloud)
|
||||
Docker image is built as follows. The Dockerfile and nginx.conf are in the
|
||||
`/dockersupport/viewer-google-cloud` folder.
|
||||
|
||||
1. [Install Meteor](https://www.meteor.com/install)
|
||||
1. Clone the repository
|
||||
|
||||
```bash
|
||||
git clone https://github.com/OHIF/Viewers.git
|
||||
cd Viewers
|
||||
```
|
||||
|
||||
1. Install meteor-build-client-fixed2 so you can build the Standalone Viewer
|
||||
|
||||
```bash
|
||||
npm install -g meteor-build-client-fixed2
|
||||
```
|
||||
|
||||
1. Build the Standalone client-only OHIF Viewer
|
||||
|
||||
```bash
|
||||
cd OHIFViewer/
|
||||
METEOR_PACKAGE_DIRS="../Packages" meteor npm install
|
||||
METEOR_PACKAGE_DIRS="../Packages" meteor-build-client-fixed2 ../dockersupport/viewer-google-cloud/build -s ../config/oidc.json
|
||||
```
|
||||
|
||||
1. Build the Docker image
|
||||
|
||||
```bash
|
||||
cd ../dockersupport/viewer-google-cloud
|
||||
docker build -t ohif/viewer-google-cloud .
|
||||
```
|
||||
|
||||
1. Run the Docker image using an OAuth Client ID
|
||||
|
||||
```bash
|
||||
docker run --env CLIENT_ID={$someID}.apps.googleusercontent.com --publish 3000:80 ohif/viewer-google-cloud
|
||||
docker run --env CLIENT_ID=$CLIENT_ID --publish 5000:80 ohif/viewer:latest
|
||||
```
|
||||
@@ -1074,10 +1074,10 @@ gitbook-plugin-sitemap@^1.2.0:
|
||||
dependencies:
|
||||
sitemap "1.5.0"
|
||||
|
||||
gitbook-plugin-theme-cornerstone@^1.1.2:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/gitbook-plugin-theme-cornerstone/-/gitbook-plugin-theme-cornerstone-1.1.3.tgz#320e1d688eaf27dbf8d580f285509b55984437fd"
|
||||
integrity sha512-fURj67XGQ5rZPj8LXbwEviMxLfWgN/s08t9Wnp1dQqffBUJwsLe00R/IeecOjsQHJ3Gvb8GjbXJ6qzIDGu5hPA==
|
||||
gitbook-plugin-theme-cornerstone@^1.1.4:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/gitbook-plugin-theme-cornerstone/-/gitbook-plugin-theme-cornerstone-1.1.4.tgz#bc21b65acee2ac67e2ddb0986442ec448fe0c7fb"
|
||||
integrity sha512-BADgFyEKhkc0pPSBRwXa5VU2UoYQkd2cRwjrtjHyV6w4h/M7uyeWYFOcsgi2ju3rpHRCwF8lMg/TtDdAV8V1SQ==
|
||||
|
||||
gitbook-plugin-versions@^2.1.4:
|
||||
version "2.1.4"
|
||||
@@ -1608,9 +1608,9 @@ lodash.map@^4.4.0:
|
||||
integrity sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=
|
||||
|
||||
lodash.merge@^4.4.0:
|
||||
version "4.6.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.1.tgz#adc25d9cb99b9391c59624f379fbba60d7111d54"
|
||||
integrity sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ==
|
||||
version "4.6.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
|
||||
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
|
||||
|
||||
lodash.pad@^4.1.0:
|
||||
version "4.5.1"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-cornerstone",
|
||||
"version": "0.0.38",
|
||||
"version": "0.0.43",
|
||||
"description": "OHIF extension for Cornerstone",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -39,7 +39,7 @@
|
||||
"@babel/runtime": "^7.2.0",
|
||||
"classnames": "^2.2.6",
|
||||
"lodash.throttle": "^4.1.1",
|
||||
"react-cornerstone-viewport": "0.1.30"
|
||||
"react-cornerstone-viewport": "0.1.32"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.2.2",
|
||||
|
||||
@@ -202,6 +202,10 @@ class OHIFCornerstoneViewport extends Component {
|
||||
frameIndex,
|
||||
} = displaySet;
|
||||
|
||||
if (!studyInstanceUid || !displaySetInstanceUid) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sopClassUids && sopClassUids.length > 1) {
|
||||
console.warn(
|
||||
'More than one SOPClassUid in the same series is not yet supported.'
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import cornerstone from 'cornerstone-core';
|
||||
import cornerstoneTools from 'cornerstone-tools';
|
||||
import OHIF from 'ohif-core';
|
||||
const scroll = cornerstoneTools.import('util/scroll');
|
||||
|
||||
const actions = {
|
||||
rotateViewport: ({ viewports, rotation }) => {
|
||||
@@ -89,16 +91,77 @@ const actions = {
|
||||
// TODO
|
||||
console.warn('updateDisplaySet: ', direction);
|
||||
},
|
||||
clearAnnotations: () => {
|
||||
console.warn('clearAnnotations: not yet implemented');
|
||||
// const toolState =
|
||||
// cornerstoneTools.globalImageIdSpecificToolStateManager.toolState;
|
||||
// if (!toolState) return;
|
||||
// Object.keys(toolState).forEach(imageId => {
|
||||
// if (!cornerstoneImageId || cornerstoneImageId === imageId)
|
||||
// delete toolState[imageId];
|
||||
// });
|
||||
clearAnnotations: ({ viewports }) => {
|
||||
const element = _getActiveViewportEnabledElement(
|
||||
viewports.viewportSpecificData,
|
||||
viewports.activeViewportIndex
|
||||
);
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
const enabledElement = cornerstone.getEnabledElement(element);
|
||||
if (!enabledElement || !enabledElement.image) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
toolState,
|
||||
} = cornerstoneTools.globalImageIdSpecificToolStateManager;
|
||||
if (
|
||||
!toolState ||
|
||||
toolState.hasOwnProperty(enabledElement.image.imageId) === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const imageIdToolState = toolState[enabledElement.image.imageId];
|
||||
|
||||
const measurementsToRemove = [];
|
||||
|
||||
Object.keys(imageIdToolState).forEach(toolType => {
|
||||
const { data } = imageIdToolState[toolType];
|
||||
|
||||
data.forEach(measurementData => {
|
||||
const { _id, lesionNamingNumber, measurementNumber } = measurementData;
|
||||
if (!_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
measurementsToRemove.push({
|
||||
toolType,
|
||||
_id,
|
||||
lesionNamingNumber,
|
||||
measurementNumber,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
measurementsToRemove.forEach(measurementData => {
|
||||
OHIF.measurements.MeasurementHandlers.onRemoved({
|
||||
detail: {
|
||||
toolType: measurementData.toolType,
|
||||
measurementData,
|
||||
},
|
||||
});
|
||||
});
|
||||
},
|
||||
nextImage: ({ viewports }) => {
|
||||
const enabledElement = _getActiveViewportEnabledElement(
|
||||
viewports.viewportSpecificData,
|
||||
viewports.activeViewportIndex
|
||||
);
|
||||
|
||||
scroll(enabledElement, 1);
|
||||
},
|
||||
previousImage: ({ viewports }) => {
|
||||
const enabledElement = _getActiveViewportEnabledElement(
|
||||
viewports.viewportSpecificData,
|
||||
viewports.activeViewportIndex
|
||||
);
|
||||
|
||||
scroll(enabledElement, -1);
|
||||
}
|
||||
};
|
||||
|
||||
const definitions = {
|
||||
@@ -147,8 +210,21 @@ const definitions = {
|
||||
storeContexts: ['viewports'],
|
||||
options: {},
|
||||
},
|
||||
// TODO: Clear Annotations
|
||||
// TODO: Next/Previous image
|
||||
clearAnnotations: {
|
||||
commandFn: actions.clearAnnotations,
|
||||
storeContexts: ['viewports'],
|
||||
options: {},
|
||||
},
|
||||
nextImage: {
|
||||
commandFn: actions.nextImage,
|
||||
storeContexts: ['viewports'],
|
||||
options: {},
|
||||
},
|
||||
previousImage: {
|
||||
commandFn: actions.previousImage,
|
||||
storeContexts: ['viewports'],
|
||||
options: {},
|
||||
},
|
||||
// TODO: First/Last image
|
||||
// Next/Previous series/DisplaySet
|
||||
nextViewportDisplaySet: {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
// -- ACTIVE_ROUTE::VIEWER
|
||||
// -- ACTIVE_VIEWPORT::CORNERSTONE
|
||||
// setToolActive commands should receive the button event that triggered
|
||||
// so we can do the "bind to this butyon" magic
|
||||
// so we can do the "bind to this button" magic
|
||||
|
||||
const TOOLBAR_BUTTON_TYPES = {
|
||||
COMMAND: 'command',
|
||||
@@ -98,6 +98,98 @@ const definitions = [
|
||||
behavior: 'CINE',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'More',
|
||||
label: 'More',
|
||||
icon: 'ellipse-circle',
|
||||
buttons: [
|
||||
{
|
||||
id: 'Magnify',
|
||||
label: 'Magnify',
|
||||
icon: 'circle',
|
||||
//
|
||||
type: TOOLBAR_BUTTON_TYPES.SET_TOOL_ACTIVE,
|
||||
commandName: 'setToolActive',
|
||||
commandOptions: { toolName: 'Magnify' },
|
||||
},
|
||||
{
|
||||
id: 'WwwcRegion',
|
||||
label: 'ROI Window',
|
||||
icon: 'stop',
|
||||
//
|
||||
type: TOOLBAR_BUTTON_TYPES.SET_TOOL_ACTIVE,
|
||||
commandName: 'setToolActive',
|
||||
commandOptions: { toolName: 'WwwcRegion' },
|
||||
},
|
||||
{
|
||||
id: 'DragProbe',
|
||||
label: 'Probe',
|
||||
icon: 'dot-circle',
|
||||
//
|
||||
type: TOOLBAR_BUTTON_TYPES.SET_TOOL_ACTIVE,
|
||||
commandName: 'setToolActive',
|
||||
commandOptions: { toolName: 'DragProbe' },
|
||||
},
|
||||
{
|
||||
id: 'EllipticalRoi',
|
||||
label: 'Ellipse',
|
||||
icon: 'circle-o',
|
||||
//
|
||||
type: TOOLBAR_BUTTON_TYPES.SET_TOOL_ACTIVE,
|
||||
commandName: 'setToolActive',
|
||||
commandOptions: { toolName: 'EllipticalRoi' },
|
||||
},
|
||||
{
|
||||
id: 'RectangleRoi',
|
||||
label: 'Rectangle',
|
||||
icon: 'square-o',
|
||||
//
|
||||
type: TOOLBAR_BUTTON_TYPES.SET_TOOL_ACTIVE,
|
||||
commandName: 'setToolActive',
|
||||
commandOptions: { toolName: 'RectangleRoi' },
|
||||
},
|
||||
{
|
||||
id: 'Invert',
|
||||
label: 'Invert',
|
||||
icon: 'adjust',
|
||||
//
|
||||
type: TOOLBAR_BUTTON_TYPES.COMMAND,
|
||||
commandName: 'invertViewport',
|
||||
},
|
||||
{
|
||||
id: 'RotateRight',
|
||||
label: 'Rotate Right',
|
||||
icon: 'rotate-right',
|
||||
//
|
||||
type: TOOLBAR_BUTTON_TYPES.COMMAND,
|
||||
commandName: 'rotateViewportCW',
|
||||
},
|
||||
{
|
||||
id: 'FlipH',
|
||||
label: 'Flip H',
|
||||
icon: 'ellipse-h',
|
||||
//
|
||||
type: TOOLBAR_BUTTON_TYPES.COMMAND,
|
||||
commandName: 'flipViewportHorizontal',
|
||||
},
|
||||
{
|
||||
id: 'FlipV',
|
||||
label: 'Flip V',
|
||||
icon: 'ellipse-v',
|
||||
//
|
||||
type: TOOLBAR_BUTTON_TYPES.COMMAND,
|
||||
commandName: 'flipViewportVertical',
|
||||
},
|
||||
{
|
||||
id: 'Clear',
|
||||
label: 'Clear',
|
||||
icon: 'trash',
|
||||
//
|
||||
type: TOOLBAR_BUTTON_TYPES.COMMAND,
|
||||
commandName: 'clearAnnotations',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default {
|
||||
|
||||
@@ -4421,10 +4421,10 @@ randomfill@^1.0.3:
|
||||
randombytes "^2.0.5"
|
||||
safe-buffer "^5.1.0"
|
||||
|
||||
react-cornerstone-viewport@0.1.30:
|
||||
version "0.1.30"
|
||||
resolved "https://registry.yarnpkg.com/react-cornerstone-viewport/-/react-cornerstone-viewport-0.1.30.tgz#3546a5164feef5f1f3b45ade10fd9a66f82fd101"
|
||||
integrity sha512-DGxdpS7FJvWi57NhpKhKMdaW3go/Fs/zfWJFtaEzGVHq2zQnEEV5G1pamC4ttRhKMfzy7T7y4h14xUVzE7IqPA==
|
||||
react-cornerstone-viewport@0.1.32:
|
||||
version "0.1.32"
|
||||
resolved "https://registry.yarnpkg.com/react-cornerstone-viewport/-/react-cornerstone-viewport-0.1.32.tgz#0777d1f3497b27b017aa5eae3d1d4c078383219b"
|
||||
integrity sha512-NdFsRq4kMgE6zpZo+DEnlQzC2B6OxOgFTkLCTDTYif0IxoXzKhngXP4xujwrUGjud98mTQKtiJOM0ah/fns3og==
|
||||
dependencies:
|
||||
lodash.debounce "^4.0.8"
|
||||
moment "^2.23.0"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-dicom-html",
|
||||
"version": "0.0.3",
|
||||
"version": "0.0.4",
|
||||
"description": "OHIF extension for rendering structured reports to HTML",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -24,6 +24,7 @@ const OHIFDicomHtmlSopClassHandler = {
|
||||
|
||||
return {
|
||||
plugin: 'html',
|
||||
modality: 'SR',
|
||||
displaySetInstanceUid: utils.guid(),
|
||||
wadoRoot: study.getData().wadoRoot,
|
||||
wadoUri: instance.getData().wadouri,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-dicom-microscopy",
|
||||
"version": "0.0.8",
|
||||
"version": "0.0.12",
|
||||
"description": "OHIF extension for Dicom Microscopy",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -19,9 +19,10 @@
|
||||
"lint": "eslint -c .eslintrc --fix src && prettier --single-quote --write src/**/*.{js,jsx,json,css}"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"ohif-core": "^0.6.0",
|
||||
"react": "^15.0.0 || ^16.0.0",
|
||||
"react-dom": "^15.0.0 || ^16.0.0",
|
||||
"ohif-core": "^0.6.0"
|
||||
"react-resize-detector": "^4.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.2.2",
|
||||
@@ -81,6 +82,7 @@
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.2.0",
|
||||
"classnames": "^2.2.6",
|
||||
"dicom-microscopy-viewer": "^0.4.3"
|
||||
"dicom-microscopy-viewer": "0.6.1",
|
||||
"lodash.debounce": "^4.0.8"
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import builtins from 'rollup-plugin-node-builtins';
|
||||
|
||||
const globals = {
|
||||
react: 'React',
|
||||
'react-resize-detector': 'ReactResizeDetector',
|
||||
'react-dom': 'ReactDOM',
|
||||
'ohif-core': 'OHIF'
|
||||
};
|
||||
@@ -52,6 +53,15 @@ export default {
|
||||
],
|
||||
'.yalc/dicom-microscopy-viewer/build/dicom-microscopy-viewer.js': [
|
||||
'api'
|
||||
],
|
||||
'node_modules/react-resize-detector/node_modules/prop-types/index.js': [
|
||||
'bool',
|
||||
'number',
|
||||
'string',
|
||||
'shape',
|
||||
'func',
|
||||
'any',
|
||||
'node'
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
@@ -16,6 +16,7 @@ const DicomMicroscopySopClassHandler = {
|
||||
|
||||
return {
|
||||
plugin: 'microscopy',
|
||||
modality: 'SM',
|
||||
displaySetInstanceUid: utils.guid(),
|
||||
dicomWebClient,
|
||||
sopInstanceUid: instance.getSOPInstanceUID(),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import ReactResizeDetector from 'react-resize-detector';
|
||||
import { api } from 'dicom-microscopy-viewer';
|
||||
import debounce from 'lodash.debounce';
|
||||
|
||||
const microscopyViewer = api.VLWholeSlideMicroscopyImageViewer;
|
||||
|
||||
@@ -9,10 +10,16 @@ class DicomMicroscopyViewport extends Component {
|
||||
error: null,
|
||||
};
|
||||
|
||||
viewer = null;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.container = React.createRef();
|
||||
|
||||
this.debouncedResize = debounce(() => {
|
||||
if (this.viewer) this.viewer.resize();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// install the microscopy renderer into the web page.
|
||||
@@ -52,17 +59,18 @@ class DicomMicroscopyViewport extends Component {
|
||||
.then(metadata => {
|
||||
metadata = metadata.filter(m => m);
|
||||
|
||||
const viewer = new microscopyViewer({
|
||||
this.viewer = new microscopyViewer({
|
||||
client: dicomWebClient,
|
||||
metadata,
|
||||
retrieveRendered: false
|
||||
});
|
||||
|
||||
viewer.render({ container, retrieveRendered: true });
|
||||
this.viewer.render({ container });
|
||||
});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { studies, displaySet } = this.props.viewportData;
|
||||
const { displaySet } = this.props.viewportData;
|
||||
|
||||
this.installOpenLayersRenderer(this.container.current, displaySet);
|
||||
}
|
||||
@@ -71,6 +79,13 @@ class DicomMicroscopyViewport extends Component {
|
||||
const style = { width: '100%', height: '100%' };
|
||||
return (
|
||||
<div className={'DicomMicroscopyViewer'} style={style}>
|
||||
{ReactResizeDetector && (
|
||||
<ReactResizeDetector
|
||||
handleWidth
|
||||
handleHeight
|
||||
onResize={this.onWindowResize}
|
||||
/>
|
||||
)}
|
||||
{this.state.error ? (
|
||||
<h2>{JSON.stringify(this.state.error)}</h2>
|
||||
) : (
|
||||
@@ -79,6 +94,10 @@ class DicomMicroscopyViewport extends Component {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onWindowResize = () => {
|
||||
this.debouncedResize();
|
||||
};
|
||||
}
|
||||
|
||||
export default DicomMicroscopyViewport;
|
||||
@@ -1617,18 +1617,18 @@ des.js@^1.0.0:
|
||||
inherits "^2.0.1"
|
||||
minimalistic-assert "^1.0.0"
|
||||
|
||||
dicom-microscopy-viewer@^0.4.3:
|
||||
version "0.4.3"
|
||||
resolved "https://registry.yarnpkg.com/dicom-microscopy-viewer/-/dicom-microscopy-viewer-0.4.3.tgz#defaa821026765bcd109b97258742df72a9b92bb"
|
||||
integrity sha512-PjT3qH0hv6dKOQzXFIHw4w/d8QCbiUOJOGZHvAkkRvnjeNRP7/ri8vbFvcSev+nYxKskNb1Tn0xd1+dsDpPGNw==
|
||||
dicom-microscopy-viewer@0.6.1:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/dicom-microscopy-viewer/-/dicom-microscopy-viewer-0.6.1.tgz#7b593da241d048154a9c1ebcbccf389742c2af18"
|
||||
integrity sha512-ZQ1QsQswAh7Yw01ba3LZXCIh7WnEYDjhSxJt9vdfRZ5iHlbgW2ghvUHR7GP80UnWJg335scNmIcqJvwxVjlIsg==
|
||||
dependencies:
|
||||
dicomweb-client "^0.4.2"
|
||||
dicomweb-client "^0.5.2"
|
||||
ol "^5.3.0"
|
||||
|
||||
dicomweb-client@^0.4.2:
|
||||
version "0.4.4"
|
||||
resolved "https://registry.yarnpkg.com/dicomweb-client/-/dicomweb-client-0.4.4.tgz#0ca0c7706556f3114330a3c40946ee66808148e4"
|
||||
integrity sha512-5S7wLYxQgHnOyEgR1zlZlt79IjzOk+YDB/Jjzte6ijRZsAoj9RUiIZcsTyxvBD9gLMYs/954n2kYHdbz83Xbtw==
|
||||
dicomweb-client@^0.5.2:
|
||||
version "0.5.2"
|
||||
resolved "https://registry.yarnpkg.com/dicomweb-client/-/dicomweb-client-0.5.2.tgz#aa5a4a6a5044b0702bb0a8a2662c86cd16901951"
|
||||
integrity sha512-e11n2+g7HfBuMyopWq76W11eSZJ707g4U1XiO8URA+23aoe0g1kGAtlPB29NKXl7zX7RznTJ7wfRBxFkW6EJDA==
|
||||
|
||||
diffie-hellman@^5.0.0:
|
||||
version "5.0.3"
|
||||
@@ -3199,6 +3199,11 @@ lodash.camelcase@^4.3.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
|
||||
|
||||
lodash.debounce@^4.0.8:
|
||||
version "4.0.8"
|
||||
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
|
||||
integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168=
|
||||
|
||||
lodash.memoize@^4.1.2:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
|
||||
@@ -3238,6 +3243,7 @@ longest-streak@^2.0.1:
|
||||
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
||||
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
|
||||
dependencies:
|
||||
js-tokens "^3.0.0 || ^4.0.0"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-dicom-pdf",
|
||||
"version": "0.0.7",
|
||||
"version": "0.0.8",
|
||||
"description": "OHIF extension for Dicom PDF",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -14,6 +14,7 @@ const OHIFDicomPDFSopClassHandler = {
|
||||
|
||||
return {
|
||||
plugin: 'pdf',
|
||||
modality: 'DOC',
|
||||
displaySetInstanceUid: utils.guid(),
|
||||
wadoRoot: study.getData().wadoRoot,
|
||||
wadoUri: instance.getData().wadouri,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/i18n",
|
||||
"version": "0.2.2",
|
||||
"version": "0.2.3",
|
||||
"description": "OHIF extension for internationalization",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
rm -rf src/locales/
|
||||
mkdir -p src/locales/
|
||||
cd src/locales/
|
||||
npx locize --config-path ../../.locize download --ver latest
|
||||
cd ../../
|
||||
node ./writeLocaleIndexFiles.js
|
||||
@@ -39,4 +39,4 @@
|
||||
"Stop": "$t(Common:Stop)",
|
||||
"Themes": "Themes",
|
||||
"Zoom": "Zoom"
|
||||
}
|
||||
}
|
||||
@@ -5,4 +5,4 @@
|
||||
"Skip to first image": "Skip to first $t(Common:Image)",
|
||||
"Skip to last image": "Skip, to last $t(Common:Image)",
|
||||
"fps": "fps"
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,15 @@
|
||||
{
|
||||
"Image": "Image",
|
||||
"Layout": "Layout",
|
||||
"Measurements": "Measurements",
|
||||
"More": "More",
|
||||
"Next": "Next",
|
||||
"Play": "Play",
|
||||
"Previous": "Previous",
|
||||
"Reset": "Reset",
|
||||
"Stop": "Stop"
|
||||
}
|
||||
"RowsPerPage": "rows per page",
|
||||
"Series": "Series",
|
||||
"Show": "Show",
|
||||
"Stop": "Stop",
|
||||
"StudyDate": "Study Date"
|
||||
}
|
||||
@@ -5,4 +5,4 @@
|
||||
"Options": "Options",
|
||||
"Preferences": "Preferences",
|
||||
"Study list": "Study list"
|
||||
}
|
||||
}
|
||||
@@ -6,4 +6,4 @@
|
||||
"NonTargets": "NonTargets",
|
||||
"Relabel": "Relabel",
|
||||
"Targets": "Targets"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"AccessionNumber": "Accession #",
|
||||
"Empty": "Empty",
|
||||
"MRN": "MRN",
|
||||
"Modality": "Modality",
|
||||
"PatientName": "Patient Name",
|
||||
"StudyDate": "Study Date",
|
||||
"StudyDescription": "Description",
|
||||
"StudyList": "Study List"
|
||||
}
|
||||
@@ -3,4 +3,4 @@
|
||||
"Reset to Defaults": "$t(Buttons:Reset to Defaults)",
|
||||
"Save": "$t(Buttons:Save)",
|
||||
"User Preferences": "User Preferences"
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,19 @@
|
||||
import Buttons from './Buttons.json';
|
||||
import CineDialog from './CineDialog.json';
|
||||
import Common from './Common.json';
|
||||
import Header from './Header.json';
|
||||
import MeasurementTable from './MeasurementTable.json';
|
||||
import UserPreferencesModal from './UserPreferencesModal.json';
|
||||
import Buttons from "./Buttons.json";
|
||||
import CineDialog from "./CineDialog.json";
|
||||
import Common from "./Common.json";
|
||||
import Header from "./Header.json";
|
||||
import MeasurementTable from "./MeasurementTable.json";
|
||||
import StudyList from "./StudyList.json";
|
||||
import UserPreferencesModal from "./UserPreferencesModal.json";
|
||||
|
||||
export default {
|
||||
export default {
|
||||
'en-US': {
|
||||
Buttons,
|
||||
CineDialog,
|
||||
Common,
|
||||
Header,
|
||||
MeasurementTable,
|
||||
StudyList,
|
||||
UserPreferencesModal,
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -1,27 +1,27 @@
|
||||
{
|
||||
"Acquired": "Acquired",
|
||||
"Acquired": "Adquirido",
|
||||
"Angle": "Ángulo",
|
||||
"Axial": "Axial",
|
||||
"Bidirectional": "Bidirectional",
|
||||
"Brush": "Escoba",
|
||||
"Bidirectional": "Bidireccional",
|
||||
"Brush": "Cepillo",
|
||||
"CINE": "CINE",
|
||||
"Cancel": "Cancelar",
|
||||
"Circle": "Circle",
|
||||
"Circle": "Círculo",
|
||||
"Clear": "Limpiar",
|
||||
"Coronal": "Coronal",
|
||||
"Crosshairs": "Cruces",
|
||||
"Crosshairs": "Punto de mira",
|
||||
"Ellipse": "Elipse",
|
||||
"Elliptical": "Elliptical",
|
||||
"Flip H": "Espejo Hor.",
|
||||
"Flip V": "Espejo Ver.",
|
||||
"Freehand": "Freehand",
|
||||
"Invert": "Invertido",
|
||||
"Elliptical": "Elíptico",
|
||||
"Flip H": "Espejo H",
|
||||
"Flip V": "Espejo V",
|
||||
"Freehand": "Mano alzada",
|
||||
"Invert": "Invertir",
|
||||
"Layout": "$t(Common:Layout)",
|
||||
"Length": "Medición",
|
||||
"Length": "Longitud",
|
||||
"Levels": "Niveles",
|
||||
"Magnify": "Lupa",
|
||||
"Magnify": "Aumentar",
|
||||
"Manual": "Manual",
|
||||
"Measurements": "Medidas",
|
||||
"Measurements": "Mediciones",
|
||||
"More": "$t(Common:More)",
|
||||
"Next": "$t(Common:Next)",
|
||||
"Pan": "Mover",
|
||||
@@ -31,12 +31,12 @@
|
||||
"ROI Window": "Ventana ROI",
|
||||
"Rectangle": "Rectángulo",
|
||||
"Reset": "$t(Common:Reset)",
|
||||
"Reset to Defaults": "$t(Common:reset) por defectos",
|
||||
"Reset to Defaults": "$t(Common:Reset) por defecto",
|
||||
"Rotate Right": "Rotar ->",
|
||||
"Sagittal": "Sagittal",
|
||||
"Sagittal": "Sagital",
|
||||
"Save": "Guardar",
|
||||
"Stack Scroll": "Avance X slice",
|
||||
"Stack Scroll": "Scroll en pila",
|
||||
"Stop": "$t(Common:Stop)",
|
||||
"Themes": "Temas",
|
||||
"Zoom": "Zoom"
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"Next image": "$t(Common:Play) $t(Common:Image)",
|
||||
"Play / Stop": "$t(Common:Play) / $t(Common:Stop)",
|
||||
"Previous image": "$t(Common:Previous) $t(Common:Image)",
|
||||
"Skip to first image": "Avanza para la primera $t(Common:Image)",
|
||||
"Skip to last image": "Pular para la ultima $t(Common:Image)",
|
||||
"fps": "fps"
|
||||
}
|
||||
"Next image": " $t(Common:Image) $t(Common:Next)",
|
||||
"Play / Stop": "$t(Common:Play) / Stop",
|
||||
"Previous image": "$t(Common:Image) $t(Common:Previous)",
|
||||
"Skip to first image": "Ir a la primera $t(Common:Image)",
|
||||
"Skip to last image": "Ir a la última $t(Common:Image)",
|
||||
"fps": "imágenes/seg."
|
||||
}
|
||||
@@ -1,10 +1,15 @@
|
||||
{
|
||||
"Image": "Imagen",
|
||||
"Layout": "Esquema",
|
||||
"Layout": "Disposición",
|
||||
"Measurements": "Medidas",
|
||||
"More": "Más",
|
||||
"Next": "Próximo",
|
||||
"Next": "Siguiente",
|
||||
"Play": "Play",
|
||||
"Previous": "Anterior",
|
||||
"Reset": "Reiniciar",
|
||||
"Stop": "Stop"
|
||||
}
|
||||
"Reset": "Resetear",
|
||||
"RowsPerPage": "filas por página",
|
||||
"Series": "Secuencia",
|
||||
"Show": "Mostrar",
|
||||
"Stop": "Detener",
|
||||
"StudyDate": "Fecha de estudo"
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"About": "Sobre",
|
||||
"Back to Viewer": "Back to Viewer",
|
||||
"INVESTIGATIONAL USE ONLY": "SOLO USO DE INVESTIGACIÓN",
|
||||
"About": "Acerca de",
|
||||
"Back to Viewer": "Volver al visor",
|
||||
"INVESTIGATIONAL USE ONLY": "SOLO USO PARA INVESTIGACIÓN",
|
||||
"Options": "Opciones",
|
||||
"Preferences": "Preferencias",
|
||||
"Study list": "Lista de estudio"
|
||||
}
|
||||
"Study list": "Lista de estudios"
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"Criteria nonconformities": "Criterios de no conformidades",
|
||||
"Criteria nonconformities": "Criterios disconformes",
|
||||
"Delete": "Borrar",
|
||||
"Description": "Descripción",
|
||||
"MAX": "máximo",
|
||||
"NonTargets": "NonObjetivos",
|
||||
"Relabel": "Reetiquetar",
|
||||
"MAX": "Máximo",
|
||||
"NonTargets": "No objetivos",
|
||||
"Relabel": "Re-etiquetar",
|
||||
"Targets": "Objetivos"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"AccessionNumber": "Número de Adhesión",
|
||||
"Empty": "vacío",
|
||||
"MRN": "MRN",
|
||||
"Modality": "Modalidad",
|
||||
"PatientName": "Nombre del paciente",
|
||||
"StudyDate": "Fetcha de estudio",
|
||||
"StudyDescription": "Descripción",
|
||||
"StudyList": "Lista de Estudio"
|
||||
}
|
||||
@@ -2,5 +2,5 @@
|
||||
"Cancel": "$t(Buttons:Cancel)",
|
||||
"Reset to Defaults": "$t(Buttons:Reset to Defaults)",
|
||||
"Save": "$t(Buttons:Save)",
|
||||
"User Preferences": "Preferencias de usuario"
|
||||
}
|
||||
"User Preferences": "Preferencias de Usuario"
|
||||
}
|
||||
@@ -1,17 +1,19 @@
|
||||
import Buttons from './Buttons.json';
|
||||
import CineDialog from './CineDialog.json';
|
||||
import Common from './Common.json';
|
||||
import Header from './Header.json';
|
||||
import MeasurementTable from './MeasurementTable.json';
|
||||
import UserPreferencesModal from './UserPreferencesModal.json';
|
||||
import Buttons from "./Buttons.json";
|
||||
import CineDialog from "./CineDialog.json";
|
||||
import Common from "./Common.json";
|
||||
import Header from "./Header.json";
|
||||
import MeasurementTable from "./MeasurementTable.json";
|
||||
import StudyList from "./StudyList.json";
|
||||
import UserPreferencesModal from "./UserPreferencesModal.json";
|
||||
|
||||
export default {
|
||||
es: {
|
||||
export default {
|
||||
'es': {
|
||||
Buttons,
|
||||
CineDialog,
|
||||
Common,
|
||||
Header,
|
||||
MeasurementTable,
|
||||
StudyList,
|
||||
UserPreferencesModal,
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -1,9 +1,13 @@
|
||||
import en_US from './en-US/';
|
||||
import es from './es/';
|
||||
import nl from './nl/';
|
||||
import pt_BR from './pt-BR/';
|
||||
import zh from './zh/';
|
||||
|
||||
export default {
|
||||
...en_US,
|
||||
...es,
|
||||
...nl,
|
||||
};
|
||||
...pt_BR,
|
||||
...zh,
|
||||
};
|
||||
@@ -3,4 +3,4 @@
|
||||
"More": "Meer",
|
||||
"Pan": "Pan",
|
||||
"Zoom": "Inzoomen"
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"More": "Meer"
|
||||
}
|
||||
}
|
||||
@@ -4,4 +4,4 @@
|
||||
"Options": "Opties",
|
||||
"Preferences": "Voorkeuren",
|
||||
"Study list": "Studie Overzicht"
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import Buttons from './Buttons.json';
|
||||
import Common from './Common.json';
|
||||
import Header from './Header.json';
|
||||
import Buttons from "./Buttons.json";
|
||||
import Common from "./Common.json";
|
||||
import Header from "./Header.json";
|
||||
|
||||
export default {
|
||||
nl: {
|
||||
export default {
|
||||
'nl': {
|
||||
Buttons,
|
||||
Common,
|
||||
Header,
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"Acquired": "Adquirido",
|
||||
"Angle": "Ângulo",
|
||||
"Axial": "Axial",
|
||||
"Bidirectional": "Bidirecional",
|
||||
"Brush": "Pincel",
|
||||
"CINE": "CINE",
|
||||
"Cancel": "Cancelar",
|
||||
"Circle": "Círculo",
|
||||
"Clear": "Limpar",
|
||||
"Coronal": "Coronal",
|
||||
"Crosshairs": "Localizador",
|
||||
"Ellipse": "Elipse",
|
||||
"Elliptical": "Elíptico",
|
||||
"Flip H": "Inverter H",
|
||||
"Flip V": "Inverter V",
|
||||
"Freehand": "Desenho livre",
|
||||
"Invert": "Inverter",
|
||||
"Layout": "Layout",
|
||||
"Length": "Tamanho",
|
||||
"Levels": "Níveis",
|
||||
"Magnify": "Ampliar",
|
||||
"Manual": "Manual",
|
||||
"Measurements": "Medidas",
|
||||
"More": "Mais",
|
||||
"Next": "Próximo",
|
||||
"Pan": "Arrastar",
|
||||
"Play": "Tocar",
|
||||
"Previous": "Anterior",
|
||||
"Probe": "Prova",
|
||||
"ROI Window": "Janela ROI",
|
||||
"Rectangle": "Retângulo",
|
||||
"Reset": "Resetar",
|
||||
"Reset to Defaults": "Restaurar para o Padrão",
|
||||
"Rotate Right": "Girar à direita",
|
||||
"Sagittal": "Sagital",
|
||||
"Save": "Salvar",
|
||||
"Stack Scroll": "Navegar Stacks",
|
||||
"Stop": "Parar",
|
||||
"Themes": "Temas",
|
||||
"Zoom": "Zoom"
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"Next image": "Próxima imagem",
|
||||
"Play / Stop": "Tocar / Parar",
|
||||
"Previous image": "Imagem Anterior",
|
||||
"Skip to first image": "Pular para a primeira imagem",
|
||||
"Skip to last image": "Pular para a última imagem",
|
||||
"fps": "fps"
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"Image": "Imagem",
|
||||
"Layout": "Layout",
|
||||
"More": "Mais",
|
||||
"Next": "Próximo",
|
||||
"Play": "Play",
|
||||
"Previous": "Anterior",
|
||||
"Reset": "Reset",
|
||||
"Stop": "Stop"
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"About": "Quem somos",
|
||||
"Back to Viewer": "Voltar para o Viewer",
|
||||
"INVESTIGATIONAL USE ONLY": "APENAS PARA USO INVESTIGATIVO",
|
||||
"Options": "Opções",
|
||||
"Preferences": "Preferências",
|
||||
"Study list": "Lista de estudos"
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"Cancel": "Cancelar",
|
||||
"Reset to Defaults": "Restaurar Default",
|
||||
"Save": "Salvar",
|
||||
"User Preferences": "Preferências do Usuário"
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import Buttons from "./Buttons.json";
|
||||
import CineDialog from "./CineDialog.json";
|
||||
import Common from "./Common.json";
|
||||
import Header from "./Header.json";
|
||||
import UserPreferencesModal from "./UserPreferencesModal.json";
|
||||
|
||||
export default {
|
||||
'pt-BR': {
|
||||
Buttons,
|
||||
CineDialog,
|
||||
Common,
|
||||
Header,
|
||||
UserPreferencesModal,
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Angle": "线角测量",
|
||||
"CINE": "播放",
|
||||
"Length": "长度测量",
|
||||
"Levels": "窗宽/窗位",
|
||||
"Measurements": "测量",
|
||||
"More": "更多",
|
||||
"Pan": "平移",
|
||||
"Play": "播放",
|
||||
"Reset": "重置",
|
||||
"Rotate Right": "顺时针",
|
||||
"Zoom": "缩放"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"Play": "播放"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"Study list": "检查"
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import Buttons from "./Buttons.json";
|
||||
import Common from "./Common.json";
|
||||
import Header from "./Header.json";
|
||||
|
||||
export default {
|
||||
'zh': {
|
||||
Buttons,
|
||||
Common,
|
||||
Header,
|
||||
}
|
||||
};
|
||||
@@ -3571,9 +3571,9 @@ lodash.memoize@^4.1.2:
|
||||
integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=
|
||||
|
||||
lodash.merge@^4.4.0:
|
||||
version "4.6.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.1.tgz#adc25d9cb99b9391c59624f379fbba60d7111d54"
|
||||
integrity sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ==
|
||||
version "4.6.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
|
||||
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
|
||||
|
||||
lodash.pick@^4.2.1:
|
||||
version "4.4.0"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-vtk",
|
||||
"version": "0.1.3",
|
||||
"version": "0.1.5",
|
||||
"description": "OHIF extension for VTK.js",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -39,8 +39,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.4.5",
|
||||
"react-vtkjs-viewport": "0.0.9",
|
||||
"vtk.js": "^8.11.0"
|
||||
"react-vtkjs-viewport": "0.0.11",
|
||||
"vtk.js": "^8.13.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.4.5",
|
||||
|
||||
@@ -189,6 +189,18 @@ class OHIFVTKViewport extends Component {
|
||||
volumeActor.setMapper(volumeMapper);
|
||||
volumeMapper.setInputData(data);
|
||||
|
||||
// TODO: Should look into implementing autoAdjustSampleDistance in vtk
|
||||
const sampleDistance =
|
||||
1.2 *
|
||||
Math.sqrt(
|
||||
data
|
||||
.getSpacing()
|
||||
.map((v) => v * v)
|
||||
.reduce((a, b) => a + b, 0)
|
||||
);
|
||||
|
||||
volumeMapper.setSampleDistance(sampleDistance);
|
||||
|
||||
volumeCache[displaySetInstanceUid] = volumeActor;
|
||||
|
||||
return volumeActor;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
vtkInteractorStyleMPRCrosshairs,
|
||||
vtkInteractorStyleMPRSlice,
|
||||
vtkInteractorStyleMPRWindowLevel,
|
||||
vtkInteractorStyleMPRSlice,
|
||||
vtkSVGCrosshairsWidget,
|
||||
vtkSVGWidgetManager,
|
||||
} from 'react-vtkjs-viewport';
|
||||
@@ -11,9 +11,13 @@ import setViewportToVTK from './utils/setViewportToVTK.js';
|
||||
import vtkCoordinate from 'vtk.js/Sources/Rendering/Core/Coordinate';
|
||||
import vtkMath from 'vtk.js/Sources/Common/Core/Math';
|
||||
import vtkMatrixBuilder from 'vtk.js/Sources/Common/Core/MatrixBuilder';
|
||||
import Constants from 'vtk.js/Sources/Rendering/Core/VolumeMapper/Constants.js';
|
||||
|
||||
const { BlendMode } = Constants;
|
||||
|
||||
// TODO: Put this somewhere else
|
||||
let apis = {};
|
||||
let currentSlabThickness = 0.1;
|
||||
|
||||
function getCrosshairCallbackForIndex(index) {
|
||||
return ({ worldPos }) => {
|
||||
@@ -96,26 +100,24 @@ function _setView(api, sliceNormal, viewUp) {
|
||||
istyle.setSliceNormal(...sliceNormal);
|
||||
camera.setViewUp(...viewUp);
|
||||
|
||||
api.volumes[0].getMapper().setSampleDistance(5.0);
|
||||
api.volumes[0].getMapper().setMaximumSamplesPerRay(2000);
|
||||
|
||||
renderWindow.render();
|
||||
}
|
||||
|
||||
function switchMPRInteractors(api, istyle) {
|
||||
const renderWindow = api.genericRenderWindow.getRenderWindow();
|
||||
const renderer = api.genericRenderWindow.getRenderer();
|
||||
const camera = renderer.getActiveCamera();
|
||||
const currentIStyle = renderWindow.getInteractor().getInteractorStyle();
|
||||
|
||||
let currentNormal;
|
||||
if (istyle.getSliceNormal) {
|
||||
currentNormal = istyle.getSliceNormal();
|
||||
if (currentIStyle.getSliceNormal && istyle.getSliceNormal) {
|
||||
currentNormal = currentIStyle.getSliceNormal();
|
||||
}
|
||||
|
||||
// TODO: This is a hacky workaround because disabling the vtkInteractorStyleMPRSlice is currently
|
||||
// broken. The camera.onModified is never removed. (https://github.com/Kitware/vtk-js/issues/1110)
|
||||
renderWindow
|
||||
.getInteractor()
|
||||
.getInteractorStyle()
|
||||
.setInteractor(null);
|
||||
let currentSlabThickness;
|
||||
if (currentIStyle.getSlabThickness && istyle.getSlabThickness) {
|
||||
currentSlabThickness = currentIStyle.getSlabThickness();
|
||||
}
|
||||
|
||||
renderWindow.getInteractor().setInteractorStyle(istyle);
|
||||
|
||||
@@ -126,6 +128,11 @@ function switchMPRInteractors(api, istyle) {
|
||||
if (currentNormal) {
|
||||
istyle.setSliceNormal(currentNormal);
|
||||
}
|
||||
|
||||
if (currentSlabThickness) {
|
||||
istyle.setSlabThickness(currentSlabThickness);
|
||||
}
|
||||
|
||||
istyle.setVolumeMapper(api.volumes[0]);
|
||||
}
|
||||
}
|
||||
@@ -152,14 +159,14 @@ const actions = {
|
||||
|
||||
_setView(api, [0, 1, 0], [0, 0, 1]);
|
||||
},
|
||||
enableRotateTool: async ({ viewports }) => {
|
||||
enableRotateTool: () => {
|
||||
apis.forEach(api => {
|
||||
const istyle = vtkInteractorStyleMPRSlice.newInstance();
|
||||
|
||||
switchMPRInteractors(api, istyle);
|
||||
});
|
||||
},
|
||||
enableCrosshairsTool: async ({ viewports }) => {
|
||||
enableCrosshairsTool: () => {
|
||||
apis.forEach((api, index) => {
|
||||
const istyle = vtkInteractorStyleMPRCrosshairs.newInstance();
|
||||
|
||||
@@ -168,13 +175,61 @@ const actions = {
|
||||
istyle.setCallback(getCrosshairCallbackForIndex(index));
|
||||
});
|
||||
},
|
||||
enableLevelTool: async ({ viewports }) => {
|
||||
enableLevelTool: () => {
|
||||
apis.forEach(api => {
|
||||
const istyle = vtkInteractorStyleMPRWindowLevel.newInstance();
|
||||
|
||||
switchMPRInteractors(api, istyle);
|
||||
});
|
||||
},
|
||||
setSlabThickness: slabThickness => {
|
||||
currentSlabThickness = slabThickness;
|
||||
|
||||
apis.forEach(api => {
|
||||
const renderWindow = api.genericRenderWindow.getRenderWindow();
|
||||
const istyle = renderWindow.getInteractor().getInteractorStyle();
|
||||
|
||||
if (istyle.setSlabThickness) {
|
||||
istyle.setSlabThickness(currentSlabThickness);
|
||||
|
||||
// TODO: Do this inside the interactors in a setSlabThickness function instead
|
||||
const renderer = api.genericRenderWindow.getRenderer();
|
||||
const camera = renderer.getActiveCamera();
|
||||
const dist = camera.getDistance();
|
||||
const near = dist - currentSlabThickness / 2;
|
||||
const far = dist + currentSlabThickness / 2;
|
||||
|
||||
camera.setClippingRange(near, far);
|
||||
}
|
||||
|
||||
renderWindow.render();
|
||||
});
|
||||
},
|
||||
changeSlabThickness: ({ change }) => {
|
||||
currentSlabThickness += change;
|
||||
currentSlabThickness = Math.max(currentSlabThickness, 0.1);
|
||||
|
||||
apis.forEach(api => {
|
||||
const renderWindow = api.genericRenderWindow.getRenderWindow();
|
||||
const istyle = renderWindow.getInteractor().getInteractorStyle();
|
||||
|
||||
if (istyle.setSlabThickness) {
|
||||
istyle.setSlabThickness(currentSlabThickness);
|
||||
}
|
||||
|
||||
renderWindow.render();
|
||||
});
|
||||
},
|
||||
setBlendMode: ({ blendMode }) => {
|
||||
apis.forEach(api => {
|
||||
const renderWindow = api.genericRenderWindow.getRenderWindow();
|
||||
const istyle = renderWindow.getInteractor().getInteractorStyle();
|
||||
|
||||
api.volumes[0].getMapper().setBlendMode(blendMode);
|
||||
|
||||
renderWindow.render();
|
||||
});
|
||||
},
|
||||
mpr2d: async ({ viewports }) => {
|
||||
const displaySet =
|
||||
viewports.viewportSpecificData[viewports.activeViewportIndex];
|
||||
@@ -199,20 +254,11 @@ const actions = {
|
||||
});
|
||||
});*/
|
||||
|
||||
apis[0].volumes[0].getMapper().setSampleDistance(1.5);
|
||||
|
||||
apiByViewport.forEach((api, index) => {
|
||||
const renderWindow = api.genericRenderWindow.getRenderWindow();
|
||||
const renderer = api.genericRenderWindow.getRenderer();
|
||||
const camera = renderer.getActiveCamera();
|
||||
|
||||
// TODO: This is a hacky workaround because disabling the vtkInteractorStyleMPRSlice is currently
|
||||
// broken. The camera.onModified is never removed. (https://github.com/Kitware/vtk-js/issues/1110)
|
||||
renderWindow
|
||||
.getInteractor()
|
||||
.getInteractorStyle()
|
||||
.setInteractor(null);
|
||||
|
||||
const istyle = vtkInteractorStyleMPRCrosshairs.newInstance();
|
||||
renderWindow.getInteractor().setInteractorStyle(istyle);
|
||||
|
||||
@@ -258,6 +304,8 @@ const actions = {
|
||||
},
|
||||
};
|
||||
|
||||
window.vtkActions = actions;
|
||||
|
||||
const definitions = {
|
||||
axial: {
|
||||
commandFn: actions.axial,
|
||||
@@ -276,19 +324,59 @@ const definitions = {
|
||||
},
|
||||
enableRotateTool: {
|
||||
commandFn: actions.enableRotateTool,
|
||||
storeContexts: ['viewports'],
|
||||
storeContexts: [],
|
||||
options: {},
|
||||
},
|
||||
enableCrosshairsTool: {
|
||||
commandFn: actions.enableCrosshairsTool,
|
||||
storeContexts: ['viewports'],
|
||||
storeContexts: [],
|
||||
options: {},
|
||||
},
|
||||
enableLevelTool: {
|
||||
commandFn: actions.enableLevelTool,
|
||||
storeContexts: ['viewports'],
|
||||
storeContexts: [],
|
||||
options: {},
|
||||
},
|
||||
setBlendModeToComposite: {
|
||||
commandFn: actions.setBlendMode,
|
||||
storeContexts: [],
|
||||
options: { blendMode: BlendMode.COMPOSITE_BLEND },
|
||||
},
|
||||
setBlendModeToMaximumIntensity: {
|
||||
commandFn: actions.setBlendMode,
|
||||
storeContexts: [],
|
||||
options: { blendMode: BlendMode.MAXIMUM_INTENSITY_BLEND },
|
||||
},
|
||||
setBlendModeToMinimumIntensity: {
|
||||
commandFn: actions.setBlendMode,
|
||||
storeContexts: [],
|
||||
options: { blendMode: BlendMode.MINIMUM_INTENSITY_BLEND },
|
||||
},
|
||||
setBlendModeToAverageIntensity: {
|
||||
commandFn: actions.setBlendMode,
|
||||
storeContexts: [],
|
||||
options: { blendMode: BlendMode.AVERAGE_INTENSITY_BLEND },
|
||||
},
|
||||
setSlabThickness: {
|
||||
// TODO: How do we pass in a function argument?
|
||||
commandFn: actions.setSlabThickness,
|
||||
storeContexts: [],
|
||||
options: {},
|
||||
},
|
||||
increaseSlabThickness: {
|
||||
commandFn: actions.changeSlabThickness,
|
||||
storeContexts: [],
|
||||
options: {
|
||||
change: 3,
|
||||
},
|
||||
},
|
||||
decreaseSlabThickness: {
|
||||
commandFn: actions.changeSlabThickness,
|
||||
storeContexts: [],
|
||||
options: {
|
||||
change: -3,
|
||||
},
|
||||
},
|
||||
mpr2d: {
|
||||
commandFn: actions.mpr2d,
|
||||
storeContexts: ['viewports'],
|
||||
|
||||
@@ -31,6 +31,43 @@ const definitions = [
|
||||
commandName: 'enableRotateTool',
|
||||
commandOptions: {},
|
||||
},
|
||||
{
|
||||
id: 'setBlendModeToComposite',
|
||||
label: 'Disable MIP',
|
||||
icon: 'times',
|
||||
//
|
||||
type: TOOLBAR_BUTTON_TYPES.COMMAND,
|
||||
commandName: 'setBlendModeToComposite',
|
||||
commandOptions: {},
|
||||
},
|
||||
{
|
||||
id: 'setBlendModeToMaximumIntensity',
|
||||
label: 'Enable MIP',
|
||||
icon: 'soft-tissue',
|
||||
//
|
||||
type: TOOLBAR_BUTTON_TYPES.COMMAND,
|
||||
commandName: 'setBlendModeToMaximumIntensity',
|
||||
commandOptions: {},
|
||||
},
|
||||
|
||||
{
|
||||
id: 'increaseSlabThickness',
|
||||
label: 'Increase Slab Thickness',
|
||||
icon: 'caret-up',
|
||||
//
|
||||
type: TOOLBAR_BUTTON_TYPES.COMMAND,
|
||||
commandName: 'increaseSlabThickness',
|
||||
commandOptions: {},
|
||||
},
|
||||
{
|
||||
id: 'decreaseSlabThickness',
|
||||
label: 'Decrease Slab Thickness',
|
||||
icon: 'caret-down',
|
||||
//
|
||||
type: TOOLBAR_BUTTON_TYPES.COMMAND,
|
||||
commandName: 'decreaseSlabThickness',
|
||||
commandOptions: {},
|
||||
},
|
||||
];
|
||||
|
||||
export default {
|
||||
|
||||
@@ -4051,9 +4051,9 @@ lodash.isequal@4.5.0:
|
||||
integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA=
|
||||
|
||||
lodash.merge@^4.6.1:
|
||||
version "4.6.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.1.tgz#adc25d9cb99b9391c59624f379fbba60d7111d54"
|
||||
integrity sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ==
|
||||
version "4.6.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
|
||||
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
|
||||
|
||||
lodash@^4.1.1, lodash@^4.17.11:
|
||||
version "4.17.11"
|
||||
@@ -5519,10 +5519,10 @@ react-viewerbase@^0.15.1:
|
||||
react-i18next "^10.11.0"
|
||||
react-with-direction "1.3.0"
|
||||
|
||||
react-vtkjs-viewport@0.0.9:
|
||||
version "0.0.9"
|
||||
resolved "https://registry.yarnpkg.com/react-vtkjs-viewport/-/react-vtkjs-viewport-0.0.9.tgz#da79aa66f52dcd731bcc5abd2587cbb120a25331"
|
||||
integrity sha512-VwRCY2DPxWMhix4v3PAtdENp3U1p+Z9nKf1Ch5+KWRKF6fWeA+B2glYKMYNpPnP4bULzVz9iwV+Zeojt3gEibA==
|
||||
react-vtkjs-viewport@0.0.11:
|
||||
version "0.0.11"
|
||||
resolved "https://registry.yarnpkg.com/react-vtkjs-viewport/-/react-vtkjs-viewport-0.0.11.tgz#cedd993ca226502bf0417a3748a5b2f806522519"
|
||||
integrity sha512-ttWw7bU/lL/nJuaaKdtAc8448vMJmlRQzpkpuP/J+3lTzIQbQPCPU40LqgONjr7LvL5++61N2GJ8m+PRgHyfxQ==
|
||||
dependencies:
|
||||
moment "^2.24.0"
|
||||
|
||||
@@ -6974,10 +6974,10 @@ void-elements@^2.0.1:
|
||||
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec"
|
||||
integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=
|
||||
|
||||
vtk.js@^8.11.0:
|
||||
version "8.11.1"
|
||||
resolved "https://registry.yarnpkg.com/vtk.js/-/vtk.js-8.11.1.tgz#cdf69705a72a2a3ce76d7442d1b3903e3de770d4"
|
||||
integrity sha512-Y5ZCFtoMOuuJt44fyj91Gqeg9ZeCQovyoJlEHG1oUHLPehQTvq0oW09WqBcxVSdiv92x3aXYWtgZjm/s4EWw7Q==
|
||||
vtk.js@^8.13.0:
|
||||
version "8.13.0"
|
||||
resolved "https://registry.yarnpkg.com/vtk.js/-/vtk.js-8.13.0.tgz#d22bbe3403d86f979703506f9be83ab8e5099248"
|
||||
integrity sha512-uoq2U0Yu2XjuKeeduuGRXlphHR6cdIvtZ5useNMJ8FobK9dpeFTT4ncoyPXx77KvwOMSueT8W+ngm/e9+FGZtA==
|
||||
dependencies:
|
||||
base64-js "1.2.1"
|
||||
blueimp-md5 "2.10.0"
|
||||
|
||||
+18
-17
@@ -28,6 +28,7 @@
|
||||
"test:unit:ci": "jest --ci --runInBand --collectCoverage --reporters=default --reporters=jest-junit && codecov",
|
||||
"test:e2e": "",
|
||||
"test:e2e:ci": "start-server-and-test start http://localhost:5000 cy:run:ci",
|
||||
"test:e2e:scriptTag": "cross-env START_SERVER=true start-server-and-test build:package http-get://localhost:5000/index.umd.js cy:run",
|
||||
"staticDeploy": "./generateStaticSite.sh",
|
||||
"docs:publish": "./publishStaticSite.sh",
|
||||
"start": "yarn run dev",
|
||||
@@ -59,6 +60,7 @@
|
||||
"not op_mini all"
|
||||
],
|
||||
"resolutions": {
|
||||
"@babel/core": "^7.5.4",
|
||||
"browserslist": "4.6.2",
|
||||
"caniuse-lite": "1.0.30000974"
|
||||
},
|
||||
@@ -70,42 +72,41 @@
|
||||
"path": "./node_modules/cz-conventional-changelog"
|
||||
}
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.4.5",
|
||||
"@ohif/extension-cornerstone": "0.0.38",
|
||||
"@ohif/extension-dicom-html": "0.0.3",
|
||||
"@ohif/extension-dicom-microscopy": "0.0.8",
|
||||
"@ohif/extension-dicom-pdf": "0.0.7",
|
||||
"@ohif/extension-vtk": "0.1.3",
|
||||
"@ohif/i18n": "0.2.2",
|
||||
"@ohif/extension-cornerstone": "0.0.43",
|
||||
"@ohif/extension-dicom-html": "0.0.4",
|
||||
"@ohif/extension-dicom-microscopy": "0.0.12",
|
||||
"@ohif/extension-dicom-pdf": "0.0.8",
|
||||
"@ohif/extension-vtk": "0.1.5",
|
||||
"@ohif/i18n": "0.2.3",
|
||||
"@tanem/react-nprogress": "^1.1.25",
|
||||
"classnames": "^2.2.6",
|
||||
"cornerstone-core": "^2.2.8",
|
||||
"cornerstone-math": "^0.1.8",
|
||||
"cornerstone-tools": "^3.15.1",
|
||||
"cornerstone-tools": "^3.16.0",
|
||||
"cornerstone-wado-image-loader": "^2.2.3",
|
||||
"dcmjs": "^0.4.7",
|
||||
"dicom-parser": "^1.8.3",
|
||||
"dicomweb-client": "^0.4.4",
|
||||
"dicomweb-client": "^0.5.2",
|
||||
"hammerjs": "^2.0.8",
|
||||
"i18next": "^17.0.3",
|
||||
"i18next-browser-languagedetector": "^3.0.1",
|
||||
"lodash.isequal": "4.5.0",
|
||||
"moment": "^2.24.0",
|
||||
"ohif-core": "0.9.1",
|
||||
"oidc-client": "1.7.x",
|
||||
"ohif-core": "0.10.6",
|
||||
"oidc-client": "1.8.x",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-dropzone": "^10.1.5",
|
||||
"react-i18next": "^10.11.0",
|
||||
"react-redux": "^7.1.0",
|
||||
"react-resize-detector": "^4.2.0",
|
||||
"react-router": "^5.0.1",
|
||||
"react-router-dom": "^5.0.1",
|
||||
"react-transition-group": "^4.1.1",
|
||||
"react-viewerbase": "0.15.3",
|
||||
"react-viewerbase": "0.17.6",
|
||||
"redux": "^4.0.1",
|
||||
"redux-logger": "^3.0.6",
|
||||
"redux-oidc": "3.1.x",
|
||||
@@ -143,8 +144,6 @@
|
||||
"lodash": "4.17.11",
|
||||
"lodash.clonedeep": "4.5.0",
|
||||
"prettier": "1.18.x",
|
||||
"react": "^16.7.0",
|
||||
"react-dom": "^16.7.0",
|
||||
"react-scripts": "^3.0.1",
|
||||
"rollup": "^1.15.5",
|
||||
"rollup-plugin-babel": "^4.3.2",
|
||||
@@ -154,6 +153,8 @@
|
||||
"rollup-plugin-node-resolve": "^5.0.2",
|
||||
"rollup-plugin-peer-deps-external": "^2.2.0",
|
||||
"rollup-plugin-postcss": "^2.0.3",
|
||||
"rollup-plugin-replace": "^2.2.0",
|
||||
"rollup-plugin-serve": "^1.0.1",
|
||||
"rollup-plugin-url": "^2.2.2",
|
||||
"semantic-release": "15.13.x",
|
||||
"start-server-and-test": "^1.9.1",
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="228" height="80"><defs><style>@font-face{font-family:"Roboto-Bold";src:url(data:application/font-woff;charset=utf-8;base64,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) format("woff"); font-weight:bold;font-style:normal;}</style></defs><path d="M6 0h216a6 6 0 0 1 6 6v68a6 6 0 0 1-6 6H6a6 6 0 0 1-6-6V6a6 6 0 0 1 6-6z" fill="#52ABD3"/><text font-family="Roboto-Bold, Roboto" font-size="14" font-weight="bold" fill="#fff"><tspan x="72.148" y="45">FILE UPLOAD</tspan></text><path d="M58.285 79.648H40.642L36.616 58.63l-24.92 10.66 11.258 5.986-2.325 4.373H6a6 6 0 0 1-6-6V55.073L22.088 13.53a1 1 0 0 1 1.352-.413l36.2 19.248L68.3 60.813 58.285 79.648zm-27.108 0h-.923l.203-.382.72.382zM54.232 34.02l-9.86 18.542 18.542 9.86-8.683-28.4z" fill="#4591B3"/></svg>
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="228" height="80"><defs><style>@font-face{font-family:"Roboto-Bold";src:url(data:application/font-woff;charset=utf-8;base64,d09GRgABAAAAAAngAA4AAAAADmgAAiMSAAAAAAAAAAAAAAAAAAAAAAAAAABPUy8yAAABRAAAAE0AAABgdXsSVWNtYXAAAAGUAAAAdAAAAYIIaRq4Y3Z0IAAAAggAAABIAAAASCt+BLVmcGdtAAACUAAAAToAAAG8X/Iaq2dhc3AAAAOMAAAADAAAAAwACAATZ2x5ZgAAA5gAAAMqAAAE8AOZz5doZWFkAAAGxAAAADYAAAA2/oPRxGhoZWEAAAb8AAAAHwAAACQM0QOkaG10eAAABxwAAAAsAAAALDJkBEFsb2NhAAAHSAAAABgAAAAYBioHqG1heHAAAAdgAAAAHgAAACACHAHcbmFtZQAAB4AAAAF0AAACi5COTQJwb3N0AAAI9AAAABQAAAAg/20Ab3ByZXAAAAkIAAAA2AAAASkqdjYweJxjYGH5zbSHgZWBgXUWqzEDA6M8hGa+yJDGxMDAAMIQ0MDAoC7AgADu/v7uDApAGMrG8I+BIY19FhOQwzgfJMdixboBLMcMADN+C04AAAB4nGNgYGBmgGAZBkYGEKgB8hjBfBaGBCAtwiAAFGFhUGBwZHBlcGPwYfBnCGAIYgj9/x8oCxJ1QRX9//z/yf8H/u/+v/3/5v87/2/5vxlqMgZgZMMujgzYCSngYmBgYYVYAPQGEycD0FAOwuYOFgAANFkaMQAqAOkApAD+AE4AYAExAKwAxQDUAHwALQAAABT+YAAUApsAIAMhAAsEOgAUBI0AEAWwABQGGAAVAaYAEQbAAA4G3wACAAAAAHicXZDLSgMxGIUTZ6ytF3ApBCFpqEJJcO9qFpmCdDM6XSQuvGALtitfQJiNMGThs5zZzez6OH0J0XSqRd3k5JyffJw/IGqMXmYrSt9dTT/fYE6rXnR/p0EV5+ncgD5o7CjQodCIFB8hGoxurHTcc3819XzEnx+niAethsHMuwsOktt5OCdWIHFse505d6kRrzFxi/EuABbfgEULCO8/NHbVmCM6y+y1RWEYEuOYEDzFMrNYGiac0+hsOwZ9nZ9s2u4pdIYa3Q0ht0gYiPN+46RA4T3zYYMfv/zra0r+B8nvIPxAWtMiayeFFGwdSCFFaOiMRk+Nc5uGiiJU3FfopxoHCjLIoarOacl9bpuExOSp7pJyYhvSj1YvjkEGOC/rY7LN1lseKSRlzcmtrSQxrCEyWhmnvwBTLIi1AAAAAQACAAgAAv//AA94nIWUa0gUURTHz7lzZx/uKrO2sxlL5vhIQnHV7Cm9iNCCKApKIftSUZBQ0UONCtKI+hDRhzB6EMRGEKh3BkOLHgpBlBCLmAaRfTD7kK+sKIyda3dmM3cNbIaBPcve8/+f3/nPAkI9AF0qM3DDdmCwoYLRUFCnaasr7YKIgnhXVxpuQn2pK5g7ZNx2Y1VrtfuUm1QZG8VnHZAl5VVob4PfKxlVdMd0WViEmk/cqnikUbOD90itZi1ZS7abTTL7xi8AgR2Tw3KB/BwUSIdygy7IsGSowvyRmB2vcOCdsmN7E3aYV9H9ibLBBFk/oVmZ2SR3SUlqdgZNVXNKFmZlOpyqP7C4eOkyueAaH2x7wD/exBsYxGoM3uCR8LXGMIavN95B0tbHH7NmXN9d3odlLS38YS8Ojg4NlH34MTIAEGOWJ5jJsGgWZvFgCotybArYTE5Hd2L++Lg4Li5i93onCg+oUBrrhqIBTnUDUYA1Miq6N25kUPTk+P7LfFoxTZ1vTe6UfNaUS8TM9djRgaH793jkWeMV9oi06yCzJt7Ztb6LP29G+iRa8ql3c/+g5SU21zbbS0XMiUOIO2bCNzzUYS3JE2pd6dnkIf8kgMSVDkV3zQyEGhcJEo4WSq/N3SIUNfzkuJWKu3+5fBFeUmAu7DHktHmWpKywQGQW5HaRLIpky6dCiXVICcUbNDxAYxHTAwnGsqykZlDV73CiqqEgKADmIbk1xnvIfCSYdrYBS/lTDl+PnDpeN05Uc0hmn7qvvig1m1Ik19F9+08Ihscmh2lE+FZglSGEhBRM73HKr0sUrplcE/eJPj9omZArXFmhneOjEf6Gm5PiHcL8i3UmlVl03Rj/jCnD0qvomssNmCS9/MPtvb1DP2wxPGrAXpXCkv9wk4SaNCVtJ81C5ZXQ+p03ARWTFH1OoieBqDgdVT/VcjQbUK6ItOCzPDxxnr/jJp9Ab83pM4f4CEFzUma17y/1/vx2+MDBc0R4cwLIu4Q3JyRB2f/WaGfP/uuJrdGduMYk+1srUJKG9i1J7ebXvbyTpGOAaLzT7MfqNsznPTL7tZUsIBrAbz0Oh+oAAAABAAAAAiMS6Qx7uF8PPPUAGQgAAAAAAMTwES4AAAAA1QFS1gAH/+wFXAXEAAEACQACAAAAAAAAeJxjYGRgYM/5x8PAwNrGwM7AxhrDABRBAdwAQ2YChAADjABkBGIAggWGAFYEVQCCBTMAggSAAIIFGwCCAf4AAAVEAHQFKQCCBWIABwAAAAAAQACaAMABBgFMAagBqAHoAjYCeHicY2BkYGDgZpBnYGIAAUYwyccA5DLuATEBCQoA/AAAeJyFkstKw0AUhv+xFyxKEQS3zsqFkEkvlGK7amsphVIhlW4EoZehCcROTUJLn8Q38o3c+ycZiroxw8x85z9zbhAAV/iEQP653DkLlGnlfIYiWpYLuMCD5SL50XIJ13iyXKa+tFzFPd4YJYoVWq/4sCxwiS/LZzgXwnIBN6JiuUi+tVzCnWhZLlN/sVzFs3gfmN0xCjZ+Ihu1el2OjNmEWo63KyV7YSi91BVLT8c62uu18szSJKZvwnVO8g/OdRQHZisbqt5sd9Oc7dzrpF4/SXYd1z0cDmqxW6x8rUy0ccNgpbexjt3JeDCczoZOQ9UwgMEOR0QIsIGPBBIN1FDnkhjRa6iH0LTG2GIFRepRCXl7p6g4szRvzVx7nmu+9Bi95E64+9wh1Z+a/EedZ9liVjGsnXam2FcTbXRPfbZ/xTqn2LSrhLN1+JO4OGRLYUFlwSl8ZlZ8GbF/l+8Dapo14mwGFxNOO8AQU8x4Olnl2jdOS28leJxjYGYAg/9ZDCkMmIAbACoqAdx4nD3NTw7BQByG4Rn/qVDSyJDQhkhEm5RNF900EVYW7EnYWhGUlXADcQM30MzGKcRRHMCC34fYPHk7nS8TpM2AmUxyJWSyK2839EKGJcxrqOtaRh4V/p9FTtuxW0NFN9O+U0bF6rrIKah4x2lWPouEa1dFFpV0rF+lRn23WUQpz9t52aNiMq3E6XWjt5uJrmxl+EoGKjFU+VoewELlvvQ0OpuAPbA1+jEAc3AEF/ACuvZd+Fj4WPgyK2g2F/hEPYAu6PIEHMEdvICHe3YJC2AbhEe8AeAyUcg=) format("woff"); font-weight:bold;font-style:normal;}</style></defs><path d="M6 0h216a6 6 0 0 1 6 6v68a6 6 0 0 1-6 6H6a6 6 0 0 1-6-6V6a6 6 0 0 1 6-6z" fill="#52ABD3"/><text font-family="Roboto-Bold, Roboto" font-size="14" font-weight="bold" fill="#fff"><tspan x="60" y="45">FOLDER UPLOAD</tspan></text><path d="M51.706 79.74H39.752L38.32 56.483l-25.104 6.482 10.012 7.274-6.904 9.502H6a6 6 0 0 1-6-6V13.585c2.513-1.005 5.48-.74 7.84.975l19.902 14.46L29.6 40.413l26.536 19.28c3.65 2.65 4.485 7.778 1.858 11.393l-6.288 8.655zm-15.4 0H26.52l3.38-4.653 6.404 4.653z" fill="#4591B3"/></svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
@@ -3,6 +3,7 @@ window.config = {
|
||||
routerBasename: '/',
|
||||
// default: ''
|
||||
relativeWebWorkerScriptsPath: '',
|
||||
showStudyList: true,
|
||||
servers: {
|
||||
dicomWeb: [
|
||||
{
|
||||
@@ -53,8 +54,8 @@ window.config = {
|
||||
{ commandName: 'fitViewportToWindow', label: 'Zoom to Fit', keys: ['='] },
|
||||
{ commandName: 'resetViewport', label: 'Reset', keys: ['space'] },
|
||||
// clearAnnotations
|
||||
// nextImage
|
||||
// previousImage
|
||||
{ commandName: 'nextImage', label: 'Next Image', keys: ['down'] },
|
||||
{ commandName: 'previousImage', label: 'Previous Image', keys: ['up'] },
|
||||
// firstImage
|
||||
// lastImage
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
window.config = {
|
||||
routerBasename: '/',
|
||||
showStudyList: true,
|
||||
servers: {
|
||||
dicomWeb: [
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
window.config = {
|
||||
routerBasename: '/',
|
||||
showStudyList: true,
|
||||
relativeWebWorkerScriptsPath: '',
|
||||
servers: {
|
||||
dicomWeb: [
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
window.config = {
|
||||
routerBasename: '/',
|
||||
showStudyList: true,
|
||||
relativeWebWorkerScriptsPath: '',
|
||||
servers: {
|
||||
// This is an array, but we'll only use the first entry for now
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
window.config = {
|
||||
routerBasename: '/',
|
||||
showStudyList: true,
|
||||
relativeWebWorkerScriptsPath: '',
|
||||
servers: {
|
||||
// This is an array, but we'll only use the first entry for now
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
window.config = {
|
||||
routerBasename: '/',
|
||||
relativeWebWorkerScriptsPath: '',
|
||||
enableGoogleCloudAdapter: true,
|
||||
servers: {
|
||||
// This is an array, but we'll only use the first entry for now
|
||||
dicomWeb: [],
|
||||
},
|
||||
// This is an array, but we'll only use the first entry for now
|
||||
oidc: [
|
||||
{
|
||||
// ~ REQUIRED
|
||||
// Authorization Server URL
|
||||
authority: 'https://accounts.google.com',
|
||||
client_id: 'YOURCLIENTID.apps.googleusercontent.com',
|
||||
redirect_uri: '/callback', // `OHIFStandaloneViewer.js`
|
||||
response_type: 'id_token token',
|
||||
scope: 'email profile openid https://www.googleapis.com/auth/cloudplatformprojects.readonly https://www.googleapis.com/auth/cloud-healthcare', // email profile openid
|
||||
// ~ OPTIONAL
|
||||
post_logout_redirect_uri: '/logout-redirect.html',
|
||||
revoke_uri: 'https://accounts.google.com/o/oauth2/revoke?token=',
|
||||
automaticSilentRenew: true,
|
||||
revokeAccessTokenOnSignout: true,
|
||||
},
|
||||
],
|
||||
studyListFunctionsEnabled: true,
|
||||
}
|
||||
@@ -2,6 +2,7 @@ window.config = {
|
||||
// default: '/'
|
||||
routerBasename: '/',
|
||||
// default: ''
|
||||
showStudyList: true,
|
||||
relativeWebWorkerScriptsPath: '',
|
||||
servers: {
|
||||
dicomWeb: [
|
||||
@@ -20,4 +21,5 @@ window.config = {
|
||||
},
|
||||
],
|
||||
},
|
||||
studyListFunctionsEnabled: true,
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
window.config = {
|
||||
routerBasename: '/demo',
|
||||
showStudyList: true,
|
||||
servers: {
|
||||
dicomWeb: [
|
||||
{
|
||||
@@ -48,8 +49,8 @@ window.config = {
|
||||
{ commandName: 'fitViewportToWindow', label: 'Zoom to Fit', keys: ['='] },
|
||||
{ commandName: 'resetViewport', label: 'Reset', keys: ['space'] },
|
||||
// clearAnnotations
|
||||
// nextImage
|
||||
// previousImage
|
||||
{ commandName: 'nextImage', label: 'Next Image', keys: ['down'] },
|
||||
{ commandName: 'previousImage', label: 'Previous Image', keys: ['up'] },
|
||||
// firstImage
|
||||
// lastImage
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
window.config = {
|
||||
routerBasename: '/',
|
||||
showStudyList: true,
|
||||
servers: {
|
||||
dicomWeb: [
|
||||
{
|
||||
|
||||
@@ -16,14 +16,6 @@
|
||||
|
||||
<title>OHIF Viewer</title>
|
||||
|
||||
<!-- Latest compiled and minified CSS -->
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
|
||||
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
|
||||
<!-- WEB FONTS -->
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700|Sanchez&display=swap"
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>OpenID Connect Logout Redirect Page</title>
|
||||
</head>
|
||||
<body>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.5.7/es5-shim.min.js"></script>
|
||||
<script type="text/javascript" src='es6-shim.min.js'></script>
|
||||
<script type="text/javascript" src='oidc-client.min.js'></script>
|
||||
<script type="text/javascript" src='polyfill.min.js'></script>
|
||||
<script type="text/javascript">
|
||||
// TODO: I think this is actually not necessary anymore
|
||||
new UserManager().signoutRedirectCallback().then(function() {
|
||||
// TODO: Keycloak + Auth0 is not properly logging out both sessions?
|
||||
// This is a hacky hardcoded workaround. This could also be added as a component to the React application rather than a static page.
|
||||
var path = 'https://www.crowds-cure.org';
|
||||
var client_id = 'z5cXMPTxeFOdB3i4xRA8JyhTonQmqMKM';
|
||||
var url = 'https://auth.crowds-cure.org/v2/logout?client_id=' + client_id + '&returnTo=' + path;
|
||||
window.location.href = url;
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
+46
-8
@@ -9,11 +9,10 @@ import svgr from '@svgr/rollup'
|
||||
import pkg from './package.json'
|
||||
// Deal with https://github.com/rollup/rollup-plugin-commonjs/issues/297
|
||||
import builtins from 'rollup-plugin-node-builtins'
|
||||
import replace from 'rollup-plugin-replace';
|
||||
import serve from 'rollup-plugin-serve'
|
||||
|
||||
const globals = {
|
||||
react: 'React',
|
||||
'react-dom': 'ReactDOM',
|
||||
}
|
||||
const startServer = process.env.START_SERVER === 'true';
|
||||
|
||||
export default {
|
||||
input: 'src/index_publish.js',
|
||||
@@ -25,25 +24,26 @@ export default {
|
||||
exports: 'named',
|
||||
name: 'OHIFStandaloneViewer',
|
||||
esModule: false,
|
||||
globals,
|
||||
},
|
||||
{
|
||||
file: pkg.module,
|
||||
format: 'es',
|
||||
exports: 'named',
|
||||
sourcemap: true,
|
||||
globals,
|
||||
},
|
||||
],
|
||||
plugins: [
|
||||
external(),
|
||||
replace({
|
||||
'process.env.NODE_ENV': JSON.stringify('production')
|
||||
}),
|
||||
postcss({
|
||||
modules: false,
|
||||
}),
|
||||
url(),
|
||||
svgr(),
|
||||
json(),
|
||||
resolve({preferBuiltins: true}),
|
||||
resolve({ preferBuiltins: true }),
|
||||
babel({
|
||||
exclude: 'node_modules/**',
|
||||
runtimeHelpers: true,
|
||||
@@ -51,6 +51,32 @@ export default {
|
||||
commonjs({
|
||||
include: 'node_modules/**',
|
||||
namedExports: {
|
||||
'node_modules/react/index.js': [
|
||||
'createContext',
|
||||
'createElement',
|
||||
'createRef',
|
||||
'cloneElement',
|
||||
'Children',
|
||||
'Fragment',
|
||||
'forwardRef',
|
||||
'useCallback',
|
||||
'isValidElement',
|
||||
'memo',
|
||||
'useContext',
|
||||
'useState',
|
||||
'useEffect',
|
||||
'useLayoutEffect',
|
||||
'Component',
|
||||
'PureComponent',
|
||||
'useRef',
|
||||
'useReducer',
|
||||
'useMemo',
|
||||
],
|
||||
'node_modules/react-dom/index.js': [
|
||||
'unstable_batchedUpdates',
|
||||
'findDOMNode',
|
||||
'render',
|
||||
],
|
||||
'node_modules/react-is/index.js': [
|
||||
'isValidElementType',
|
||||
'isContextConsumer',
|
||||
@@ -58,18 +84,30 @@ export default {
|
||||
'node_modules/redux-oidc/dist/redux-oidc.js': [
|
||||
'reducer',
|
||||
'CallbackComponent',
|
||||
'SignoutCallbackComponent',
|
||||
'loadUser',
|
||||
'OidcProvider',
|
||||
'createUserManager',
|
||||
],
|
||||
'node_modules/oidc-client/lib/oidc-client.min.js': [
|
||||
'WebStorageStateStore',
|
||||
'InMemoryWebStorage',
|
||||
],
|
||||
'node_modules/cornerstoneTools/dist/cornerstoneTools.min.js': [
|
||||
'cornerstoneTools',
|
||||
],
|
||||
'node_modules/dcmjs/build/dcmjs.js': ['data', 'adapters'],
|
||||
'node_modules/prop-types/index.js': ['bool', 'number', 'string', 'shape', 'func', 'any', 'node']
|
||||
'node_modules/prop-types/index.js': ['oneOfType', 'element', 'bool', 'number', 'string', 'shape', 'func', 'any', 'node']
|
||||
},
|
||||
}),
|
||||
builtins(),
|
||||
startServer && serve({
|
||||
open: true,
|
||||
// Multiple folders to serve from
|
||||
contentBase: ['.', 'dist', 'cypress/support/script-tag', 'public'],
|
||||
host: 'localhost',
|
||||
port: 5000,
|
||||
})
|
||||
],
|
||||
external: Object.keys(pkg.peerDependencies || {}),
|
||||
}
|
||||
+44
-9
@@ -37,10 +37,12 @@ import { getActiveContexts } from './store/layout/selectors.js';
|
||||
import i18n from '@ohif/i18n';
|
||||
import setupTools from './setupTools.js';
|
||||
import store from './store';
|
||||
import UserManagerContext from './UserManagerContext';
|
||||
|
||||
// ~~~~ APP SETUP
|
||||
initCornerstoneTools({
|
||||
globalToolSyncEnabled: true,
|
||||
showSVGCursors: true,
|
||||
});
|
||||
|
||||
const commandsManagerConfig = {
|
||||
@@ -70,7 +72,7 @@ extensionManager.registerExtensions([
|
||||
]);
|
||||
|
||||
// Must run after extension commands are registered
|
||||
if (window.config.hotkeys) {
|
||||
if (window.config && window.config.hotkeys) {
|
||||
hotkeysManager.setHotkeys(window.config.hotkeys, true);
|
||||
}
|
||||
|
||||
@@ -83,6 +85,23 @@ function handleServers(servers) {
|
||||
}
|
||||
}
|
||||
|
||||
function isAbsoluteUrl(url) {
|
||||
return url.includes('http://') || url.includes('https://');
|
||||
}
|
||||
|
||||
function makeAbsoluteIfNecessary(url, base_url) {
|
||||
if (isAbsoluteUrl(url)) {
|
||||
return url;
|
||||
}
|
||||
|
||||
// Make sure base_url and url are not duplicating slashes
|
||||
if (base_url[base_url.length - 1] === "/") {
|
||||
base_url = base_url.slice(0, base_url.length - 1);
|
||||
}
|
||||
|
||||
return base_url + url;
|
||||
}
|
||||
|
||||
class App extends Component {
|
||||
static propTypes = {
|
||||
routerBasename: PropTypes.string.isRequired,
|
||||
@@ -103,9 +122,23 @@ class App extends Component {
|
||||
if (this.props.oidc.length) {
|
||||
const firstOpenIdClient = this.props.oidc[0];
|
||||
|
||||
const { protocol, host } = window.location;
|
||||
const { routerBasename } = this.props;
|
||||
const baseUri = `${protocol}//${host}${routerBasename}`;
|
||||
|
||||
const redirect_uri = firstOpenIdClient.redirect_uri || '/callback';
|
||||
const silent_redirect_uri = firstOpenIdClient.silent_redirect_uri || '/silent-refresh.html';
|
||||
const post_logout_redirect_uri = firstOpenIdClient.post_logout_redirect_uri || '/';
|
||||
|
||||
const openIdConnectConfiguration = Object.assign({}, firstOpenIdClient, {
|
||||
redirect_uri: makeAbsoluteIfNecessary(redirect_uri, baseUri),
|
||||
silent_redirect_uri: makeAbsoluteIfNecessary(silent_redirect_uri, baseUri),
|
||||
post_logout_redirect_uri: makeAbsoluteIfNecessary(post_logout_redirect_uri, baseUri),
|
||||
});
|
||||
|
||||
this.userManager = getUserManagerForOpenIdConnectClient(
|
||||
store,
|
||||
firstOpenIdClient
|
||||
openIdConnectConfiguration,
|
||||
);
|
||||
}
|
||||
handleServers(this.props.servers);
|
||||
@@ -123,13 +156,15 @@ class App extends Component {
|
||||
<Provider store={store}>
|
||||
<I18nextProvider i18n={i18n}>
|
||||
<OidcProvider store={store} userManager={userManager}>
|
||||
<Router basename={this.props.routerBasename}>
|
||||
<WhiteLabellingContext.Provider
|
||||
value={this.props.whiteLabelling}
|
||||
>
|
||||
<OHIFStandaloneViewer userManager={userManager} />
|
||||
</WhiteLabellingContext.Provider>
|
||||
</Router>
|
||||
<UserManagerContext.Provider value={userManager}>
|
||||
<Router basename={this.props.routerBasename}>
|
||||
<WhiteLabellingContext.Provider
|
||||
value={this.props.whiteLabelling}
|
||||
>
|
||||
<OHIFStandaloneViewer userManager={userManager} />
|
||||
</WhiteLabellingContext.Provider>
|
||||
</Router>
|
||||
</UserManagerContext.Provider>
|
||||
</OidcProvider>
|
||||
</I18nextProvider>
|
||||
</Provider>
|
||||
|
||||
@@ -13,6 +13,11 @@ body,
|
||||
Helvetica Neue, Helvetica, Arial, Lucida Grande, sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
font-size: 14px;
|
||||
line-height: 1.42857143;
|
||||
}
|
||||
|
||||
#root .fade-enter {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
@@ -5,10 +5,12 @@ import { Route, Switch } from 'react-router-dom';
|
||||
import { NProgress } from '@tanem/react-nprogress';
|
||||
import { CSSTransition } from 'react-transition-group';
|
||||
import { connect } from 'react-redux';
|
||||
import { SignoutCallbackComponent } from 'redux-oidc';
|
||||
import { ViewerbaseDragDropContext } from 'react-viewerbase';
|
||||
// import asyncComponent from './components/AsyncComponent.js'
|
||||
import IHEInvokeImageDisplay from './routes/IHEInvokeImageDisplay.js';
|
||||
import ViewerRouting from './routes/ViewerRouting.js';
|
||||
import ViewerLocalFileData from './connectedComponents/ViewerLocalFileData.js';
|
||||
import StudyListRouting from './studylist/StudyListRouting.js';
|
||||
import StandaloneRouting from './routes/StandaloneRouting.js';
|
||||
import CallbackPage from './routes/CallbackPage.js';
|
||||
@@ -42,6 +44,9 @@ class OHIFStandaloneViewer extends Component {
|
||||
static propTypes = {
|
||||
history: PropTypes.object.isRequired,
|
||||
user: PropTypes.object,
|
||||
setContext: PropTypes.func,
|
||||
userManager: PropTypes.object,
|
||||
location: PropTypes.object,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
@@ -70,14 +75,29 @@ class OHIFStandaloneViewer extends Component {
|
||||
return (
|
||||
<Switch>
|
||||
<Route exact path="/silent-refresh.html" onEnter={reload} />
|
||||
<Route exact path="/logout-redirect.html" onEnter={reload} />
|
||||
<Route exact path="/logout-redirect" render={() =>
|
||||
<SignoutCallbackComponent
|
||||
userManager={userManager}
|
||||
successCallback={() => console.log('Signout successful')}
|
||||
errorCallback={(error) => {
|
||||
console.warn(error);
|
||||
console.warn('Signout failed');
|
||||
}}
|
||||
/>
|
||||
}/>
|
||||
<Route
|
||||
path="/callback"
|
||||
render={() => <CallbackPage userManager={userManager} />}
|
||||
/>
|
||||
<Route
|
||||
component={() => {
|
||||
userManager.signinRedirect();
|
||||
userManager.getUser().then(user => {
|
||||
if (user) {
|
||||
userManager.signinSilent();
|
||||
} else {
|
||||
userManager.signinRedirect();
|
||||
}
|
||||
});
|
||||
|
||||
return null;
|
||||
}}
|
||||
@@ -94,12 +114,8 @@ class OHIFStandaloneViewer extends Component {
|
||||
*/
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
Component: StudyListRouting,
|
||||
},
|
||||
{
|
||||
path: '/studylist',
|
||||
Component: StudyListRouting,
|
||||
path: '/local',
|
||||
Component: ViewerLocalFileData,
|
||||
},
|
||||
{
|
||||
path: '/viewer',
|
||||
@@ -110,7 +126,7 @@ class OHIFStandaloneViewer extends Component {
|
||||
Component: ViewerRouting,
|
||||
},
|
||||
{
|
||||
path: '/study/:studyInstanceUid/series/:seriesInstanceUids',
|
||||
path: '/study/:studyInstanceUids/series/:seriesInstanceUids',
|
||||
Component: ViewerRouting,
|
||||
},
|
||||
{
|
||||
@@ -119,6 +135,21 @@ class OHIFStandaloneViewer extends Component {
|
||||
},
|
||||
];
|
||||
|
||||
const showStudyList =
|
||||
window.config && window.config.showStudyList !== undefined
|
||||
? window.config.showStudyList
|
||||
: true;
|
||||
if (showStudyList) {
|
||||
routes.push({
|
||||
path: '/studylist',
|
||||
Component: StudyListRouting,
|
||||
});
|
||||
routes.push({
|
||||
path: '/',
|
||||
Component: StudyListRouting,
|
||||
});
|
||||
}
|
||||
|
||||
const currentPath = this.props.location.pathname;
|
||||
const noMatchingRoutes = !routes.find(r =>
|
||||
matchPath(currentPath, {
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import React from 'react';
|
||||
|
||||
const UserManagerContext = React.createContext();
|
||||
|
||||
export default UserManagerContext;
|
||||
@@ -140,6 +140,7 @@ function convertTimepointsToTableData(timepoints) {
|
||||
return [
|
||||
{
|
||||
label: 'Study date:',
|
||||
key: 'StudyDate',
|
||||
date: moment(timepoints[0].latestDate).format('DD-MMM-YY'),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -17,6 +17,7 @@ class Header extends Component {
|
||||
location: PropTypes.object.isRequired,
|
||||
children: PropTypes.node,
|
||||
t: PropTypes.func.isRequired,
|
||||
userManager: PropTypes.object
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
@@ -59,6 +60,16 @@ class Header extends Component {
|
||||
},
|
||||
];
|
||||
|
||||
if (this.props.user && this.props.userManager) {
|
||||
this.options.push({
|
||||
title: t('Logout'),
|
||||
icon: { name: 'power-off' },
|
||||
onClick: () => {
|
||||
this.props.userManager.signoutRedirect();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
this.hotKeysData = hotkeysManager.hotkeyDefinitions;
|
||||
}
|
||||
|
||||
@@ -71,6 +82,10 @@ class Header extends Component {
|
||||
|
||||
render() {
|
||||
const { t } = this.props;
|
||||
const showStudyList =
|
||||
window.config.showStudyList !== undefined
|
||||
? window.config.showStudyList
|
||||
: true;
|
||||
return (
|
||||
<div className={`entry-header ${this.props.home ? 'header-big' : ''}`}>
|
||||
<div className="header-left-box">
|
||||
@@ -85,7 +100,7 @@ class Header extends Component {
|
||||
|
||||
{this.props.children}
|
||||
|
||||
{!this.props.home && (
|
||||
{showStudyList && !this.props.home && (
|
||||
<Link
|
||||
className="header-btn header-studyListLinkSection"
|
||||
to={{
|
||||
|
||||
+7
-1
@@ -4,9 +4,15 @@ import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
|
||||
import dicomParser from 'dicom-parser';
|
||||
import version from './version.js';
|
||||
|
||||
let homepage;
|
||||
const { process } = window;
|
||||
if (process && process.env && process.env.PUBLIC_URL) {
|
||||
homepage = process.env.PUBLIC_URL;
|
||||
}
|
||||
|
||||
window.info = {
|
||||
version,
|
||||
homepage: `${process.env.PUBLIC_URL}`,
|
||||
homepage,
|
||||
};
|
||||
|
||||
// For debugging
|
||||
|
||||
@@ -3,6 +3,7 @@ import { connect } from 'react-redux';
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
user: state.oidc && state.oidc.user,
|
||||
isOpen: state.ui.userPreferencesModalOpen,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@ class PluginSwitch extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div className="PluginSwitch">
|
||||
<ExpandableToolMenu buttons={this.props.buttons} />
|
||||
<ExpandableToolMenu buttons={this.props.buttons} text={'View'} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ import './ToolbarRow.css';
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import {
|
||||
ExpandableToolMenu,
|
||||
RoundedButtonGroup,
|
||||
ToolbarButton,
|
||||
ExpandableToolMenu,
|
||||
} from 'react-viewerbase';
|
||||
import { commandsManager, extensionManager } from './../App.js';
|
||||
|
||||
@@ -13,6 +13,7 @@ import ConnectedLayoutButton from './ConnectedLayoutButton';
|
||||
import ConnectedPluginSwitch from './ConnectedPluginSwitch.js';
|
||||
import { MODULE_TYPES } from 'ohif-core';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
|
||||
class ToolbarRow extends Component {
|
||||
// TODO: Simplify these? isOpen can be computed if we say "any" value for selected,
|
||||
@@ -53,7 +54,7 @@ class ToolbarRow extends Component {
|
||||
{
|
||||
value: 'studies',
|
||||
icon: 'th-large',
|
||||
bottomLabel: 'Series',
|
||||
bottomLabel: this.props.t('Series'),
|
||||
},
|
||||
],
|
||||
right: [],
|
||||
@@ -155,10 +156,17 @@ class ToolbarRow extends Component {
|
||||
*/
|
||||
function _getButtonComponents(toolbarButtons, activeButtons) {
|
||||
return toolbarButtons.map((button, index) => {
|
||||
if (button.buttons) {
|
||||
let activeCommand = undefined;
|
||||
|
||||
if (button.buttons && button.buttons.length) {
|
||||
// Iterate over button definitions and update `onClick` behavior
|
||||
const childButtons = button.buttons.map(childButton => {
|
||||
childButton.onClick = _handleToolbarButtonClick.bind(this, childButton);
|
||||
|
||||
if (activeButtons.indexOf(childButton.id) > -1) {
|
||||
activeCommand = childButton.id;
|
||||
}
|
||||
|
||||
return childButton;
|
||||
});
|
||||
|
||||
@@ -168,10 +176,10 @@ function _getButtonComponents(toolbarButtons, activeButtons) {
|
||||
text={button.label}
|
||||
icon={button.icon}
|
||||
buttons={childButtons}
|
||||
activeCommand={activeCommand}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ToolbarButton
|
||||
key={button.id}
|
||||
@@ -239,4 +247,4 @@ function _handleBuiltIn({ behavior } = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
export default ToolbarRow;
|
||||
export default withTranslation('Common')(ToolbarRow);
|
||||
@@ -13,6 +13,7 @@ import ConnectedStudyBrowser from './ConnectedStudyBrowser.js';
|
||||
import ConnectedViewerMain from './ConnectedViewerMain.js';
|
||||
import SidePanel from './../components/SidePanel.js';
|
||||
import { extensionManager } from './../App.js';
|
||||
import UserManagerContext from '../UserManagerContext';
|
||||
import './Viewer.css';
|
||||
/**
|
||||
* Inits OHIF Hanging Protocol's onReady.
|
||||
@@ -230,9 +231,14 @@ class Viewer extends Component {
|
||||
{/* HEADER */}
|
||||
<WhiteLabellingContext.Consumer>
|
||||
{whiteLabelling => (
|
||||
<ConnectedHeader home={false}>
|
||||
{whiteLabelling.logoComponent}
|
||||
</ConnectedHeader>
|
||||
<UserManagerContext.Consumer>
|
||||
{ userManager => (
|
||||
<ConnectedHeader home={false} userManager={userManager}>
|
||||
{whiteLabelling.logoComponent}
|
||||
</ConnectedHeader>
|
||||
)
|
||||
}
|
||||
</UserManagerContext.Consumer>
|
||||
)}
|
||||
</WhiteLabellingContext.Consumer>
|
||||
|
||||
@@ -319,7 +325,6 @@ export default Viewer;
|
||||
*
|
||||
* TODO[react]:
|
||||
* - Add sorting of display sets
|
||||
* - Add useMiddleSeriesInstanceAsThumbnail
|
||||
* - Add showStackLoadingProgressBar option
|
||||
*
|
||||
* @param {Study[]} studies
|
||||
@@ -339,12 +344,19 @@ const _mapStudiesToThumbnails = function(studies) {
|
||||
} = displaySet;
|
||||
|
||||
let imageId;
|
||||
let altImageText = ' '; // modality
|
||||
let altImageText;
|
||||
|
||||
if (displaySet.images && displaySet.images.length) {
|
||||
imageId = displaySet.images[0].getImageId();
|
||||
if (displaySet.modality && displaySet.modality === 'SEG') {
|
||||
// TODO: We want to replace this with a thumbnail showing
|
||||
// the segmentation map on the image, but this is easier
|
||||
// and better than what we have right now.
|
||||
altImageText = 'SEG';
|
||||
} else if (displaySet.images && displaySet.images.length) {
|
||||
const imageIndex = Math.floor(displaySet.images.length / 2);
|
||||
|
||||
imageId = displaySet.images[imageIndex].getImageId();
|
||||
} else {
|
||||
altImageText = 'SR';
|
||||
altImageText = displaySet.modality ? displaySet.modality : 'UN';
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
.drag-drop-instructions {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.drag-drop-instructions h3 {
|
||||
color: var(--active-color);
|
||||
}
|
||||
|
||||
.drag-drop-instructions h4 {
|
||||
color: var(--text-secondary-color);
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
import React, { Component } from 'react';
|
||||
import { metadata, utils } from 'ohif-core';
|
||||
|
||||
import ConnectedViewer from './ConnectedViewer.js';
|
||||
import PropTypes from 'prop-types';
|
||||
import { extensionManager } from './../App.js';
|
||||
import Dropzone from 'react-dropzone';
|
||||
import filesToStudies from '../lib/filesToStudies';
|
||||
import './ViewerLocalFileData.css';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
|
||||
const { OHIFStudyMetadata } = metadata;
|
||||
const { studyMetadataManager, updateMetaDataManager } = utils;
|
||||
|
||||
class ViewerLocalFileData extends Component {
|
||||
static propTypes = {
|
||||
studies: PropTypes.array,
|
||||
};
|
||||
|
||||
state = {
|
||||
studies: null,
|
||||
loading: false,
|
||||
error: null,
|
||||
};
|
||||
|
||||
updateStudies = studies => {
|
||||
// Render the viewer when the data is ready
|
||||
studyMetadataManager.purge();
|
||||
|
||||
// Map studies to new format, update metadata manager?
|
||||
const updatedStudies = studies.map(study => {
|
||||
const studyMetadata = new OHIFStudyMetadata(
|
||||
study,
|
||||
study.studyInstanceUid
|
||||
);
|
||||
const sopClassHandlerModules =
|
||||
extensionManager.modules['sopClassHandlerModule'];
|
||||
|
||||
study.displaySets =
|
||||
study.displaySets ||
|
||||
studyMetadata.createDisplaySets(sopClassHandlerModules);
|
||||
studyMetadata.setDisplaySets(study.displaySets);
|
||||
|
||||
// Updates WADO-RS metaDataManager
|
||||
updateMetaDataManager(study);
|
||||
|
||||
studyMetadataManager.add(studyMetadata);
|
||||
|
||||
return study;
|
||||
});
|
||||
|
||||
this.setState({
|
||||
studies: updatedStudies,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const onDrop = async acceptedFiles => {
|
||||
this.setState({ loading: true });
|
||||
|
||||
const studies = await filesToStudies(acceptedFiles);
|
||||
const updatedStudies = this.updateStudies(studies);
|
||||
|
||||
if (!updatedStudies) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({ studies: updatedStudies, loading: false });
|
||||
};
|
||||
|
||||
if (this.state.error) {
|
||||
return <div>Error: {JSON.stringify(this.state.error)}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Dropzone onDrop={onDrop}>
|
||||
{({ getRootProps, getInputProps }) => (
|
||||
<div {...getRootProps()} style={{ width: '100%', height: '100%' }}>
|
||||
{this.state.studies ? (
|
||||
<ConnectedViewer
|
||||
studies={this.state.studies}
|
||||
studyInstanceUids={
|
||||
this.state.studies &&
|
||||
this.state.studies.map(a => a.studyInstanceUid)
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<div className={'drag-drop-instructions'}>
|
||||
<div className={'drag-drop-contents'}>
|
||||
{this.state.loading ? (
|
||||
<h3>{this.props.t('Loading...')}</h3>
|
||||
) : (
|
||||
<>
|
||||
<h3>
|
||||
{this.props.t(
|
||||
'Drag and Drop DICOM files here to load them in the Viewer'
|
||||
)}
|
||||
</h3>
|
||||
<h4>
|
||||
{this.props.t(
|
||||
"Or click to load the browser's file selector"
|
||||
)}
|
||||
</h4>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<input {...getInputProps()} style={{ display: 'none' }} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</Dropzone>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTranslation('Common')(ViewerLocalFileData);
|
||||
@@ -0,0 +1,25 @@
|
||||
import { connect } from 'react-redux';
|
||||
import DicomFileUploaderModal from './DicomFileUploaderModal.js';
|
||||
import OHIF from 'ohif-core';
|
||||
|
||||
const isActive = a => a.active === true;
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const activeServer = state.servers.servers.find(isActive);
|
||||
|
||||
// TODO: Not sure I like this approach since it means we are recreating
|
||||
// this function every time redux changes
|
||||
const retrieveAuthHeaderFunction = () => OHIF.DICOMWeb.getAuthorizationHeader(activeServer);
|
||||
|
||||
return {
|
||||
retrieveAuthHeaderFunction,
|
||||
url: activeServer && activeServer.qidoRoot,
|
||||
};
|
||||
};
|
||||
|
||||
const ConnectedDicomFileUploader = connect(
|
||||
mapStateToProps,
|
||||
null
|
||||
)(DicomFileUploaderModal);
|
||||
|
||||
export default ConnectedDicomFileUploader;
|
||||
@@ -0,0 +1,32 @@
|
||||
import { connect } from 'react-redux';
|
||||
import DicomStorePickerModal from './DicomStorePickerModal.js';
|
||||
|
||||
const isActive = a => a.active === true;
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const activeServer = state.servers.servers.find(isActive);
|
||||
|
||||
return {
|
||||
user: state.oidc && state.oidc.user,
|
||||
url: activeServer && activeServer.qidoRoot,
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
setServers: servers => {
|
||||
const action = {
|
||||
type: 'SET_SERVERS',
|
||||
servers,
|
||||
};
|
||||
dispatch(action);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const ConnectedDicomStorePicker = connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(DicomStorePickerModal);
|
||||
|
||||
export default ConnectedDicomStorePicker;
|
||||
@@ -0,0 +1,55 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import api from './api/GoogleCloudApi';
|
||||
import DatasetsList from './DatasetsList';
|
||||
import './googleCloud.css';
|
||||
|
||||
export default class DatasetPicker extends Component {
|
||||
state = {
|
||||
error: null,
|
||||
loading: true,
|
||||
datasets: [],
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
project: PropTypes.object,
|
||||
location: PropTypes.object,
|
||||
onSelect: PropTypes.func,
|
||||
accessToken: PropTypes.string,
|
||||
};
|
||||
|
||||
async componentDidMount() {
|
||||
api.setAccessToken(this.props.accessToken);
|
||||
|
||||
const response = await api.loadDatasets(
|
||||
this.props.project.projectId,
|
||||
this.props.location.locationId
|
||||
);
|
||||
|
||||
if (response.isError) {
|
||||
this.setState({
|
||||
error: response.message,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
datasets: response.data.datasets || [],
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { datasets, loading, error } = this.state;
|
||||
const { onSelect } = this.props;
|
||||
return (
|
||||
<DatasetsList
|
||||
datasets={datasets}
|
||||
loading={loading}
|
||||
error={error}
|
||||
onSelect={onSelect}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import DicomStorePicker from './DicomStorePicker';
|
||||
import DatasetPicker from './DatasetPicker';
|
||||
import ProjectPicker from './ProjectPicker';
|
||||
import LocationPicker from './LocationPicker';
|
||||
import GoogleCloudApi from './api/GoogleCloudApi';
|
||||
import './googleCloud.css';
|
||||
|
||||
class DatasetSelector extends Component {
|
||||
state = {
|
||||
project: null,
|
||||
location: null,
|
||||
dataset: null,
|
||||
unloading: false,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
id: PropTypes.string,
|
||||
event: PropTypes.string,
|
||||
user: PropTypes.object,
|
||||
canClose: PropTypes.string,
|
||||
setServers: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
onProjectSelect = project => {
|
||||
this.setState({
|
||||
project,
|
||||
});
|
||||
};
|
||||
|
||||
onLocationSelect = location => {
|
||||
this.setState({
|
||||
location,
|
||||
});
|
||||
};
|
||||
|
||||
onDatasetSelect = dataset => {
|
||||
this.setState({
|
||||
dataset,
|
||||
});
|
||||
};
|
||||
|
||||
onProjectClick = () => {
|
||||
this.setState({
|
||||
dataset: null,
|
||||
location: null,
|
||||
project: null,
|
||||
});
|
||||
};
|
||||
|
||||
onLocationClick = () => {
|
||||
this.setState({
|
||||
dataset: null,
|
||||
location: null,
|
||||
});
|
||||
};
|
||||
|
||||
onDatasetClick = () => {
|
||||
this.setState({
|
||||
dataset: null,
|
||||
});
|
||||
};
|
||||
|
||||
onDicomStoreSelect = dicomStoreJson => {
|
||||
const dicomStore = dicomStoreJson.name;
|
||||
const parts = dicomStore.split('/');
|
||||
const result = {
|
||||
wadoUriRoot: GoogleCloudApi.urlBase + `/${dicomStore}/dicomWeb`,
|
||||
qidoRoot: GoogleCloudApi.urlBase + `/${dicomStore}/dicomWeb`,
|
||||
wadoRoot: GoogleCloudApi.urlBase + `/${dicomStore}/dicomWeb`,
|
||||
project: parts[1],
|
||||
location: parts[3],
|
||||
dataset: parts[5],
|
||||
dicomStore: parts[7],
|
||||
};
|
||||
this.props.setServers(result);
|
||||
};
|
||||
|
||||
render() {
|
||||
const accessToken = this.props.user.access_token;
|
||||
|
||||
const { project, location, dataset } = this.state;
|
||||
const {
|
||||
onProjectClick,
|
||||
onLocationClick,
|
||||
onDatasetClick,
|
||||
onProjectSelect,
|
||||
onLocationSelect,
|
||||
onDatasetSelect,
|
||||
onDicomStoreSelect,
|
||||
} = this;
|
||||
|
||||
let projectBreadcrumbs = (
|
||||
<div className="gcp-picker--path">
|
||||
<span>{this.props.t('Select a Project')}</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
if (project) {
|
||||
projectBreadcrumbs = (
|
||||
<div className="gcp-picker--path">
|
||||
<span onClick={onProjectClick}>{project.name}</span>
|
||||
{project && location && (
|
||||
<span onClick={onLocationClick}>
|
||||
{' '}
|
||||
-> {location.name.split('/')[3]}
|
||||
</span>
|
||||
)}
|
||||
{project && location && dataset && (
|
||||
<span onClick={onDatasetClick}>
|
||||
{' '}
|
||||
-> {dataset.name.split('/')[5]}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{projectBreadcrumbs}
|
||||
{!project && (
|
||||
<ProjectPicker
|
||||
accessToken={accessToken}
|
||||
onSelect={onProjectSelect}
|
||||
/>
|
||||
)}
|
||||
|
||||
{project && !location && (
|
||||
<LocationPicker
|
||||
accessToken={accessToken}
|
||||
project={project}
|
||||
onSelect={onLocationSelect}
|
||||
/>
|
||||
)}
|
||||
{project && location && !dataset && (
|
||||
<DatasetPicker
|
||||
accessToken={accessToken}
|
||||
project={project}
|
||||
location={location}
|
||||
onSelect={onDatasetSelect}
|
||||
/>
|
||||
)}
|
||||
{project && location && dataset && (
|
||||
<DicomStorePicker
|
||||
accessToken={accessToken}
|
||||
dataset={dataset}
|
||||
onSelect={onDicomStoreSelect}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTranslation('Common')(DatasetSelector);
|
||||
@@ -0,0 +1,80 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import './googleCloud.css';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import { Icon } from 'react-viewerbase';
|
||||
|
||||
class DatasetsList extends Component {
|
||||
state = {
|
||||
search: '',
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
datasets: PropTypes.array,
|
||||
loading: PropTypes.bool,
|
||||
error: PropTypes.string,
|
||||
onSelect: PropTypes.func,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
loading: true,
|
||||
};
|
||||
|
||||
renderTableRow = dataset => {
|
||||
return (
|
||||
<tr
|
||||
key={dataset.name}
|
||||
className={
|
||||
this.state.highlightedItem === dataset.name
|
||||
? 'noselect active'
|
||||
: 'noselect'
|
||||
}
|
||||
onMouseEnter={() => {
|
||||
this.onHighlightItem(dataset.name);
|
||||
}}
|
||||
onClick={() => {
|
||||
this.props.onSelect(dataset);
|
||||
}}
|
||||
>
|
||||
<td>{dataset.name.split('/')[5]}</td>
|
||||
</tr>
|
||||
);
|
||||
};
|
||||
|
||||
onHighlightItem(dataset) {
|
||||
this.setState({ highlightedItem: dataset });
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.props.error) {
|
||||
return <p>{this.props.error}</p>;
|
||||
}
|
||||
|
||||
const loadingIcon = (
|
||||
<Icon name="circle-notch" className="loading-icon-spin loading-icon" />
|
||||
);
|
||||
|
||||
if (this.props.loading) {
|
||||
return loadingIcon;
|
||||
}
|
||||
|
||||
const body = (
|
||||
<tbody id="DatasetList">
|
||||
{this.props.datasets.map(this.renderTableRow)}
|
||||
</tbody>
|
||||
);
|
||||
|
||||
return (
|
||||
<table id="tblDatasetList" className="gcp-table table noselect">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{this.props.t('Dataset')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{this.props.datasets && body}
|
||||
</table>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTranslation('Common')(DatasetsList);
|
||||
@@ -0,0 +1,49 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Modal from 'react-bootstrap-modal';
|
||||
import DicomUploader from './DicomUploader';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
|
||||
class DicomFileUploaderModal extends Component {
|
||||
static propTypes = {
|
||||
url: PropTypes.string,
|
||||
retrieveAuthHeaderFunction: PropTypes.func,
|
||||
onClose: PropTypes.func,
|
||||
};
|
||||
|
||||
state = {
|
||||
uploaded: false,
|
||||
};
|
||||
|
||||
render() {
|
||||
if (!this.props.url) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
show={this.props.isOpen}
|
||||
onHide={this.props.onClose}
|
||||
aria-labelledby="ModalHeader"
|
||||
className="modal fade themed in"
|
||||
backdrop={false}
|
||||
size={'md'}
|
||||
keyboard={true}
|
||||
>
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Title>
|
||||
{this.props.t('Upload DICOM Files')}
|
||||
</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<DicomUploader
|
||||
url={this.props.url}
|
||||
retrieveAuthHeaderFunction={this.props.retrieveAuthHeaderFunction}
|
||||
/>
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTranslation('Common')(DicomFileUploaderModal);
|
||||
@@ -0,0 +1,78 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import './googleCloud.css';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import { Icon } from 'react-viewerbase';
|
||||
|
||||
class DicomStoreList extends Component {
|
||||
state = {
|
||||
search: '',
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
stores: PropTypes.array,
|
||||
loading: PropTypes.bool.isRequired,
|
||||
error: PropTypes.string,
|
||||
onSelect: PropTypes.func,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
loading: true,
|
||||
};
|
||||
|
||||
renderTableRow = store => {
|
||||
return (
|
||||
<tr
|
||||
key={store.name}
|
||||
className={
|
||||
this.state.highlightedItem === store.name
|
||||
? 'noselect active'
|
||||
: 'noselect'
|
||||
}
|
||||
onMouseEnter={() => {
|
||||
this.onHighlightItem(store.name);
|
||||
}}
|
||||
onClick={() => {
|
||||
this.props.onSelect(store);
|
||||
}}
|
||||
>
|
||||
<td className="project">{store.name.split('/')[7]}</td>
|
||||
</tr>
|
||||
);
|
||||
};
|
||||
|
||||
onHighlightItem(store) {
|
||||
this.setState({ highlightedItem: store });
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.props.error) {
|
||||
return <p>{this.props.error}</p>;
|
||||
}
|
||||
|
||||
const loadingIcon = (
|
||||
<Icon name="circle-notch" className="loading-icon-spin loading-icon" />
|
||||
);
|
||||
|
||||
if (this.props.loading) {
|
||||
return loadingIcon;
|
||||
}
|
||||
|
||||
const body = (
|
||||
<tbody id="StoreList">{this.props.stores.map(this.renderTableRow)}</tbody>
|
||||
);
|
||||
|
||||
return (
|
||||
<table id="tblStoreList" className="gcp-table table noselect">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{this.props.t('DICOM Store')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{this.props.stores && body}
|
||||
</table>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTranslation('Common')(DicomStoreList);
|
||||
@@ -0,0 +1,53 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import api from './api/GoogleCloudApi';
|
||||
import DicomStoreList from './DicomStoreList';
|
||||
import './googleCloud.css';
|
||||
|
||||
export default class DicomStorePicker extends Component {
|
||||
state = {
|
||||
error: null,
|
||||
loading: true,
|
||||
stores: [],
|
||||
locations: [],
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
dataset: PropTypes.object,
|
||||
onSelect: PropTypes.func,
|
||||
accessToken: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
async componentDidMount() {
|
||||
api.setAccessToken(this.props.accessToken);
|
||||
|
||||
const response = await api.loadDicomStores(this.props.dataset.name);
|
||||
|
||||
if (response.isError) {
|
||||
this.setState({
|
||||
error: response.message,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
stores: response.data.dicomStores || [],
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { stores, loading, error } = this.state;
|
||||
const { onSelect } = this.props;
|
||||
|
||||
return (
|
||||
<DicomStoreList
|
||||
stores={stores}
|
||||
loading={loading}
|
||||
error={error}
|
||||
onSelect={onSelect}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
Loaded 100 of 120 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user