├── .all-contributorsrc
├── .babelrc
├── .circleci
└── config.yml
├── .editorconfig
├── .env
├── .eslintrc
├── .gitattributes
├── .github
└── ISSUE_TEMPLATE
│ ├── ---bug-report.md
│ ├── ---feature-request.md
│ └── ---support-question.md
├── .gitignore
├── .nvmrc
├── .prettierrc
├── .vscode
├── extensions.json
└── settings.json
├── LICENSE
├── README.md
├── doczrc.js
├── netlify.toml
├── package.json
├── public
└── docz.css
├── rollup.config.js
├── src
├── LayoutChooser
│ ├── LayoutManager.css
│ ├── LayoutManager.js
│ ├── LayoutPanelDropTarget.css
│ └── LayoutPanelDropTarget.js
├── ScrollableArea
│ ├── ScrollableArea.js
│ └── ScrollableArea.styl
├── __docs__
│ ├── NameSpace.js
│ ├── assets
│ │ ├── github.png
│ │ ├── npm.png
│ │ ├── react.png
│ │ └── typescript.png
│ ├── compatibility.mdx
│ ├── config
│ │ └── Wrapper.js
│ ├── docs-settings.mdx
│ ├── getting-started.mdx
│ ├── index.html
│ ├── introduction.mdx
│ ├── styling-and-theming.mdx
│ ├── translating.mdx
│ ├── wrapper.js
│ └── wrapper.styl
├── components
│ ├── checkbox
│ │ ├── __docs__
│ │ │ └── checkbox.mdx
│ │ ├── checkbox.css
│ │ ├── checkbox.js
│ │ └── index.js
│ ├── cineDialog
│ │ ├── CineDialog.js
│ │ ├── CineDialog.styl
│ │ ├── __docs__
│ │ │ └── cineDialog.mdx
│ │ └── index.js
│ ├── index.js
│ ├── languageSwitcher
│ │ ├── LanguageSwitcher.js
│ │ ├── LanguageSwitcher.styl
│ │ └── index.js
│ ├── layoutButton
│ │ ├── LayoutButton.js
│ │ ├── LayoutChooser.js
│ │ ├── LayoutChooser.styl
│ │ ├── __docs__
│ │ │ ├── layoutButton.css
│ │ │ └── layoutButton.mdx
│ │ └── index.js
│ ├── measurementTable
│ │ ├── MeasurementTable.js
│ │ ├── MeasurementTable.styl
│ │ ├── MeasurementTableItem.js
│ │ ├── MeasurementTableItem.styl
│ │ ├── __docs__
│ │ │ ├── measurementTable.mdx
│ │ │ ├── measurements.js
│ │ │ ├── timepoints.js
│ │ │ └── warnings.js
│ │ └── index.js
│ ├── overlayTrigger
│ │ ├── Fade.js
│ │ ├── Overlay.js
│ │ ├── OverlayTrigger.js
│ │ ├── __docs__
│ │ │ └── OverlayTrigger.mdx
│ │ ├── createChainedFunction.js
│ │ └── index.js
│ ├── quickSwitch
│ │ ├── QuickSwitch.js
│ │ ├── QuickSwitch.styl
│ │ ├── SeriesList.js
│ │ ├── SeriesList.styl
│ │ ├── StudiesItem.js
│ │ ├── StudiesItem.styl
│ │ ├── StudiesList.js
│ │ ├── StudiesList.styl
│ │ ├── __docs__
│ │ │ ├── quickSwitch.mdx
│ │ │ └── studies.js
│ │ └── index.js
│ ├── radioButtonList
│ │ ├── RadioButtonList.css
│ │ ├── RadioButtonList.js
│ │ ├── __docs__
│ │ │ └── radioButtonList.mdx
│ │ └── index.js
│ ├── roundedButtonGroup
│ │ ├── RoundedButtonGroup.css
│ │ ├── RoundedButtonGroup.js
│ │ ├── __docs__
│ │ │ └── roundedButtonGroup.mdx
│ │ └── index.js
│ ├── selectTree
│ │ ├── InputRadio.js
│ │ ├── SelectTree.js
│ │ ├── SelectTree.styl
│ │ ├── SelectTreeBreadcrumb.js
│ │ ├── __docs__
│ │ │ ├── selectTree.mdx
│ │ │ └── selectTreeItems.js
│ │ └── index.js
│ ├── simpleDialog
│ │ ├── SimpleDialog.js
│ │ ├── SimpleDialog.styl
│ │ ├── __docs__
│ │ │ └── simpleDialog.mdx
│ │ └── index.js
│ ├── studyBrowser
│ │ ├── DragPreview.js
│ │ ├── DragPreview.styl
│ │ ├── ExampleDropTarget.css
│ │ ├── ExampleDropTarget.js
│ │ ├── ImageThumbnail.js
│ │ ├── ImageThumbnail.styl
│ │ ├── StudyBrowser.js
│ │ ├── StudyBrowser.styl
│ │ ├── ThumbnailEntry.js
│ │ ├── ThumbnailEntry.styl
│ │ ├── ThumbnailEntryDragSource.js
│ │ ├── __docs__
│ │ │ ├── exampleStudies.js
│ │ │ ├── studyBrowser.mdx
│ │ │ └── wrappedStudyBrowser.js
│ │ └── index.js
│ ├── studyList
│ │ ├── CustomDateRangePicker.js
│ │ ├── CustomDateRangePicker.styl
│ │ ├── PaginationArea.js
│ │ ├── PaginationArea.styl
│ │ ├── StudyList.js
│ │ ├── StudyList.styl
│ │ ├── StudyListLoadingText.js
│ │ ├── StudyListToolbar.js
│ │ ├── StudyListToolbar.styl
│ │ ├── __docs__
│ │ │ ├── onSearch.js
│ │ │ ├── studies.js
│ │ │ └── studyList.mdx
│ │ └── index.js
│ ├── tableList
│ │ ├── TableList.js
│ │ ├── TableList.styl
│ │ ├── TableListItem.js
│ │ ├── TableListItem.styl
│ │ ├── __docs__
│ │ │ └── tableList.mdx
│ │ └── index.js
│ ├── toolbarSection
│ │ ├── ToolbarSection.js
│ │ ├── ToolbarSection.styl
│ │ ├── __docs__
│ │ │ ├── exampleButtons.js
│ │ │ └── toolbarSection.docs.mdx
│ │ └── index.js
│ ├── tooltip
│ │ ├── Tooltip.js
│ │ ├── Tooltip.styl
│ │ ├── __docs__
│ │ │ └── tooltip.noshow-mdx
│ │ ├── __tests__
│ │ │ └── .githold
│ │ └── index.js
│ └── userPreferencesModal
│ │ ├── AboutModal.js
│ │ ├── AboutModal.styl
│ │ ├── GeneralPreferences.js
│ │ ├── HotKeysPreferences.js
│ │ ├── HotKeysPreferences.styl
│ │ ├── UserPreferences.js
│ │ ├── UserPreferences.styl
│ │ ├── UserPreferencesModal.js
│ │ ├── UserPreferencesModal.styl
│ │ ├── WindowLevelPreferences.js
│ │ ├── WindowLevelPreferences.styl
│ │ ├── __docs__
│ │ ├── about.mdx
│ │ ├── generalDefaults.js
│ │ ├── hotkeyDefaults.js
│ │ ├── userPreferences.mdx
│ │ └── windowLevelDefaults.js
│ │ ├── hotKeysConfig.js
│ │ └── index.js
├── design
│ └── styles
│ │ └── common
│ │ ├── button.styl
│ │ ├── form.styl
│ │ ├── global.styl
│ │ ├── modal.styl
│ │ ├── navbar.styl
│ │ ├── state.styl
│ │ └── table.styl
├── elements
│ ├── Icon
│ │ ├── Icon.js
│ │ ├── Icon.styl
│ │ ├── __docs__
│ │ │ └── icon.mdx
│ │ ├── getIcon.js
│ │ ├── icons
│ │ │ ├── 3d-rotate.svg
│ │ │ ├── adjust.svg
│ │ │ ├── angle-double-down.svg
│ │ │ ├── angle-double-up.svg
│ │ │ ├── angle-left.svg
│ │ │ ├── arrows-alt-h.svg
│ │ │ ├── arrows-alt-v.svg
│ │ │ ├── arrows.svg
│ │ │ ├── bars.svg
│ │ │ ├── brain.svg
│ │ │ ├── caret-down.svg
│ │ │ ├── caret-up.svg
│ │ │ ├── check-circle-o.svg
│ │ │ ├── check-circle.svg
│ │ │ ├── check.svg
│ │ │ ├── chevron-down.svg
│ │ │ ├── circle-notch.svg
│ │ │ ├── circle-o.svg
│ │ │ ├── circle.svg
│ │ │ ├── cog.svg
│ │ │ ├── create-comment.svg
│ │ │ ├── create-screen-capture.svg
│ │ │ ├── crosshairs.svg
│ │ │ ├── cube.svg
│ │ │ ├── database.svg
│ │ │ ├── dot-circle.svg
│ │ │ ├── edit.svg
│ │ │ ├── ellipse-circle.svg
│ │ │ ├── ellipse-h.svg
│ │ │ ├── ellipse-v.svg
│ │ │ ├── exclamation-circle.svg
│ │ │ ├── exclamation-triangle.svg
│ │ │ ├── fast-backward.svg
│ │ │ ├── fast-forward.svg
│ │ │ ├── info.svg
│ │ │ ├── inline-edit.svg
│ │ │ ├── level.svg
│ │ │ ├── link-circles.svg
│ │ │ ├── link.svg
│ │ │ ├── list.svg
│ │ │ ├── liver.svg
│ │ │ ├── lock-alt.svg
│ │ │ ├── lock.svg
│ │ │ ├── lung.svg
│ │ │ ├── measure-non-target.svg
│ │ │ ├── measure-target-cr.svg
│ │ │ ├── measure-target-ne.svg
│ │ │ ├── measure-target-un.svg
│ │ │ ├── measure-target.svg
│ │ │ ├── measure-temp.svg
│ │ │ ├── object-group.svg
│ │ │ ├── ohif-logo.svg
│ │ │ ├── oval.svg
│ │ │ ├── palette.svg
│ │ │ ├── play.svg
│ │ │ ├── plus.svg
│ │ │ ├── power-off.svg
│ │ │ ├── reset.svg
│ │ │ ├── rotate-right.svg
│ │ │ ├── rotate.svg
│ │ │ ├── search-plus.svg
│ │ │ ├── search.svg
│ │ │ ├── soft-tissue.svg
│ │ │ ├── sort-down.svg
│ │ │ ├── sort-up.svg
│ │ │ ├── sort.svg
│ │ │ ├── square-o.svg
│ │ │ ├── star.svg
│ │ │ ├── step-backward.svg
│ │ │ ├── step-forward.svg
│ │ │ ├── stop.svg
│ │ │ ├── sun.svg
│ │ │ ├── th-large.svg
│ │ │ ├── th-list.svg
│ │ │ ├── th.svg
│ │ │ ├── times.svg
│ │ │ ├── trash.svg
│ │ │ ├── user.svg
│ │ │ └── youtube.svg
│ │ └── index.js
│ ├── form
│ │ ├── DropdownMenu.css
│ │ ├── DropdownMenu.js
│ │ ├── Label.css
│ │ ├── Label.js
│ │ ├── Range.css
│ │ ├── Range.js
│ │ ├── Select.css
│ │ ├── Select.js
│ │ ├── TextArea.css
│ │ ├── TextArea.js
│ │ ├── TextInput.css
│ │ ├── TextInput.js
│ │ ├── __docs__
│ │ │ ├── dropdownMenu.doc.mdx
│ │ │ ├── label.doc.mdx
│ │ │ ├── range.doc.mdx
│ │ │ ├── select.docs.mdx
│ │ │ ├── textArea.doc.mdx
│ │ │ └── textInput.docs.mdx
│ │ └── index.js
│ └── index.js
├── index.js
├── test.js
├── utils
│ ├── LanguageProvider.js
│ ├── getScrollbarSize.js
│ ├── styleProperty.js
│ ├── throttled.js
│ └── viewerbaseDragDropContext.js
└── viewer
│ ├── ExpandableToolMenu.js
│ ├── ExpandableToolMenu.styl
│ ├── PlayClipButton.js
│ ├── PresetToggle.js
│ ├── SimpleToolbarButton.js
│ ├── Toolbar.js
│ ├── ToolbarButton.js
│ ├── ViewportErrorIndicator.js
│ ├── ViewportLoadingIndicator.js
│ └── toolbar-button.styl
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@babel/preset-env",
5 | {
6 | "targets": {
7 | "ie": "11"
8 | }
9 | }
10 | ],
11 | "@babel/preset-react"
12 | ],
13 | "plugins": [
14 | "inline-react-svg",
15 | "@babel/plugin-proposal-class-properties",
16 | "@babel/plugin-transform-runtime"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------
1 | ####
2 | ##
3 | ## We need to pin WebPack's version for docz to work. See this issue:
4 | ## https://github.com/pedronauck/docz/issues/704#issuecomment-480295032
5 | ##
6 | ## react-scripts checks that dependency versions match before running
7 | ## Our top level webpack version causes the preflight check to fail
8 | ## This prevents that check
9 | ##
10 | ####
11 | SKIP_PREFLIGHT_CHECK=true
12 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "react-app",
4 | "eslint:recommended",
5 | "plugin:react/recommended",
6 | "plugin:prettier/recommended"
7 | ],
8 | "parser": "babel-eslint",
9 | "env": {
10 | "jest": true
11 | },
12 | "settings": {
13 | "react": {
14 | "version": "detect"
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text eol=lf
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/---bug-report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "\U0001F41B Bug report"
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: 'Bug Report :bug:'
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Before Creating an issue**
11 |
12 | - Are you running the latest version?
13 | - Are you reporting to the correct repository?
14 | - Did you search existing issues?
15 |
16 | **Describe the bug**
17 |
18 | *A clear and concise description of what the bug is.*
19 |
20 | **Steps To Reproduce**
21 |
22 | 1. [First Step]
23 | 2. [Second Step]
24 | 3. ...
25 |
26 | ```
27 | Please use code blocks to show formatted errors or code snippets
28 | ```
29 |
30 | **Expected behavior**
31 |
32 | *A clear and concise description of what you expected to happen.*
33 |
34 |
35 | **Environment**
36 | - OS: [e.g. iOS]
37 | - Browser [e.g. chrome, safari]
38 | - Version [e.g. 22]
39 |
40 | **Additional context**
41 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/---feature-request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "\U0001F680 Feature request"
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: enhancement
6 | assignees: ''
7 |
8 | ---
9 |
10 | ## Awesome, do you have an idea? 😍
11 |
12 | If you have a **feature request, improvement or idea**, check [our official roadmap](https://github.com/OHIF/react-viewerbase/projects) to see if it is already planned!
13 |
14 | ### 👉 [Go to Roadmap](https://github.com/OHIF/react-viewerbase/projects)
15 |
16 | If your feature request isn't there, continue with this issue and we can discuss it 🤟
17 |
18 | Please include the reasons why you think your change should be made, and any supporting evidence that helps us asses its value and priority.
19 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/---support-question.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "\U0001F917 Support Question"
3 | about: "I have a question \U0001F4AC"
4 | title: ''
5 | labels: question
6 | assignees: ''
7 |
8 | ---
9 |
10 | We are a small team with limited resources. Your question is much more likely to be answered if it is [a good question](https://stackoverflow.com/help/how-to-ask)
11 |
12 | **Description**
13 |
14 |
15 | Questions can often be answered by our documentation. Unable to find an answer in our docs? We'll try to help. In the meantime, if you answer your own question, please respond with the answer here so that others may benefit as well. Better yet, open a PR to expand our docs ^_^
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # See https://help.github.com/ignore-files/ for more about ignoring files.
3 |
4 | # dependencies
5 | node_modules
6 | # Don't track package-lock if we intend to use yarn.lock
7 | package-lock.json
8 |
9 | # builds
10 | build
11 | dist/
12 | .rpt2_cache
13 | .docz/
14 | .yalc
15 | yalc.lock
16 |
17 | # Log
18 | npm-debug.log*
19 | yarn-debug.log*
20 | yarn-error.log*
21 | .idea
22 |
23 | # misc
24 | .DS_Store
25 | .env.local
26 | .env.development.local
27 | .env.test.local
28 | .env.production.local
29 |
30 | # Can be removed after we update netlify
31 | # to use default .docz/ output destination
32 | example/
33 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 10.15.3
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "es5",
3 | "printWidth": 80,
4 | "proseWrap": "always",
5 | "tabWidth": 2,
6 | "semi": true,
7 | "singleQuote": true,
8 | "endOfLine": "lf"
9 | }
10 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "esbenp.prettier-vscode",
4 | "sysoev.language-stylus",
5 | "silvenon.mdx",
6 | "dbaeumer.vscode-eslint"
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.rulers": [80, 120],
3 |
4 | // ===
5 | // Spacing
6 | // ===
7 |
8 | "editor.insertSpaces": true,
9 | "editor.tabSize": 2,
10 | "editor.trimAutoWhitespace": true,
11 | "files.trimTrailingWhitespace": true,
12 | "files.eol": "\n",
13 | "files.insertFinalNewline": true,
14 | "files.trimFinalNewlines": true,
15 |
16 | // ===
17 | // Event Triggers
18 | // ===
19 |
20 | "editor.formatOnSave": true,
21 | "eslint.autoFixOnSave": true,
22 | "eslint.run": "onSave",
23 | "eslint.validate": [
24 | { "language": "javascript", "autoFix": true },
25 | { "language": "javascriptreact", "autoFix": true }
26 | ],
27 | "prettier.disableLanguages": [],
28 | "prettier.endOfLine": "lf"
29 | }
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Open Health Imaging Foundation
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/netlify.toml:
--------------------------------------------------------------------------------
1 | # https://www.netlify.com/docs/continuous-deployment/#deploy-contexts
2 | #
3 | # Global settings applied to the whole site.
4 | #
5 | # “base” is the directory to change to before starting build. If you set base:
6 | # that is where we will look for package.json/.nvmrc/etc, not repo root!
7 | # “command” is your build command.
8 | # “publish” is the directory to publish (relative to the root of your repo).
9 |
10 | # COMMENT: NODE_VERSION in root `.nvmrc` takes priority
11 | # COMMENT: Why we specify YARN_FLAGS: https://www.netlify.com/docs/build-gotchas/#yarn
12 | [build.environment]
13 | NODE_VERSION = "10.15.3"
14 | YARN_VERSION = "1.15.2"
15 | YARN_FLAGS = "--no-ignore-optional --pure-lockfile"
16 |
17 | # COMMENT: This a rule for Single Page Applications
18 | [[redirects]]
19 | from = "/*"
20 | to = "/index.html"
21 | status = 200
--------------------------------------------------------------------------------
/public/docz.css:
--------------------------------------------------------------------------------
1 | /* Tip & Success "Call outs" */
2 |
3 | p.tip {
4 | border-left-color: #f66;
5 | }
6 | p.success {
7 | border-left-color: #42b983;
8 | }
9 | p.tip::before {
10 | content: '!';
11 | background-color: #f66;
12 | }
13 | p.success::before {
14 | content: '\f00c';
15 | font-family: FontAwesome;
16 | background-color: #42b983;
17 | }
18 | p.tip,
19 | p.success {
20 | padding: 12px 24px 12px 30px;
21 | margin: 2em 0;
22 | line-height: 1.6em;
23 | border-left-width: 4px;
24 | border-left-style: solid;
25 | background-color: #f8f8f8;
26 | position: relative;
27 | border-bottom-right-radius: 2px;
28 | border-top-right-radius: 2px;
29 | }
30 | p.tip::before,
31 | p.success::before {
32 | position: absolute;
33 | top: 14px;
34 | left: -12px;
35 | color: #fff;
36 | width: 20px;
37 | height: 20px;
38 | border-radius: 100%;
39 | text-align: center;
40 | line-height: 20px;
41 | font-weight: bold;
42 | font-family: 'Dosis', 'Source Sans Pro', 'Helvetica Neue', Arial, sans-serif;
43 | font-size: 14px;
44 | }
45 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import autoprefixer from 'autoprefixer';
2 | import babel from 'rollup-plugin-babel';
3 | import builtins from 'rollup-plugin-node-builtins';
4 | import commonjs from 'rollup-plugin-commonjs';
5 | import external from 'rollup-plugin-peer-deps-external';
6 | import pkg from './package.json';
7 | import postcss from 'rollup-plugin-postcss';
8 | import resolve from 'rollup-plugin-node-resolve';
9 | import url from 'rollup-plugin-url';
10 |
11 | const globals = {
12 | react: 'React',
13 | 'react-dom': 'ReactDOM',
14 | 'react-i18next': 'reactI18next',
15 | '@ohif/i18n': 'i18n',
16 | };
17 |
18 | export default {
19 | input: 'src/index.js',
20 | output: [
21 | {
22 | file: pkg.main,
23 | format: 'umd',
24 | name: 'react-viewerbase',
25 | sourcemap: true,
26 | exports: 'named',
27 | globals,
28 | },
29 | {
30 | file: pkg.module,
31 | format: 'es',
32 | sourcemap: true,
33 | globals,
34 | },
35 | ],
36 | plugins: [
37 | builtins(),
38 | external(),
39 | postcss({
40 | modules: false,
41 | plugins: [autoprefixer],
42 | }),
43 | url(),
44 | babel({
45 | exclude: 'node_modules/**',
46 | runtimeHelpers: true,
47 | }),
48 | resolve({
49 | browser: true,
50 | }),
51 | commonjs({
52 | // https://github.com/airbnb/react-dates/issues/1183#issuecomment-392073823
53 | namedExports: {
54 | 'node_modules/react-dates/index.js': [
55 | 'DateRangePicker',
56 | 'isInclusivelyBeforeDay',
57 | ],
58 | },
59 | }),
60 | ],
61 | };
62 |
--------------------------------------------------------------------------------
/src/LayoutChooser/LayoutManager.css:
--------------------------------------------------------------------------------
1 | .viewport-container {
2 | float: left;
3 | position: relative;
4 | border: var(--viewport-border-thickness) solid var(--ui-border-color);
5 | }
6 |
7 | .viewport-container.active {
8 | border: var(--viewport-border-thickness) solid var(--active-color);
9 | }
10 |
11 | .EmptyViewport {
12 | display: flex;
13 | justify-content: center;
14 | align-items: center;
15 | height: 100%;
16 | color: var(--text-secondary-color);
17 | }
18 |
19 | .LayoutPanel {
20 | width: 100%;
21 | height: 100%;
22 | }
23 |
--------------------------------------------------------------------------------
/src/LayoutChooser/LayoutPanelDropTarget.css:
--------------------------------------------------------------------------------
1 | .LayoutPanelDropTarget {
2 | width: 100%;
3 | height: 100%;
4 |
5 | opacity: 1;
6 | transition: 0.3s all ease;
7 | }
8 |
9 | .LayoutPanelDropTarget.hovered {
10 | opacity: 0.5;
11 | cursor: copy;
12 | }
13 |
--------------------------------------------------------------------------------
/src/LayoutChooser/LayoutPanelDropTarget.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React, { Component } from 'react';
3 | import { DropTarget } from 'react-dnd';
4 | import './LayoutPanelDropTarget.css';
5 |
6 | // Drag sources and drop targets only interact
7 | // if they have the same string type.
8 | const Types = {
9 | THUMBNAIL: 'thumbnail',
10 | };
11 |
12 | const divTarget = {
13 | drop(props, monitor, component) {
14 | const item = monitor.getItem();
15 |
16 | if (props.onDrop) {
17 | props.onDrop({
18 | viewportIndex: props.viewportIndex,
19 | item,
20 | });
21 | }
22 |
23 | return {
24 | id: `LayoutPanelDropTarget-${props.viewportIndex}`,
25 | viewportIndex: props.viewportIndex,
26 | item,
27 | };
28 | },
29 | };
30 |
31 | // TODO: Find out why we can't move this into the Example app instead.
32 | // It looks like the context isn't properly shared.
33 | class LayoutPanelDropTarget extends Component {
34 | static className = 'LayoutPanelDropTarget';
35 |
36 | static defaultProps = {
37 | isOver: false,
38 | canDrop: false,
39 | };
40 |
41 | static propTypes = {
42 | connectDropTarget: PropTypes.func.isRequired,
43 | canDrop: PropTypes.bool.isRequired,
44 | isOver: PropTypes.bool.isRequired,
45 | viewportComponent: PropTypes.object,
46 | };
47 |
48 | render() {
49 | const { canDrop, isOver, connectDropTarget } = this.props;
50 | const isActive = canDrop && isOver;
51 |
52 | let className = LayoutPanelDropTarget.className;
53 |
54 | if (isActive) {
55 | className += ' hovered';
56 | } else if (canDrop) {
57 | className += ' can-drop';
58 | }
59 |
60 | return connectDropTarget(
61 |
{this.props.children}
62 | );
63 | }
64 | }
65 |
66 | const collect = (connect, monitor) => ({
67 | connectDropTarget: connect.dropTarget(),
68 | canDrop: monitor.canDrop(),
69 | isOver: monitor.isOver(),
70 | });
71 |
72 | export default DropTarget(Types.THUMBNAIL, divTarget, collect)(
73 | LayoutPanelDropTarget
74 | );
75 |
--------------------------------------------------------------------------------
/src/ScrollableArea/ScrollableArea.styl:
--------------------------------------------------------------------------------
1 | .scrollArea
2 | overflow: hidden
3 | position: relative
4 |
5 | .scrollable
6 | max-height: inherit
7 | overflow: hidden
8 | zoom: 1
9 |
10 | &.scrollX
11 | overflow-x: scroll
12 |
13 | &.scrollY
14 | overflow-y: scroll
15 |
16 | &.fit
17 | height: 100%
18 | width: 100%
19 |
20 | .scrollable
21 | bottom: 0
22 | left: 0
23 | max-height: none
24 | position: absolute
25 | right: 0
26 | top: 0
27 |
28 | .scrollNav
29 | background-color: rgba(0, 0, 0, 0.75)
30 | box-shadow: 0 0 10px 10px rgba(0, 0, 0, 0.75)
31 | cursor: pointer
32 | height: 24px
33 | left: 10px
34 | opacity: 0
35 | position: absolute
36 | right: 10px
37 | transition: all 0.3s ease
38 |
39 | .scrollNavIcon
40 | color: var(--active-color);
41 | display: block;
42 | width: 18px;
43 | height: 18px;
44 | margin: 0 auto;
45 | transition: color 0.3s ease;
46 |
47 | &:hover
48 | background-color: rgba(0, 0, 0, 0.9)
49 | box-shadow: 0 0 10px 10px rgba(0, 0, 0, 0.9)
50 |
51 | .scrollNavIcon
52 | color: var(--hover-color)
53 |
54 | .scrollNavUp
55 | border-bottom-left-radius: 12px
56 | border-bottom-right-radius: 12px
57 | top: 0
58 | transform: translateY(-24px)
59 |
60 | .scrollNavDown
61 | border-top-left-radius: 12px
62 | border-top-right-radius: 12px
63 | bottom: 0
64 | transform: translateY(24px)
65 |
66 | &.canScrollUp .scrollNavUp,
67 | &.canScrollDown .scrollNavDown
68 | opacity: 1
69 | transform: translateY(0)
70 |
--------------------------------------------------------------------------------
/src/__docs__/NameSpace.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from 'react';
2 | import { Link } from 'docz';
3 | import PropTypes from 'prop-types';
4 |
5 | const NameSpace = ({ name }) => (
6 |
7 | The namespace used on this component was: {name}
Check the{' '}
8 | translation docs{' '}
9 | to see how to override it.
10 |
11 | );
12 |
13 | NameSpace.propTypes = {
14 | name: PropTypes.string.isRequired,
15 | };
16 |
17 | export default NameSpace;
18 |
--------------------------------------------------------------------------------
/src/__docs__/assets/github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OHIF/react-viewerbase/08cb405ddaae17f58f64868bed688aee0420dc49/src/__docs__/assets/github.png
--------------------------------------------------------------------------------
/src/__docs__/assets/npm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OHIF/react-viewerbase/08cb405ddaae17f58f64868bed688aee0420dc49/src/__docs__/assets/npm.png
--------------------------------------------------------------------------------
/src/__docs__/assets/react.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OHIF/react-viewerbase/08cb405ddaae17f58f64868bed688aee0420dc49/src/__docs__/assets/react.png
--------------------------------------------------------------------------------
/src/__docs__/assets/typescript.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OHIF/react-viewerbase/08cb405ddaae17f58f64868bed688aee0420dc49/src/__docs__/assets/typescript.png
--------------------------------------------------------------------------------
/src/__docs__/compatibility.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: Compatibility
3 | route: /compatibility
4 | ---
5 |
6 | # Compatibility
7 |
8 | ## Browser Support
9 |
10 | React Viewerbase relies on
11 | [browsers supported by Styled Components](https://www.styled-components.com/docs/faqs#which-browsers-are-supported)
12 | or browsers supported by emotion.
13 |
14 | Supported Browsers: IE11, IE 9+ (with Map + Set polyfills), Chrome, Firefox (and
15 | derivatives), Edge, and Safari.
16 |
--------------------------------------------------------------------------------
/src/__docs__/config/Wrapper.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OHIF/react-viewerbase/08cb405ddaae17f58f64868bed688aee0420dc49/src/__docs__/config/Wrapper.js
--------------------------------------------------------------------------------
/src/__docs__/docs-settings.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: Docs Settings
3 | route: /docs-settings
4 | ---
5 |
6 | import LanguageSwitcher from '../components/languageSwitcher';
7 |
8 | # Components Language
9 |
10 | You can change the language of the components displayed in this documentation by
11 | choosing an option in the selector below:
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/__docs__/getting-started.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: Getting Started
3 | route: /getting-started
4 | ---
5 |
6 | # Getting Started
7 |
8 | ## Installation
9 |
10 | > This component library is pre- v1.0. All realeases until a v1.0 have the
11 | > possibility of introducing breaking changes. Please depend on an "exact"
12 | > version in your projects to prevent issues caused by loose versioning.
13 |
14 | Install `react-viewerbase` from npm:
15 |
16 | ```shell
17 | // with npm
18 | npm i react-viewerbase --save-exact
19 |
20 | // with yarn
21 | yarn add react-viewerbase --exact
22 | ```
23 |
24 | ## Usage
25 |
26 | There is some setup required for `react-viewerbase`. We are working to eliminate
27 | these requirements with the goal of providing ready to use components.
28 |
29 | ### External Dependencies
30 |
31 | We have tried to keep the number of external dependencies limited to make setup
32 | as easy as possible. You will, however, need to include either the default font
33 | or the font you set for your theme:
34 |
35 | ```js
36 | // Google Fonts, Sanchez & Roboto
37 | 'https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700|Sanchez&display=swap';
38 | ```
39 |
40 | React Viewerbase also looks for theme CSS variabled defined on `:root`. You can
41 | find a list of variables [on the theming page](/styling-and-theming)
42 |
43 | ### Import & Use
44 |
45 | You can use components in your React app:
46 |
47 | ```js
48 | import React, { Component } from 'react';
49 | import { LayoutButton } from 'react-viewerbase';
50 |
51 | class Example extends Component {
52 | constructor(props) {
53 | super(props);
54 |
55 | this.state = {
56 | selectedCell: {
57 | className: 'hover',
58 | col: 1,
59 | row: 1,
60 | },
61 | };
62 | }
63 |
64 | render() {
65 | return (
66 | this.setState({ selectedCell: cell })}
69 | />
70 | );
71 | }
72 | }
73 | ```
74 |
75 | > NOTE: It is heavily recommended that you use a CSS reset or normalize. As well
76 | > as the following style: `* { Box-sizing: Border-box }`
77 |
--------------------------------------------------------------------------------
/src/__docs__/introduction.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: Introduction
3 | route: /
4 | ---
5 |
6 | import { Playground, Props } from 'docz'
7 | import { State } from 'react-powerplug'
8 | import { TableList, TableListItem } from './../index.js'
9 |
10 | # Introduction
11 |
12 | **React Viewerbase is a collection of components and utilities** that power
13 | OHIF's [zero-footprint DICOM viewer](https://github.com/OHIF/Viewers)
14 | ([demo][demo-url]). We maintain them as a separate component library to:
15 |
16 | - Decouple presentation from business logic
17 | - Test and develop components in isolation
18 | - Provide well documented, reusable components
19 | - Aid rapid application development for context specific viewers
20 |
21 | ## Quick Example
22 |
23 | > This component library is pre- v1.0. All realeases until a v1.0 have the
24 | > possibility of introducing breaking changes. Please depend on an "exact"
25 | > version in your projects to prevent issues caused by loose versioning.
26 |
27 | ```js
28 | // Add 'react-viewerbase' as a dependency
29 | yarn add react-viewerbase
30 |
31 | // Import and use components
32 | import { TableList } from 'react-viewerbase'
33 | ```
34 |
35 |
36 |
47 | {({ state, setState }) => (
48 |
49 | {state.listItems.map((item, index) => {
50 | return (
51 | alert('item clicked')}
56 | >
57 |
58 |
59 | )
60 | })}
61 |
62 | )}
63 |
64 |
65 |
66 | ## FAQ
67 |
68 | ...
69 |
70 |
73 |
74 |
75 | [demo-url]: https://docs.ohif.org/demo/
76 |
77 |
--------------------------------------------------------------------------------
/src/__docs__/styling-and-theming.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: Styling & Theming
3 | route: /styling-and-theming
4 | ---
5 |
6 | # Styling & Theming
7 |
8 | ```css
9 | :root {
10 | /* Interface UI Colors */
11 | --default-color: #9ccef9;
12 | --hover-color: #ffffff;
13 | --active-color: #20a5d6;
14 | --ui-border-color: #44626f;
15 | --ui-border-color-dark: #3c5d80;
16 | --ui-border-color-active: #00a4d9;
17 | --primary-background-color: #000000;
18 | --box-background-color: #3e5975;
19 |
20 | --text-primary-color: #ffffff;
21 | --text-secondary-color: #91b9cd;
22 | --input-background-color: #2c363f;
23 | --input-placeholder-color: #d3d3d3;
24 |
25 | --table-hover-color: #2c363f;
26 | --table-text-primary-color: #ffffff;
27 | --table-text-secondary-color: #91b9cd;
28 |
29 | --large-numbers-color: #6fbde2;
30 |
31 | --state-error: #ffcccc;
32 | --state-error-border: #ffcccc;
33 | --state-error-text: #ffcccc;
34 |
35 | /* Common palette */
36 | --ui-yellow: #e29e4a;
37 | --ui-sky-blue: #6fbde2;
38 |
39 | /* State palette */
40 | --ui-state-error: #ffcccc;
41 | --ui-state-error-border: #993333;
42 | --ui-state-error-text: #661111;
43 | --ui-gray-lighter: #436270;
44 | --ui-gray-light: #516873;
45 | --ui-gray: #263340;
46 | --ui-gray-dark: #16202b;
47 | --ui-gray-darker: #151a1f;
48 | --ui-gray-darkest: #14202a;
49 |
50 | --calendar-day-color: #d3d3d3;
51 | --calendar-day-border-color: #d3d3d3;
52 | --calendar-day-active-hover-background-color: #516873;
53 | --calendar-main-color: #263340;
54 | --viewport-border-thickness: 1px;
55 | }
56 | ```
57 |
--------------------------------------------------------------------------------
/src/__docs__/wrapper.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import PropTypes from 'prop-types';
4 | import LanguageSwitcher from '../components/languageSwitcher';
5 |
6 | import './wrapper.styl';
7 |
8 | const Wrapper = ({ children }) => (
9 |
10 |
11 | Display components in:
12 |
13 |
14 | {children}
15 |
16 | );
17 |
18 | Wrapper.propTypes = {
19 | children: PropTypes.node.isRequired,
20 | };
21 |
22 | export default Wrapper;
23 |
--------------------------------------------------------------------------------
/src/__docs__/wrapper.styl:
--------------------------------------------------------------------------------
1 | [class*="Sidebar__Menus"]
2 | @media (min-width: 1120px)
3 | margin-top: 80px
4 |
5 | .sidebarLanguageSwitcher
6 | position: fixed
7 | top: 155px
8 | left 0
9 | z-index: 1049
10 | padding: 0 24px 20px
11 | border-bottom: 1px dotted #CED4DE
12 | background: #F5F6F7
13 | transition: all 200ms ease
14 | @media (max-width: 1119px)
15 | left -100%
16 |
--------------------------------------------------------------------------------
/src/components/checkbox/__docs__/checkbox.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: Checkbox
3 | menu: Elements
4 | route: /components/checkbox
5 | ---
6 |
7 | import { Playground, Props } from 'docz'
8 | import { State } from 'react-powerplug'
9 | import { Checkbox } from './../index'
10 | import NameSpace from '../../../__docs__/NameSpace'
11 |
12 | # Checkbox
13 |
14 | ## Basic usage
15 |
16 |
17 |
23 | {({ state, setState }) => (
24 |
25 |
26 |
{JSON.stringify(state, null, 2)}
27 |
28 |
29 |
30 |
31 |
32 | )}
33 |
34 |
35 |
36 | ## API
37 |
38 |
39 |
40 | ## Translation Namespace
41 |
42 |
--------------------------------------------------------------------------------
/src/components/checkbox/checkbox.css:
--------------------------------------------------------------------------------
1 | .ohif-check-container {
2 | --check-button-dim: 15px;
3 | }
4 |
5 | .ohif-check-container input {
6 | position: absolute;
7 | opacity: 0;
8 | height: inherit;
9 | width: inherit;
10 | cursor: default;
11 | }
12 |
13 | .ohif-check-container {
14 | display: block;
15 | position: relative;
16 | padding-left: 35px;
17 | margin-bottom: 12px;
18 | cursor: pointer;
19 | -webkit-user-select: none;
20 | -moz-user-select: none;
21 | -ms-user-select: none;
22 | user-select: none;
23 | }
24 |
25 | .ohif-checkbox {
26 | width: var(--check-button-dim);
27 | height: var(--check-button-dim);
28 | position: absolute;
29 | top: 20%;
30 | left: 5%;
31 | cursor: pointer;
32 | background-color: var(--ui-gray-lighter);
33 | }
34 |
35 | .ohif-checkbox:hover {
36 | background-color: var(--default-color);
37 | }
38 |
39 | .ohif-checkbox:after {
40 | content: '';
41 | position: absolute;
42 | width: 70%;
43 | height: 70%;
44 | }
45 |
46 | .ohif-checkbox.ohif-checked:after {
47 | display: block;
48 | top: 14%;
49 | left: 14%;
50 | background: white;
51 | }
52 |
53 | .ohif-check-label {
54 | font-size: 12px;
55 | font-weight: 500;
56 | }
57 |
--------------------------------------------------------------------------------
/src/components/checkbox/checkbox.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import './checkbox.css';
4 |
5 | export class Checkbox extends Component {
6 | static propTypes = {
7 | label: PropTypes.string.isRequired,
8 | checked: PropTypes.bool,
9 | };
10 |
11 | constructor(props) {
12 | super(props);
13 | this.state = { checked: props.checked, label: props.label };
14 | }
15 |
16 | handleChange(e) {
17 | this.setState({ checked: e.target.checked });
18 | }
19 |
20 | render() {
21 | let checkbox;
22 | if (this.state.checked) {
23 | checkbox = ;
24 | } else {
25 | checkbox = ;
26 | }
27 |
28 | return (
29 |
30 |
43 |
44 | );
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/components/checkbox/index.js:
--------------------------------------------------------------------------------
1 | export { Checkbox } from './checkbox.js';
2 |
--------------------------------------------------------------------------------
/src/components/cineDialog/__docs__/cineDialog.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: CINE Dialog
3 | menu: Components
4 | route: /components/cine-dialog
5 | ---
6 |
7 | import { Playground, Props } from 'docz'
8 | import { State } from 'react-powerplug'
9 | import { CineDialog } from './../index'
10 | import NameSpace from '../../../__docs__/NameSpace'
11 |
12 | # CINE Dialog
13 |
14 | ## Basic usage
15 |
16 |
17 |
25 | {({ state, setState }) => (
26 |
27 |
28 |
{JSON.stringify(state, null, 2)}
29 |
30 |
31 |
34 | setState({ lastChange: 'Clicked SkipToStart' })
35 | }
36 | onClickSkipToEnd={() =>
37 | setState({ lastChange: 'Clicked SkipToEnd' })
38 | }
39 | onClickNextButton={() => setState({ lastChange: 'Clicked Next' })}
40 | onClickBackButton={() => setState({ lastChange: 'Clicked Back' })}
41 | onLoopChanged={value => setState({ isLoopEnabled: value })}
42 | onFrameRateChanged={value => setState({ cineFrameRate: value })}
43 | onPlayPauseChanged={() => setState({ isPlaying: !state.isPlaying })}
44 | />
45 |
46 |
47 | )}
48 |
49 |
50 |
51 | ## API
52 |
53 |
54 |
55 | ## Translation Namespace
56 |
57 |
58 |
--------------------------------------------------------------------------------
/src/components/cineDialog/index.js:
--------------------------------------------------------------------------------
1 | import CineDialog from './CineDialog';
2 | export { CineDialog };
3 |
--------------------------------------------------------------------------------
/src/components/index.js:
--------------------------------------------------------------------------------
1 | import {
2 | ExampleDropTarget,
3 | StudyBrowser,
4 | ThumbnailEntry,
5 | } from './studyBrowser';
6 | import { LayoutButton, LayoutChooser } from './layoutButton';
7 | import { MeasurementTable, MeasurementTableItem } from './measurementTable';
8 | import { Overlay, OverlayTrigger } from './overlayTrigger';
9 | import { TableList, TableListItem } from './tableList';
10 | import {
11 | AboutModal,
12 | UserPreferences,
13 | UserPreferencesModal,
14 | } from './userPreferencesModal';
15 |
16 | import { CineDialog } from './cineDialog';
17 | import { QuickSwitch } from './quickSwitch';
18 | import { RoundedButtonGroup } from './roundedButtonGroup';
19 | import { SelectTree } from './selectTree';
20 | import { SimpleDialog } from './simpleDialog';
21 | import { StudyList } from './studyList';
22 | import { ToolbarSection } from './toolbarSection';
23 | import { Tooltip } from './tooltip';
24 |
25 | export {
26 | CineDialog,
27 | ExampleDropTarget,
28 | LayoutButton,
29 | LayoutChooser,
30 | MeasurementTable,
31 | MeasurementTableItem,
32 | Overlay,
33 | OverlayTrigger,
34 | QuickSwitch,
35 | RoundedButtonGroup,
36 | SelectTree,
37 | SimpleDialog,
38 | StudyBrowser,
39 | StudyList,
40 | TableList,
41 | TableListItem,
42 | ThumbnailEntry,
43 | ToolbarSection,
44 | Tooltip,
45 | AboutModal,
46 | UserPreferences,
47 | UserPreferencesModal,
48 | };
49 |
--------------------------------------------------------------------------------
/src/components/languageSwitcher/LanguageSwitcher.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import i18n from '@ohif/i18n';
3 |
4 | import './LanguageSwitcher.styl';
5 | import { withTranslation } from '../../utils/LanguageProvider';
6 |
7 | const LanguageSwitcher = () => {
8 | const getCurrentLanguage = (language = i18n.language) =>
9 | language.split('-')[0];
10 |
11 | const [currentLanguage, setCurrentLanguage] = useState(getCurrentLanguage());
12 | const languages = [
13 | // TODO: list of available languages should come from i18n.options.resources
14 | {
15 | value: 'en',
16 | label: 'English',
17 | },
18 | {
19 | value: 'es',
20 | label: 'Spanish',
21 | },
22 | ];
23 |
24 | const onChange = () => {
25 | const { value } = event.target;
26 | const language = getCurrentLanguage(value);
27 | setCurrentLanguage(language);
28 |
29 | i18n.init({
30 | fallbackLng: language,
31 | lng: language,
32 | });
33 | };
34 |
35 | useEffect(() => {
36 | let mounted = true;
37 |
38 | i18n.on('languageChanged', () => {
39 | if (mounted) {
40 | setCurrentLanguage(getCurrentLanguage());
41 | }
42 | });
43 |
44 | return () => {
45 | mounted = false;
46 | };
47 | }, []);
48 |
49 | return (
50 |
63 | );
64 | };
65 |
66 | export default withTranslation('UserPreferencesModal')(LanguageSwitcher);
67 |
--------------------------------------------------------------------------------
/src/components/languageSwitcher/LanguageSwitcher.styl:
--------------------------------------------------------------------------------
1 | .language-select
2 | color: var(--primary-background-color)
3 | display: block
4 | min-width: 150px
5 |
--------------------------------------------------------------------------------
/src/components/languageSwitcher/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './LanguageSwitcher';
2 |
--------------------------------------------------------------------------------
/src/components/layoutButton/LayoutButton.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 |
3 | import { LayoutChooser } from './LayoutChooser.js';
4 | import PropTypes from 'prop-types';
5 | import ToolbarButton from '../../viewer/ToolbarButton';
6 |
7 | export class LayoutButton extends PureComponent {
8 | static defaultProps = {
9 | dropdownVisible: false,
10 | };
11 |
12 | static propTypes = {
13 | dropdownVisible: PropTypes.bool.isRequired,
14 | /** Called with the selectedCell number when grid sell is selected */
15 | onChange: PropTypes.func,
16 | /** The cell to show as selected */
17 | selectedCell: PropTypes.object,
18 | };
19 |
20 | state = {
21 | dropdownVisible: this.props.dropdownVisible,
22 | };
23 |
24 | componentDidUpdate(prevProps) {
25 | if (this.props.dropdownVisible !== prevProps.dropdownVisible) {
26 | this.setState({
27 | dropdownVisible: this.props.dropdownVisible,
28 | });
29 | }
30 | }
31 |
32 | onClick = () => {
33 | this.setState({
34 | dropdownVisible: !this.state.dropdownVisible,
35 | });
36 | };
37 |
38 | onChange = selectedCell => {
39 | if (this.props.onChange) {
40 | this.props.onChange(selectedCell);
41 | }
42 | };
43 |
44 | render() {
45 | return (
46 |
47 |
53 |
59 |
60 | );
61 | }
62 | }
63 |
64 | export default LayoutButton;
65 |
--------------------------------------------------------------------------------
/src/components/layoutButton/LayoutChooser.styl:
--------------------------------------------------------------------------------
1 | @import './../../design/styles/common/button.styl'
2 | @import './../../design/styles/common/global.styl'
3 |
4 | $borderColor = rgba(77, 99, 110, 0.81)
5 |
6 | .layoutChooser-dropdown-menu
7 | position: absolute;
8 | top: 100%;
9 | left: 0;
10 | z-index: 1000;
11 | display: none;
12 | float: left;
13 | min-width: 160px;
14 | padding: 5px 0;
15 | margin: 2px 0 0;
16 | font-size: 14px;
17 | text-align: left;
18 | list-style: none;
19 | background-color: #fff;
20 | -webkit-background-clip: padding-box;
21 | background-clip: padding-box;
22 | border: 1px solid #ccc;
23 | border: 1px solid rgba(0,0,0,.15);
24 | border-radius: 4px;
25 | -webkit-box-shadow: 0 6px 12px rgba(0,0,0,.175);
26 | box-shadow: 0 6px 12px rgba(0,0,0,.175);
27 |
28 | .layoutChooser
29 | .selectedBefore
30 | background-color: #5cc3eb
31 |
32 | border: 1px solid $borderColor;
33 | border-radius: 8px
34 | padding: 5px 0;
35 | position: absolute;
36 | z-index: 5000
37 |
38 | table
39 | margin: 0 auto
40 | border-spacing: 0;
41 | border-collapse: collapse;
42 | td
43 | cursor: pointer
44 | transition(background-color 0.1s ease)
45 |
46 | &:hover, &.hover // Add the hover class here to be triggered by mouseenter/mouseleave
47 | background-color: #209ac9
48 |
--------------------------------------------------------------------------------
/src/components/layoutButton/__docs__/layoutButton.css:
--------------------------------------------------------------------------------
1 | .LayoutExample {
2 | background-color: var(--primary-background-color);
3 | width: 160px;
4 | height: 160px;
5 | }
6 |
7 | .GridLayout td {
8 | padding: 5px;
9 | border: 1px solid black;
10 | }
11 |
--------------------------------------------------------------------------------
/src/components/layoutButton/__docs__/layoutButton.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: Layout Button
3 | menu: Components
4 | route: /components/layout-button
5 | ---
6 |
7 | import { Playground, Props } from 'docz'
8 | import { State } from 'react-powerplug'
9 | import { LayoutButton } from './../index.js'
10 | import './layoutButton.css'
11 | import NameSpace from '../../../__docs__/NameSpace'
12 |
13 | # Layout Button
14 |
15 | ## Basic usage
16 |
17 |
18 |
25 | {({ state, setState }) => (
26 |
27 | {/* STATE */}
28 | {JSON.stringify(state, null, 2) }
29 |
30 | {/* COMPONENT */}
31 |
32 | setState({ selectedCell: cell })}
35 | />
36 |
37 |
38 | )}
39 |
40 |
41 |
42 |
43 | ## API
44 |
45 |
46 |
47 | ## Translation Namespace
48 |
49 |
50 |
--------------------------------------------------------------------------------
/src/components/layoutButton/index.js:
--------------------------------------------------------------------------------
1 | export { LayoutButton } from './LayoutButton.js';
2 | export { LayoutChooser } from './LayoutChooser.js';
3 |
--------------------------------------------------------------------------------
/src/components/measurementTable/MeasurementTableItem.styl:
--------------------------------------------------------------------------------
1 | .measurementItem
2 | .rowActions
3 | background-color: var(--ui-gray-darker)
4 | height: 0
5 | overflow: hidden
6 | transition: all 0.3s ease
7 | visibility: hidden
8 | padding-left: 14px
9 |
10 | .btnAction
11 | background-color: transparent;
12 | border: none;
13 | color: var(--default-color)
14 | cursor: pointer
15 | line-height: 35px
16 | height: 35px
17 | transition: all 0.3s ease
18 |
19 | &:hover, &:active
20 | color: var(--text-primary-color)
21 |
22 | i
23 | margin-right: 4px;
24 |
25 | &.selected
26 | .rowActions
27 | height: 35px
28 | visibility: visible
29 |
30 | .measurementLocation
31 | margin-left: 9px;
32 | margin-top: 9px;
33 |
34 | .measurementDisplayText
35 | display: inline-block;
36 | margin-top: 9px;
37 | margin-left: 9px;
38 | padding-left: 9px;
39 | width: 90px;
40 | border-left: 1px solid var(--text-secondary-color);
41 | color: var(--text-primary-color);
42 |
43 | .itemIndex
44 | -webkit-box-sizing: initial;
45 | -moz-box-sizing: initial;
46 | box-sizing: initial;
47 |
48 | &.hasWarnings
49 | .itemIndex
50 | opacity: 1
51 | background-color: #e29e4a;
52 | color: #fff;
53 |
54 | .warning-icon
55 | display: block;
56 | margin: 7px auto 0;
57 |
58 | svg
59 | width: 22px;
60 | height: 20px;
61 | pointer-events: inherit;
62 |
--------------------------------------------------------------------------------
/src/components/measurementTable/__docs__/measurementTable.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: Measurement Table
3 | menu: Components
4 | route: /components/measurement-table
5 | ---
6 |
7 | import { Playground, Props } from 'docz'
8 | import { State } from 'react-powerplug'
9 | import { MeasurementTable } from './../index.js'
10 | import NameSpace from '../../../__docs__/NameSpace'
11 | //
12 | import measurements from './measurements.js'
13 | import timepoints from './timepoints.js'
14 | import warnings from './warnings.js'
15 |
16 | # Measurement Table
17 |
18 | ## Basic usage
19 |
20 |
21 |
29 | {({ state, setState }) => (
30 |
31 | alert('onItemClick')}
34 | onRelabelClick={() => alert('onRelabelClick')}
35 | onDeleteClick={() => alert('onDeleteClick')}
36 | onEditDescriptionClick={() => alert('onEditDescriptionClick')}
37 | />
38 |
39 | )}
40 |
41 |
42 |
43 | ## API
44 |
45 |
46 |
47 | ## Translation Namespace
48 |
49 |
50 |
--------------------------------------------------------------------------------
/src/components/measurementTable/__docs__/timepoints.js:
--------------------------------------------------------------------------------
1 | export default [
2 | {
3 | label: 'Follow-up 2',
4 | date: '20-Dec-18',
5 | },
6 | {
7 | label: 'Follow-up 1',
8 | date: '15-Jun-18',
9 | },
10 | {
11 | label: 'Baseline',
12 | date: '10-Apr-18',
13 | },
14 | ];
15 |
--------------------------------------------------------------------------------
/src/components/measurementTable/__docs__/warnings.js:
--------------------------------------------------------------------------------
1 | export default {
2 | warningList: [
3 | 'All measurements should have a location',
4 | 'Nodal lesions must be >= 15mm short axis AND >= double the acquisition slice thickness by CT and MR',
5 | ],
6 | };
7 |
--------------------------------------------------------------------------------
/src/components/measurementTable/index.js:
--------------------------------------------------------------------------------
1 | export { MeasurementTable } from './MeasurementTable.js';
2 | export { MeasurementTableItem } from './MeasurementTableItem.js';
3 |
--------------------------------------------------------------------------------
/src/components/overlayTrigger/__docs__/OverlayTrigger.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: Overlay Trigger
3 | menu: Components
4 | route: /components/overlay-trigger
5 | ---
6 |
7 | import { Playground, Props } from 'docz'
8 | import { Tooltip } from './../../tooltip'
9 | import { OverlayTrigger } from './../index.js'
10 |
11 | # Overlay Trigger
12 |
13 | ## Basic usage
14 |
15 | ### On Hover
16 |
17 |
18 |
19 |
23 | {
24 | ['right', 'left', 'bottom', 'top'].map((placement) => {
25 | return (
26 |
27 |
36 | Here I am!
37 |
38 | }
39 | >
40 |
41 |
42 |
43 | )
44 | })
45 | }
46 |
47 |
48 |
49 |
50 | ### On Click
51 |
52 |
53 |
59 | {['right', 'left', 'bottom', 'top'].map(placement => {
60 | return (
61 |
71 | Here I am!
72 |
73 | }
74 | >
75 |
76 |
77 | )
78 | })}
79 |
80 |
81 |
82 | ## API
83 |
84 |
85 |
--------------------------------------------------------------------------------
/src/components/overlayTrigger/createChainedFunction.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Safe chained function
3 | *
4 | * Will only create a new function if needed,
5 | * otherwise will pass back existing functions or null.
6 | *
7 | * @param {function} functions to chain
8 | * @returns {function|null}
9 | */
10 | function createChainedFunction(...funcs) {
11 | return funcs
12 | .filter(f => f != null)
13 | .reduce((acc, f) => {
14 | if (typeof f !== 'function') {
15 | throw new Error(
16 | 'Invalid Argument Type, must only provide functions, undefined, or null.'
17 | );
18 | }
19 |
20 | if (acc === null) {
21 | return f;
22 | }
23 |
24 | return function chainedFunction(...args) {
25 | acc.apply(this, args);
26 | f.apply(this, args);
27 | };
28 | }, null);
29 | }
30 |
31 | export default createChainedFunction;
32 |
--------------------------------------------------------------------------------
/src/components/overlayTrigger/index.js:
--------------------------------------------------------------------------------
1 | export { Overlay } from './Overlay.js';
2 | export { OverlayTrigger } from './OverlayTrigger.js';
3 |
--------------------------------------------------------------------------------
/src/components/quickSwitch/SeriesList.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | import { ThumbnailEntry } from './../studyBrowser';
5 | import './SeriesList.styl';
6 |
7 | export class SeriesList extends Component {
8 | static propTypes = {
9 | seriesItems: PropTypes.array.isRequired,
10 | onClick: PropTypes.func.isRequired,
11 | activeDisplaySetInstanceUid: PropTypes.string,
12 | };
13 |
14 | render() {
15 | return (
16 |
17 |
18 |
{this.getSeriesItems()}
19 |
20 |
21 | );
22 | }
23 |
24 | getSeriesItems = () => {
25 | return this.props.seriesItems.map((seriesData, index) => {
26 | return (
27 | this.props.onClick(seriesData)}
36 | />
37 | );
38 | });
39 | };
40 | }
41 |
--------------------------------------------------------------------------------
/src/components/quickSwitch/SeriesList.styl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OHIF/react-viewerbase/08cb405ddaae17f58f64868bed688aee0420dc49/src/components/quickSwitch/SeriesList.styl
--------------------------------------------------------------------------------
/src/components/quickSwitch/StudiesItem.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | import './StudiesItem.styl';
5 |
6 | export class StudiesItem extends Component {
7 | static propTypes = {
8 | onClick: PropTypes.func.isRequired,
9 | studyData: PropTypes.object.isRequired,
10 | active: PropTypes.bool,
11 | };
12 |
13 | render() {
14 | const {
15 | studyDate,
16 | studyDescription,
17 | modalities,
18 | studyAvailable,
19 | } = this.props.studyData;
20 | const activeClass = this.props.active ? ' active' : '';
21 | const hasDescriptionAndDate = studyDate && studyDescription;
22 | return (
23 |
27 |
28 |
29 |
33 | {modalities}
34 |
35 |
36 |
37 | {hasDescriptionAndDate ? (
38 |
39 | {studyDate}
40 | {studyDescription}
41 |
42 | ) : (
43 |
44 | {studyAvailable ? (
45 | N/A
46 | ) : (
47 | Click to load
48 | )}
49 |
50 | )}
51 |
52 |
53 |
54 | );
55 | }
56 |
57 | getModalitiesStyle = () => {
58 | return {};
59 | };
60 | }
61 |
--------------------------------------------------------------------------------
/src/components/quickSwitch/StudiesList.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | import { StudiesItem } from './StudiesItem.js';
5 | import './StudiesList.styl';
6 |
7 | export class StudiesList extends Component {
8 | static propTypes = {
9 | class: PropTypes.string,
10 | studyListData: PropTypes.array.isRequired,
11 | onClick: PropTypes.func.isRequired,
12 | activeStudyInstanceUid: PropTypes.string,
13 | };
14 |
15 | render() {
16 | return (
17 |
18 | {this.getBrowserItems()}
19 |
20 | );
21 | }
22 |
23 | getBrowserItems = () => {
24 | return this.props.studyListData.map((studyData, index) => {
25 | return (
26 | this.props.onClick(studyData)}
33 | />
34 | );
35 | });
36 | };
37 | }
38 |
--------------------------------------------------------------------------------
/src/components/quickSwitch/StudiesList.styl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OHIF/react-viewerbase/08cb405ddaae17f58f64868bed688aee0420dc49/src/components/quickSwitch/StudiesList.styl
--------------------------------------------------------------------------------
/src/components/quickSwitch/__docs__/quickSwitch.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: Quick Switch
3 | menu: Components
4 | route: /components/quick-switch
5 | ---
6 |
7 | import { Playground, Props } from 'docz'
8 | import { State } from 'react-powerplug'
9 | import { QuickSwitch } from './../index.js'
10 | //
11 | import studies from './studies.js'
12 |
13 | # Quick Switch
14 |
15 | ## Basic usage
16 |
17 |
18 |
21 | {({ state, setState }) => (
22 | {
28 | return !!study.active;
29 | }).thumbnails}
30 | onSeriesSelected={(selectedSeries) => {
31 | const { studyListData } = state;
32 | const selectedId = selectedSeries.displaySetInstanceUid;
33 |
34 | studyListData.forEach(study => {
35 | study.thumbnails.forEach(series => {
36 | series.active = series.displaySetInstanceUid === selectedId;
37 | })
38 | });
39 |
40 | setState({ studyListData });
41 | }}
42 | onStudySelected={(selectedStudy) => {
43 | const { studyListData } = state;
44 | const selectedId = selectedStudy.studyInstanceUid;
45 |
46 | studyListData.forEach(study => {
47 | study.active = study.studyInstanceUid === selectedId;
48 | });
49 |
50 | setState({ studyListData });
51 | }}
52 | />
53 | )}
54 |
55 |
56 |
57 |
58 |
59 | ## API
60 |
61 |
62 |
--------------------------------------------------------------------------------
/src/components/quickSwitch/index.js:
--------------------------------------------------------------------------------
1 | export { QuickSwitch } from './QuickSwitch.js';
2 |
--------------------------------------------------------------------------------
/src/components/radioButtonList/RadioButtonList.css:
--------------------------------------------------------------------------------
1 | .ohif-radio-button-container {
2 | --radio-button-dim: 20px;
3 | }
4 |
5 | .ohif-radio-button-container input {
6 | position: absolute;
7 | opacity: 0;
8 | z-index: 10000;
9 | cursor: pointer;
10 | height: inherit;
11 | width: inherit;
12 | }
13 |
14 | .ohif-radio-button-container {
15 | display: block;
16 | position: relative;
17 | padding-left: 35px;
18 | margin-bottom: 12px;
19 | cursor: pointer;
20 | font-size: 22px;
21 | -webkit-user-select: none;
22 | -moz-user-select: none;
23 | -ms-user-select: none;
24 | user-select: none;
25 | }
26 |
27 | .ohif-radio-button {
28 | width: var(--radio-button-dim);
29 | height: var(--radio-button-dim);
30 | position: absolute;
31 | top: 25%;
32 | left: 0;
33 | background-color: var(--ui-gray-lighter);
34 | border-radius: 50%;
35 | z-index: 1;
36 | }
37 |
38 | .ohif-radio-button:hover {
39 | background-color: var(--default-color);
40 | border-radius: 100%;
41 | }
42 |
43 | .ohif-radio-button:after {
44 | content: '';
45 | position: absolute;
46 | display: none;
47 | width: 70%;
48 | height: 70%;
49 | }
50 |
51 | .ohif-radio-button-container .ohif-radio-button.ohif-selected:after {
52 | display: block;
53 | top: 14%;
54 | left: 14%;
55 | border-radius: 50%;
56 | background: white;
57 | }
58 |
59 | .ohif-radio-button-label {
60 | font-size: 12px;
61 | font-weight: 500;
62 | }
63 |
--------------------------------------------------------------------------------
/src/components/radioButtonList/RadioButtonList.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import './RadioButtonList.css';
4 |
5 | export class RadioButtonList extends Component {
6 | static className = 'RadioButtonList';
7 |
8 | //TODO: Add fields to propTypes.description?
9 | //These would be label (required), id (required), and checked (optional).
10 | static propTypes = {
11 | description: PropTypes.arrayOf(
12 | PropTypes.shape({
13 | label: PropTypes.string.isRequired,
14 | id: PropTypes.string.isRequired,
15 | checked: PropTypes.bool,
16 | })
17 | ),
18 | };
19 |
20 | constructor(props) {
21 | super(props);
22 | this.state = {};
23 |
24 | for (let button of props.description) {
25 | if (button.checked) {
26 | this.state.checked = button.id;
27 | }
28 | }
29 | }
30 |
31 | handleChange(e) {
32 | this.setState({ checked: e.target.value });
33 | }
34 |
35 | render() {
36 | let buttons = this.props.description.map(button => {
37 | let input = (
38 | {
42 | this.handleChange(e);
43 | }}
44 | value={button.id}
45 | />
46 | );
47 |
48 | //needed to style the custom radio check
49 | let inputSpan;
50 | if (this.state.checked === button.id) {
51 | inputSpan = (
52 | {input}
53 | );
54 | } else {
55 | inputSpan = {input};
56 | }
57 |
58 | return (
59 |
60 |
64 |
65 | );
66 | });
67 |
68 | return (
69 |
70 |
71 |
72 | );
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/components/radioButtonList/__docs__/radioButtonList.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: Radio Button List
3 | menu: Elements
4 | route: /components/radio-button-list
5 | ---
6 |
7 | import { Playground, Props } from 'docz'
8 | import { State } from 'react-powerplug'
9 | import { RadioButtonList } from './../index'
10 | import NameSpace from '../../../__docs__/NameSpace'
11 |
12 | #Radio Button List
13 |
14 | ## Basic usage
15 |
16 |
17 |
38 | {({ state, setState }) => (
39 |
40 |
41 |
{JSON.stringify(state, null, 2)}
42 |
43 |
44 |
62 |
63 |
64 | )}
65 |
66 |
67 |
68 | ## API
69 |
70 |
71 |
72 | ## Translation Namespace
73 |
74 |
--------------------------------------------------------------------------------
/src/components/radioButtonList/index.js:
--------------------------------------------------------------------------------
1 | export { RadioButtonList } from './RadioButtonList.js';
2 |
--------------------------------------------------------------------------------
/src/components/roundedButtonGroup/RoundedButtonGroup.css:
--------------------------------------------------------------------------------
1 | .RoundedButtonGroup {
2 | --height: 25px;
3 |
4 | position: relative;
5 | z-index: 0;
6 | }
7 |
8 | .roundedButtonWrapper {
9 | cursor: pointer;
10 | display: inline-block;
11 | float: left;
12 | margin-left: -2px;
13 | text-decoration: none;
14 | text-align: center;
15 | }
16 |
17 | .roundedButtonWrapper.disabled {
18 | opacity: 0.5;
19 | cursor: not-allowed;
20 | }
21 |
22 | .RoundedButtonGroup .roundedButtonWrapper .roundedButton {
23 | align-items: center;
24 | background-color: var(--ui-gray-dark);
25 | border: 2px solid var(--ui-border-color-dark);
26 | color: var(--text-secondary-color);
27 | display: flex;
28 | font-size: 15px;
29 | font-weight: 500;
30 | justify-content: center;
31 | height: var(--height);
32 | line-height: var(--height);
33 | padding: 0 22px;
34 | position: relative;
35 | text-transform: uppercase;
36 | transition: var(--sidebar-transition);
37 | z-index: 1;
38 | }
39 |
40 | .roundedButtonWrapper
41 | .roundedButton
42 | svg
43 | .roundedButtonWrapper
44 | .roundedButton
45 | span {
46 | margin: 0 2px;
47 | }
48 |
49 | .roundedButtonWrapper .roundedButton i {
50 | line-height: 15px;
51 | font-size: 15px;
52 | }
53 |
54 | .bottomLabel {
55 | color: var(--text-secondary-color);
56 | font-size: 12px;
57 | font-weight: 500;
58 | line-height: 12px;
59 | margin-top: 8px;
60 | }
61 |
62 | .roundedButtonWrapper:first-child {
63 | margin-left: 0;
64 | }
65 |
66 | .RoundedButtonGroup .roundedButtonWrapper:first-child .roundedButton {
67 | border-bottom-left-radius: var(--height);
68 | border-top-left-radius: var(--height);
69 | }
70 |
71 | .RoundedButtonGroup .roundedButtonWrapper:last-child .roundedButton {
72 | border-bottom-right-radius: var(--height);
73 | border-top-right-radius: var(--height);
74 | }
75 |
76 | .roundedButtonWrapper:hover .roundedButton {
77 | background-color: var(--box-background-color);
78 | color: var(--ui-gray-dark);
79 | }
80 |
81 | .roundedButtonWrapper.active .roundedButton {
82 | background-color: var(--active-color);
83 | border-color: var(--ui-border-color-active);
84 | color: var(--ui-gray-dark);
85 | z-index: 2;
86 | }
87 |
--------------------------------------------------------------------------------
/src/components/roundedButtonGroup/__docs__/roundedButtonGroup.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: Rounded Button Group
3 | menu: Components
4 | route: /components/rounded-button-group
5 | ---
6 |
7 | import { Playground, Props } from 'docz';
8 | import { State } from 'react-powerplug';
9 | import { RoundedButtonGroup } from './../index.js';
10 |
11 | # Rounded Button Group
12 |
13 | A basic styled toggle switch
14 |
15 | ## Basic usage
16 |
17 |
18 |
35 | {({ state, setState }) => (
36 |
37 |
38 |
value: {JSON.stringify(state.value, null, 2)}
39 |
40 | setState({ value })}
43 | />
44 |
45 | )}
46 |
47 |
48 |
49 | ## API
50 |
51 |
52 |
--------------------------------------------------------------------------------
/src/components/roundedButtonGroup/index.js:
--------------------------------------------------------------------------------
1 | export { RoundedButtonGroup } from './RoundedButtonGroup.js';
2 |
--------------------------------------------------------------------------------
/src/components/selectTree/InputRadio.js:
--------------------------------------------------------------------------------
1 | import { Component } from 'react';
2 | import React from 'react';
3 | import PropTypes from 'prop-types';
4 |
5 | export default class InputRadio extends Component {
6 | static propTypes = {
7 | value: PropTypes.string,
8 | label: PropTypes.string.isRequired,
9 | itemData: PropTypes.object.isRequired,
10 | labelClass: PropTypes.string,
11 | id: PropTypes.string.isRequired,
12 | onSelected: PropTypes.func.isRequired,
13 | };
14 |
15 | render() {
16 | const labelClass = this.props.labelClass ? this.props.labelClass : '';
17 | return (
18 |
31 | );
32 | }
33 |
34 | onSelected = evt => {
35 | this.props.onSelected(evt, this.props.itemData);
36 | };
37 | }
38 |
--------------------------------------------------------------------------------
/src/components/selectTree/SelectTreeBreadcrumb.js:
--------------------------------------------------------------------------------
1 | import { Component } from 'react';
2 | import React from 'react';
3 | import PropTypes from 'prop-types';
4 | import { Icon } from './../../elements/Icon';
5 |
6 | export default class SelectTreeBreadcrumb extends Component {
7 | static propTypes = {
8 | label: PropTypes.string.isRequired,
9 | value: PropTypes.string.isRequired,
10 | onSelected: PropTypes.func.isRequired,
11 | };
12 |
13 | render() {
14 | return (
15 |
16 |
34 |
35 | );
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/components/selectTree/__docs__/selectTree.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: Select Tree
3 | menu: Components
4 | route: /components/select-tree
5 | ---
6 |
7 | import { Playground, Props } from 'docz'
8 | import { State } from 'react-powerplug'
9 | import { SelectTree } from './../index.js'
10 |
11 | import selectTreeItems from './selectTreeItems.js'
12 |
13 | # SelectTree
14 |
15 | ## Basic usage
16 |
17 |
18 |
21 | {({ state, setState }) => (
22 |
23 |
24 | {JSON.stringify(state.items, null, 2)}
25 |
26 |
27 | setState({ itemLabelSelected: item.label }) }
30 | selectTreeFirstTitle="Select Tree Example Header"
31 | autoFocus={false}
32 | />
33 |
34 | )}
35 |
36 |
37 |
38 |
39 |
40 | ## API
41 |
42 |
43 |
--------------------------------------------------------------------------------
/src/components/selectTree/__docs__/selectTreeItems.js:
--------------------------------------------------------------------------------
1 | export default [
2 | {
3 | label: 'A',
4 | value: 'A',
5 | items: [
6 | {
7 | label: 'Abdominal Wall',
8 | value: 'AbdominalWall',
9 | },
10 | {
11 | label: 'Adrenal Left',
12 | value: 'AdrenalLeft',
13 | },
14 | ],
15 | },
16 | {
17 | label: 'B',
18 | value: 'B',
19 | items: [
20 | {
21 | label: 'Brain',
22 | value: 'Brain',
23 | },
24 | {
25 | label: 'Breast',
26 | value: 'Breast',
27 | },
28 | ],
29 | },
30 | ];
31 |
--------------------------------------------------------------------------------
/src/components/selectTree/index.js:
--------------------------------------------------------------------------------
1 | export { SelectTree } from './SelectTree';
2 |
--------------------------------------------------------------------------------
/src/components/simpleDialog/SimpleDialog.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | import './SimpleDialog.styl';
5 |
6 | class SimpleDialog extends Component {
7 | static propTypes = {
8 | children: PropTypes.node,
9 | componentRef: PropTypes.any,
10 | componentStyle: PropTypes.object,
11 | rootClass: PropTypes.string,
12 | isOpen: PropTypes.bool,
13 | headerTitle: PropTypes.string.isRequired,
14 | onClose: PropTypes.func.isRequired,
15 | onConfirm: PropTypes.func.isRequired,
16 | };
17 |
18 | static defaultProps = {
19 | isOpen: true,
20 | componentStyle: {},
21 | rootClass: '',
22 | };
23 |
24 | render() {
25 | return (
26 |
27 | {this.props.isOpen && (
28 |
51 | )}
52 |
53 | );
54 | }
55 |
56 | onClose = event => {
57 | event.preventDefault();
58 | event.stopPropagation();
59 | this.props.onClose();
60 | };
61 |
62 | onConfirm = event => {
63 | event.preventDefault();
64 | event.stopPropagation();
65 | this.props.onConfirm();
66 | };
67 | }
68 |
69 | export { SimpleDialog };
70 |
--------------------------------------------------------------------------------
/src/components/simpleDialog/index.js:
--------------------------------------------------------------------------------
1 | export { SimpleDialog } from './SimpleDialog.js';
2 |
--------------------------------------------------------------------------------
/src/components/studyBrowser/DragPreview.styl:
--------------------------------------------------------------------------------
1 | .DragPreview
2 |
3 | .source-preview {
4 | border-radius: 5px;
5 | background-color: rgba(0, 0, 0, 0.5);
6 | position: fixed;
7 | opacity: 0.5;
8 | z-index: 9999;
9 | left: 0;
10 | top: 0;
11 | transition: none;
12 | pointer-events: none;
13 | -webkit-touch-callout: none;
14 | }
15 |
--------------------------------------------------------------------------------
/src/components/studyBrowser/ExampleDropTarget.css:
--------------------------------------------------------------------------------
1 | .ExampleDropTarget {
2 | display: flex;
3 | justify-content: center;
4 | flex-direction: column;
5 |
6 | min-height: 100px;
7 |
8 | border: 2px dashed black;
9 | padding: 20px;
10 | transition: 0.2s all ease;
11 | }
12 |
13 | .ExampleDropTarget h4,
14 | .ExampleDropTarger p {
15 | text-align: center;
16 | word-break: break-word;
17 | }
18 |
19 | .ExampleDropTarget.can-drop {
20 | border: 2px dashed #20a5d6;
21 | background-color: #fafafa;
22 | color: black;
23 | }
24 |
25 | .ExampleDropTarget.hovered {
26 | border: 2px dashed #20a5d6;
27 | background-color: black;
28 | color: #91b9cd;
29 | cursor: copy;
30 | }
31 |
--------------------------------------------------------------------------------
/src/components/studyBrowser/ImageThumbnail.styl:
--------------------------------------------------------------------------------
1 | :root {
2 | --text-secondary-color: #91B9CD;
3 | --active-color: #20A5D6;
4 | --primary-background-color: black;
5 | --ui-border-color-dark: #3C5D80
6 | --sidebar-transition: all 0.3s ease;
7 | }
8 |
9 | .ThumbnailEntry.active .ImageThumbnail
10 | border-color: var(--active-color);
11 | box-shadow: none
12 | transition: var(--sidebar-transition);
13 |
14 | .ImageThumbnail
15 | background-color: var(--primary-background-color);
16 | box-shadow: inset 0 0 0 1px var(--ui-border-color-dark);
17 | border: 5px solid transparent
18 | border-radius: 12px
19 | height: 135px
20 | margin: 0 auto
21 | padding 5px
22 | position: relative
23 | transition: var(--sidebar-transition);
24 | width: 217px
25 | -moz-background-clip: padding
26 | -webkit-background-clip: padding
27 | background-clip: padding-box
28 |
29 | .image-thumbnail-canvas
30 | height: 100%
31 | overflow: hidden
32 | display: flex
33 | justify-content: center
34 |
35 | img
36 | -webkit-user-drag: none
37 | pointer-events: none
38 |
39 |
40 | .thumbnailLoadingIndicator
41 | display: none
42 | pointer-events: none
43 | color: var(--text-secondary-color);
44 | height: 20px
45 | width: 100%
46 | top: 0
47 | left: 0
48 | right: 0
49 | bottom: 0
50 | margin: auto
51 | position: absolute
52 |
53 | &.d-block
54 | display: block;
55 |
56 | p
57 | text-align: center
58 | font-size: 10pt
59 |
60 | .image-thumbnail-progress-bar
61 | position: relative
62 | width: 100%
63 | height: 3px
64 | top: -5px
65 |
66 | .image-thumbnail-progress-bar-inner
67 | height: 100%
68 | width: 0
69 | border-radius: 5px
70 | background-color: var(--active-color);
71 |
--------------------------------------------------------------------------------
/src/components/studyBrowser/StudyBrowser.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import { ThumbnailEntry } from './ThumbnailEntry';
4 | import ThumbnailEntryDragSource from './ThumbnailEntryDragSource.js';
5 | import './StudyBrowser.styl';
6 |
7 | class StudyBrowser extends Component {
8 | static defaultProps = {
9 | studies: [],
10 | supportsDragAndDrop: true,
11 | };
12 |
13 | static propTypes = {
14 | studies: PropTypes.array.isRequired,
15 | supportsDragAndDrop: PropTypes.bool.isRequired,
16 | onThumbnailClick: PropTypes.func,
17 | onThumbnailDoubleClick: PropTypes.func,
18 | };
19 |
20 | render() {
21 | const studies = this.props.studies;
22 |
23 | const thumbnails = studies.map((study, studyIndex) => {
24 | return study.thumbnails.map((thumb, thumbIndex) => {
25 | if (this.props.supportsDragAndDrop) {
26 | return (
27 |
35 | );
36 | } else {
37 | return (
38 |
39 |
47 |
48 | );
49 | }
50 | });
51 | });
52 |
53 | const components = thumbnails.flat();
54 | return (
55 |
58 | );
59 | }
60 | }
61 |
62 | export { StudyBrowser };
63 |
--------------------------------------------------------------------------------
/src/components/studyBrowser/StudyBrowser.styl:
--------------------------------------------------------------------------------
1 | .StudyBrowser
2 | float: left
3 | height: 100%
4 | width: 100%
5 | overflow: hidden
6 | background-color: black
7 | padding-bottom: 20px
8 | padding-top: 10px
9 |
10 | .scrollable-study-thumbnails
11 | height: 100%
12 | overflow-y: auto
13 | overflow-x: hidden
14 | padding-bottom: 50px
15 | padding-right: 16px
16 | padding-left: 4px
17 | margin-right: -16px
18 |
19 | display: flex;
20 | flex-direction: column;
21 |
22 | &::-webkit-scrollbar
23 | display: none
24 |
25 | .ThumbnailEntryContainer
26 | margin: 0 auto;
27 | padding-bottom: 1.5rem;
28 |
29 | .noselect
30 | -moz-user-select: none; /* Firefox */
31 | -ms-user-select: none; /* Internet Explorer */
32 | -khtml-user-select: none; /* KHTML browsers (e.g. Konqueror) */
33 | -webkit-user-select: none; /* Chrome, Safari, and Opera */
34 | -webkit-touch-callout: none; /* Disable Android and iOS callouts*/
35 |
36 |
37 | .draggable
38 | cursor: copy
39 | cursor: -webkit-grab
40 | cursor: -moz-grab
41 |
--------------------------------------------------------------------------------
/src/components/studyBrowser/ThumbnailEntryDragSource.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import { DragSource } from 'react-dnd';
4 | import DragPreview from './DragPreview';
5 | import { ThumbnailEntry } from './ThumbnailEntry.js';
6 |
7 | // Drag sources and drop targets only interact
8 | // if they have the same string type.
9 | const Types = {
10 | THUMBNAIL: 'thumbnail',
11 | };
12 |
13 | const thumbnailSource = {
14 | /*canDrag(props) {
15 | return props.error === false;
16 | },*/
17 |
18 | beginDrag(props) {
19 | return props;
20 | },
21 |
22 | endDrag(props, monitor) {
23 | //const item = monitor.getItem();
24 | const dropResult = monitor.getDropResult();
25 |
26 | if (dropResult) {
27 | //console.log(`You dropped ${item.id} into ${dropResult.id}!`);
28 | //console.log(item);
29 | }
30 | },
31 | };
32 |
33 | class ThumbnailEntryDragSource extends Component {
34 | static propTypes = {
35 | connectDragSource: PropTypes.func.isRequired,
36 | isDragging: PropTypes.bool.isRequired,
37 | };
38 |
39 | static defaultProps = {
40 | isDragging: false,
41 | };
42 |
43 | render() {
44 | const { connectDragSource } = this.props;
45 | const dropEffect = 'copy';
46 |
47 | return connectDragSource(
48 |
49 |
50 |
51 |
,
52 | { dropEffect }
53 | );
54 | }
55 | }
56 |
57 | const collect = (connect, monitor) => ({
58 | connectDragSource: connect.dragSource(),
59 | isDragging: monitor.isDragging(),
60 | });
61 |
62 | export default DragSource(Types.THUMBNAIL, thumbnailSource, collect)(
63 | ThumbnailEntryDragSource
64 | );
65 |
--------------------------------------------------------------------------------
/src/components/studyBrowser/__docs__/studyBrowser.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: Study Browser
3 | menu: Components
4 | route: /components/study-browser
5 | ---
6 |
7 | import { Playground, Props } from 'docz'
8 | import { State } from 'react-powerplug'
9 | import { WrappedStudyBrowser } from './wrappedStudyBrowser.js'
10 | import { StudyBrowser } from './../index.js'
11 |
12 | # Study Browser
13 |
14 | ## Basic usage
15 |
16 |
17 |
18 |
19 |
20 | ## API
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/components/studyBrowser/__docs__/wrappedStudyBrowser.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { DragDropContext } from 'react-dnd';
3 | import TouchBackend from 'react-dnd-touch-backend';
4 |
5 | //
6 | import {
7 | studies,
8 | onThumbnailClick,
9 | onThumbnailDoubleClick,
10 | } from './exampleStudies.js';
11 | import { ExampleDropTarget, StudyBrowser } from './../index.js';
12 |
13 | class StudyBrowserContainer extends Component {
14 | render() {
15 | //const viewportData = [null, null, null, null];
16 |
17 | return (
18 |
19 |
20 |
25 |
26 | );
27 | }
28 | }
29 |
30 | // Note:
31 | // Normally, the top level APP component is wrapped with the DragDropContext
32 | // We wrap this component to create a simple/local example.
33 | const WrappedStudyBrowser = DragDropContext(
34 | TouchBackend({ enableMouseEvents: true }),
35 | null,
36 | true
37 | )(StudyBrowserContainer);
38 |
39 | // http://react-dnd.github.io/react-dnd/docs/api/drag-drop-context
40 | export { WrappedStudyBrowser };
41 |
--------------------------------------------------------------------------------
/src/components/studyBrowser/index.js:
--------------------------------------------------------------------------------
1 | export { ExampleDropTarget } from './ExampleDropTarget.js';
2 | export { StudyBrowser } from './StudyBrowser.js';
3 | export { ThumbnailEntry } from './ThumbnailEntry.js';
4 |
--------------------------------------------------------------------------------
/src/components/studyList/PaginationArea.styl:
--------------------------------------------------------------------------------
1 | .pagination-area
2 | color: var(--text-secondary-color);
3 | font-size: 13px
4 | font-weight: normal !important
5 |
6 | label
7 | font-weight: normal
8 |
9 | select
10 | margin: 5px
11 | background-color: var(--primary-background-color)
12 | color: white
13 |
14 | .row
15 | display: flex;
16 |
17 | .rows-dropdown
18 | width: 25%;
19 | padding-right: 15px;
20 | padding-left: 15px;
21 |
22 | .pagination-buttons
23 | width: 75%;
24 | padding-right: 15px;
25 | padding-left: 15px;
26 |
27 | .form-group
28 | margin-bottom: 15px;
29 |
30 | .rows-per-page label.wrapperLabel
31 | display: inline-table !important
32 | margin: 0 4px
33 |
34 | select
35 | margin: 0px 4px 0px 4px
36 | width: 42px
37 |
38 | .page-buttons
39 | margin: 0
40 | text-align: right
41 | font-weight: normal
42 | ul.pagination-control
43 | margin: 0
44 |
45 | li
46 | display: table-cell
47 | padding: 5px 2px
48 |
49 | button
50 | padding: 4px 8px
51 | background-color: var(--primary-background-color)
52 | border-color: var(--ui-gray)
53 | color: var(--ui-gray-darkest)
54 | color: white
55 | text-decoration: none
56 |
57 | &:hover:enabled
58 | color: var(--active-color)
59 |
60 | .active
61 | button
62 | background-color: var(--ui-gray)
63 | border-color: #ddd
64 | color: white
65 |
--------------------------------------------------------------------------------
/src/components/studyList/StudyListLoadingText.js:
--------------------------------------------------------------------------------
1 | import { Icon } from './../../elements/Icon';
2 | import React from 'react';
3 |
4 | function StudyListLoadingText() {
5 | return (
6 |
7 | Loading...
8 |
9 | );
10 | }
11 |
12 | export { StudyListLoadingText };
13 |
--------------------------------------------------------------------------------
/src/components/studyList/StudyListToolbar.js:
--------------------------------------------------------------------------------
1 | import './StudyListToolbar.styl';
2 |
3 | import React, { PureComponent } from 'react';
4 |
5 | import { Icon } from './../../elements/Icon';
6 | import PropTypes from 'prop-types';
7 |
8 | class StudylistToolbar extends PureComponent {
9 | static propTypes = {
10 | onImport: PropTypes.func,
11 | };
12 |
13 | onImport = event => {
14 | if (this.props.onImport) {
15 | this.props.onImport(event);
16 | }
17 | };
18 |
19 | getImportTool() {
20 | if (this.props.onImport) {
21 | return (
22 |
23 |
30 |
31 | );
32 | }
33 | }
34 |
35 | render() {
36 | return {this.getImportTool()}
;
37 | }
38 | }
39 |
40 | export { StudylistToolbar };
41 |
--------------------------------------------------------------------------------
/src/components/studyList/StudyListToolbar.styl:
--------------------------------------------------------------------------------
1 | .studyListToolbar
2 | .addNewStudy
3 | margin: 0 10px
4 |
5 | label
6 | font-weight: 400
7 | cursor: pointer
8 | display: inline-block;
9 | max-width: 100%;
10 | margin-bottom: 5px;
11 |
12 | input
13 | width: 0.1px;
14 | height: 0.1px
15 | opacity: 0
16 | overflow: hidden
17 | position: absolute
18 | z-index: -1
19 |
20 | color: var(--text-secondary-color)
21 |
22 | &:hover
23 | color: var(--hover-color)
24 |
25 | &:active
26 | color: var(--active-color)
27 |
--------------------------------------------------------------------------------
/src/components/studyList/__docs__/onSearch.js:
--------------------------------------------------------------------------------
1 | import moment from 'moment';
2 |
3 | export default function(searchData) {
4 | this.setState({ searchData });
5 |
6 | const filter = (key, searchData, study) => {
7 | if (key === 'studyDateFrom' && searchData[key] && study['studyDate']) {
8 | const studyDate = moment(study['studyDate'], 'YYYYMMDD');
9 | return studyDate.isBetween(
10 | searchData['studyDateFrom'],
11 | searchData['studyDateTo'],
12 | 'days',
13 | '[]'
14 | );
15 | } else if (searchData[key] && !study[key].includes(searchData[key])) {
16 | return false;
17 | } else {
18 | return true;
19 | }
20 | };
21 |
22 | const { field, order } = searchData.sortData;
23 |
24 | // just a example of local filtering
25 | let filteredStudies = this.defaultStudies
26 | .filter(function(study) {
27 | const all = [
28 | 'patientName',
29 | 'patientId',
30 | 'accessionNumber',
31 | 'modalities',
32 | 'studyDescription',
33 | 'studyDateFrom',
34 | ].every(key => {
35 | return filter(key, searchData, study);
36 | });
37 |
38 | return all;
39 | })
40 | .sort(function(a, b) {
41 | if (order === 'desc') {
42 | if (a[field] < b[field]) {
43 | return -1;
44 | }
45 | if (a[field] > b[field]) {
46 | return 1;
47 | }
48 | return 0;
49 | } else {
50 | if (a[field] > b[field]) {
51 | return -1;
52 | }
53 | if (a[field] < b[field]) {
54 | return 1;
55 | }
56 | return 0;
57 | }
58 | });
59 |
60 | // User can notice the loading icon
61 | return new Promise(resolve => {
62 | setTimeout(() => {
63 | const first = searchData.currentPage * searchData.rowsPerPage;
64 | let last =
65 | searchData.currentPage * searchData.rowsPerPage +
66 | searchData.rowsPerPage;
67 | last = last >= filteredStudies.length ? filteredStudies.length : last;
68 | this.setState({ studies: filteredStudies.slice(first, last) });
69 | resolve();
70 | }, 500);
71 | });
72 | }
73 |
--------------------------------------------------------------------------------
/src/components/studyList/__docs__/studyList.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: Study List
3 | menu: Components
4 | route: /components/study-list
5 | ---
6 |
7 | import { Playground, Props } from 'docz'
8 | import { State } from 'react-powerplug'
9 | import { StudyList } from './../index.js'
10 | // Data
11 | import defaultStudies from './studies.js'
12 | import onSearch from './onSearch.js'
13 | import moment from 'moment'
14 |
15 | # Study List
16 |
17 | ## Basic usage
18 |
19 |
20 | {
23 | const studyDate = moment(study['studyDate'], 'YYYYMMDD');
24 | const startDate = moment().subtract(5, 'days');
25 | const endDate = moment();
26 |
27 | return studyDate.isBetween(startDate, endDate, 'days', '[]');
28 | }).slice(0, 5)
29 | }}>
30 | {({ state, setState }) => (
31 |
32 |
33 | alert('Import study mock ' + e)}
38 | onSelectItem={(studyInstanceUid) => { alert(studyInstanceUid + ' has selected! Now you can open your study.'); }}
39 | rowsPerPage={5}
40 | defaultSort={{ field: 'patientName', order: 'desc', }}
41 | studyListDateFilterNumDays={7}
42 | onSearch={onSearch.bind({ setState, defaultStudies })}
43 | />
44 |
45 |
46 | )}
47 |
48 |
49 |
50 |
51 | ## API
52 |
53 |
54 |
--------------------------------------------------------------------------------
/src/components/studyList/index.js:
--------------------------------------------------------------------------------
1 | export { StudyList } from './StudyList.js';
2 |
--------------------------------------------------------------------------------
/src/components/tableList/TableList.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | import './TableList.styl';
5 |
6 | export class TableList extends Component {
7 | static propTypes = {
8 | customHeader: PropTypes.node,
9 | defaultItems: PropTypes.object,
10 | children: PropTypes.node.isRequired,
11 | headerTitle: PropTypes.string,
12 | };
13 |
14 | render() {
15 | return (
16 |
17 |
18 | {this.getHeader()}
19 |
20 |
{this.props.children}
21 |
22 | );
23 | }
24 |
25 | getHeader = () => {
26 | if (this.props.customHeader) {
27 | return this.props.customHeader;
28 | } else {
29 | return (
30 |
31 | {this.props.headerTitle}
32 | {this.props.children.length}
33 |
34 | );
35 | }
36 | };
37 | }
38 |
--------------------------------------------------------------------------------
/src/components/tableList/TableList.styl:
--------------------------------------------------------------------------------
1 |
2 | $headerRowHeight = 63px
3 |
4 | .tableList
5 | background-color: var(--primary-background-color)
6 | height: 100%;
7 | width: 100%;
8 |
9 | .tableListHeader
10 | background-color: var(--ui-gray-darker)
11 | color: var(--text-secondary-color)
12 | display: flex
13 | height: $headerRowHeight
14 | line-height: $headerRowHeight
15 | margin-top: 2px
16 | overflow: hidden
17 | width: 100%
18 |
19 | .tableListHeaderTitle
20 | color: var(--text-secondary-color)
21 | font-size: 22px
22 | font-weight: 300
23 | line-height: $headerRowHeight
24 | padding: 0 10px
25 | text-align: left
26 | flex: 1;
27 |
28 | .numberOfItems
29 | color: var(--ui-sky-blue)
30 | float: right
31 | font-weight: 300
32 | font-size: 40px
33 | max-width: 54px
34 | height: $headerRowHeight
35 | line-height: 66px
36 | flex: 1;
37 |
--------------------------------------------------------------------------------
/src/components/tableList/TableListItem.js:
--------------------------------------------------------------------------------
1 | import './TableListItem.styl';
2 |
3 | import { Component } from 'react';
4 | import { Icon } from './../../elements/Icon';
5 | import PropTypes from 'prop-types';
6 | import React from 'react';
7 |
8 | export class TableListItem extends Component {
9 | static propTypes = {
10 | children: PropTypes.node,
11 | itemClass: PropTypes.string,
12 | itemIndex: PropTypes.number,
13 | itemKey: PropTypes.oneOfType(['number', 'string']),
14 | onItemClick: PropTypes.func.isRequired,
15 | };
16 |
17 | render() {
18 | return (
19 |
23 |
24 | {this.props.itemIndex}
25 |
26 |
27 |
28 |
29 |
{this.props.children}
30 |
31 | );
32 | }
33 |
34 | onItemClick = event => {
35 | if (this.props.onItemClick) {
36 | event.preventDefault();
37 | event.stopPropagation();
38 |
39 | this.props.onItemClick(event, this.props.itemKey);
40 | }
41 | };
42 | }
43 |
--------------------------------------------------------------------------------
/src/components/tableList/TableListItem.styl:
--------------------------------------------------------------------------------
1 | .tableListItem
2 | display: flex
3 | margin-left: -6px
4 | margin-top: 2px
5 | padding-left: 6px
6 | opacity: 0.7
7 | cursor: pointer;
8 | width: calc(100% + 6px)
9 |
10 | &:hover
11 | opacity 1
12 |
13 | &.selected
14 | opacity 1
15 | .itemIndex
16 | color: $activeColor
17 |
18 | &.hasWarning
19 | .warning
20 | display: block
21 |
22 | .itemIndex
23 | background-color: var(--ui-gray)
24 | color: var(--text-secondary-color)
25 | cursor: pointer
26 | flex: 1
27 | max-width: 25px
28 | transition(all 0.3s ease)
29 | font-size: 14px
30 | font-weight: 400
31 | margin-right: 5px
32 | padding: 10px
33 | text-align: center
34 | -webkit-box-sizing: initial;
35 | -moz-box-sizing: initial;
36 | box-sizing: initial;
37 |
38 | .itemContent
39 | flex: 1
40 | color: var(--text-secondary-color)
41 | font-weight: 400
42 | margin-left: 9px;
43 | margin-top: 9px;
44 |
45 | .warning-icon
46 | display: none
47 |
--------------------------------------------------------------------------------
/src/components/tableList/__docs__/tableList.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: Table List
3 | menu: Components
4 | route: /components/table-list
5 | ---
6 |
7 | import { Playground, Props } from 'docz'
8 | import { State } from 'react-powerplug'
9 | import { TableList, TableListItem } from './../index.js'
10 |
11 | # Table List
12 |
13 | ## Basic usage
14 |
15 |
16 |
25 | {({ state, setState }) => (
26 |
27 | {state.listItems.map( (item, index) => {
28 | return (
29 | alert('item clicked')}
34 | >
35 |
36 |
37 | )}
38 | )}
39 |
40 | )}
41 |
42 |
43 |
44 |
45 |
46 | ## API
47 |
48 |
49 |
--------------------------------------------------------------------------------
/src/components/tableList/index.js:
--------------------------------------------------------------------------------
1 | export { TableList } from './TableList';
2 | export { TableListItem } from './TableListItem';
3 |
--------------------------------------------------------------------------------
/src/components/toolbarSection/ToolbarSection.js:
--------------------------------------------------------------------------------
1 | import './ToolbarSection.styl';
2 |
3 | import React, { PureComponent } from 'react';
4 |
5 | import ExpandableToolMenu from '../../viewer/ExpandableToolMenu';
6 | import PropTypes from 'prop-types';
7 | import ToolbarButton from '../../viewer/ToolbarButton';
8 | import classnames from 'classnames';
9 |
10 | class ToolbarSection extends PureComponent {
11 | static defaultProps = {
12 | className: '',
13 | };
14 |
15 | static propTypes = {
16 | buttons: PropTypes.arrayOf(
17 | PropTypes.shape({
18 | id: PropTypes.string,
19 | label: PropTypes.string.isRequired,
20 | icon: PropTypes.oneOfType([
21 | PropTypes.string,
22 | PropTypes.shape({
23 | name: PropTypes.string.isRequired,
24 | }),
25 | ]),
26 | /** Optional: Expandable Tool Menu */
27 | buttons: PropTypes.arrayOf(PropTypes.shape({})),
28 | })
29 | ).isRequired,
30 | /** Array of string button ids that should show as active */
31 | activeButtons: PropTypes.arrayOf(PropTypes.string).isRequired,
32 | /** Class for toolbar section container */
33 | className: PropTypes.string,
34 | };
35 |
36 | render() {
37 | const items = this.props.buttons.map((button, index) => {
38 | if (button.buttons && Array.isArray(button.buttons)) {
39 | return (
40 |
45 | );
46 | } else {
47 | return (
48 |
53 | );
54 | }
55 | });
56 |
57 | return (
58 |
59 | {items}
60 |
61 | );
62 | }
63 | }
64 |
65 | export { ToolbarSection };
66 |
--------------------------------------------------------------------------------
/src/components/toolbarSection/ToolbarSection.styl:
--------------------------------------------------------------------------------
1 | .ToolbarSection
2 | float: left;
3 | background-color: var(--primary-background-color);
4 | border-radius: 8px;
5 |
--------------------------------------------------------------------------------
/src/components/toolbarSection/__docs__/exampleButtons.js:
--------------------------------------------------------------------------------
1 | export default [
2 | {
3 | id: 'Pan',
4 | label: 'Pan',
5 | icon: 'arrows',
6 | },
7 | {
8 | label: 'Zoom',
9 | icon: 'search',
10 | },
11 | {
12 | label: 'Bidirectional',
13 | icon: 'measure-target',
14 | },
15 | {
16 | label: 'Stack Scroll',
17 | icon: 'bars',
18 | },
19 | {
20 | label: 'Reset',
21 | icon: 'reset',
22 | },
23 | {
24 | label: 'Manual',
25 | icon: 'level',
26 | },
27 | {
28 | label: 'More',
29 | icon: 'ellipse-circle',
30 | buttons: [
31 | {
32 | label: 'Reset 2',
33 | icon: 'reset',
34 | },
35 | {
36 | label: 'Manual 2',
37 | icon: 'adjust',
38 | },
39 | ],
40 | },
41 | ];
42 |
--------------------------------------------------------------------------------
/src/components/toolbarSection/__docs__/toolbarSection.docs.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: Toolbar Section
3 | menu: Components
4 | route: /components/toolbar-section
5 | ---
6 |
7 | import { Playground, Props } from 'docz';
8 | import { State } from 'react-powerplug';
9 | import { ToolbarSection } from './../index.js';
10 | import exampleButtons from './exampleButtons.js';
11 | import NameSpace from '../../../__docs__/NameSpace';
12 |
13 | # ToolbarSection
14 |
15 | A basic row of buttons for a toolbar. Active command is `WwwcTool`
16 |
17 | ## Basic usage
18 |
19 |
20 | {
25 | alert('pressed');
26 | },
27 | }}
28 | >
29 | {({ state, setState }) => }
30 |
31 |
32 |
33 | ## API
34 |
35 |
36 |
37 | ## Translation Namespace
38 |
39 |
40 |
--------------------------------------------------------------------------------
/src/components/toolbarSection/index.js:
--------------------------------------------------------------------------------
1 | export { ToolbarSection } from './ToolbarSection.js';
2 |
--------------------------------------------------------------------------------
/src/components/tooltip/Tooltip.js:
--------------------------------------------------------------------------------
1 | import './Tooltip.styl';
2 |
3 | import classNames from 'classnames';
4 | import React from 'react';
5 | import PropTypes from 'prop-types';
6 |
7 | const propTypes = {
8 | /** Sets the direction the Tooltip is positioned towards. */
9 | placement: PropTypes.oneOf(['top', 'right', 'bottom', 'left']),
10 |
11 | /** The "top" position value for the Tooltip. */
12 | positionTop: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
13 | /** The "left" position value for the Tooltip. */
14 | positionLeft: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
15 |
16 | /** The "top" position value for the Tooltip arrow. */
17 | arrowOffsetTop: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
18 | /** The "left" position value for the Tooltip arrow. */
19 | arrowOffsetLeft: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
20 | };
21 |
22 | const defaultProps = {
23 | placement: 'right',
24 | };
25 |
26 | class Tooltip extends React.Component {
27 | render() {
28 | const {
29 | placement,
30 | positionTop,
31 | positionLeft,
32 | arrowOffsetTop,
33 | arrowOffsetLeft,
34 | className,
35 | style,
36 | children,
37 | } = this.props;
38 |
39 | const outerStyle = {
40 | top: positionTop,
41 | left: positionLeft,
42 | ...style,
43 | };
44 |
45 | const arrowStyle = {
46 | top: arrowOffsetTop,
47 | left: arrowOffsetLeft,
48 | };
49 |
50 | return (
51 |
56 |
57 |
{children}
58 |
59 | );
60 | }
61 | }
62 |
63 | Tooltip.propTypes = propTypes;
64 | Tooltip.defaultProps = defaultProps;
65 |
66 | export { Tooltip };
67 |
--------------------------------------------------------------------------------
/src/components/tooltip/Tooltip.styl:
--------------------------------------------------------------------------------
1 | @import './../../design/styles/common/global.styl'
2 |
3 | .tooltip
4 | position: absolute;
5 | z-index: 1070;
6 | display: block;
7 | font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
8 | font-size: 12px;
9 | font-style: normal;
10 | font-weight: 400;
11 | line-height: 1.42857143;
12 | text-align: left;
13 | text-align: start;
14 | text-decoration: none;
15 | text-shadow: none;
16 | text-transform: none;
17 | letter-spacing: normal;
18 | word-break: normal;
19 | word-spacing: normal;
20 | word-wrap: normal;
21 | white-space: normal;
22 | opacity: 0;
23 | line-break: auto;
24 |
25 | &.in
26 | opacity: .9;
27 |
28 | &.top
29 | margin-top: -3px;
30 | padding: 5px 0;
31 |
32 | &.right
33 | margin-left: 3px;
34 | padding: 0 5px;
35 |
36 | &.bottom
37 | margin-top: 3px;
38 | padding: 5px 0;
39 |
40 | &.left
41 | margin-left: -3px;
42 | padding: 0 5px;
43 |
44 | &.top .tooltip-arrow {
45 | bottom: 0;
46 | left: 50%;
47 | margin-left: -5px;
48 | border-width: 5px 5px 0;
49 | border-top-color: #000;
50 | }
51 |
52 | &.right .tooltip-arrow {
53 | top: 50%;
54 | left: 0;
55 | margin-top: -5px;
56 | border-width: 5px 5px 5px 0;
57 | border-right-color: #000;
58 | }
59 | &.left .tooltip-arrow {
60 | top: 50%;
61 | right: 0;
62 | margin-top: -5px;
63 | border-width: 5px 0 5px 5px;
64 | border-left-color: #000;
65 | }
66 | &.bottom .tooltip-arrow {
67 | top: 0;
68 | left: 50%;
69 | margin-left: -5px;
70 | border-width: 0 5px 5px;
71 | border-bottom-color: #000;
72 | }
73 |
74 | .tooltip-inner {
75 | max-width: 200px;
76 | padding: 3px 8px;
77 | color: #fff;
78 | text-align: center;
79 | background-color: #000;
80 | border-radius: 4px;
81 | }
82 |
83 | .tooltip-arrow {
84 | position: absolute;
85 | width: 0;
86 | height: 0;
87 | border-color: transparent;
88 | border-style: solid;
89 | }
90 |
91 | .tooltip.in
92 | opacity: .9;
93 |
--------------------------------------------------------------------------------
/src/components/tooltip/__docs__/tooltip.noshow-mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: Tooltip
3 | menu: Components
4 | route: /components/tooltip
5 | ---
6 |
7 | import { Playground, Props } from 'docz'
8 | import { Tooltip } from './../index.js'
9 |
10 | # Tooltip
11 |
12 | ## Basic usage
13 |
14 |
15 |
16 |
17 |
18 | ## API
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/components/tooltip/__tests__/.githold:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OHIF/react-viewerbase/08cb405ddaae17f58f64868bed688aee0420dc49/src/components/tooltip/__tests__/.githold
--------------------------------------------------------------------------------
/src/components/tooltip/index.js:
--------------------------------------------------------------------------------
1 | export { Tooltip } from './Tooltip.js';
2 |
--------------------------------------------------------------------------------
/src/components/userPreferencesModal/AboutModal.styl:
--------------------------------------------------------------------------------
1 | @import './../../design/styles/common/button.styl'
2 | @import './../../design/styles/common/table.styl'
3 |
4 | .AboutModal
5 | .btn
6 | border-color: #ccc;
7 |
8 | .table
9 | thead:first-child
10 | tr:first-child
11 | th, td
12 | border-top: 0;
13 |
14 | thead, tbody, tfoot
15 | tr
16 | th, td
17 | padding: 8px;
18 | line-height: 1.42857143;
19 | vertical-align: top;
20 | border-top: 1px solid #ddd;
21 |
--------------------------------------------------------------------------------
/src/components/userPreferencesModal/GeneralPreferences.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import LanguageSwitcher from '../languageSwitcher';
3 |
4 | export class GeneralPreferences extends Component {
5 | render() {
6 | return (
7 |
8 |
9 |
12 |
13 |
14 |
15 | );
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/userPreferencesModal/HotKeysPreferences.styl:
--------------------------------------------------------------------------------
1 | .HotKeysPreferences
2 | display: flex;
3 | margin-right: -15px;
4 | margin-left: -15px;
5 |
6 | .column
7 | width: 50%;
8 | padding-right: 15px;
9 | padding-left: 15px;
10 |
11 | .column-full
12 | width: 100%;
13 | padding-right: 15px;
14 | padding-left: 15px;
15 |
--------------------------------------------------------------------------------
/src/components/userPreferencesModal/UserPreferences.styl:
--------------------------------------------------------------------------------
1 | @import './../../design/styles/common/form.styl'
2 | @import './../../design/styles/common/navbar.styl'
3 | @import './../../design/styles/common/state.styl'
4 | @import './../../design/styles/common/global.styl'
5 |
6 | .modal-body
7 | overflow: hidden
8 |
9 | .errorMessage
10 | color: var(--state-error-text)
11 | font-size: 10px
12 | text-transform: uppercase;
13 |
14 | .form-content
15 | border-bottom: 3px solid var(--primary-background-color)
16 | margin-bottom: 20px
17 | margin-left: -22px
18 | margin-right: -22px
19 | max-height: 70vh
20 | overflow-y: auto
21 | padding: 22px
22 | min-height: 500px
23 |
24 | .popover
25 | width: 300px
26 |
--------------------------------------------------------------------------------
/src/components/userPreferencesModal/UserPreferencesModal.styl:
--------------------------------------------------------------------------------
1 | @import './../../design/styles/common/navbar.styl'
2 | @import './../../design/styles/common/global.styl'
3 | @import './../../design/styles/common/modal.styl'
4 | @import './../../design/styles/common/button.styl'
5 |
6 | .close
7 | float: right;
8 | font-size: 21px;
9 | font-weight: 700;
10 | line-height: 1;
11 | color: #000;
12 | text-shadow: 0 1px 0 #fff;
13 | opacity: .2;
14 |
15 | .ModalHeader
16 | ol, ul
17 | margin-top: 0;
18 |
--------------------------------------------------------------------------------
/src/components/userPreferencesModal/WindowLevelPreferences.styl:
--------------------------------------------------------------------------------
1 | @import './UserPreferences.styl'
2 |
3 | .presetIndex
4 | padding: 0px 10px 0px 10px
--------------------------------------------------------------------------------
/src/components/userPreferencesModal/__docs__/about.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: About Modal
3 | menu: Components
4 | route: /components/about-modal
5 | ---
6 |
7 | import { Playground, Props } from 'docz'
8 | import { State } from 'react-powerplug'
9 | import { AboutModal } from './../index.js'
10 |
11 | # About Modal
12 |
13 | ## Basic usage
14 |
15 |
16 |
19 |
20 | {({ state, setState }) => (
21 |
22 |
29 | setState({ isOpen: false })}
32 | />
33 |
34 | )}
35 |
36 |
37 |
38 |
39 |
40 | ## API
41 |
42 |
43 |
--------------------------------------------------------------------------------
/src/components/userPreferencesModal/__docs__/generalDefaults.js:
--------------------------------------------------------------------------------
1 | export default {
2 | currentLanguage: 'en',
3 | languages: [
4 | {
5 | value: 'en',
6 | label: 'English',
7 | },
8 | {
9 | value: 'es',
10 | label: 'Spanish',
11 | },
12 | ],
13 | onChange: language => {},
14 | };
15 |
--------------------------------------------------------------------------------
/src/components/userPreferencesModal/__docs__/userPreferences.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: User Preferences Modal
3 | menu: Components
4 | route: /components/user-preferences-modal
5 | ---
6 |
7 | import { Playground, Props } from 'docz'
8 | import { State } from 'react-powerplug'
9 | import { UserPreferencesModal } from './../index.js'
10 | import NameSpace from '../../../__docs__/NameSpace'
11 | //
12 | import windowLevelDefaults from './windowLevelDefaults.js'
13 | import hotkeyDefaults from './hotkeyDefaults.js'
14 |
15 | # User Preferences Modal
16 |
17 | ## Basic usage
18 |
19 |
20 |
25 |
26 | {({ state, setState }) => (
27 |
28 |
35 | setState({ isOpen: false })}
38 | onSave={() => alert('on save')}
39 | onResetToDefaults={() => alert('on reset')}
40 | />
41 |
42 | )}
43 |
44 |
45 |
46 |
47 |
48 | ## API
49 |
50 |
51 |
52 | ## Translation Namespace
53 |
54 |
55 |
--------------------------------------------------------------------------------
/src/components/userPreferencesModal/__docs__/windowLevelDefaults.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 0: { description: 'Soft tissue', window: 400, level: 40 },
3 | 1: { description: 'Lung', window: 1500, level: -600 },
4 | 2: { description: 'Liver', window: 150, level: 90 },
5 | 3: { description: 'Bone', window: 2500, level: 480 },
6 | 4: { description: 'Brain', window: 80, level: 40 },
7 | 5: { description: '', window: '', level: '' },
8 | 6: { description: '', window: '', level: '' },
9 | 7: { description: '', window: '', level: '' },
10 | 8: { description: '', window: '', level: '' },
11 | 9: { description: '', window: '', level: '' },
12 | 10: { description: '', window: '', level: '' },
13 | };
14 |
--------------------------------------------------------------------------------
/src/components/userPreferencesModal/hotKeysConfig.js:
--------------------------------------------------------------------------------
1 | const range = (start, end) => {
2 | return new Array(end - start).fill().map((d, i) => i + start);
3 | };
4 |
5 | export const disallowedCombinations = {
6 | '': [],
7 | ALT: ['SPACE'],
8 | SHIFT: [],
9 | CTRL: [
10 | 'F4',
11 | 'F5',
12 | 'F11',
13 | 'W',
14 | 'R',
15 | 'T',
16 | 'O',
17 | 'P',
18 | 'A',
19 | 'D',
20 | 'F',
21 | 'G',
22 | 'H',
23 | 'J',
24 | 'L',
25 | 'Z',
26 | 'X',
27 | 'C',
28 | 'V',
29 | 'B',
30 | 'N',
31 | 'PAGEDOWN',
32 | 'PAGEUP',
33 | ],
34 | 'CTRL+SHIFT': ['Q', 'W', 'R', 'T', 'P', 'A', 'H', 'V', 'B', 'N'],
35 | };
36 |
37 | export const allowedKeys = [
38 | ...[8, 13, 27, 32, 46], // BACKSPACE, ENTER, ESCAPE, SPACE, DELETE
39 | ...[12, 106, 107, 109, 110, 111], // Numpad keys
40 | ...range(218, 220), // [\]
41 | ...range(185, 190), // ;=,-./
42 | ...range(111, 131), // F1-F19
43 | ...range(32, 41), // arrow keys, home/end, pg dn/up
44 | ...range(47, 58), // 0-9
45 | ...range(64, 91), // A-Z
46 | ];
47 |
48 | export const specialKeys = {
49 | 8: 'backspace',
50 | 9: 'tab',
51 | 13: 'return',
52 | 16: 'shift',
53 | 17: 'ctrl',
54 | 18: 'alt',
55 | 19: 'pause',
56 | 20: 'capslock',
57 | 27: 'esc',
58 | 32: 'space',
59 | 33: 'pageup',
60 | 34: 'pagedown',
61 | 35: 'end',
62 | 36: 'home',
63 | 37: 'left',
64 | 38: 'up',
65 | 39: 'right',
66 | 40: 'down',
67 | 45: 'insert',
68 | 46: 'del',
69 | 96: '0',
70 | 97: '1',
71 | 98: '2',
72 | 99: '3',
73 | 100: '4',
74 | 101: '5',
75 | 102: '6',
76 | 103: '7',
77 | 104: '8',
78 | 105: '9',
79 | 106: '*',
80 | 107: '+',
81 | 109: '-',
82 | 110: '.',
83 | 111: '/',
84 | 112: 'f1',
85 | 113: 'f2',
86 | 114: 'f3',
87 | 115: 'f4',
88 | 116: 'f5',
89 | 117: 'f6',
90 | 118: 'f7',
91 | 119: 'f8',
92 | 120: 'f9',
93 | 121: 'f10',
94 | 122: 'f11',
95 | 123: 'f12',
96 | 144: 'numlock',
97 | 145: 'scroll',
98 | 191: '/',
99 | 224: 'meta',
100 | };
101 |
--------------------------------------------------------------------------------
/src/components/userPreferencesModal/index.js:
--------------------------------------------------------------------------------
1 | export { UserPreferences } from './UserPreferences.js';
2 | export { AboutModal } from './AboutModal.js';
3 | export { UserPreferencesModal } from './UserPreferencesModal.js';
4 | export { GeneralPreferences } from './GeneralPreferences.js';
5 |
--------------------------------------------------------------------------------
/src/design/styles/common/button.styl:
--------------------------------------------------------------------------------
1 | .btn-danger
2 | color: #fff;
3 | background-color: #d9534f;
4 | border-color: #d43f3a;
5 |
6 | .btn-primary
7 | color: #fff;
8 | background-color: #337ab7;
9 | border-color: #2e6da4;
10 |
11 | .btn-default
12 | color: #333;
13 | background-color: #fff;
14 | border-color: #ccc;
15 |
16 | .btn
17 | display: inline-block;
18 | padding: 6px 12px;
19 | margin-bottom: 0;
20 | font-size: 14px;
21 | font-weight: 400;
22 | line-height: 1.42857143;
23 | text-align: center;
24 | white-space: nowrap;
25 | vertical-align: middle;
26 | -ms-touch-action: manipulation;
27 | touch-action: manipulation;
28 | cursor: pointer;
29 | -webkit-user-select: none;
30 | -moz-user-select: none;
31 | -ms-user-select: none;
32 | user-select: none;
33 | background-image: none;
34 | border: 1px solid transparent;
35 | border-radius: 4px;
36 |
37 | .btn.disabled, .btn[disabled], fieldset[disabled] .btn
38 | cursor: not-allowed;
39 | -webkit-box-shadow: none;
40 | box-shadow: none;
41 | opacity: .65;
42 |
43 | button.close
44 | -webkit-appearance: none;
45 | padding: 0;
46 | cursor: pointer;
47 | background: 0 0;
48 | border: 0;
49 |
50 | .btn-group, .btn-group-vertical
51 | position: relative;
52 | display: inline-block;
53 | vertical-align: middle;
54 |
--------------------------------------------------------------------------------
/src/design/styles/common/global.styl:
--------------------------------------------------------------------------------
1 | *
2 | -webkit-box-sizing: border-box;
3 | -moz-box-sizing: border-box;
4 | box-sizing: border-box;
5 |
6 | .full-width
7 | width: 100%
8 |
9 | .full-height
10 | height: 100%
11 |
12 | .flex-h
13 | display: flex
14 | flex-direction: row
15 |
16 | .flex-v
17 | display: flex
18 | flex-direction: column
19 |
20 | .flex-grow
21 | flex-grow: 1
22 |
23 | .nowrap
24 | white-space: nowrap
25 |
26 | .text-right
27 | text-align: right;
28 |
29 | .text-center
30 | text-align: center;
31 |
32 | .text-left
33 | text-align: left;
34 |
35 | h1
36 | font-size: 36px;
37 |
38 | h3, h1
39 | font-family: inherit;
40 | font-weight: 500;
41 | line-height: 1.1;
42 | margin-top: 20px;
43 | margin-bottom: 10px;
44 |
45 | pre
46 | display: block;
47 | padding: 9.5px;
48 | margin: 0 0 10px;
49 | font-size: 13px;
50 | line-height: 1.42857143;
51 | color: #333;
52 | word-break: break-all;
53 | word-wrap: break-word;
54 | background-color: #f5f5f5;
55 | border: 1px solid #ccc;
56 | border-radius: 4px;
57 |
58 | button, input, select, textarea
59 | font-family: inherit;
60 | font-size: inherit;
61 | line-height: inherit;
62 |
63 | .row
64 | margin-right: -15px;
65 | margin-left: -15px;
66 |
67 | .pull-left
68 | float: left!important;
69 |
70 | .pull-right
71 | float: right!important;
72 |
73 | .fade
74 | opacity: 0;
75 | -webkit-transition: opacity .15s linear;
76 | -o-transition: opacity .15s linear;
77 | transition: opacity .15s linear;
78 |
79 | .fade.in
80 | opacity: 1;
81 |
82 | a
83 | color: #337ab7;
84 | text-decoration: none;
85 |
--------------------------------------------------------------------------------
/src/design/styles/common/navbar.styl:
--------------------------------------------------------------------------------
1 | .nav-tabs
2 | border-bottom: 0
3 | margin-bottom: 3px
4 | position: relative
5 | z-index: 1
6 |
7 | :hover
8 | cursor: pointer
9 |
10 | &>li
11 | font-size: 14px
12 | font-weight: normal
13 | height: 40px
14 | line-height: 40px
15 | margin-bottom: 0
16 |
17 | &>button
18 | display: block
19 | width: 100%
20 | line-height: inherit
21 | margin: 0
22 | height: inherit
23 | padding: 0 10px 0 10px
24 | outline: none
25 |
26 | &:after
27 | background-color: transparent
28 | bottom: -3px
29 | content: ' '
30 | display: block
31 | height: 3px
32 | transition(background-color 0.3s ease)
33 | width: 100%
34 |
35 | &>button, &.active>button
36 | &, &:hover, &:active, &:focus
37 | color: var(--active-color)
38 | background-color: transparent
39 | border: 0
40 |
41 | &.active>button
42 | font-weight: bold
43 |
44 | &:after
45 | background-color: var(--active-color)
46 |
47 | .nav
48 | padding-left: 0;
49 | list-style: none;
50 |
51 | > li
52 | position: relative;
53 | display: block;
54 |
55 | > li
56 | float: left;
57 | margin-bottom: -1px;
58 |
--------------------------------------------------------------------------------
/src/design/styles/common/state.styl:
--------------------------------------------------------------------------------
1 | .form-themed .state-error, .state-error
2 |
3 | &.wrapperLabel input + .wrapperText
4 | color: var(--state-error-text)
5 |
6 | &+.tooltip
7 |
8 | .tooltip-inner
9 | color: white
10 | background-color: var(--state-error-border)
11 |
12 | &.top .tooltip-arrow
13 | border-top-color: var(--state-error-border)
14 | &.right .tooltip-arrow
15 | border-right-color: var(--state-error-border)
16 | &.bottom .tooltip-arrow
17 | border-bottom-color: var(--state-error-border)
18 | &.left .tooltip-arrow
19 | border-left-color: var(--state-error-border)
20 |
21 |
22 | &:not(.component-group)
23 | &.form-control, .form-control
24 | background-color: var(--state-error-border)
25 | border-color: var(--state-error-border)
26 |
--------------------------------------------------------------------------------
/src/design/styles/common/table.styl:
--------------------------------------------------------------------------------
1 | .table-responsive
2 | min-height: .01%;
3 | overflow-x: auto;
4 |
5 | .table
6 | width: 100%;
7 | max-width: 100%;
8 | margin-bottom: 20px;
9 |
10 | table
11 | border-spacing: 0;
12 | border-collapse: collapse;
13 |
14 | th
15 | text-align: left;
16 |
--------------------------------------------------------------------------------
/src/elements/Icon/Icon.js:
--------------------------------------------------------------------------------
1 | import './Icon.styl';
2 |
3 | import PropTypes from 'prop-types';
4 | import getIcon from './getIcon.js';
5 |
6 | const Icon = props => {
7 | return getIcon(props.name, props);
8 | };
9 |
10 | Icon.propTypes = {
11 | /** The string name of the icon to display */
12 | name: PropTypes.string.isRequired,
13 | };
14 |
15 | export default Icon;
16 |
--------------------------------------------------------------------------------
/src/elements/Icon/Icon.styl:
--------------------------------------------------------------------------------
1 | .icon-pulse
2 | fa-spin 1s infinite steps(8)
3 |
4 | @keyframes fa-spin{
5 | 0%{ transform:rotate(0deg) }
6 | to{ transform:rotate(1turn) }
7 | }
8 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/3d-rotate.svg:
--------------------------------------------------------------------------------
1 |
14 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/adjust.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/angle-double-down.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/angle-double-up.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/angle-left.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/arrows-alt-h.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/arrows-alt-v.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/arrows.svg:
--------------------------------------------------------------------------------
1 |
23 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/bars.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/caret-down.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/caret-up.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/check-circle-o.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/check-circle.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/check.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/chevron-down.svg:
--------------------------------------------------------------------------------
1 |
15 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/circle-notch.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/circle-o.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/circle.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/cog.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/create-comment.svg:
--------------------------------------------------------------------------------
1 |
13 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/create-screen-capture.svg:
--------------------------------------------------------------------------------
1 |
16 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/crosshairs.svg:
--------------------------------------------------------------------------------
1 |
16 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/database.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/dot-circle.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/edit.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/ellipse-circle.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/ellipse-h.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/ellipse-v.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/exclamation-circle.svg:
--------------------------------------------------------------------------------
1 |
14 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/exclamation-triangle.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/fast-backward.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/fast-forward.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/info.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/inline-edit.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/level.svg:
--------------------------------------------------------------------------------
1 |
14 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/link-circles.svg:
--------------------------------------------------------------------------------
1 |
16 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/link.svg:
--------------------------------------------------------------------------------
1 |
13 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/list.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/liver.svg:
--------------------------------------------------------------------------------
1 |
17 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/lock-alt.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/lock.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/lung.svg:
--------------------------------------------------------------------------------
1 |
26 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/measure-non-target.svg:
--------------------------------------------------------------------------------
1 |
30 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/measure-target-cr.svg:
--------------------------------------------------------------------------------
1 |
24 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/measure-target-ne.svg:
--------------------------------------------------------------------------------
1 |
17 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/measure-target-un.svg:
--------------------------------------------------------------------------------
1 |
24 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/measure-target.svg:
--------------------------------------------------------------------------------
1 |
14 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/measure-temp.svg:
--------------------------------------------------------------------------------
1 |
17 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/object-group.svg:
--------------------------------------------------------------------------------
1 |
21 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/ohif-logo.svg:
--------------------------------------------------------------------------------
1 |
18 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/oval.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/palette.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/play.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/plus.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/power-off.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/reset.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/rotate-right.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/rotate.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/search-plus.svg:
--------------------------------------------------------------------------------
1 |
15 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/search.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/soft-tissue.svg:
--------------------------------------------------------------------------------
1 |
17 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/sort-down.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/sort-up.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/sort.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/square-o.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/star.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/step-backward.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/step-forward.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/stop.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/sun.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/th-large.svg:
--------------------------------------------------------------------------------
1 |
13 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/th-list.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/th.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/times.svg:
--------------------------------------------------------------------------------
1 |
13 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/trash.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/user.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/icons/youtube.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/elements/Icon/index.js:
--------------------------------------------------------------------------------
1 | import { ICONS } from './getIcon.js';
2 | import Icon from './Icon.js';
3 |
4 | export { Icon, ICONS };
5 |
--------------------------------------------------------------------------------
/src/elements/form/DropdownMenu.css:
--------------------------------------------------------------------------------
1 | .dd-menu {
2 | float: left;
3 | margin-right: 10px;
4 | cursor: pointer;
5 | position: relative;
6 | }
7 |
8 | .dd-title {
9 | font-size: 13px;
10 | }
11 |
12 | .dd-menu-toggle {
13 | display: inline-block;
14 | }
15 |
16 | .dd-caret-down {
17 | display: inline-block;
18 | width: 0;
19 | height: 0;
20 | margin-top: 0.5rem;
21 | margin-left: 0.5rem;
22 | border-top: 5px solid;
23 | border-right: 5px solid transparent;
24 | border-left: 5px solid transparent;
25 | }
26 |
27 | .dd-menu-list {
28 | position: absolute;
29 | top: 100%;
30 | margin-top: 10px;
31 | background: white;
32 | z-index: 999;
33 | border-radius: 5px;
34 | -webkit-border-radius: 5px;
35 | -moz-border-radius: 5px;
36 | -ms-border-radius: 5px;
37 | -o-border-radius: 5px;
38 | overflow: hidden;
39 | transition: all 300ms ease;
40 | -webkit-transition: all 300ms ease;
41 | -moz-transition: all 300ms ease;
42 | -ms-transition: all 300ms ease;
43 | -o-transition: all 300ms ease;
44 | }
45 |
46 | .dd-menu-list.open {
47 | display: inline-block;
48 | }
49 |
50 | .dd-menu-list.left {
51 | left: 0;
52 | }
53 |
54 | .dd-menu-list.right {
55 | right: 0;
56 | }
57 |
58 | .dd-menu-list.center {
59 | left: 50%;
60 | transform: translateX(-50%);
61 | -webkit-transform: translateX(-50%);
62 | -moz-transform: translateX(-50%);
63 | -ms-transform: translateX(-50%);
64 | -o-transform: translateX(-50%);
65 | }
66 |
67 | .dd-item {
68 | display: flex;
69 | color: var(--text-color-active);
70 | padding: 10px 15px;
71 | border-bottom: 1px solid #ccc;
72 | }
73 |
74 | .dd-item:hover {
75 | text-decoration: none;
76 | background: #eee;
77 | }
78 |
79 | .dd-item:last-child {
80 | border-bottom: none;
81 | }
82 |
83 | .dd-item-icon {
84 | margin-right: 10px;
85 | margin-top: 2px;
86 | }
87 |
--------------------------------------------------------------------------------
/src/elements/form/Label.css:
--------------------------------------------------------------------------------
1 | .label-ohif {
2 | font-size: 1em;
3 | color: #ffffff;
4 | }
5 |
6 | .label-example {
7 | background-color: var(--ui-gray-darker);
8 | }
9 |
--------------------------------------------------------------------------------
/src/elements/form/Label.js:
--------------------------------------------------------------------------------
1 | import './Label.css';
2 |
3 | import React from 'react';
4 |
5 | import PropTypes from 'prop-types';
6 |
7 | class Label extends React.Component {
8 | constructor(props) {
9 | super(props);
10 | this.state = { value: this.props.text };
11 | }
12 |
13 | render() {
14 | return (
15 |
18 | );
19 | }
20 | }
21 |
22 | Label.propTypes = {
23 | text: PropTypes.string.isRequired,
24 | for: PropTypes.string,
25 | };
26 |
27 | export { Label };
28 |
--------------------------------------------------------------------------------
/src/elements/form/Range.css:
--------------------------------------------------------------------------------
1 | .range {
2 | margin: 0;
3 | width: 100%;
4 | -webkit-appearance: none;
5 | }
6 |
7 | .range:focus {
8 | outline: none;
9 | }
10 |
11 | .range::-webkit-slider-runnable-track {
12 | width: 100%;
13 | height: 2px;
14 | cursor: pointer;
15 | box-shadow: none;
16 | background-color: var(--ui-border-color-dark);
17 | border-radius: 0px;
18 | border: 0px solid var(--ui-border-color-dark);
19 | }
20 |
21 | .range::-moz-range-track {
22 | width: 100%;
23 | height: 2px;
24 | cursor: pointer;
25 | box-shadow: none;
26 | background-color: var(--ui-border-color-dark);
27 | border-radius: 0px;
28 | border: 0px solid var(--ui-border-color-dark);
29 | }
30 |
31 | .range::-webkit-slider-thumb {
32 | box-shadow: none;
33 | border: 0px solid var(--active-color);
34 | box-shadow: 0px 10px 10px rgba(0, 0, 0, 0.25);
35 | height: 21px;
36 | width: 11px;
37 | border-radius: 11px;
38 | background: var(--active-color);
39 | cursor: pointer;
40 | -webkit-appearance: none;
41 | margin-top: -10px;
42 | }
43 |
44 | .range::-moz-range-thumb {
45 | box-shadow: none;
46 | border: 0px solid var(--active-color);
47 | box-shadow: 0px 10px 10px rgba(0, 0, 0, 0.25);
48 | height: 21px;
49 | width: 11px;
50 | border-radius: 11px;
51 | background: var(--active-color);
52 | cursor: pointer;
53 | -webkit-appearance: none;
54 | margin-top: -10px;
55 | }
56 |
57 | .range::-moz-focus-outer {
58 | border: 0;
59 | }
60 |
61 | .range-example {
62 | background: black;
63 | height: 2em;
64 | }
65 |
--------------------------------------------------------------------------------
/src/elements/form/Range.js:
--------------------------------------------------------------------------------
1 | import './Range.css';
2 |
3 | import React, { Component } from 'react';
4 | import PropTypes from 'prop-types';
5 |
6 | class Range extends React.Component {
7 | constructor(props) {
8 | super(props);
9 | this.state = { value: props.value };
10 | }
11 |
12 | handleChange = event => {
13 | this.setState({ value: event.target.value });
14 | if (this.props.onChange) this.props.onChange();
15 | };
16 |
17 | render() {
18 | return (
19 |
28 | );
29 | }
30 | }
31 |
32 | Range.propTypes = {
33 | value: PropTypes.number,
34 | min: PropTypes.number.isRequired,
35 | max: PropTypes.number.isRequired,
36 | id: PropTypes.string,
37 | onChange: PropTypes.func,
38 | };
39 |
40 | export { Range };
41 |
--------------------------------------------------------------------------------
/src/elements/form/Select.css:
--------------------------------------------------------------------------------
1 | .select-ohif {
2 | display: block;
3 | font-family: Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue',
4 | sans-serif;
5 | font-size: 0.8em;
6 | font-weight: 600;
7 | color: #444;
8 | line-height: 1.3;
9 | padding: 0.6em 1.4em 0.5em 0.8em;
10 | width: 100%;
11 | max-width: 100%;
12 | box-sizing: border-box;
13 | margin: 0;
14 | border: 1px solid #aaa;
15 | box-shadow: 0 1px 0 1px rgba(0, 0, 0, 0.04);
16 | border-radius: 0.5em;
17 | -moz-appearance: none;
18 | -webkit-appearance: none;
19 | appearance: none;
20 | background-color: #fff;
21 | background-image: url('data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%23007CB2%22%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%205.4-7.8%205.4-12.8%200-5-1.9-9.2-5.5-12.8z%22%2F%3E%3C%2Fsvg%3E'),
22 | linear-gradient(to bottom, #ffffff 0%, #e5e5e5 100%);
23 | background-repeat: no-repeat, repeat;
24 | background-position: right 0.7em top 50%, 0 0;
25 | background-size: 0.65em auto, 100%;
26 | }
27 |
28 | .select-ohif::-ms-expand {
29 | display: none;
30 | }
31 |
32 | .select-ohif:hover {
33 | border-color: #888;
34 | }
35 |
36 | .select-ohif:focus {
37 | border-color: #aaa;
38 | box-shadow: 0 0 1px 3px rgba(59, 153, 252, 0.7);
39 | box-shadow: 0 0 0 3px -moz-mac-focusring;
40 | color: #222;
41 | outline: none;
42 | }
43 |
44 | .select-ohif option {
45 | font-weight: normal;
46 | }
47 |
--------------------------------------------------------------------------------
/src/elements/form/Select.js:
--------------------------------------------------------------------------------
1 | import './Select.css';
2 |
3 | import React, { Component } from 'react';
4 |
5 | import PropTypes from 'prop-types';
6 |
7 | class Select extends Component {
8 | constructor(props) {
9 | super(props);
10 | this.state = { value: this.props.value };
11 | }
12 |
13 | handleChange = event => {
14 | this.setState({ value: event.target.value });
15 | if (this.props.onChange) this.props.onChange();
16 | };
17 |
18 | render() {
19 | return (
20 |
33 | );
34 | }
35 | }
36 |
37 | Select.propTypes = {
38 | options: PropTypes.arrayOf(
39 | PropTypes.shape({
40 | key: PropTypes.string.isRequired,
41 | value: PropTypes.string.isRequired,
42 | })
43 | ),
44 | value: PropTypes.string,
45 | onChange: PropTypes.func,
46 | };
47 |
48 | export { Select };
49 |
--------------------------------------------------------------------------------
/src/elements/form/TextArea.css:
--------------------------------------------------------------------------------
1 | .textarea-ohif {
2 | background-color: #b6b6b6;
3 | border-color: #b6b6b6;
4 | font-family: Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue',
5 | sans-serif;
6 | font-size: 1em;
7 | }
8 |
--------------------------------------------------------------------------------
/src/elements/form/TextArea.js:
--------------------------------------------------------------------------------
1 | import './TextArea.css';
2 |
3 | import React from 'react';
4 | import PropTypes from 'prop-types';
5 |
6 | class TextArea extends React.Component {
7 | constructor(props) {
8 | super(props);
9 | this.state = { value: this.props.value };
10 | }
11 |
12 | handleChange = event => {
13 | this.setState({ value: event.target.value });
14 | if (this.props.onChange) this.props.onChange();
15 | };
16 |
17 | render() {
18 | return (
19 |
27 | );
28 | }
29 | }
30 |
31 | TextArea.propTypes = {
32 | value: PropTypes.string,
33 | rows: PropTypes.number,
34 | cols: PropTypes.number,
35 | id: PropTypes.string,
36 | onChange: PropTypes.func,
37 | };
38 |
39 | export { TextArea };
40 |
--------------------------------------------------------------------------------
/src/elements/form/TextInput.css:
--------------------------------------------------------------------------------
1 | .input-ohif {
2 | background-color: #b6b6b6;
3 | border-color: #b6b6b6;
4 | font-family: Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue',
5 | sans-serif;
6 | font-size: 1em;
7 | }
8 |
--------------------------------------------------------------------------------
/src/elements/form/TextInput.js:
--------------------------------------------------------------------------------
1 | import './TextInput.css';
2 |
3 | import React from 'react';
4 | import PropTypes from 'prop-types';
5 |
6 | class TextInput extends React.Component {
7 | constructor(props) {
8 | super(props);
9 | this.state = { value: props.value };
10 | }
11 |
12 | handleChange = event => {
13 | this.setState({ value: event.target.value });
14 | if (this.props.onChange) this.props.onChange();
15 | };
16 |
17 | render() {
18 | return (
19 |
26 | );
27 | }
28 | }
29 |
30 | TextInput.propTypes = {
31 | value: PropTypes.string,
32 | id: PropTypes.string,
33 | onChange: PropTypes.func,
34 | };
35 |
36 | export { TextInput };
37 |
--------------------------------------------------------------------------------
/src/elements/form/__docs__/dropdownMenu.doc.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: Form / Dropdown Menu
3 | menu: Elements
4 | route: /elements/form-ddMenu
5 | ---
6 |
7 | import { Playground, Props } from 'docz';
8 | import { State } from 'react-powerplug';
9 | import { DropdownMenu } from './../index.js';
10 |
11 | # Select
12 |
13 | ## Basic usage
14 |
15 |
16 |
36 | {({ state, setState }) => }
37 |
38 |
39 |
40 | ## API
41 |
42 |
43 |
--------------------------------------------------------------------------------
/src/elements/form/__docs__/label.doc.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: Form / Label
3 | menu: Elements
4 | route: /elements/form-label
5 | ---
6 |
7 | import { Playground, Props } from 'docz';
8 | import { State } from 'react-powerplug';
9 | import { Label } from './../index.js';
10 |
11 | # Label
12 |
13 | ## Basic usage
14 |
15 |
16 |
22 | {({ state, setState }) => (
23 |
24 |
25 |
26 | )}
27 |
28 |
29 |
30 | ## API
31 |
32 |
33 |
--------------------------------------------------------------------------------
/src/elements/form/__docs__/range.doc.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: Form / Range
3 | menu: Elements
4 | route: /elements/form-range
5 | ---
6 |
7 | import { Playground, Props } from 'docz';
8 | import { State } from 'react-powerplug';
9 | import { Range } from './../index.js';
10 |
11 | # Range
12 |
13 | ## Basic usage
14 |
15 |
16 |
23 | {({ state, setState }) => (
24 |
25 |
26 |
27 | )}
28 |
29 |
30 |
31 | ## API
32 |
33 |
34 |
--------------------------------------------------------------------------------
/src/elements/form/__docs__/select.docs.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: Form / Select
3 | menu: Elements
4 | route: /elements/form-select
5 | ---
6 |
7 | import { Playground, Props } from 'docz';
8 | import { State } from 'react-powerplug';
9 | import { Select } from './../index.js';
10 |
11 | # Select
12 |
13 | ## Basic usage
14 |
15 |
16 |
31 |
32 | {({ state, setState }) => }
33 |
34 |
35 |
36 |
37 | ## API
38 |
39 |
40 |
--------------------------------------------------------------------------------
/src/elements/form/__docs__/textArea.doc.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: Form / Text Area
3 | menu: Elements
4 | route: /elements/form-textArea
5 | ---
6 |
7 | import { Playground, Props } from 'docz';
8 | import { State } from 'react-powerplug';
9 | import { TextArea } from './../index.js';
10 |
11 | # Text Area
12 |
13 | ## Basic usage
14 |
15 |
16 |
23 | {({ state, setState }) => }
24 |
25 |
26 |
27 | ## API
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/elements/form/__docs__/textInput.docs.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: Form / Text Input
3 | menu: Elements
4 | route: /elements/form-textInput
5 | ---
6 |
7 | import { Playground, Props } from 'docz';
8 | import { State } from 'react-powerplug';
9 | import { TextInput } from './../index.js';
10 |
11 | # Text Input
12 |
13 | ## Basic usage
14 |
15 |
16 |
21 | {({ state, setState }) => }
22 |
23 |
24 |
25 | ## API
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/elements/form/index.js:
--------------------------------------------------------------------------------
1 | export { Select } from './Select.js';
2 | export { Label } from './Label.js';
3 | export { Range } from './Range.js';
4 | export { TextArea } from './TextArea.js';
5 | export { TextInput } from './TextInput.js';
6 | export { DropdownMenu } from './DropdownMenu.js';
7 |
--------------------------------------------------------------------------------
/src/elements/index.js:
--------------------------------------------------------------------------------
1 | import { ICONS, Icon } from './Icon';
2 |
3 | export { Icon, ICONS };
4 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import {
2 | CineDialog,
3 | ExampleDropTarget,
4 | LayoutButton,
5 | LayoutChooser,
6 | MeasurementTable,
7 | MeasurementTableItem,
8 | Overlay,
9 | OverlayTrigger,
10 | QuickSwitch,
11 | RoundedButtonGroup,
12 | SelectTree,
13 | SimpleDialog,
14 | StudyBrowser,
15 | StudyList,
16 | TableList,
17 | TableListItem,
18 | ThumbnailEntry,
19 | ToolbarSection,
20 | Tooltip,
21 | AboutModal,
22 | UserPreferences,
23 | UserPreferencesModal,
24 | } from './components';
25 | import { ICONS, Icon } from './elements';
26 |
27 | // Alias this for now as not all dependents are using strict versioning
28 | import { DropdownMenu as Dropdown } from './elements/form';
29 | import ExpandableToolMenu from './viewer/ExpandableToolMenu.js';
30 | import LayoutManager from './LayoutChooser/LayoutManager.js';
31 | import LayoutPanelDropTarget from './LayoutChooser/LayoutPanelDropTarget.js';
32 | import PlayClipButton from './viewer/PlayClipButton.js';
33 | import { ScrollableArea } from './ScrollableArea/ScrollableArea.js';
34 | import Toolbar from './viewer/Toolbar.js';
35 | import ToolbarButton from './viewer/ToolbarButton.js';
36 | import ViewerbaseDragDropContext from './utils/viewerbaseDragDropContext.js';
37 |
38 | export {
39 | ICONS,
40 | //
41 | CineDialog,
42 | Dropdown,
43 | ExpandableToolMenu,
44 | ExampleDropTarget,
45 | Icon,
46 | LayoutButton,
47 | LayoutChooser,
48 | LayoutManager,
49 | LayoutPanelDropTarget,
50 | MeasurementTable,
51 | MeasurementTableItem,
52 | Overlay,
53 | OverlayTrigger,
54 | PlayClipButton,
55 | QuickSwitch,
56 | RoundedButtonGroup,
57 | ScrollableArea,
58 | SelectTree,
59 | SimpleDialog,
60 | StudyBrowser,
61 | StudyList,
62 | TableList,
63 | TableListItem,
64 | ThumbnailEntry,
65 | Toolbar,
66 | ToolbarButton,
67 | ToolbarSection,
68 | Tooltip,
69 | AboutModal,
70 | UserPreferences,
71 | UserPreferencesModal,
72 | ViewerbaseDragDropContext,
73 | };
74 |
--------------------------------------------------------------------------------
/src/test.js:
--------------------------------------------------------------------------------
1 | import * as ReactViewerbase from './index.js';
2 |
3 | describe('Top level exports', () => {
4 | test('have not changed', () => {
5 | const expectedExports = [
6 | 'ICONS',
7 | //
8 | 'CineDialog',
9 | 'Dropdown',
10 | 'ExpandableToolMenu',
11 | 'ExampleDropTarget',
12 | 'Icon',
13 | 'LayoutButton',
14 | 'LayoutChooser',
15 | 'LayoutManager',
16 | 'LayoutPanelDropTarget',
17 | 'MeasurementTable',
18 | 'MeasurementTableItem',
19 | 'Overlay',
20 | 'OverlayTrigger',
21 | 'PlayClipButton',
22 | 'QuickSwitch',
23 | 'RoundedButtonGroup',
24 | 'ScrollableArea',
25 | 'SelectTree',
26 | 'SimpleDialog',
27 | 'StudyBrowser',
28 | 'StudyList',
29 | 'TableList',
30 | 'TableListItem',
31 | 'ThumbnailEntry',
32 | 'Toolbar',
33 | 'ToolbarButton',
34 | 'ToolbarSection',
35 | 'Tooltip',
36 | 'AboutModal',
37 | 'UserPreferences',
38 | 'UserPreferencesModal',
39 | 'ViewerbaseDragDropContext',
40 | ].sort();
41 |
42 | const exports = Object.keys(ReactViewerbase).sort();
43 |
44 | expect(exports).toEqual(expectedExports);
45 | });
46 | });
47 |
--------------------------------------------------------------------------------
/src/utils/LanguageProvider.js:
--------------------------------------------------------------------------------
1 | // Reference > https://reactjs.org/docs/context.html
2 | import React from 'react';
3 | import {
4 | withTranslation as I18NextWithTranslation,
5 | I18nextProvider,
6 | } from 'react-i18next';
7 | import i18n from '@ohif/i18n';
8 |
9 | const WrapperI18n = Component => {
10 | const WrapperComponent = props => (
11 |
12 |
13 |
14 | );
15 |
16 | return WrapperComponent;
17 | };
18 |
19 | const withTranslation = namespace => Component => {
20 | const TranslatedComponent = props => {
21 | return ;
22 | };
23 |
24 | return WrapperI18n(I18NextWithTranslation(namespace)(TranslatedComponent));
25 | };
26 |
27 | export { withTranslation };
28 | export default withTranslation;
29 |
--------------------------------------------------------------------------------
/src/utils/getScrollbarSize.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Get the vertical and horizontal scrollbar sizes
3 | * Got from https://stackoverflow.com/questions/986937/how-can-i-get-the-browsers-scrollbar-sizes
4 | *
5 | * @returns {Array} Array containing the scrollbar horizontal and vertical sizes
6 | */
7 | export default function getScrollbarSize() {
8 | const inner = document.createElement('p');
9 | inner.style.width = '100%';
10 | inner.style.height = '100%';
11 |
12 | const outer = document.createElement('div');
13 | outer.style.position = 'absolute';
14 | outer.style.top = '0px';
15 | outer.style.left = '0px';
16 | outer.style.visibility = 'hidden';
17 | outer.style.width = '100px';
18 | outer.style.height = '100px';
19 | outer.style.overflow = 'hidden';
20 | outer.appendChild(inner);
21 |
22 | document.body.appendChild(outer);
23 |
24 | const w1 = inner.offsetWidth;
25 | const h1 = inner.offsetHeight;
26 | outer.style.overflow = 'scroll';
27 | let w2 = inner.offsetWidth;
28 | let h2 = inner.offsetHeight;
29 |
30 | if (w1 === w2) {
31 | w2 = outer.clientWidth;
32 | }
33 |
34 | if (h1 === h2) {
35 | h2 = outer.clientHeight;
36 | }
37 |
38 | document.body.removeChild(outer);
39 |
40 | return [w1 - w2, h1 - h2];
41 | }
42 |
--------------------------------------------------------------------------------
/src/utils/styleProperty.js:
--------------------------------------------------------------------------------
1 | // https://github.com/swederik/dragula/blob/ccc15d75186f5168e7abadbe3077cf12dab09f8b/styleProperty.js
2 |
3 | const browserProps = {};
4 |
5 | function eachVendor(prop, fn) {
6 | const prefixes = ['Webkit', 'Moz', 'ms', 'O'];
7 | fn(prop);
8 | for (let i = 0; i < prefixes.length; i++) {
9 | fn(prefixes[i] + prop.charAt(0).toUpperCase() + prop.slice(1));
10 | }
11 | }
12 |
13 | function check(property, testValue) {
14 | const sandbox = document.createElement('iframe');
15 | const element = document.createElement('p');
16 |
17 | document.body.appendChild(sandbox);
18 | sandbox.contentDocument.body.appendChild(element);
19 | const support = set(element, property, testValue);
20 |
21 | // We have to do this because remove() is not supported by IE11 and below
22 | sandbox.parentElement.removeChild(sandbox);
23 | return support;
24 | }
25 |
26 | function checkComputed(el, prop) {
27 | const computed = window.getComputedStyle(el).getPropertyValue(prop);
28 | return computed !== void 0 && computed.length > 0 && computed !== 'none';
29 | }
30 |
31 | function set(el, prop, value) {
32 | let match = false;
33 |
34 | if (browserProps[prop] === void 0) {
35 | eachVendor(prop, function(vendorProp) {
36 | if (el.style[vendorProp] !== void 0 && match === false) {
37 | el.style[vendorProp] = value;
38 | if (checkComputed(el, vendorProp)) {
39 | match = true;
40 | browserProps[prop] = vendorProp;
41 | }
42 | }
43 | });
44 | } else {
45 | el.style[browserProps[prop]] = value;
46 | return true;
47 | }
48 |
49 | return match;
50 | }
51 |
52 | const styleProperty = {
53 | check,
54 | set,
55 | };
56 |
57 | export default styleProperty;
58 |
--------------------------------------------------------------------------------
/src/utils/throttled.js:
--------------------------------------------------------------------------------
1 | export default function throttled(delay, callback) {
2 | let isThrottled = false,
3 | args,
4 | context;
5 |
6 | function wrapper() {
7 | if (isThrottled) {
8 | args = arguments;
9 | context = this;
10 | return;
11 | }
12 |
13 | isThrottled = true;
14 | callback.apply(this, arguments);
15 |
16 | setTimeout(() => {
17 | isThrottled = false;
18 | if (args) {
19 | wrapper.apply(context, args);
20 | args = context = null;
21 | }
22 | }, delay);
23 | }
24 |
25 | return wrapper;
26 | }
27 |
--------------------------------------------------------------------------------
/src/utils/viewerbaseDragDropContext.js:
--------------------------------------------------------------------------------
1 | import { DragDropContext } from 'react-dnd';
2 | import TouchBackend from 'react-dnd-touch-backend';
3 |
4 | // See https://github.com/react-dnd/react-dnd/issues/186#issuecomment-335429067
5 | // https://github.com/react-dnd/react-dnd/issues/186#issuecomment-282789420
6 | // TODO: Find a way for this context to be used in the parent application as well.
7 |
8 | // http://react-dnd.github.io/react-dnd/docs/api/drag-drop-context
9 | export default function viewerbaseDragDropContext(DecoratedClass) {
10 | return DragDropContext(TouchBackend({ enableMouseEvents: true }), null, true)(
11 | DecoratedClass
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/src/viewer/PlayClipButton.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | import { Icon } from './../elements/Icon';
4 | import PropTypes from 'prop-types';
5 |
6 | export default class PlayClipButton extends Component {
7 | static propTypes = {
8 | isPlaying: PropTypes.bool.isRequired,
9 | };
10 |
11 | static defaultProps = {
12 | isPlaying: false,
13 | };
14 |
15 | render() {
16 | const iconName = this.props.isPlaying ? 'stop' : 'play';
17 |
18 | return (
19 |
20 |
31 |
42 |
43 | );
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/viewer/PresetToggle.js:
--------------------------------------------------------------------------------
1 | import { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 | import ToolbarButton from './ToolbarButton.js';
5 |
6 | const wLPresetIDs = [
7 | 'setWLPresetSoftTissue',
8 | 'setWLPresetLung',
9 | 'setWLPresetLiver',
10 | 'setWLPresetBrain',
11 | ];
12 |
13 | export default class PresetToggle extends Component {
14 | static propTypes = {
15 | buttons: PropTypes.arrayOf(
16 | PropTypes.shape({
17 | label: PropTypes.string.isRequired,
18 | icon: PropTypes.oneOfType([
19 | PropTypes.string,
20 | PropTypes.shape({
21 | name: PropTypes.string.isRequired,
22 | }),
23 | ]),
24 | })
25 | ).isRequired,
26 | setToolActive: PropTypes.func.isRequired,
27 | };
28 |
29 | constructor(props) {
30 | super(props);
31 |
32 | this.state = {
33 | presetSelected: null,
34 | };
35 | }
36 | render() {
37 | /*const items = this.props.buttons.map((item, index) => {
38 | return ;
39 | });*/
40 |
41 | const wlPresetItems = this.props.buttons.map((button, index) => {
42 | if (wLPresetIDs.includes(button.command)) {
43 | return ;
44 | }
45 | return '';
46 | });
47 |
48 | const toolItems = this.props.buttons.map((button, index) => {
49 | if (!wLPresetIDs.includes(button.command)) {
50 | return ;
51 | }
52 | return '';
53 | });
54 |
55 | const selectedButton = this.props.buttons.find(button => {
56 | return button.id === this.state.selected;
57 | });
58 |
59 | return (
60 |
61 |
{wlPresetItems}
62 |
{toolItems}
63 |
64 | LEVELS:
65 | {selectedButton ? selectedButton.label : 'Manual'}
66 |
67 |
68 | );
69 | }
70 |
71 | onClick = id => {
72 | const buttonItem = this.props.buttons.find(button => button.command === id);
73 |
74 | this.setState({
75 | selected: buttonItem.id,
76 | });
77 | };
78 | }
79 |
--------------------------------------------------------------------------------
/src/viewer/SimpleToolbarButton.js:
--------------------------------------------------------------------------------
1 | import { Icon } from './../elements/Icon';
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 | import classnames from 'classnames';
5 |
6 | export function SimpleToolbarButton(props) {
7 | const className = classnames(props.className, 'btn btn-sm btn-default');
8 |
9 | return (
10 |
21 | );
22 | }
23 |
24 | SimpleToolbarButton.propTypes = {
25 | icon: PropTypes.string,
26 | title: PropTypes.string,
27 | className: PropTypes.string,
28 | id: PropTypes.string,
29 | onClick: PropTypes.func,
30 | };
31 | export default SimpleToolbarButton;
32 |
--------------------------------------------------------------------------------
/src/viewer/ToolbarButton.js:
--------------------------------------------------------------------------------
1 | import './toolbar-button.styl';
2 |
3 | import { Icon } from './../elements/Icon';
4 | import PropTypes from 'prop-types';
5 | import React from 'react';
6 | import classnames from 'classnames';
7 | import { withTranslation } from '../utils/LanguageProvider';
8 |
9 | export function ToolbarButton(props) {
10 | const { isActive, icon, labelWhenActive, onClick, t } = props;
11 | const className = classnames(props.className, { active: isActive });
12 | const iconProps = typeof icon === 'string' ? { name: icon } : icon;
13 | const label = isActive && labelWhenActive ? labelWhenActive : props.label;
14 |
15 | const arrowIconName = props.isExpanded ? 'caret-up' : 'caret-down';
16 | const arrowIcon = props.isExpandable && (
17 |
18 | );
19 |
20 | const handleClick = event => {
21 | if (onClick) {
22 | onClick(event, props);
23 | }
24 | };
25 |
26 | return (
27 |
28 | {iconProps &&
}
29 |
30 | {t(label)}
31 | {arrowIcon}
32 |
33 |
34 | );
35 | }
36 |
37 | ToolbarButton.propTypes = {
38 | id: PropTypes.string,
39 | isActive: PropTypes.bool,
40 | /** Display label for button */
41 | label: PropTypes.string.isRequired,
42 | /** Alternative text to show when button is active */
43 | labelWhenActive: PropTypes.string,
44 | className: PropTypes.string.isRequired,
45 | icon: PropTypes.oneOfType([
46 | PropTypes.string,
47 | PropTypes.shape({
48 | name: PropTypes.string.isRequired,
49 | }),
50 | ]),
51 | onClick: PropTypes.func,
52 | /** Determines if we show expandable 'caret' symbol */
53 | isExpandable: PropTypes.bool,
54 | /** Direction of expandable 'caret' symbol */
55 | isExpanded: PropTypes.bool,
56 | t: PropTypes.func.isRequired,
57 | };
58 |
59 | ToolbarButton.defaultProps = {
60 | isActive: false,
61 | className: 'toolbar-button',
62 | };
63 |
64 | export default withTranslation('Buttons')(ToolbarButton);
65 |
--------------------------------------------------------------------------------
/src/viewer/ViewportErrorIndicator.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | export function ViewportErrorIndicator(props) {
5 | return (
6 |
19 |
Error
20 |
An error has occurred.
21 |
{props.details}
22 |
23 | );
24 | }
25 |
26 | ViewportErrorIndicator.propTypes = {
27 | details: PropTypes.string,
28 | };
29 |
30 | export default ViewportErrorIndicator;
31 |
--------------------------------------------------------------------------------
/src/viewer/ViewportLoadingIndicator.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | export function ViewportLoadingIndicator(props) {
5 | return (
6 |
19 | Loading {props.percentComplete}
20 |
21 | );
22 | }
23 |
24 | ViewportLoadingIndicator.propTypes = {
25 | percentComplete: PropTypes.number,
26 | };
27 |
28 | export default ViewportLoadingIndicator;
29 |
--------------------------------------------------------------------------------
/src/viewer/toolbar-button.styl:
--------------------------------------------------------------------------------
1 | .toolbar-button
2 | height: 48px;
3 | color: var(--default-color);
4 | float: left;
5 | text-align: center;
6 | padding: 0 10px;
7 | cursor: pointer;
8 | -webkit-touch-callout: none; /* iOS Safari */
9 | -webkit-user-select: none; /* Safari */
10 | -khtml-user-select: none; /* Konqueror HTML */
11 | -moz-user-select: none; /* Firefox */
12 | -ms-user-select: none; /* Internet Explorer/Edge */
13 | user-select: none; /* Chrome and Opera */
14 |
15 | .toolbar-button-label
16 | font-size: 12px;
17 | font-weight: 500;
18 |
19 | .expand-caret
20 | width: 8px;
21 | height: 8px;
22 | transform: translate(2px, 2px);
23 |
24 | svg
25 | height: 21px;
26 | width: 21px;
27 | margin: 2px;
28 |
29 | &:hover
30 | color: var(--hover-color);
31 |
32 | &.active, &:active
33 | color: var(--active-color);
34 |
--------------------------------------------------------------------------------