├── .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 |
31 | 42 |
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 |
{buttons}
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 |
33 |
34 |
35 | 36 | x 37 | 38 |

{this.props.headerTitle}

39 |
40 |
{this.props.children}
41 |
42 | 45 | 48 |
49 |
50 |
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 |
56 |
{components}
57 |
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 | 9 | 3D Rotate 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/adjust.svg: -------------------------------------------------------------------------------- 1 | 9 | Adjust 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/angle-double-down.svg: -------------------------------------------------------------------------------- 1 | 9 | Angle Double Down 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/angle-double-up.svg: -------------------------------------------------------------------------------- 1 | 9 | Angle Double Up 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/angle-left.svg: -------------------------------------------------------------------------------- 1 | 9 | Angle Left 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/arrows-alt-h.svg: -------------------------------------------------------------------------------- 1 | 9 | Arrows Alt H 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/arrows-alt-v.svg: -------------------------------------------------------------------------------- 1 | 9 | Arrows Alt V 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/arrows.svg: -------------------------------------------------------------------------------- 1 | 13 | Arrows 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/bars.svg: -------------------------------------------------------------------------------- 1 | 9 | Bars 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/caret-down.svg: -------------------------------------------------------------------------------- 1 | 9 | Caret Down 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/caret-up.svg: -------------------------------------------------------------------------------- 1 | 9 | Caret Up 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/check-circle-o.svg: -------------------------------------------------------------------------------- 1 | 9 | Check Circle 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/check-circle.svg: -------------------------------------------------------------------------------- 1 | 9 | Check Circle 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/check.svg: -------------------------------------------------------------------------------- 1 | 9 | Check 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/chevron-down.svg: -------------------------------------------------------------------------------- 1 | 9 | Chevron Down 10 | 14 | 15 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/circle-notch.svg: -------------------------------------------------------------------------------- 1 | 9 | Circle Notch 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/circle-o.svg: -------------------------------------------------------------------------------- 1 | 9 | Circle Outline 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/circle.svg: -------------------------------------------------------------------------------- 1 | 9 | Circle 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/cog.svg: -------------------------------------------------------------------------------- 1 | 9 | Cog 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/create-comment.svg: -------------------------------------------------------------------------------- 1 | 9 | Create Comment 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/create-screen-capture.svg: -------------------------------------------------------------------------------- 1 | 9 | Create Screen Capture 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/crosshairs.svg: -------------------------------------------------------------------------------- 1 | 9 | Crosshairs 10 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/database.svg: -------------------------------------------------------------------------------- 1 | 9 | Database 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/dot-circle.svg: -------------------------------------------------------------------------------- 1 | 9 | Dot Circle 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/edit.svg: -------------------------------------------------------------------------------- 1 | 9 | Edit 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/ellipse-circle.svg: -------------------------------------------------------------------------------- 1 | 9 | Ellipse Circle 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/ellipse-h.svg: -------------------------------------------------------------------------------- 1 | 9 | Ellipse Horizontal 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/ellipse-v.svg: -------------------------------------------------------------------------------- 1 | 9 | Ellipse Vertical 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/exclamation-circle.svg: -------------------------------------------------------------------------------- 1 | 11 | Exclamation Circle 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/exclamation-triangle.svg: -------------------------------------------------------------------------------- 1 | 9 | Exclamation Triangle 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/fast-backward.svg: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/fast-forward.svg: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/info.svg: -------------------------------------------------------------------------------- 1 | 9 | Info 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/inline-edit.svg: -------------------------------------------------------------------------------- 1 | 9 | Inline Edit 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/level.svg: -------------------------------------------------------------------------------- 1 | 10 | Level 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/link-circles.svg: -------------------------------------------------------------------------------- 1 | 10 | Link Circles 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/link.svg: -------------------------------------------------------------------------------- 1 | 9 | Link 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/list.svg: -------------------------------------------------------------------------------- 1 | 9 | List 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/liver.svg: -------------------------------------------------------------------------------- 1 | 12 | Liver 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/lock-alt.svg: -------------------------------------------------------------------------------- 1 | 9 | Lock 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/lock.svg: -------------------------------------------------------------------------------- 1 | 9 | Lock 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/lung.svg: -------------------------------------------------------------------------------- 1 | 12 | Lung 13 | 14 | 15 | 16 | 20 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/measure-non-target.svg: -------------------------------------------------------------------------------- 1 | 13 | Measure Non-Target 14 | 20 | 24 | 29 | 30 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/measure-target-cr.svg: -------------------------------------------------------------------------------- 1 | 13 | Measure Target CR 14 | 20 | CR 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/measure-target-ne.svg: -------------------------------------------------------------------------------- 1 | 13 | Measure Target NE 14 | NE 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/measure-target-un.svg: -------------------------------------------------------------------------------- 1 | 13 | Measure Target UN 14 | 20 | UN 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/measure-target.svg: -------------------------------------------------------------------------------- 1 | 10 | Measure Target 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/measure-temp.svg: -------------------------------------------------------------------------------- 1 | 10 | Measure Temp 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/object-group.svg: -------------------------------------------------------------------------------- 1 | 12 | Object Group 13 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/ohif-logo.svg: -------------------------------------------------------------------------------- 1 | 12 | OHIF Logo 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/oval.svg: -------------------------------------------------------------------------------- 1 | 9 | Oval 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/palette.svg: -------------------------------------------------------------------------------- 1 | 9 | Pallete 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/play.svg: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/plus.svg: -------------------------------------------------------------------------------- 1 | 9 | Add 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/power-off.svg: -------------------------------------------------------------------------------- 1 | 9 | Power Off 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/reset.svg: -------------------------------------------------------------------------------- 1 | 9 | Reset 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/rotate-right.svg: -------------------------------------------------------------------------------- 1 | 9 | Rotate Right 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/rotate.svg: -------------------------------------------------------------------------------- 1 | 9 | Rotate 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/search-plus.svg: -------------------------------------------------------------------------------- 1 | 9 | Search Plus 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/search.svg: -------------------------------------------------------------------------------- 1 | 9 | Search 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/soft-tissue.svg: -------------------------------------------------------------------------------- 1 | 14 | Soft Tissue 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/sort-down.svg: -------------------------------------------------------------------------------- 1 | 9 | Sort Down 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/sort-up.svg: -------------------------------------------------------------------------------- 1 | 9 | Sort Up 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/sort.svg: -------------------------------------------------------------------------------- 1 | 9 | Sort 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/square-o.svg: -------------------------------------------------------------------------------- 1 | 9 | Square Outline 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/star.svg: -------------------------------------------------------------------------------- 1 | 9 | Star 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/step-backward.svg: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/step-forward.svg: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/stop.svg: -------------------------------------------------------------------------------- 1 | 9 | Stop 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/sun.svg: -------------------------------------------------------------------------------- 1 | 9 | Sun 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/th-large.svg: -------------------------------------------------------------------------------- 1 | 10 | TH Large 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/th-list.svg: -------------------------------------------------------------------------------- 1 | 9 | TH List 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/th.svg: -------------------------------------------------------------------------------- 1 | 9 | TH 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/times.svg: -------------------------------------------------------------------------------- 1 | 10 | Times 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/trash.svg: -------------------------------------------------------------------------------- 1 | 9 | Trash 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/user.svg: -------------------------------------------------------------------------------- 1 | 9 | User 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/elements/Icon/icons/youtube.svg: -------------------------------------------------------------------------------- 1 | 9 | YouTube Logo 10 | 11 | 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 |