From ca7506c5f07fbf6d5eb1b5e8489a37638056e456 Mon Sep 17 00:00:00 2001 From: Erik Ziegler Date: Wed, 21 Jul 2021 13:16:50 +0200 Subject: [PATCH] Try to get views to work for component library --- platform/core/src/utils/hotkeys/index.js | 14 + .../core/src/utils/hotkeys/pausePlugin.js | 32 ++ .../core/src/utils/hotkeys/recordPlugin.js | 218 +++++++++++ platform/docs/docs/configuration/index.md | 2 +- .../docs/deployment/build-for-production.md | 2 +- .../example-views/StudyList.mdx | 345 +++++++++++++++++ .../{views => example-views}/Viewer.mdx.todo | 0 .../example-views/_category_.json | 4 + .../views/StudyList.mdx.todo | 346 ------------------ platform/docs/docs/platform/themeing.md | 53 ++- platform/docs/package.json | 1 + .../{ui => docs}/src/mocks/studyList.json | 0 .../docs/src/theme/ReactLiveScope/index.js | 6 + platform/docs/src/utils/getMockedStudies.js | 17 + platform/docs/src/utils/index.js | 7 + .../components/HotkeyField/HotkeyField.jsx | 2 +- .../HotkeysPreferences/HotkeysPreferences.jsx | 2 +- .../UserPreferences/UserPreferences.jsx | 2 +- yarn.lock | 23 +- 19 files changed, 694 insertions(+), 382 deletions(-) create mode 100644 platform/core/src/utils/hotkeys/index.js create mode 100644 platform/core/src/utils/hotkeys/pausePlugin.js create mode 100644 platform/core/src/utils/hotkeys/recordPlugin.js create mode 100644 platform/docs/docs/platform/component-library/example-views/StudyList.mdx rename platform/docs/docs/platform/component-library/{views => example-views}/Viewer.mdx.todo (100%) create mode 100644 platform/docs/docs/platform/component-library/example-views/_category_.json delete mode 100644 platform/docs/docs/platform/component-library/views/StudyList.mdx.todo rename platform/{ui => docs}/src/mocks/studyList.json (100%) create mode 100644 platform/docs/src/utils/getMockedStudies.js create mode 100644 platform/docs/src/utils/index.js diff --git a/platform/core/src/utils/hotkeys/index.js b/platform/core/src/utils/hotkeys/index.js new file mode 100644 index 000000000..9118e672f --- /dev/null +++ b/platform/core/src/utils/hotkeys/index.js @@ -0,0 +1,14 @@ +import Mousetrap from 'mousetrap'; +import pausePlugin from './pausePlugin'; +import recordPlugin from './recordPlugin'; + +Mousetrap.initialize = () => { + if (!Mousetrap._initialized) { + recordPlugin(Mousetrap); + pausePlugin(Mousetrap); + + Mousetrap._initialized = true; + } +}; + +export default Mousetrap; diff --git a/platform/core/src/utils/hotkeys/pausePlugin.js b/platform/core/src/utils/hotkeys/pausePlugin.js new file mode 100644 index 000000000..80c5513f0 --- /dev/null +++ b/platform/core/src/utils/hotkeys/pausePlugin.js @@ -0,0 +1,32 @@ +/** + * adds a pause and unpause method to Mousetrap + * this allows you to enable or disable keyboard shortcuts + * without having to reset Mousetrap and rebind everything + * + * https://github.com/ccampbell/mousetrap/blob/master/plugins/pause/mousetrap-pause.js + */ +export default function(Mousetrap) { + var _originalStopCallback = Mousetrap.prototype.stopCallback; + + Mousetrap.prototype.stopCallback = function(e, element, combo) { + var self = this; + + if (self.paused) { + return true; + } + + return _originalStopCallback.call(self, e, element, combo); + }; + + Mousetrap.prototype.pause = function() { + var self = this; + self.paused = true; + }; + + Mousetrap.prototype.unpause = function() { + var self = this; + self.paused = false; + }; + + Mousetrap.init(); +} diff --git a/platform/core/src/utils/hotkeys/recordPlugin.js b/platform/core/src/utils/hotkeys/recordPlugin.js new file mode 100644 index 000000000..186964967 --- /dev/null +++ b/platform/core/src/utils/hotkeys/recordPlugin.js @@ -0,0 +1,218 @@ +/** + * This extension allows you to record a sequence using Mousetrap. + * {@link https://craig.is/killing/mice} + * + * @author Dan Tao + */ +export default function (Mousetrap) { + /** + * the sequence currently being recorded + * + * @type {Array} + */ + var _recordedSequence = [], + /** + * a callback to invoke after recording a sequence + * + * @type {Function|null} + */ + _recordedSequenceCallback = null, + /** + * a list of all of the keys currently held down + * + * @type {Array} + */ + _currentRecordedKeys = [], + /** + * temporary state where we remember if we've already captured a + * character key in the current combo + * + * @type {boolean} + */ + _recordedCharacterKey = false, + /** + * a handle for the timer of the current recording + * + * @type {null|number} + */ + _recordTimer = null, + /** + * the original handleKey method to override when Mousetrap.record() is + * called + * + * @type {Function} + */ + _origHandleKey = Mousetrap.prototype.handleKey; + + /** + * handles a character key event + * + * @param {string} character + * @param {Array} modifiers + * @param {Event} e + * @returns void + */ + function _handleKey(character, modifiers, e) { + var self = this; + + if (!self.recording) { + _origHandleKey.apply(self, arguments); + return; + } + + // remember this character if we're currently recording a sequence + if (e.type == 'keydown') { + if (character.length === 1 && _recordedCharacterKey) { + _recordCurrentCombo(); + } + + for (let i = 0; i < modifiers.length; ++i) { + _recordKey(modifiers[i]); + } + _recordKey(character); + + // once a key is released, all keys that were held down at the time + // count as a keypress + } else if (e.type == 'keyup' && _currentRecordedKeys.length > 0) { + _recordCurrentCombo(); + } + } + + /** + * marks a character key as held down while recording a sequence + * + * @param {string} key + * @returns void + */ + function _recordKey(key) { + // one-off implementation of Array.indexOf, since IE6-9 don't support it + for (let i = 0; i < _currentRecordedKeys.length; ++i) { + if (_currentRecordedKeys[i] === key) { + return; + } + } + + _currentRecordedKeys.push(key); + + if (key.length === 1) { + _recordedCharacterKey = true; + } + } + + /** + * marks whatever key combination that's been recorded so far as finished + * and gets ready for the next combo + * + * @returns void + */ + function _recordCurrentCombo() { + _recordedSequence.push(_currentRecordedKeys); + _currentRecordedKeys = []; + _recordedCharacterKey = false; + _finishRecording(); + } + + /** + * ensures each combo in a sequence is in a predictable order and formats + * key combos to be '+'-delimited + * + * modifies the sequence in-place + * + * @param {Array} sequence + * @returns void + */ + function _normalizeSequence(sequence) { + for (let i = 0; i < sequence.length; ++i) { + sequence[i].sort(function (x, y) { + // modifier keys always come first, in alphabetical order + if (x.length > 1 && y.length === 1) { + return -1; + } else if (x.length === 1 && y.length > 1) { + return 1; + } + + // character keys come next (list should contain no duplicates, + // so no need for equality check) + return x > y ? 1 : -1; + }); + + sequence[i] = sequence[i].join('+'); + } + } + + /** + * finishes the current recording, passes the recorded sequence to the stored + * callback, and sets Mousetrap.handleKey back to its original function + * + * @returns void + */ + function _finishRecording() { + if (_recordedSequenceCallback) { + _normalizeSequence(_recordedSequence); + _recordedSequenceCallback(_recordedSequence); + } + + // reset all recorded state + _recordedSequence = []; + _recordedSequenceCallback = null; + _currentRecordedKeys = []; + } + + /** + * called to set a 1 second timeout on the current recording + * + * this is so after each key press in the sequence the recording will wait for + * 1 more second before executing the callback + * + * @returns void + */ + function _restartRecordTimer() { + clearTimeout(_recordTimer); + _recordTimer = setTimeout(_finishRecording, 1000); + } + + /** + * records the next sequence and passes it to a callback once it's + * completed + * + * @param {Function} callback + * @returns void + */ + Mousetrap.prototype.record = function (callback) { + var self = this; + self.recording = true; + _recordedSequenceCallback = function () { + self.recording = false; + callback.apply(self, arguments); + }; + }; + + /** + * stop recording + * + * @param {Function} callback + * @returns void + */ + Mousetrap.prototype.stopRecord = function () { + var self = this; + self.recording = false; + }; + + /** + * start recording + * + * @param {Function} callback + * @returns void + */ + Mousetrap.prototype.startRecording = function () { + var self = this; + self.recording = true; + }; + + Mousetrap.prototype.handleKey = function () { + var self = this; + _handleKey.apply(self, arguments); + }; + + Mousetrap.init(); +} diff --git a/platform/docs/docs/configuration/index.md b/platform/docs/docs/configuration/index.md index 18c3a98f0..16e359140 100644 --- a/platform/docs/docs/configuration/index.md +++ b/platform/docs/docs/configuration/index.md @@ -61,7 +61,7 @@ window.config = { > data! > > You can read more about data sources at -> [Data Source section in Modes](../modes/index.md) +> [Data Source section in Modes](../platform/modes/index.md) The configuration can also be written as a JS Function in case you need to inject dependencies like external services: diff --git a/platform/docs/docs/deployment/build-for-production.md b/platform/docs/docs/deployment/build-for-production.md index cc3d55c5d..2df1daa82 100644 --- a/platform/docs/docs/deployment/build-for-production.md +++ b/platform/docs/docs/deployment/build-for-production.md @@ -70,7 +70,7 @@ and registered extension's features, are configured using this file. The easiest way to apply your own configuration is to modify the `default.js` file. For more advanced cofiguration options, check out our -[configuration essentials guide](../../configuration/index.md). +[configuration essentials guide](../configuration/index.md). ## Next Steps diff --git a/platform/docs/docs/platform/component-library/example-views/StudyList.mdx b/platform/docs/docs/platform/component-library/example-views/StudyList.mdx new file mode 100644 index 000000000..be2c08b93 --- /dev/null +++ b/platform/docs/docs/platform/component-library/example-views/StudyList.mdx @@ -0,0 +1,345 @@ +--- +name: Study List +menu: Views +route: examples/studyList +--- + +import { useState } from 'react'; +import { + Icon, + StudyListExpandedRow, + Button, + NavBar, + Svg, + IconButton, + EmptyStudies, + StudyListTable, + StudyListPagination, + StudyListFilter, +} from '@ohif/ui'; +import utils from '../../../../src/utils'; + +import classnames from 'classnames'; +import moment from 'moment'; + +# Study List + +This example shows you how you can build a Study List page using the available +components. + +```jsx live +function () { + const defaultFilterValues = { + patientName: '', + mrn: '', + studyDate: { + startDate: null, + endDate: null, + }, + description: '', + modality: undefined, + accession: '', + sortBy: '', + sortDirection: 'none', + page: 0, + resultsPerPage: 25, + }; + const [filterValues, setFilterValues] = useState(defaultFilterValues); + const [studies, setStudies] = useState([]); + const numOfStudies = studies.length; + const [expandedRows, setExpandedRows] = useState([]); + const filtersMeta = [ + { + name: 'patientName', + displayName: 'Patient Name', + inputType: 'Text', + isSortable: true, + gridCol: 4, + }, + { + name: 'mrn', + displayName: 'MRN', + inputType: 'Text', + isSortable: true, + gridCol: 2, + }, + { + name: 'studyDate', + displayName: 'Study date', + inputType: 'DateRange', + isSortable: true, + gridCol: 5, + }, + { + name: 'description', + displayName: 'Description', + inputType: 'Text', + isSortable: true, + gridCol: 4, + }, + { + name: 'modality', + displayName: 'Modality', + inputType: 'MultiSelect', + inputProps: { + options: [ + { value: 'SEG', label: 'SEG' }, + { value: 'CT', label: 'CT' }, + { value: 'MR', label: 'MR' }, + { value: 'SR', label: 'SR' }, + ], + }, + isSortable: true, + gridCol: 3, + }, + { + name: 'accession', + displayName: 'Accession', + inputType: 'Text', + isSortable: true, + gridCol: 4, + }, + { + name: 'instances', + displayName: 'Instances', + inputType: 'None', + isSortable: true, + gridCol: 2, + }, + ]; + const isFiltering = (filterValues, defaultFilterValues) => { + return Object.keys(defaultFilterValues).some(name => { + return filterValues[name] !== defaultFilterValues[name]; + }); + }; + const tableDataSource = studies.map((study, key) => { + const rowKey = key + 1; + const isExpanded = expandedRows.some(k => k === rowKey); + const { + AccessionNumber, + Modalities, + Instances, + StudyDescription, + PatientId, + PatientName, + StudyDate, + series, + } = study; + const seriesTableColumns = { + description: 'Description', + seriesNumber: 'Series', + modality: 'Modality', + Instances: 'Instances', + }; + const seriesTableDataSource = series.map(seriesItem => { + const { SeriesNumber, Modality, instances } = seriesItem; + return { + description: 'Patient Protocol', + seriesNumber: SeriesNumber, + modality: Modality, + Instances: instances.length, + }; + }); + return { + row: [ + { + key: 'patientName', + content: PatientName, + gridCol: 4, + }, + { + key: 'mrn', + content: PatientId, + gridCol: 2, + }, + { + key: 'studyDate', + content: ( +
+ + {moment(StudyDate).format('MMM-DD-YYYY')} + + {moment(StudyDate).format('hh:mm A')} +
+ ), + gridCol: 5, + }, + { + key: 'description', + content: StudyDescription, + gridCol: 4, + }, + { + key: 'modality', + content: Modalities, + gridCol: 3, + }, + { + key: 'accession', + content: AccessionNumber, + gridCol: 4, + }, + { + key: 'instances', + content: ( + <> + + {Instances} + + ), + gridCol: 4, + }, + ], + expandedContent: ( + + + + +
+ + Feedback text lorem ipsum dolor sit amet +
+
+ ), + onClickRow: () => + setExpandedRows(s => + isExpanded ? s.filter(n => rowKey !== n) : [...s, rowKey] + ), + isExpanded, + }; + }); + const handleStudyList = number => { + const studies = utils.getMockedStudies(number); + setStudies(studies); + setCurrentPage(1); + }; + const [currentPage, setCurrentPage] = useState(1); + const [perPage, setPerPage] = useState(25); + const totalPages = Math.floor(numOfStudies / perPage); + const onChangePage = page => { + if (page > totalPages) { + return; + } + setCurrentPage(page); + }; + const onChangePerPage = perPage => { + setPerPage(perPage); + setCurrentPage(1); + }; + const hasStudies = numOfStudies > 0; + return ( +
+
+ + + + +
+
+ +
+
+ +
+
+
+ + FOR INVESTIGATIONAL USE ONLY + + {}} + > + + + + + +
+
+ setFilterValues(defaultFilterValues)} + isFiltering={isFiltering(filterValues, defaultFilterValues)} + /> + {hasStudies ? ( + <> + + + + ) : ( +
+ +
+ )} +
+
+ ); +} +``` diff --git a/platform/docs/docs/platform/component-library/views/Viewer.mdx.todo b/platform/docs/docs/platform/component-library/example-views/Viewer.mdx.todo similarity index 100% rename from platform/docs/docs/platform/component-library/views/Viewer.mdx.todo rename to platform/docs/docs/platform/component-library/example-views/Viewer.mdx.todo diff --git a/platform/docs/docs/platform/component-library/example-views/_category_.json b/platform/docs/docs/platform/component-library/example-views/_category_.json new file mode 100644 index 000000000..f9601aa2b --- /dev/null +++ b/platform/docs/docs/platform/component-library/example-views/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Example Views", + "position": 6 +} diff --git a/platform/docs/docs/platform/component-library/views/StudyList.mdx.todo b/platform/docs/docs/platform/component-library/views/StudyList.mdx.todo deleted file mode 100644 index 83139ec63..000000000 --- a/platform/docs/docs/platform/component-library/views/StudyList.mdx.todo +++ /dev/null @@ -1,346 +0,0 @@ ---- -name: Study List -menu: Views -route: examples/studyList ---- - -import { useState } from 'react'; -import { - Icon, - StudyListExpandedRow, - Button, - NavBar, - Svg, - IconButton, - EmptyStudies, - StudyListTable, - StudyListPagination, - StudyListFilter -} from '@ohif/ui'; -import utils from '../../utils'; - - -import classnames from 'classnames'; -import moment from 'moment'; - -# Study List - -This example shows you how you can build a Study List page using the available -components. - -```jsx live - {() => { - const defaultFilterValues = { - patientName: '', - mrn: '', - studyDate: { - startDate: null, - endDate: null, - }, - description: '', - modality: undefined, - accession: '', - sortBy: '', - sortDirection: 'none', - page: 0, - resultsPerPage: 25, - }; - const [filterValues, setFilterValues] = useState(defaultFilterValues); - const [studies, setStudies] = useState([]); - const numOfStudies = studies.length; - const [expandedRows, setExpandedRows] = useState([]); - const filtersMeta = [ - { - name: 'patientName', - displayName: 'Patient Name', - inputType: 'Text', - isSortable: true, - gridCol: 4, - }, - { - name: 'mrn', - displayName: 'MRN', - inputType: 'Text', - isSortable: true, - gridCol: 2, - }, - { - name: 'studyDate', - displayName: 'Study date', - inputType: 'DateRange', - isSortable: true, - gridCol: 5, - }, - { - name: 'description', - displayName: 'Description', - inputType: 'Text', - isSortable: true, - gridCol: 4, - }, - { - name: 'modality', - displayName: 'Modality', - inputType: 'MultiSelect', - inputProps: { - options: [ - { value: 'SEG', label: 'SEG' }, - { value: 'CT', label: 'CT' }, - { value: 'MR', label: 'MR' }, - { value: 'SR', label: 'SR' }, - ], - }, - isSortable: true, - gridCol: 3, - }, - { - name: 'accession', - displayName: 'Accession', - inputType: 'Text', - isSortable: true, - gridCol: 4, - }, - { - name: 'instances', - displayName: 'Instances', - inputType: 'None', - isSortable: true, - gridCol: 2, - }, - ]; - const isFiltering = (filterValues, defaultFilterValues) => { - return Object.keys(defaultFilterValues).some((name) => { - return filterValues[name] !== defaultFilterValues[name]; - }); - }; - const tableDataSource = studies.map((study, key) => { - const rowKey = key + 1; - const isExpanded = expandedRows.some((k) => k === rowKey); - const { - AccessionNumber, - Modalities, - Instances, - StudyDescription, - PatientId, - PatientName, - StudyDate, - series, - } = study; - const seriesTableColumns = { - description: 'Description', - seriesNumber: 'Series', - modality: 'Modality', - Instances: 'Instances', - }; - const seriesTableDataSource = series.map((seriesItem) => { - const { SeriesNumber, Modality, instances } = seriesItem; - return { - description: 'Patient Protocol', - seriesNumber: SeriesNumber, - modality: Modality, - Instances: instances.length, - }; - }); - return { - row: [ - { - key: 'patientName', - content: PatientName, - gridCol: 4, - }, - { - key: 'mrn', - content: PatientId, - gridCol: 2, - }, - { - key: 'studyDate', - content: ( -
- - {moment(StudyDate).format('MMM-DD-YYYY')} - - {moment(StudyDate).format('hh:mm A')} -
- ), - gridCol: 5, - }, - { - key: 'description', - content: StudyDescription, - gridCol: 4, - }, - { - key: 'modality', - content: Modalities, - gridCol: 3, - }, - { - key: 'accession', - content: AccessionNumber, - gridCol: 4, - }, - { - key: 'instances', - content: ( - <> - - {Instances} - - ), - gridCol: 4, - }, - ], - expandedContent: ( - - - - -
- - Feedback text lorem ipsum dolor sit amet -
-
- ), - onClickRow: () => - setExpandedRows((s) => - isExpanded ? s.filter((n) => rowKey !== n) : [...s, rowKey] - ), - isExpanded, - }; - }); - const handleStudyList = (number) => { - const studies = utils.getMockedStudies(number); - setStudies(studies); - setCurrentPage(1); - }; - const [currentPage, setCurrentPage] = useState(1); - const [perPage, setPerPage] = useState(25); - const totalPages = Math.floor(numOfStudies / perPage); - const onChangePage = (page) => { - if (page > totalPages) { - return; - } - setCurrentPage(page); - }; - const onChangePerPage = (perPage) => { - setPerPage(perPage); - setCurrentPage(1); - }; - const hasStudies = numOfStudies > 0; - return ( -
-
- - - - -
-
- -
-
- -
-
-
- - FOR INVESTIGATIONAL USE ONLY - - {}} - > - - - - - -
-
- setFilterValues(defaultFilterValues)} - isFiltering={isFiltering(filterValues, defaultFilterValues)} - /> - {hasStudies ? ( - <> - - - - ) : ( -
- -
- )} -
-
- ); - }} -``` diff --git a/platform/docs/docs/platform/themeing.md b/platform/docs/docs/platform/themeing.md index 012d3284c..48a881c20 100644 --- a/platform/docs/docs/platform/themeing.md +++ b/platform/docs/docs/platform/themeing.md @@ -2,23 +2,22 @@ sidebar_position: 2 sidebar_label: Theming --- + # Viewer: Theming - -`OHIF-v3` has introduced the [`LayoutTemplateModule`](../extensions/modules/layout-template.md) which enables addition of custom layouts. You can easily design your custom components inside an extension and consume it via the layoutTemplate module you write. - - - +`OHIF-v3` has introduced the +[`LayoutTemplateModule`](./extensions/modules/layout-template.md) which enables +addition of custom layouts. You can easily design your custom components inside +an extension and consume it via the layoutTemplate module you write. ## Tailwind CSS -[Tailwind CSS](https://tailwindcss.com/) is a utility-first CSS framework for creating custom user interfaces. - - -Below you can see a compiled version of the tailwind configs. -Each section can be edited accordingly. For instance screen size break points, primary -and secondary colors, etc. +[Tailwind CSS](https://tailwindcss.com/) is a utility-first CSS framework for +creating custom user interfaces. +Below you can see a compiled version of the tailwind configs. Each section can +be edited accordingly. For instance screen size break points, primary and +secondary colors, etc. ```js module.exports = { @@ -79,13 +78,11 @@ module.exports = { }, }, }, -} +}; ``` - You can also use the color variable like before. For instance: - ```js primary: { default: ‘var(--default-color)‘, @@ -96,19 +93,22 @@ primary: { } ``` - ## White Labeling -A white-label product is a product or service produced by one company (the producer) that other companies (the marketers) rebrand to make it appear as if they had made it - [Wikipedia: White-Label Product](https://en.wikipedia.org/wiki/White-label_product) +A white-label product is a product or service produced by one company (the +producer) that other companies (the marketers) rebrand to make it appear as if +they had made it - +[Wikipedia: White-Label Product](https://en.wikipedia.org/wiki/White-label_product) -Current white-labeling options are limited. -We expose the ability to replace the "Logo" section of the application with a custom "Logo" component. You can do this by adding a whiteLabeling key to your configuration file. +Current white-labeling options are limited. We expose the ability to replace the +"Logo" section of the application with a custom "Logo" component. You can do +this by adding a whiteLabeling key to your configuration file. ```js window.config = { /** .. **/ whiteLabeling: { - createLogoComponentFn: function (React) { + createLogoComponentFn: function(React) { return React.createElement( 'a', { @@ -118,24 +118,22 @@ window.config = { href: 'http://radicalimaging.com', }, React.createElement('h5', {}, 'RADICAL IMAGING') - ) + ); }, }, /** .. **/ -} +}; ``` > You can simply use the stylings from tailwind CSS in the whiteLabeling - In addition to text, you can also add your custom logo - ```js window.config = { /** .. **/ whiteLabeling: { - createLogoComponentFn: function (React) { + createLogoComponentFn: function(React) { return React.createElement( 'a', { @@ -148,20 +146,17 @@ window.config = { src: './customLogo.svg', // className: 'w-8 h-8', }) - ) + ); }, }, /** .. **/ -} +}; ``` The output will look like - ![custom-logo](../assets/img/custom-logo.png) - - diff --git a/platform/docs/package.json b/platform/docs/package.json index 7ef0f252a..f78b6cf58 100644 --- a/platform/docs/package.json +++ b/platform/docs/package.json @@ -35,6 +35,7 @@ "@mdx-js/react": "^1.6.21", "@ohif/ui": "^2.0.0", "@svgr/webpack": "^5.5.0", + "classnames": "^2.3.1", "clsx": "^1.1.1", "file-loader": "^6.2.0", "plugin-image-zoom": "ataft/plugin-image-zoom", diff --git a/platform/ui/src/mocks/studyList.json b/platform/docs/src/mocks/studyList.json similarity index 100% rename from platform/ui/src/mocks/studyList.json rename to platform/docs/src/mocks/studyList.json diff --git a/platform/docs/src/theme/ReactLiveScope/index.js b/platform/docs/src/theme/ReactLiveScope/index.js index 737713998..e5c30d72e 100644 --- a/platform/docs/src/theme/ReactLiveScope/index.js +++ b/platform/docs/src/theme/ReactLiveScope/index.js @@ -6,13 +6,19 @@ */ import React from 'react'; +import classnames from 'classnames'; +import moment from 'moment'; import * as ui from '@ohif/ui'; +import utils from '../../utils/'; // Add react-live imports you need here const ReactLiveScope = { React, ...React, ...ui, + classnames, + utils, + moment, }; export default ReactLiveScope; diff --git a/platform/docs/src/utils/getMockedStudies.js b/platform/docs/src/utils/getMockedStudies.js new file mode 100644 index 000000000..04868cf65 --- /dev/null +++ b/platform/docs/src/utils/getMockedStudies.js @@ -0,0 +1,17 @@ +import studyListMock from '../mocks/studyList'; + +/** Values can be env vars */ +const DEFAULT_MOCKED_STUDIES_LIMIT = 1000; + +/** + * Method to get a mocked study list + * @param {number} items Number of studies to be loaded + * @returns {array} Study list + */ +const getMockedStudies = (items = 50) => { + const num = + items > DEFAULT_MOCKED_STUDIES_LIMIT ? DEFAULT_MOCKED_STUDIES_LIMIT : items; + return new Array(num).fill(studyListMock.studies[0]); +}; + +export default getMockedStudies; diff --git a/platform/docs/src/utils/index.js b/platform/docs/src/utils/index.js new file mode 100644 index 000000000..659d89d46 --- /dev/null +++ b/platform/docs/src/utils/index.js @@ -0,0 +1,7 @@ +import getMockedStudies from './getMockedStudies'; + +const utils = { getMockedStudies }; + +export { getMockedStudies }; + +export default utils; diff --git a/platform/ui/src/components/HotkeyField/HotkeyField.jsx b/platform/ui/src/components/HotkeyField/HotkeyField.jsx index 3173ddc3c..23f6fd641 100644 --- a/platform/ui/src/components/HotkeyField/HotkeyField.jsx +++ b/platform/ui/src/components/HotkeyField/HotkeyField.jsx @@ -56,7 +56,7 @@ HotkeyField.propTypes = { className: PropTypes.string, modifierKeys: PropTypes.array, disabled: PropTypes.bool, - hotkeys: PropTypes.object({ + hotkeys: PropTypes.shape({ initialize: PropTypes.func.isRequired, pause: PropTypes.func.isRequired, unpause: PropTypes.func.isRequired, diff --git a/platform/ui/src/components/HotkeysPreferences/HotkeysPreferences.jsx b/platform/ui/src/components/HotkeysPreferences/HotkeysPreferences.jsx index 480cb29a2..08129cc21 100644 --- a/platform/ui/src/components/HotkeysPreferences/HotkeysPreferences.jsx +++ b/platform/ui/src/components/HotkeysPreferences/HotkeysPreferences.jsx @@ -107,7 +107,7 @@ HotkeysPreferences.propTypes = { onChange: PropTypes.func, disabled: PropTypes.bool, hotkeyDefinitions: PropTypes.object.isRequired, - hotkeysModule: PropTypes.object({ + hotkeysModule: PropTypes.shape({ initialize: PropTypes.func.isRequired, pause: PropTypes.func.isRequired, unpause: PropTypes.func.isRequired, diff --git a/platform/ui/src/components/UserPreferences/UserPreferences.jsx b/platform/ui/src/components/UserPreferences/UserPreferences.jsx index 00634ec75..57c47326a 100644 --- a/platform/ui/src/components/UserPreferences/UserPreferences.jsx +++ b/platform/ui/src/components/UserPreferences/UserPreferences.jsx @@ -126,7 +126,7 @@ UserPreferences.propTypes = { onCancel: PropTypes.func, onSubmit: PropTypes.func, onReset: PropTypes.func, - hotkeysModule: PropTypes.object({ + hotkeysModule: PropTypes.shape({ initialize: PropTypes.func.isRequired, pause: PropTypes.func.isRequired, unpause: PropTypes.func.isRequired, diff --git a/yarn.lock b/yarn.lock index e29c84a8a..f26bc17c3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1114,13 +1114,27 @@ core-js-pure "^3.15.0" regenerator-runtime "^0.13.4" -"@babel/runtime@7.1.2", "@babel/runtime@7.7.6", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.4", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": +"@babel/runtime@7.1.2": + version "7.1.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.1.2.tgz#81c89935f4647706fc54541145e6b4ecfef4b8e3" + integrity sha512-Y3SCjmhSupzFB6wcv1KmmFucH6gDVnI30WjOcicV10ju0cZjak3Jcs67YLIXBrmZYw1xCrVeJPbycFwrqNyxpg== + dependencies: + regenerator-runtime "^0.12.0" + +"@babel/runtime@7.7.6", "@babel/runtime@^7.1.2", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.4", "@babel/runtime@^7.7.6": version "7.7.6" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.7.6.tgz#d18c511121aff1b4f2cd1d452f1bac9601dd830f" integrity sha512-BWAJxpNVa0QlE5gZdWjSxXtemZyZ9RmrmVozxt3NUXeZhVIJ5ANyqmMc0JDrivBZyxUuQvFxlvH4OWWOogGfUw== dependencies: regenerator-runtime "^0.13.2" +"@babel/runtime@^7.10.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": + version "7.14.8" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.8.tgz#7119a56f421018852694290b9f9148097391b446" + integrity sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.12.7", "@babel/template@^7.14.5", "@babel/template@^7.4.0": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4" @@ -5450,7 +5464,7 @@ classnames@2.2.6: resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce" integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q== -classnames@^2.2.5, classnames@^2.2.6: +classnames@^2.2.5, classnames@^2.2.6, classnames@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e" integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA== @@ -15590,6 +15604,11 @@ regenerate@^1.4.0: resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== +regenerator-runtime@^0.12.0: + version "0.12.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de" + integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg== + regenerator-runtime@^0.13.1, regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.3, regenerator-runtime@^0.13.4: version "0.13.7" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"