From a538824cc715a18b8c205ecbd0292daf3f7834bf Mon Sep 17 00:00:00 2001 From: Rodrigo Antinarelli Date: Fri, 19 Jun 2020 20:59:09 -0300 Subject: [PATCH 01/25] feat: Modal + ViewportDownloadForm --- .../src/CornerstoneViewportDownloadForm.js | 6 +- platform/ui/index.js | 2 + platform/ui/src/assets/icons/link.svg | 11 + platform/ui/src/assets/icons/unlink.svg | 11 + platform/ui/src/components/Icon/getIcon.jsx | 4 + .../components/InputNumber/InputNumber.jsx | 50 +++ .../components/InputNumber/InputNumber.mdx | 44 ++ .../ui/src/components/InputNumber/index.js | 2 + .../ui/src/components/InputText/InputText.jsx | 12 +- platform/ui/src/components/Modal/Modal.jsx | 32 +- platform/ui/src/components/Select/Select.jsx | 5 + .../ViewportDownloadForm.jsx | 425 ++++++++++++++++++ .../components/ViewportDownloadForm/index.js | 1 + platform/ui/src/components/index.js | 4 + yarn.lock | 10 - 15 files changed, 592 insertions(+), 27 deletions(-) create mode 100644 platform/ui/src/assets/icons/link.svg create mode 100644 platform/ui/src/assets/icons/unlink.svg create mode 100644 platform/ui/src/components/InputNumber/InputNumber.jsx create mode 100644 platform/ui/src/components/InputNumber/InputNumber.mdx create mode 100644 platform/ui/src/components/InputNumber/index.js create mode 100644 platform/ui/src/components/ViewportDownloadForm/ViewportDownloadForm.jsx create mode 100644 platform/ui/src/components/ViewportDownloadForm/index.js diff --git a/extensions/cornerstone/src/CornerstoneViewportDownloadForm.js b/extensions/cornerstone/src/CornerstoneViewportDownloadForm.js index babf3bf69..7582c0960 100644 --- a/extensions/cornerstone/src/CornerstoneViewportDownloadForm.js +++ b/extensions/cornerstone/src/CornerstoneViewportDownloadForm.js @@ -145,8 +145,4 @@ CornerstoneViewportDownloadForm.propTypes = { activeViewportIndex: PropTypes.number.isRequired, }; -// export default CornerstoneViewportDownloadForm; - -export default function HelloWorld() { - return
Hello World
; -} +export default CornerstoneViewportDownloadForm; diff --git a/platform/ui/index.js b/platform/ui/index.js index db85039d9..4e819e60a 100644 --- a/platform/ui/index.js +++ b/platform/ui/index.js @@ -39,6 +39,7 @@ export { InputGroup, InputLabelWrapper, InputMultiSelect, + InputNumber, InputText, Label, LayoutSelector, @@ -73,6 +74,7 @@ export { Typography, Viewport, ViewportActionBar, + ViewportDownloadForm, ViewportGrid, ViewportPane, } from './src/components'; diff --git a/platform/ui/src/assets/icons/link.svg b/platform/ui/src/assets/icons/link.svg new file mode 100644 index 000000000..7c99fc27c --- /dev/null +++ b/platform/ui/src/assets/icons/link.svg @@ -0,0 +1,11 @@ + + + + diff --git a/platform/ui/src/assets/icons/unlink.svg b/platform/ui/src/assets/icons/unlink.svg new file mode 100644 index 000000000..37c53bbb9 --- /dev/null +++ b/platform/ui/src/assets/icons/unlink.svg @@ -0,0 +1,11 @@ + + Unlink + + diff --git a/platform/ui/src/components/Icon/getIcon.jsx b/platform/ui/src/components/Icon/getIcon.jsx index 145539060..7b1fc4dc2 100644 --- a/platform/ui/src/components/Icon/getIcon.jsx +++ b/platform/ui/src/components/Icon/getIcon.jsx @@ -16,6 +16,7 @@ import info from './../../assets/icons/info.svg'; import infoLink from './../../assets/icons/info-link.svg'; import launchArrow from './../../assets/icons/launch-arrow.svg'; import launchInfo from './../../assets/icons/launch-info.svg'; +import link from './../../assets/icons/link.svg'; import listBullets from './../../assets/icons/list-bullets.svg'; import lock from './../../assets/icons/lock.svg'; import logoOhifSmall from './../../assets/icons/logo-ohif-small.svg'; @@ -30,6 +31,7 @@ import sorting from './../../assets/icons/sorting.svg'; import sortingActiveDown from './../../assets/icons/sorting-active-down.svg'; import sortingActiveUp from './../../assets/icons/sorting-active-up.svg'; import tracked from './../../assets/icons/tracked.svg'; +import unlink from './../../assets/icons/unlink.svg'; /** Tools */ import toolZoom from './../../assets/icons/tool-zoom.svg'; @@ -59,6 +61,7 @@ const ICONS = { 'info-link': infoLink, 'launch-arrow': launchArrow, 'launch-info': launchInfo, + link: link, 'list-bullets': listBullets, lock: lock, 'logo-ohif-small': logoOhifSmall, @@ -73,6 +76,7 @@ const ICONS = { 'sorting-active-up': sortingActiveUp, sorting: sorting, tracked: tracked, + unlink: unlink, /** Tools */ 'tool-zoom': toolZoom, diff --git a/platform/ui/src/components/InputNumber/InputNumber.jsx b/platform/ui/src/components/InputNumber/InputNumber.jsx new file mode 100644 index 000000000..c6fca474f --- /dev/null +++ b/platform/ui/src/components/InputNumber/InputNumber.jsx @@ -0,0 +1,50 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import { Input, InputLabelWrapper } from '@ohif/ui'; + +const InputNumber = ({ + label, + isSortable, + sortDirection, + onLabelClick, + value, + onChange, +}) => { + return ( + + { + onChange(event.target.value); + }} + /> + + ); +}; + +InputNumber.defaultProps = { + value: '', + isSortable: false, + onLabelClick: () => {}, + sortDirection: 'none', +}; + +InputNumber.propTypes = { + label: PropTypes.string.isRequired, + isSortable: PropTypes.bool, + sortDirection: PropTypes.oneOf(['ascending', 'descending', 'none']), + onLabelClick: PropTypes.func, + value: PropTypes.number, + onChange: PropTypes.func.isRequired, +}; + +export default InputNumber; diff --git a/platform/ui/src/components/InputNumber/InputNumber.mdx b/platform/ui/src/components/InputNumber/InputNumber.mdx new file mode 100644 index 000000000..6435c32ab --- /dev/null +++ b/platform/ui/src/components/InputNumber/InputNumber.mdx @@ -0,0 +1,44 @@ +--- +name: InputNumber +menu: Form +route: components/InputNumber +--- + +import { useState } from 'react'; +import { Playground, Props } from 'docz'; +import { InputNumber } from '@ohif/ui'; + +# Input Text + +## Import + +```javascript +import { InputNumber } from '@ohif/ui'; +``` + +## Basic usage + + + {() => { + const [number, setNumber] = useState(10); + return ( +
+
+ { + setText(value); + }} + /> +
+
+ ); + }} +
+ +## Properties + + diff --git a/platform/ui/src/components/InputNumber/index.js b/platform/ui/src/components/InputNumber/index.js new file mode 100644 index 000000000..566112449 --- /dev/null +++ b/platform/ui/src/components/InputNumber/index.js @@ -0,0 +1,2 @@ +import InputNumber from './InputNumber'; +export default InputNumber; diff --git a/platform/ui/src/components/InputText/InputText.jsx b/platform/ui/src/components/InputText/InputText.jsx index 2a244d7e8..fc62e3cdd 100644 --- a/platform/ui/src/components/InputText/InputText.jsx +++ b/platform/ui/src/components/InputText/InputText.jsx @@ -23,7 +23,7 @@ const InputText = ({ type="text" containerClassName="mr-2" value={value} - onChange={(event) => { + onChange={event => { onChange(event.target.value); }} /> @@ -33,14 +33,16 @@ const InputText = ({ InputText.defaultProps = { value: '', + isSortable: false, + onLabelClick: () => {}, + sortDirection: 'none', }; InputText.propTypes = { label: PropTypes.string.isRequired, - isSortable: PropTypes.bool.isRequired, - sortDirection: PropTypes.oneOf(['ascending', 'descending', 'none']) - .isRequired, - onLabelClick: PropTypes.func.isRequired, + isSortable: PropTypes.bool, + sortDirection: PropTypes.oneOf(['ascending', 'descending', 'none']), + onLabelClick: PropTypes.func, value: PropTypes.string, onChange: PropTypes.func.isRequired, }; diff --git a/platform/ui/src/components/Modal/Modal.jsx b/platform/ui/src/components/Modal/Modal.jsx index 4ef669cbf..79fa9709e 100644 --- a/platform/ui/src/components/Modal/Modal.jsx +++ b/platform/ui/src/components/Modal/Modal.jsx @@ -3,13 +3,16 @@ import PropTypes from 'prop-types'; import ReactModal from 'react-modal'; import classNames from 'classnames'; +import { Typography } from '@ohif/ui'; + const customStyle = { overlay: { zIndex: 1071, - backgroundColor: 'rgb(0, 0, 0, 0.5)', + backgroundColor: 'rgb(0, 0, 0, 0.8)', display: 'flex', alignItems: 'center', justifyContent: 'center', + padding: '40px 0', }, }; @@ -27,10 +30,14 @@ const Modal = ({ const renderHeader = () => { return ( title && ( -
-

{title}

+
+ {title} {closeButton && ( - )} @@ -41,20 +48,31 @@ const Modal = ({ return ( <> - {renderHeader()} -
{children}
+
{renderHeader()}
+
+ {children} +
); }; +Modal.defaultProps = { + shouldCloseOnEsc: true, +}; + Modal.propTypes = { className: PropTypes.string, closeButton: PropTypes.bool, diff --git a/platform/ui/src/components/Select/Select.jsx b/platform/ui/src/components/Select/Select.jsx index 3a47d8a6a..b51b64056 100644 --- a/platform/ui/src/components/Select/Select.jsx +++ b/platform/ui/src/components/Select/Select.jsx @@ -76,6 +76,11 @@ const Select = ({ options={options} value={selectedOptions} onChange={(selectedOptions, { action }) => { + if (!isMulti) { + onChange(selectedOptions, action); + return; + } + const newSelection = selectedOptions.reduce( (acc, curr) => acc.concat([curr.value]), [] diff --git a/platform/ui/src/components/ViewportDownloadForm/ViewportDownloadForm.jsx b/platform/ui/src/components/ViewportDownloadForm/ViewportDownloadForm.jsx new file mode 100644 index 000000000..6939ae0f7 --- /dev/null +++ b/platform/ui/src/components/ViewportDownloadForm/ViewportDownloadForm.jsx @@ -0,0 +1,425 @@ +import React, { + useCallback, + useEffect, + useState, + createRef, + useRef, +} from 'react'; + +import { + Typography, + InputText, + InputNumber, + Tooltip, + IconButton, + Icon, + Select, + InputLabelWrapper, + Button, +} from '@ohif/ui'; + +const FILE_TYPE_OPTIONS = [ + { + value: 'jpg', + label: 'jpg', + }, + { + value: 'png', + label: 'png', + }, +]; + +const DEFAULT_FILENAME = 'image'; +const REFRESH_VIEWPORT_TIMEOUT = 1000; + +const ViewportDownloadForm = ({ + activeViewport, + onClose, + updateViewportPreview, + enableViewport, + disableViewport, + toggleAnnotations, + loadImage, + downloadBlob, + defaultSize, + minimumSize, + maximumSize, + canvasClass, +}) => { + const [filename, setFilename] = useState(DEFAULT_FILENAME); + const [fileType, setFileType] = useState(['jpg']); + + const [dimensions, setDimensions] = useState({ + width: defaultSize, + height: defaultSize, + }); + + const [showAnnotations, setShowAnnotations] = useState(true); + + const [keepAspect, setKeepAspect] = useState(true); + const [aspectMultiplier, setAspectMultiplier] = useState({ + width: 1, + height: 1, + }); + + const [viewportElement, setViewportElement] = useState(); + const [viewportElementDimensions, setViewportElementDimensions] = useState({ + width: defaultSize, + height: defaultSize, + }); + + const [downloadCanvas, setDownloadCanvas] = useState({ + ref: createRef(), + width: defaultSize, + height: defaultSize, + }); + + const [viewportPreview, setViewportPreview] = useState({ + src: null, + width: defaultSize, + height: defaultSize, + }); + + const [error, setError] = useState({ + width: false, + height: false, + filename: false, + }); + + const hasError = Object.values(error).includes(true); + + const refreshViewport = useRef(null); + + const onKeepAspectToggle = () => { + const { width, height } = dimensions; + const aspectMultiplier = { ...aspectMultiplier }; + if (!keepAspect) { + const base = Math.min(width, height); + aspectMultiplier.width = width / base; + aspectMultiplier.height = height / base; + setAspectMultiplier(aspectMultiplier); + } + + setKeepAspect(!keepAspect); + }; + + const downloadImage = () => { + downloadBlob( + filename || DEFAULT_FILENAME, + fileType, + viewportElement, + downloadCanvas.ref.current + ); + }; + + /** + * @param {object} value - Input value + * @param {string} dimension - "height" | "width" + */ + const onDimensionsChange = (value, dimension) => { + const oppositeDimension = dimension === 'height' ? 'width' : 'height'; + const sanitizedTargetValue = value.replace(/\D/, ''); + const isEmpty = sanitizedTargetValue === ''; + const newDimensions = { ...dimensions }; + const updatedDimension = isEmpty + ? '' + : Math.min(sanitizedTargetValue, maximumSize); + + if (updatedDimension === dimensions[dimension]) { + return; + } + + newDimensions[dimension] = updatedDimension; + + if (keepAspect && newDimensions[oppositeDimension] !== '') { + newDimensions[oppositeDimension] = Math.round( + newDimensions[dimension] * aspectMultiplier[oppositeDimension] + ); + } + + // In current code, keepAspect is always `true` + // And we always start w/ a square width/height + setDimensions(newDimensions); + + // Only update if value is non-empty + if (!isEmpty) { + setViewportElementDimensions(newDimensions); + setDownloadCanvas(state => ({ + ...state, + ...newDimensions, + })); + } + }; + + const error_messages = { + width: 'The minimum valid width is 100px.', + height: 'The minimum valid height is 100px.', + filename: 'The file name cannot be empty.', + }; + + const renderErrorHandler = errorType => { + if (!error[errorType]) { + return null; + } + + return ( + + {error_messages[errorType]} + + ); + }; + + const validSize = value => (value >= minimumSize ? value : minimumSize); + + const loadAndUpdateViewports = useCallback(async () => { + const { width: scaledWidth, height: scaledHeight } = await loadImage( + activeViewport, + viewportElement, + dimensions.width, + dimensions.height + ); + + toggleAnnotations(showAnnotations, viewportElement); + + const scaledDimensions = { + height: validSize(scaledHeight), + width: validSize(scaledWidth), + }; + + setViewportElementDimensions(scaledDimensions); + setDownloadCanvas(state => ({ + ...state, + ...scaledDimensions, + })); + + const { + dataUrl, + width: viewportElementWidth, + height: viewportElementHeight, + } = await updateViewportPreview( + viewportElement, + downloadCanvas.ref.current, + fileType + ); + + setViewportPreview(state => ({ + ...state, + src: dataUrl, + width: validSize(viewportElementWidth), + height: validSize(viewportElementHeight), + })); + }, [ + loadImage, + activeViewport, + viewportElement, + dimensions.width, + dimensions.height, + toggleAnnotations, + showAnnotations, + validSize, + updateViewportPreview, + downloadCanvas.ref, + fileType, + ]); + + useEffect(() => { + enableViewport(viewportElement); + + return () => { + disableViewport(viewportElement); + }; + }, [disableViewport, enableViewport, viewportElement]); + + useEffect(() => { + if (refreshViewport.current !== null) { + clearTimeout(refreshViewport.current); + } + + refreshViewport.current = setTimeout(() => { + refreshViewport.current = null; + loadAndUpdateViewports(); + }, REFRESH_VIEWPORT_TIMEOUT); + }, [ + activeViewport, + viewportElement, + showAnnotations, + dimensions, + loadImage, + toggleAnnotations, + updateViewportPreview, + fileType, + downloadCanvas.ref, + minimumSize, + maximumSize, + loadAndUpdateViewports, + ]); + + useEffect(() => { + const { width, height } = dimensions; + const hasError = { + width: width < minimumSize, + height: height < minimumSize, + filename: !filename, + }; + + setError({ ...hasError }); + }, [dimensions, filename, minimumSize]); + + return ( +
+ + Please specify the dimensions, filename, and desired type for the output + image. + + +
+
+ setFilename(value)} + label="File Name" + /> + {renderErrorHandler('filename')} +
+
+
+
+
+ onDimensionsChange(value, 'width')} + data-cy="image-width" + /> + {renderErrorHandler('width')} +
+
+ onDimensionsChange(value, 'height')} + data-cy="image-height" + /> + {renderErrorHandler('height')} +
+
+ +
+ + + + + +
+
+ +
+
+ {}} + > + setShowAnnotations(event.target.checked)} + /> + Show Annotations + +
+
+
+
+ +
+
setViewportElement(ref)} + > + +
+ + {viewportPreview.src ? ( +
+
Image preview
+ Preview +
+ ) : ( +
+ Loading Image Preview... +
+ )} +
+ +
+ + +
+
+ ); +}; + +export default ViewportDownloadForm; diff --git a/platform/ui/src/components/ViewportDownloadForm/index.js b/platform/ui/src/components/ViewportDownloadForm/index.js new file mode 100644 index 000000000..d630f3a19 --- /dev/null +++ b/platform/ui/src/components/ViewportDownloadForm/index.js @@ -0,0 +1 @@ +export { default } from './ViewportDownloadForm'; diff --git a/platform/ui/src/components/index.js b/platform/ui/src/components/index.js index 564522ab0..d67bbb9ad 100644 --- a/platform/ui/src/components/index.js +++ b/platform/ui/src/components/index.js @@ -10,6 +10,7 @@ import InputDateRange from './InputDateRange'; import InputGroup from './InputGroup'; import InputLabelWrapper from './InputLabelWrapper'; import InputMultiSelect from './InputMultiSelect'; +import InputNumber from './InputNumber'; import InputText from './InputText'; import Label from './Label'; import LayoutSelector from './LayoutSelector'; @@ -43,6 +44,7 @@ import Tooltip from './Tooltip'; import Typography from './Typography'; import Viewport from './Viewport'; import ViewportActionBar from './ViewportActionBar'; +import ViewportDownloadForm from './ViewportDownloadForm'; import ViewportGrid from './ViewportGrid'; import ViewportPane from './ViewportPane'; @@ -59,6 +61,7 @@ export { InputGroup, InputLabelWrapper, InputMultiSelect, + InputNumber, InputText, Label, LayoutSelector, @@ -93,6 +96,7 @@ export { Typography, Viewport, ViewportActionBar, + ViewportDownloadForm, ViewportGrid, ViewportPane, }; diff --git a/yarn.lock b/yarn.lock index b909ed6c5..eb88b95ac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17889,16 +17889,6 @@ react-cornerstone-viewport@2.3.8: prop-types "^15.7.2" react-resize-detector "^4.2.1" -react-cornerstone-viewport@2.3.9: - version "2.3.9" - resolved "https://registry.yarnpkg.com/react-cornerstone-viewport/-/react-cornerstone-viewport-2.3.9.tgz#f9761da8e536f0a217137c6ca1a983f5882249f9" - integrity sha512-qrhq8CbX/jq6b93cQjV2qC/mhHOvFBpxzxcHlvHzEQZt/rmRMcaYxCqjpNaNbUmmBu61wNkxesUVsggkPTTcqg== - dependencies: - classnames "^2.2.6" - date-fns "^2.2.1" - prop-types "^15.7.2" - react-resize-detector "^4.2.1" - react-dates@21.2.1: version "21.2.1" resolved "https://registry.yarnpkg.com/react-dates/-/react-dates-21.2.1.tgz#a979ed6876326ccfbf754a019bc95458cc061ad8" From c89f3682cc9a31f855b6b6812675abac552f87ef Mon Sep 17 00:00:00 2001 From: Rodrigo Antinarelli Date: Mon, 22 Jun 2020 20:14:59 -0300 Subject: [PATCH 02/25] styles for image preview box --- .../ViewportDownloadForm/ViewportDownloadForm.jsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/platform/ui/src/components/ViewportDownloadForm/ViewportDownloadForm.jsx b/platform/ui/src/components/ViewportDownloadForm/ViewportDownloadForm.jsx index 6939ae0f7..3f1283441 100644 --- a/platform/ui/src/components/ViewportDownloadForm/ViewportDownloadForm.jsx +++ b/platform/ui/src/components/ViewportDownloadForm/ViewportDownloadForm.jsx @@ -388,10 +388,13 @@ const ViewportDownloadForm = ({ {viewportPreview.src ? ( -
-
Image preview
+
+ Image preview Preview Date: Mon, 22 Jun 2020 22:38:39 -0300 Subject: [PATCH 03/25] modal update & close handler --- platform/core/src/services/UIModalService/index.js | 2 +- platform/ui/src/components/Modal/Modal.jsx | 9 ++++++++- platform/ui/src/contextProviders/ModalComponent.jsx | 2 +- platform/ui/src/contextProviders/ModalProvider.jsx | 4 ++-- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/platform/core/src/services/UIModalService/index.js b/platform/core/src/services/UIModalService/index.js index ed2eab96a..884e53500 100644 --- a/platform/core/src/services/UIModalService/index.js +++ b/platform/core/src/services/UIModalService/index.js @@ -33,7 +33,7 @@ const serviceImplementation = { function _show({ content = null, contentProps = null, - shouldCloseOnEsc = false, + shouldCloseOnEsc = true, isOpen = true, closeButton = true, title = null, diff --git a/platform/ui/src/components/Modal/Modal.jsx b/platform/ui/src/components/Modal/Modal.jsx index 79fa9709e..8e3418829 100644 --- a/platform/ui/src/components/Modal/Modal.jsx +++ b/platform/ui/src/components/Modal/Modal.jsx @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import ReactModal from 'react-modal'; import classNames from 'classnames'; -import { Typography } from '@ohif/ui'; +import { Typography, useModal } from '@ohif/ui'; const customStyle = { overlay: { @@ -27,6 +27,12 @@ const Modal = ({ onClose, children, }) => { + const { hide } = useModal(); + + const handleClose = () => { + hide(); + }; + const renderHeader = () => { return ( title && ( @@ -52,6 +58,7 @@ const Modal = ({ `relative py-6 w-11/12 lg:w-10/12 xl:w-1/2 max-h-full outline-none bg-primary-dark border border-secondary-main text-white rounded ${className}` )} shouldCloseOnEsc={shouldCloseOnEsc} + onRequestClose={handleClose} isOpen={isOpen} title={title} style={customStyle} diff --git a/platform/ui/src/contextProviders/ModalComponent.jsx b/platform/ui/src/contextProviders/ModalComponent.jsx index b3b6602a7..cf7965812 100644 --- a/platform/ui/src/contextProviders/ModalComponent.jsx +++ b/platform/ui/src/contextProviders/ModalComponent.jsx @@ -16,7 +16,7 @@ const ModalComponent = ({ ModalComponent.defaultProps = { content: null, contentProps: null, - shouldCloseOnEsc: false, + shouldCloseOnEsc: true, isOpen: true, closeButton: true, title: null, diff --git a/platform/ui/src/contextProviders/ModalProvider.jsx b/platform/ui/src/contextProviders/ModalProvider.jsx index e8d65635d..ff9224bca 100644 --- a/platform/ui/src/contextProviders/ModalProvider.jsx +++ b/platform/ui/src/contextProviders/ModalProvider.jsx @@ -19,7 +19,7 @@ export const useModal = () => useContext(ModalContext); * @typedef {Object} ModalProps * @property {ReactElement|HTMLElement} [content=null] Modal content. * @property {Object} [contentProps=null] Modal content props. - * @property {boolean} [shouldCloseOnEsc=false] Modal is dismissible via the esc key. + * @property {boolean} [shouldCloseOnEsc=true] Modal is dismissible via the esc key. * @property {boolean} [isOpen=true] Make the Modal visible or hidden. * @property {boolean} [closeButton=true] Should the modal body render the close button. * @property {string} [title=null] Should the modal render the title independently of the body content. @@ -30,7 +30,7 @@ const ModalProvider = ({ children, modal: Modal, service }) => { const DEFAULT_OPTIONS = { content: null, contentProps: null, - shouldCloseOnEsc: false, + shouldCloseOnEsc: true, isOpen: true, closeButton: true, title: null, From 92a2071a041c295aca92d12b4808aa40af97adb6 Mon Sep 17 00:00:00 2001 From: Rodrigo Antinarelli Date: Mon, 22 Jun 2020 22:38:52 -0300 Subject: [PATCH 04/25] open modal for Learn More (search info) --- .../components/StudyListFilter/StudyListFilter.jsx | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/platform/ui/src/components/StudyListFilter/StudyListFilter.jsx b/platform/ui/src/components/StudyListFilter/StudyListFilter.jsx index 2d409a555..7c7a777b1 100644 --- a/platform/ui/src/components/StudyListFilter/StudyListFilter.jsx +++ b/platform/ui/src/components/StudyListFilter/StudyListFilter.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Button, Icon, Typography, InputGroup } from '@ohif/ui'; +import { Button, Icon, Typography, InputGroup, useModal } from '@ohif/ui'; const StudyListFilter = ({ filtersMeta, @@ -20,6 +20,16 @@ const StudyListFilter = ({ }); }; const isSortingEnable = numOfStudies > 0 && numOfStudies <= 100; + const { show } = useModal(); + + const showLearnMoreContent = () => { + const modalContent = () =>
Search Instructions
; + + show({ + content: modalContent, + title: 'Learn More', + }); + }; return ( @@ -40,7 +50,7 @@ const StudyListFilter = ({ startIcon={} > - Learn more + Learn more From 487bcf043b7a68589fa4e78bc574638b2e081170 Mon Sep 17 00:00:00 2001 From: Rodrigo Antinarelli Date: Tue, 23 Jun 2020 00:09:31 -0300 Subject: [PATCH 05/25] feat: Dropdown component --- platform/ui/index.js | 1 + .../ui/src/components/Dropdown/Dropdown.jsx | 107 ++++++++++++++++++ .../ui/src/components/Dropdown/Dropdown.mdx | 22 ++++ platform/ui/src/components/Dropdown/index.js | 1 + platform/ui/src/components/index.js | 2 + 5 files changed, 133 insertions(+) create mode 100644 platform/ui/src/components/Dropdown/Dropdown.jsx create mode 100644 platform/ui/src/components/Dropdown/Dropdown.mdx create mode 100644 platform/ui/src/components/Dropdown/index.js diff --git a/platform/ui/index.js b/platform/ui/index.js index 4e819e60a..0d7fe423e 100644 --- a/platform/ui/index.js +++ b/platform/ui/index.js @@ -31,6 +31,7 @@ export { ButtonGroup, DateRange, Dialog, + Dropdown, EmptyStudies, Icon, IconButton, diff --git a/platform/ui/src/components/Dropdown/Dropdown.jsx b/platform/ui/src/components/Dropdown/Dropdown.jsx new file mode 100644 index 000000000..752e2b6f9 --- /dev/null +++ b/platform/ui/src/components/Dropdown/Dropdown.jsx @@ -0,0 +1,107 @@ +import React, { useEffect, useState, useRef } from 'react'; +import PropTypes from 'prop-types'; +import classnames from 'classnames'; + +import { Icon, Typography } from '@ohif/ui'; + +const Dropdown = ({ titleElement, title, list }) => { + const [open, setOpen] = useState(false); + const element = useRef(null); + + const renderTitleElement = () => { + if (titleElement) { + return titleElement; + } + + return ( +
+ {title} + +
+ ); + }; + + const toggleList = () => { + setOpen(s => !s); + }; + + const handleClick = e => { + if (element.current && !element.current.contains(e.target)) { + setOpen(false); + } + }; + + const renderList = () => { + const itemsAmount = list.length; + + return ( +
+ {list.map((item, idx) => ( +
{ + setOpen(false); + + if (item.onClick) { + item.onClick(); + } + }} + > + {!!item.icon && ( + + )} + {item.title} +
+ ))} +
+ ); + }; + + useEffect(() => { + document.addEventListener('click', handleClick); + + return () => { + document.removeEventListener('click', handleClick); + }; + }, []); + + return ( +
+
+ {renderTitleElement()} +
+ + {renderList()} +
+ ); +}; + +Dropdown.propTypes = { + titleElement: PropTypes.node, + title: PropTypes.string.isRequired, + /** Items to render in the select's drop down */ + list: PropTypes.arrayOf( + PropTypes.shape({ + title: PropTypes.string.isRequired, + icon: PropTypes.object, + onClick: PropTypes.func, + link: PropTypes.string, + }) + ), +}; + +export default Dropdown; diff --git a/platform/ui/src/components/Dropdown/Dropdown.mdx b/platform/ui/src/components/Dropdown/Dropdown.mdx new file mode 100644 index 000000000..087877f56 --- /dev/null +++ b/platform/ui/src/components/Dropdown/Dropdown.mdx @@ -0,0 +1,22 @@ +--- +name: Dropdown +menu: General +route: components/dropdown +--- + +import { Playground, Props } from 'docz'; +import { Dropdown } from '@ohif/ui'; + +# Dropdown + +... + +## Import + +```javascript +import { Dropdown } from '@ohif/ui'; +``` + +## Properties + + diff --git a/platform/ui/src/components/Dropdown/index.js b/platform/ui/src/components/Dropdown/index.js new file mode 100644 index 000000000..453771730 --- /dev/null +++ b/platform/ui/src/components/Dropdown/index.js @@ -0,0 +1 @@ +export { default } from './Dropdown'; diff --git a/platform/ui/src/components/index.js b/platform/ui/src/components/index.js index d67bbb9ad..be5d75e1e 100644 --- a/platform/ui/src/components/index.js +++ b/platform/ui/src/components/index.js @@ -2,6 +2,7 @@ import Button from './Button'; import ButtonGroup from './ButtonGroup'; import DateRange from './DateRange'; import Dialog from './Dialog'; +import Dropdown from './Dropdown'; import EmptyStudies from './EmptyStudies'; import Icon from './Icon'; import IconButton from './IconButton'; @@ -53,6 +54,7 @@ export { ButtonGroup, DateRange, Dialog, + Dropdown, EmptyStudies, Icon, IconButton, From aecd3d907156b1b9e0cf8ac8aeefbce62858eecc Mon Sep 17 00:00:00 2001 From: Rodrigo Antinarelli Date: Tue, 23 Jun 2020 00:09:49 -0300 Subject: [PATCH 06/25] change modal default alignment --- platform/ui/src/components/Modal/Modal.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/ui/src/components/Modal/Modal.jsx b/platform/ui/src/components/Modal/Modal.jsx index 8e3418829..1ce7e3cd7 100644 --- a/platform/ui/src/components/Modal/Modal.jsx +++ b/platform/ui/src/components/Modal/Modal.jsx @@ -10,7 +10,7 @@ const customStyle = { zIndex: 1071, backgroundColor: 'rgb(0, 0, 0, 0.8)', display: 'flex', - alignItems: 'center', + alignItems: 'flex-start', justifyContent: 'center', padding: '40px 0', }, From 70cf31b44900cf8916fffc96d45b961465ba337e Mon Sep 17 00:00:00 2001 From: Rodrigo Antinarelli Date: Tue, 23 Jun 2020 00:10:44 -0300 Subject: [PATCH 07/25] fix navbar height --- platform/ui/src/components/NavBar/NavBar.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/ui/src/components/NavBar/NavBar.jsx b/platform/ui/src/components/NavBar/NavBar.jsx index 5ab60f626..8adc0ce3b 100644 --- a/platform/ui/src/components/NavBar/NavBar.jsx +++ b/platform/ui/src/components/NavBar/NavBar.jsx @@ -8,7 +8,7 @@ const NavBar = ({ className, children, isSticky }) => { return (
Date: Tue, 23 Jun 2020 00:10:58 -0300 Subject: [PATCH 08/25] using dropdown and opening modal for preferences --- .../default/src/ViewerLayout/Header.jsx | 53 +++++++++++++++---- .../PreferencesDropdown.jsx | 51 ++++++++++++++++++ .../components/PreferencesDropdown/index.js | 1 + .../viewer/src/routes/WorkList/WorkList.jsx | 15 ++---- 4 files changed, 97 insertions(+), 23 deletions(-) create mode 100644 platform/viewer/src/components/PreferencesDropdown/PreferencesDropdown.jsx create mode 100644 platform/viewer/src/components/PreferencesDropdown/index.js diff --git a/extensions/default/src/ViewerLayout/Header.jsx b/extensions/default/src/ViewerLayout/Header.jsx index b5f8010c0..3fefdbd60 100644 --- a/extensions/default/src/ViewerLayout/Header.jsx +++ b/extensions/default/src/ViewerLayout/Header.jsx @@ -3,10 +3,28 @@ import PropTypes from 'prop-types'; // TODO: This may fail if package is split from PWA build import { useHistory } from 'react-router-dom'; // -import { NavBar, Svg, Icon, IconButton } from '@ohif/ui'; +import { NavBar, Svg, Icon, IconButton, Dropdown, useModal } from '@ohif/ui'; function Header({ children }) { const history = useHistory(); + const { show } = useModal(); + + const showAboutModal = () => { + const modalComponent = () =>
About modal
; + show({ + title: 'About', + content: modalComponent, + }); + }; + + const showPreferencesModal = () => { + const modalComponent = () =>
Preferences modal
; + show({ + title: 'Preferences', + content: modalComponent, + }); + }; + // const dropdownContent = [ // { // name: 'Soft tissue', @@ -42,16 +60,29 @@ function Header({ children }) { FOR INVESTIGATIONAL USE ONLY - {}} - > - - - - + {}} + > + + + + + } + list={[ + { title: 'About', icon: 'info', onClick: () => showAboutModal() }, + { + title: 'Preferences', + icon: 'settings', + onClick: () => showPreferencesModal(), + }, + ]} + />
diff --git a/platform/viewer/src/components/PreferencesDropdown/PreferencesDropdown.jsx b/platform/viewer/src/components/PreferencesDropdown/PreferencesDropdown.jsx new file mode 100644 index 000000000..1167aa04f --- /dev/null +++ b/platform/viewer/src/components/PreferencesDropdown/PreferencesDropdown.jsx @@ -0,0 +1,51 @@ +import React from 'react'; + +import { Dropdown, IconButton, Icon, useModal } from '@ohif/ui'; + +const PreferencesDropdown = () => { + const { show } = useModal(); + + const showAboutModal = () => { + const modalComponent = () =>
About modal
; + show({ + content: modalComponent, + title: 'About', + }); + }; + + const showPreferencesModal = () => { + const modalComponent = () =>
Preferences modal
; + show({ + content: modalComponent, + title: 'Preferences', + }); + }; + + return ( + {}} + > + + + + + } + list={[ + { title: 'About', icon: 'info', onClick: () => showAboutModal() }, + { + title: 'Preferences', + icon: 'settings', + onClick: () => showPreferencesModal(), + }, + ]} + /> + ); +}; + +export default PreferencesDropdown; diff --git a/platform/viewer/src/components/PreferencesDropdown/index.js b/platform/viewer/src/components/PreferencesDropdown/index.js new file mode 100644 index 000000000..edf8641a2 --- /dev/null +++ b/platform/viewer/src/components/PreferencesDropdown/index.js @@ -0,0 +1 @@ +export { default } from './PreferencesDropdown'; diff --git a/platform/viewer/src/routes/WorkList/WorkList.jsx b/platform/viewer/src/routes/WorkList/WorkList.jsx index 264bc87e2..d32ef7a73 100644 --- a/platform/viewer/src/routes/WorkList/WorkList.jsx +++ b/platform/viewer/src/routes/WorkList/WorkList.jsx @@ -9,13 +9,14 @@ import filtersMeta from './filtersMeta.js'; import { useAppConfig } from '@state'; import { useDebounce, useQuery } from '@hooks'; +import PreferencesDropdown from '../../components/PreferencesDropdown'; + import { Icon, StudyListExpandedRow, Button, NavBar, Svg, - IconButton, EmptyStudies, StudyListTable, StudyListPagination, @@ -364,17 +365,7 @@ function WorkList({ history, data: studies, dataSource }) { FOR INVESTIGATIONAL USE ONLY - {}} - > - - - - - +
Date: Tue, 23 Jun 2020 00:14:55 -0300 Subject: [PATCH 09/25] change modal default alignment --- platform/ui/src/components/Modal/Modal.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/ui/src/components/Modal/Modal.jsx b/platform/ui/src/components/Modal/Modal.jsx index 8e3418829..1ce7e3cd7 100644 --- a/platform/ui/src/components/Modal/Modal.jsx +++ b/platform/ui/src/components/Modal/Modal.jsx @@ -10,7 +10,7 @@ const customStyle = { zIndex: 1071, backgroundColor: 'rgb(0, 0, 0, 0.8)', display: 'flex', - alignItems: 'center', + alignItems: 'flex-start', justifyContent: 'center', padding: '40px 0', }, From ef0eb53d25679b8ce5cab0289a5455d551aa9a95 Mon Sep 17 00:00:00 2001 From: Rodrigo Antinarelli Date: Tue, 23 Jun 2020 23:05:37 -0300 Subject: [PATCH 10/25] add i18n provider in the viewer --- platform/viewer/src/App.jsx | 51 ++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/platform/viewer/src/App.jsx b/platform/viewer/src/App.jsx index 969977107..420695713 100644 --- a/platform/viewer/src/App.jsx +++ b/platform/viewer/src/App.jsx @@ -1,12 +1,13 @@ // External import React from 'react'; import PropTypes from 'prop-types'; +import i18n from '@ohif/i18n'; +import { I18nextProvider } from 'react-i18next'; import { BrowserRouter, HashRouter } from 'react-router-dom'; import { DialogProvider, Modal, ModalProvider, - Notification, SnackbarProvider, ThemeWrapper, ViewportDialogProvider, @@ -97,29 +98,31 @@ function App({ config, defaultExtensions }) { return ( - - - - - - - - {appRoutes} - - - - - - - + + + + + + + + + {appRoutes} + + + + + + + + ); } From 8b2c68d1bfe637684a550b3001669d03f7217d6c Mon Sep 17 00:00:00 2001 From: Rodrigo Antinarelli Date: Tue, 23 Jun 2020 23:05:45 -0300 Subject: [PATCH 11/25] add i18n to header --- extensions/default/package.json | 2 ++ .../default/src/ViewerLayout/Header.jsx | 24 +++++++++++++------ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/extensions/default/package.json b/extensions/default/package.json index e9633cdc1..f47b06d1d 100644 --- a/extensions/default/package.json +++ b/extensions/default/package.json @@ -28,6 +28,8 @@ }, "peerDependencies": { "@ohif/core": "^0.50.0", + "@ohif/i18n": "^0.52.8", + "react-i18next": "^10.11.0", "prop-types": "^15.6.2", "react": "^16.13.1", "react-dom": "^16.13.1", diff --git a/extensions/default/src/ViewerLayout/Header.jsx b/extensions/default/src/ViewerLayout/Header.jsx index 3fefdbd60..1593ba893 100644 --- a/extensions/default/src/ViewerLayout/Header.jsx +++ b/extensions/default/src/ViewerLayout/Header.jsx @@ -1,26 +1,32 @@ import React from 'react'; import PropTypes from 'prop-types'; +import { useTranslation } from 'react-i18next'; // TODO: This may fail if package is split from PWA build import { useHistory } from 'react-router-dom'; // import { NavBar, Svg, Icon, IconButton, Dropdown, useModal } from '@ohif/ui'; function Header({ children }) { + const { t } = useTranslation(); const history = useHistory(); const { show } = useModal(); const showAboutModal = () => { - const modalComponent = () =>
About modal
; + const modalComponent = () => ( +
{t('AboutModal:OHIF Viewer - About')}
+ ); show({ - title: 'About', + title: t('AboutModal:OHIF Viewer - About'), content: modalComponent, }); }; const showPreferencesModal = () => { - const modalComponent = () =>
Preferences modal
; + const modalComponent = () => ( +
{t('UserPreferencesModal:User Preferences')}
+ ); show({ - title: 'Preferences', + title: t('UserPreferencesModal:User Preferences'), content: modalComponent, }); }; @@ -58,7 +64,7 @@ function Header({ children }) {
{children}
- FOR INVESTIGATIONAL USE ONLY + {t('Header:INVESTIGATIONAL USE ONLY')} } list={[ - { title: 'About', icon: 'info', onClick: () => showAboutModal() }, { - title: 'Preferences', + title: t('Header:About'), + icon: 'info', + onClick: () => showAboutModal(), + }, + { + title: t('Header:Preferences'), icon: 'settings', onClick: () => showPreferencesModal(), }, From 9dbbc709f42e07fb4121874f09e7e356a289fc27 Mon Sep 17 00:00:00 2001 From: Rodrigo Antinarelli Date: Tue, 23 Jun 2020 23:22:57 -0300 Subject: [PATCH 12/25] remove fragment --- extensions/default/src/ViewerLayout/Header.jsx | 5 ++--- .../components/PreferencesDropdown/PreferencesDropdown.jsx | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/extensions/default/src/ViewerLayout/Header.jsx b/extensions/default/src/ViewerLayout/Header.jsx index 1593ba893..a23d8e986 100644 --- a/extensions/default/src/ViewerLayout/Header.jsx +++ b/extensions/default/src/ViewerLayout/Header.jsx @@ -75,9 +75,8 @@ function Header({ children }) { className="text-primary-active" onClick={() => {}} > - - - + + } list={[ diff --git a/platform/viewer/src/components/PreferencesDropdown/PreferencesDropdown.jsx b/platform/viewer/src/components/PreferencesDropdown/PreferencesDropdown.jsx index 1167aa04f..77813684c 100644 --- a/platform/viewer/src/components/PreferencesDropdown/PreferencesDropdown.jsx +++ b/platform/viewer/src/components/PreferencesDropdown/PreferencesDropdown.jsx @@ -31,9 +31,8 @@ const PreferencesDropdown = () => { className="text-primary-active" onClick={() => {}} > - - - + + } list={[ From 9745f06c4c54ddc357c1d53c84f6a50db7de3f76 Mon Sep 17 00:00:00 2001 From: Rodrigo Antinarelli Date: Tue, 23 Jun 2020 23:40:35 -0300 Subject: [PATCH 13/25] fix eventListener --- platform/ui/src/components/Dropdown/Dropdown.jsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/platform/ui/src/components/Dropdown/Dropdown.jsx b/platform/ui/src/components/Dropdown/Dropdown.jsx index 752e2b6f9..8d298dc86 100644 --- a/platform/ui/src/components/Dropdown/Dropdown.jsx +++ b/platform/ui/src/components/Dropdown/Dropdown.jsx @@ -10,7 +10,7 @@ const Dropdown = ({ titleElement, title, list }) => { const renderTitleElement = () => { if (titleElement) { - return titleElement; + return <>{titleElement}; } return ( @@ -74,10 +74,10 @@ const Dropdown = ({ titleElement, title, list }) => { useEffect(() => { document.addEventListener('click', handleClick); - return () => { + if (!open) { document.removeEventListener('click', handleClick); - }; - }, []); + } + }, [open]); return (
@@ -92,12 +92,12 @@ const Dropdown = ({ titleElement, title, list }) => { Dropdown.propTypes = { titleElement: PropTypes.node, - title: PropTypes.string.isRequired, + title: PropTypes.string, /** Items to render in the select's drop down */ list: PropTypes.arrayOf( PropTypes.shape({ title: PropTypes.string.isRequired, - icon: PropTypes.object, + icon: PropTypes.string, onClick: PropTypes.func, link: PropTypes.string, }) From 6ce531a6d29f56c430fbeda153eba8a523cb7973 Mon Sep 17 00:00:00 2001 From: Rodrigo Antinarelli Date: Tue, 23 Jun 2020 23:40:45 -0300 Subject: [PATCH 14/25] fix icon dropdown --- .../default/src/ViewerLayout/Header.jsx | 28 +++++++++++------- .../PreferencesDropdown.jsx | 29 ++++++++++++------- 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/extensions/default/src/ViewerLayout/Header.jsx b/extensions/default/src/ViewerLayout/Header.jsx index a23d8e986..09b802c50 100644 --- a/extensions/default/src/ViewerLayout/Header.jsx +++ b/extensions/default/src/ViewerLayout/Header.jsx @@ -68,16 +68,24 @@ function Header({ children }) { {}} - > - - - + <> + + + + + + + } list={[ { diff --git a/platform/viewer/src/components/PreferencesDropdown/PreferencesDropdown.jsx b/platform/viewer/src/components/PreferencesDropdown/PreferencesDropdown.jsx index 77813684c..fe591bf35 100644 --- a/platform/viewer/src/components/PreferencesDropdown/PreferencesDropdown.jsx +++ b/platform/viewer/src/components/PreferencesDropdown/PreferencesDropdown.jsx @@ -24,16 +24,25 @@ const PreferencesDropdown = () => { return ( {}} - > - - - + <> + + + + {}} + > + + + } list={[ { title: 'About', icon: 'info', onClick: () => showAboutModal() }, From 987dae9b2dae55b23febbec7928cb826c70a5721 Mon Sep 17 00:00:00 2001 From: Rodrigo Antinarelli Date: Wed, 24 Jun 2020 01:16:39 -0300 Subject: [PATCH 15/25] fix dropdown --- .../ui/src/components/Dropdown/Dropdown.jsx | 54 ++++++++++--------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/platform/ui/src/components/Dropdown/Dropdown.jsx b/platform/ui/src/components/Dropdown/Dropdown.jsx index 8d298dc86..f13852aad 100644 --- a/platform/ui/src/components/Dropdown/Dropdown.jsx +++ b/platform/ui/src/components/Dropdown/Dropdown.jsx @@ -10,7 +10,7 @@ const Dropdown = ({ titleElement, title, list }) => { const renderTitleElement = () => { if (titleElement) { - return <>{titleElement}; + return titleElement; } return ( @@ -44,29 +44,35 @@ const Dropdown = ({ titleElement, title, list }) => { } )} > - {list.map((item, idx) => ( -
{ - setOpen(false); - - if (item.onClick) { - item.onClick(); - } - }} - > - {!!item.icon && ( - - )} - {item.title} -
- ))} + {list.map( + ( + { + title: itemTitle, + icon: itemIcon, + onClick: itemOnClick = () => {}, + }, + idx + ) => ( +
{ + setOpen(false); + itemOnClick(); + }} + > + {!!itemIcon && ( + + )} + {itemTitle} +
+ ) + )}
); }; From db0ce2f6f7780511b4b3b9b2de8662ab1a0b05a0 Mon Sep 17 00:00:00 2001 From: Rodrigo Antinarelli Date: Wed, 24 Jun 2020 01:16:56 -0300 Subject: [PATCH 16/25] minor fixes --- extensions/default/src/ViewerLayout/Header.jsx | 4 ++-- .../components/PreferencesDropdown/PreferencesDropdown.jsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions/default/src/ViewerLayout/Header.jsx b/extensions/default/src/ViewerLayout/Header.jsx index 09b802c50..b795f2fcb 100644 --- a/extensions/default/src/ViewerLayout/Header.jsx +++ b/extensions/default/src/ViewerLayout/Header.jsx @@ -91,12 +91,12 @@ function Header({ children }) { { title: t('Header:About'), icon: 'info', - onClick: () => showAboutModal(), + onClick: showAboutModal, }, { title: t('Header:Preferences'), icon: 'settings', - onClick: () => showPreferencesModal(), + onClick: showPreferencesModal, }, ]} /> diff --git a/platform/viewer/src/components/PreferencesDropdown/PreferencesDropdown.jsx b/platform/viewer/src/components/PreferencesDropdown/PreferencesDropdown.jsx index fe591bf35..3df73650a 100644 --- a/platform/viewer/src/components/PreferencesDropdown/PreferencesDropdown.jsx +++ b/platform/viewer/src/components/PreferencesDropdown/PreferencesDropdown.jsx @@ -45,11 +45,11 @@ const PreferencesDropdown = () => { } list={[ - { title: 'About', icon: 'info', onClick: () => showAboutModal() }, + { title: 'About', icon: 'info', onClick: showAboutModal }, { title: 'Preferences', icon: 'settings', - onClick: () => showPreferencesModal(), + onClick: showPreferencesModal, }, ]} /> From a19c1acd7ba8d7e6ce92f8827f10b41b8b5f9416 Mon Sep 17 00:00:00 2001 From: Rodrigo Antinarelli Date: Wed, 24 Jun 2020 01:27:02 -0300 Subject: [PATCH 17/25] fix dropdown title --- .../default/src/ViewerLayout/Header.jsx | 40 +++++++++--------- .../ui/src/components/Dropdown/Dropdown.jsx | 22 +++++----- .../PreferencesDropdown.jsx | 42 +++++++++---------- 3 files changed, 51 insertions(+), 53 deletions(-) diff --git a/extensions/default/src/ViewerLayout/Header.jsx b/extensions/default/src/ViewerLayout/Header.jsx index b795f2fcb..132fd7916 100644 --- a/extensions/default/src/ViewerLayout/Header.jsx +++ b/extensions/default/src/ViewerLayout/Header.jsx @@ -67,26 +67,7 @@ function Header({ children }) { {t('Header:INVESTIGATIONAL USE ONLY')} - - - - - - - - } + showDropdownIcon={false} list={[ { title: t('Header:About'), @@ -99,7 +80,24 @@ function Header({ children }) { onClick: showPreferencesModal, }, ]} - /> + > + + + + + + +
diff --git a/platform/ui/src/components/Dropdown/Dropdown.jsx b/platform/ui/src/components/Dropdown/Dropdown.jsx index f13852aad..149c55ece 100644 --- a/platform/ui/src/components/Dropdown/Dropdown.jsx +++ b/platform/ui/src/components/Dropdown/Dropdown.jsx @@ -4,19 +4,17 @@ import classnames from 'classnames'; import { Icon, Typography } from '@ohif/ui'; -const Dropdown = ({ titleElement, title, list }) => { +const Dropdown = ({ children, showDropdownIcon, list }) => { const [open, setOpen] = useState(false); const element = useRef(null); const renderTitleElement = () => { - if (titleElement) { - return titleElement; - } - return ( -
- {title} - +
+ {children} + {showDropdownIcon && ( + + )}
); }; @@ -96,9 +94,13 @@ const Dropdown = ({ titleElement, title, list }) => { ); }; +Dropdown.defaultProps = { + showDropdownIcon: true, +}; + Dropdown.propTypes = { - titleElement: PropTypes.node, - title: PropTypes.string, + children: PropTypes.node.isRequired, + showDropdownIcon: PropTypes.bool, /** Items to render in the select's drop down */ list: PropTypes.arrayOf( PropTypes.shape({ diff --git a/platform/viewer/src/components/PreferencesDropdown/PreferencesDropdown.jsx b/platform/viewer/src/components/PreferencesDropdown/PreferencesDropdown.jsx index 3df73650a..791905819 100644 --- a/platform/viewer/src/components/PreferencesDropdown/PreferencesDropdown.jsx +++ b/platform/viewer/src/components/PreferencesDropdown/PreferencesDropdown.jsx @@ -23,27 +23,7 @@ const PreferencesDropdown = () => { return ( - - - - {}} - > - - - - } + showDropdownIcon={false} list={[ { title: 'About', icon: 'info', onClick: showAboutModal }, { @@ -52,7 +32,25 @@ const PreferencesDropdown = () => { onClick: showPreferencesModal, }, ]} - /> + > + + + + {}} + > + + + ); }; From e70c7a455a9f2ad6138b56bbfaaf3c638e356fe4 Mon Sep 17 00:00:00 2001 From: Rodrigo Antinarelli Date: Thu, 25 Jun 2020 13:46:24 -0300 Subject: [PATCH 18/25] split dropdown item into a separated render component --- .../ui/src/components/Dropdown/Dropdown.jsx | 77 +++++++++++-------- 1 file changed, 45 insertions(+), 32 deletions(-) diff --git a/platform/ui/src/components/Dropdown/Dropdown.jsx b/platform/ui/src/components/Dropdown/Dropdown.jsx index 149c55ece..b25240458 100644 --- a/platform/ui/src/components/Dropdown/Dropdown.jsx +++ b/platform/ui/src/components/Dropdown/Dropdown.jsx @@ -8,6 +8,42 @@ const Dropdown = ({ children, showDropdownIcon, list }) => { const [open, setOpen] = useState(false); const element = useRef(null); + const DropdownItem = ({ title, icon, onClick, index }) => { + const itemsAmount = list.length; + const isLastItem = itemsAmount === index + 1; + + return ( +
{ + setOpen(false); + onClick(); + }} + > + {!!icon && } + {title} +
+ ); + }; + + DropdownItem.defaultProps = { + icon: '', + onClick: () => {}, + }; + + DropdownItem.propTypes = { + title: PropTypes.string.isRequired, + icon: PropTypes.string, + onClick: PropTypes.func, + index: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + }; + const renderTitleElement = () => { return (
@@ -30,8 +66,6 @@ const Dropdown = ({ children, showDropdownIcon, list }) => { }; const renderList = () => { - const itemsAmount = list.length; - return (
{ } )} > - {list.map( - ( - { - title: itemTitle, - icon: itemIcon, - onClick: itemOnClick = () => {}, - }, - idx - ) => ( -
{ - setOpen(false); - itemOnClick(); - }} - > - {!!itemIcon && ( - - )} - {itemTitle} -
- ) - )} + {list.map((item, idx) => ( + + ))}
); }; @@ -107,7 +121,6 @@ Dropdown.propTypes = { title: PropTypes.string.isRequired, icon: PropTypes.string, onClick: PropTypes.func, - link: PropTypes.string, }) ), }; From 547e11d877b3bc9e205380647cfd1fdb1e897dd7 Mon Sep 17 00:00:00 2001 From: Rodrigo Antinarelli Date: Thu, 25 Jun 2020 13:52:40 -0300 Subject: [PATCH 19/25] minor fix class order --- platform/ui/src/components/Dropdown/Dropdown.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/ui/src/components/Dropdown/Dropdown.jsx b/platform/ui/src/components/Dropdown/Dropdown.jsx index b25240458..ef4ae250a 100644 --- a/platform/ui/src/components/Dropdown/Dropdown.jsx +++ b/platform/ui/src/components/Dropdown/Dropdown.jsx @@ -69,7 +69,7 @@ const Dropdown = ({ children, showDropdownIcon, list }) => { return (
Date: Thu, 25 Jun 2020 14:08:09 -0300 Subject: [PATCH 20/25] useCallback to avoid redeclaring functions --- .../default/src/ViewerLayout/Header.jsx | 23 ++++++------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/extensions/default/src/ViewerLayout/Header.jsx b/extensions/default/src/ViewerLayout/Header.jsx index 132fd7916..6000e5c22 100644 --- a/extensions/default/src/ViewerLayout/Header.jsx +++ b/extensions/default/src/ViewerLayout/Header.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useCallback } from 'react'; import PropTypes from 'prop-types'; import { useTranslation } from 'react-i18next'; // TODO: This may fail if package is split from PWA build @@ -11,7 +11,8 @@ function Header({ children }) { const history = useHistory(); const { show } = useModal(); - const showAboutModal = () => { + // TODO: IT SHOULD BE REFACTORED WHEN THE MODAL CONTENT IS DEFINED + const showAboutModal = useCallback(() => { const modalComponent = () => (
{t('AboutModal:OHIF Viewer - About')}
); @@ -19,9 +20,10 @@ function Header({ children }) { title: t('AboutModal:OHIF Viewer - About'), content: modalComponent, }); - }; + }, [show, t]); - const showPreferencesModal = () => { + // TODO: IT SHOULD BE REFACTORED WHEN THE MODAL CONTENT IS DEFINED + const showPreferencesModal = useCallback(() => { const modalComponent = () => (
{t('UserPreferencesModal:User Preferences')}
); @@ -29,18 +31,7 @@ function Header({ children }) { title: t('UserPreferencesModal:User Preferences'), content: modalComponent, }); - }; - - // const dropdownContent = [ - // { - // name: 'Soft tissue', - // value: '400/40', - // }, - // { name: 'Lung', value: '1500 / -600' }, - // { name: 'Liver', value: '150 / 90' }, - // { name: 'Bone', value: '2500 / 480' }, - // { name: 'Brain', value: '80 / 40' }, - // ] + }, [show, t]); return ( From b0935e4fb673e8fb79327b646df89b75d0435aaa Mon Sep 17 00:00:00 2001 From: Rodrigo Antinarelli Date: Thu, 25 Jun 2020 15:42:36 -0300 Subject: [PATCH 21/25] fix icon size --- platform/ui/src/assets/icons/link.svg | 2 -- platform/ui/src/assets/icons/unlink.svg | 2 -- 2 files changed, 4 deletions(-) diff --git a/platform/ui/src/assets/icons/link.svg b/platform/ui/src/assets/icons/link.svg index 7c99fc27c..1f7f94009 100644 --- a/platform/ui/src/assets/icons/link.svg +++ b/platform/ui/src/assets/icons/link.svg @@ -2,8 +2,6 @@ xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" aria-labelledby="title" - width="1em" - height="1em" fill="currentColor" > diff --git a/platform/ui/src/assets/icons/unlink.svg b/platform/ui/src/assets/icons/unlink.svg index 37c53bbb9..ed9526b7f 100644 --- a/platform/ui/src/assets/icons/unlink.svg +++ b/platform/ui/src/assets/icons/unlink.svg @@ -2,8 +2,6 @@ xmlns="http://www.w3.org/2000/svg" aria-labelledby="unlink" viewBox="0 0 512 512" - width="1em" - height="1em" fill="currentColor" > Unlink From eeeaa8d1dabe865e7bb2f27c72f969551958fb48 Mon Sep 17 00:00:00 2001 From: Rodrigo Antinarelli Date: Thu, 25 Jun 2020 18:45:28 -0300 Subject: [PATCH 22/25] fix tailwind class --- .../ui/src/components/Dropdown/Dropdown.jsx | 23 ++++++------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/platform/ui/src/components/Dropdown/Dropdown.jsx b/platform/ui/src/components/Dropdown/Dropdown.jsx index ef4ae250a..b0b58497f 100644 --- a/platform/ui/src/components/Dropdown/Dropdown.jsx +++ b/platform/ui/src/components/Dropdown/Dropdown.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState, useRef } from 'react'; +import React, { useEffect, useCallback, useState, useRef } from 'react'; import PropTypes from 'prop-types'; import classnames from 'classnames'; @@ -8,18 +8,12 @@ const Dropdown = ({ children, showDropdownIcon, list }) => { const [open, setOpen] = useState(false); const element = useRef(null); - const DropdownItem = ({ title, icon, onClick, index }) => { - const itemsAmount = list.length; - const isLastItem = itemsAmount === index + 1; - + const DropdownItem = useCallback(({ title, icon, onClick }) => { return (
{ setOpen(false); @@ -30,18 +24,16 @@ const Dropdown = ({ children, showDropdownIcon, list }) => { {title}
); - }; + }, []); DropdownItem.defaultProps = { icon: '', - onClick: () => {}, }; DropdownItem.propTypes = { title: PropTypes.string.isRequired, icon: PropTypes.string, - onClick: PropTypes.func, - index: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + onClick: PropTypes.func.isRequired, }; const renderTitleElement = () => { @@ -82,7 +74,6 @@ const Dropdown = ({ children, showDropdownIcon, list }) => { icon={item.icon} onClick={item.onClick} key={idx} - index={idx} /> ))}
@@ -120,9 +111,9 @@ Dropdown.propTypes = { PropTypes.shape({ title: PropTypes.string.isRequired, icon: PropTypes.string, - onClick: PropTypes.func, + onClick: PropTypes.func.isRequired, }) - ), + ).isRequired, }; export default Dropdown; From d220dd24899868531643149eb7160873cd7a7117 Mon Sep 17 00:00:00 2001 From: Rodrigo Antinarelli Date: Fri, 26 Jun 2020 12:34:10 -0300 Subject: [PATCH 23/25] create dropdown documentation --- .../ui/src/components/Dropdown/Dropdown.mdx | 52 +++++++++++++++++-- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/platform/ui/src/components/Dropdown/Dropdown.mdx b/platform/ui/src/components/Dropdown/Dropdown.mdx index 087877f56..dc4b02555 100644 --- a/platform/ui/src/components/Dropdown/Dropdown.mdx +++ b/platform/ui/src/components/Dropdown/Dropdown.mdx @@ -5,11 +5,13 @@ route: components/dropdown --- import { Playground, Props } from 'docz'; -import { Dropdown } from '@ohif/ui'; +import { Dropdown, IconButton, Icon } from '@ohif/ui'; # Dropdown -... +This component is used when there are more than a few options to choose from. By +hovering or clicking on the trigger, a dropdown menu will appear, which allows +you to choose an option and execute the relevant action. ## Import @@ -17,6 +19,50 @@ import { Dropdown } from '@ohif/ui'; import { Dropdown } from '@ohif/ui'; ``` + + {() => { + const handleClick = () => { + alert("Clicked"); + } + return ( +
+ + + + + + + + +
+ ); + }} +
+ ## Properties - + From c93faf628b7c64d102e2f9b306fc55466f9b7134 Mon Sep 17 00:00:00 2001 From: Rodrigo Antinarelli Date: Fri, 26 Jun 2020 12:44:30 -0300 Subject: [PATCH 24/25] nuke inputnumber --- platform/ui/index.js | 1 - .../components/InputNumber/InputNumber.jsx | 50 ------------------- .../components/InputNumber/InputNumber.mdx | 44 ---------------- .../ui/src/components/InputNumber/index.js | 2 - platform/ui/src/components/index.js | 2 - 5 files changed, 99 deletions(-) delete mode 100644 platform/ui/src/components/InputNumber/InputNumber.jsx delete mode 100644 platform/ui/src/components/InputNumber/InputNumber.mdx delete mode 100644 platform/ui/src/components/InputNumber/index.js diff --git a/platform/ui/index.js b/platform/ui/index.js index 5032f0271..e3c8a8f25 100644 --- a/platform/ui/index.js +++ b/platform/ui/index.js @@ -42,7 +42,6 @@ export { InputGroup, InputLabelWrapper, InputMultiSelect, - InputNumber, InputText, Label, LayoutSelector, diff --git a/platform/ui/src/components/InputNumber/InputNumber.jsx b/platform/ui/src/components/InputNumber/InputNumber.jsx deleted file mode 100644 index c6fca474f..000000000 --- a/platform/ui/src/components/InputNumber/InputNumber.jsx +++ /dev/null @@ -1,50 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -import { Input, InputLabelWrapper } from '@ohif/ui'; - -const InputNumber = ({ - label, - isSortable, - sortDirection, - onLabelClick, - value, - onChange, -}) => { - return ( - - { - onChange(event.target.value); - }} - /> - - ); -}; - -InputNumber.defaultProps = { - value: '', - isSortable: false, - onLabelClick: () => {}, - sortDirection: 'none', -}; - -InputNumber.propTypes = { - label: PropTypes.string.isRequired, - isSortable: PropTypes.bool, - sortDirection: PropTypes.oneOf(['ascending', 'descending', 'none']), - onLabelClick: PropTypes.func, - value: PropTypes.number, - onChange: PropTypes.func.isRequired, -}; - -export default InputNumber; diff --git a/platform/ui/src/components/InputNumber/InputNumber.mdx b/platform/ui/src/components/InputNumber/InputNumber.mdx deleted file mode 100644 index 6435c32ab..000000000 --- a/platform/ui/src/components/InputNumber/InputNumber.mdx +++ /dev/null @@ -1,44 +0,0 @@ ---- -name: InputNumber -menu: Form -route: components/InputNumber ---- - -import { useState } from 'react'; -import { Playground, Props } from 'docz'; -import { InputNumber } from '@ohif/ui'; - -# Input Text - -## Import - -```javascript -import { InputNumber } from '@ohif/ui'; -``` - -## Basic usage - - - {() => { - const [number, setNumber] = useState(10); - return ( -
-
- { - setText(value); - }} - /> -
-
- ); - }} -
- -## Properties - - diff --git a/platform/ui/src/components/InputNumber/index.js b/platform/ui/src/components/InputNumber/index.js deleted file mode 100644 index 566112449..000000000 --- a/platform/ui/src/components/InputNumber/index.js +++ /dev/null @@ -1,2 +0,0 @@ -import InputNumber from './InputNumber'; -export default InputNumber; diff --git a/platform/ui/src/components/index.js b/platform/ui/src/components/index.js index 8ba67e4ab..deb140bbb 100644 --- a/platform/ui/src/components/index.js +++ b/platform/ui/src/components/index.js @@ -11,7 +11,6 @@ import InputDateRange from './InputDateRange'; import InputGroup from './InputGroup'; import InputLabelWrapper from './InputLabelWrapper'; import InputMultiSelect from './InputMultiSelect'; -import InputNumber from './InputNumber'; import InputText from './InputText'; import Label from './Label'; import LayoutSelector from './LayoutSelector'; @@ -67,7 +66,6 @@ export { InputGroup, InputLabelWrapper, InputMultiSelect, - InputNumber, InputText, Label, LayoutSelector, From cdc88ff55273b7df67d42627e5eac89d2077ed8b Mon Sep 17 00:00:00 2001 From: Rodrigo Antinarelli Date: Fri, 26 Jun 2020 12:59:06 -0300 Subject: [PATCH 25/25] fix learn more --- platform/ui/src/components/StudyListFilter/StudyListFilter.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/ui/src/components/StudyListFilter/StudyListFilter.jsx b/platform/ui/src/components/StudyListFilter/StudyListFilter.jsx index a4d5c0589..0d06bb2e3 100644 --- a/platform/ui/src/components/StudyListFilter/StudyListFilter.jsx +++ b/platform/ui/src/components/StudyListFilter/StudyListFilter.jsx @@ -51,7 +51,7 @@ const StudyListFilter = ({ onClick={showLearnMoreContent} > - Learn more + Learn more