diff --git a/platform/ui/index.js b/platform/ui/index.js index 7005902cd..10dbaf413 100644 --- a/platform/ui/index.js +++ b/platform/ui/index.js @@ -30,6 +30,8 @@ export { Select, SegmentationTable, SidePanel, + StudyBrowser, + StudyItem, StudyListExpandedRow, StudyListPagination, StudyListTable, @@ -40,6 +42,9 @@ export { TableHead, TableRow, ThemeWrapper, + Thumbnail, + ThumbnailSR, + ThumbnailList, Typography, } from './src/components'; diff --git a/platform/ui/src/assets/icons/circled-checkmark.svg b/platform/ui/src/assets/icons/circled-checkmark.svg new file mode 100644 index 000000000..a45ac3e51 --- /dev/null +++ b/platform/ui/src/assets/icons/circled-checkmark.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/platform/ui/src/assets/icons/dotted-circle.svg b/platform/ui/src/assets/icons/dotted-circle.svg new file mode 100644 index 000000000..9fcbdcf5a --- /dev/null +++ b/platform/ui/src/assets/icons/dotted-circle.svg @@ -0,0 +1,3 @@ + + + diff --git a/platform/ui/src/assets/icons/tracked.svg b/platform/ui/src/assets/icons/tracked.svg new file mode 100644 index 000000000..a45ac3e51 --- /dev/null +++ b/platform/ui/src/assets/icons/tracked.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/platform/ui/src/assets/styles/styles.css b/platform/ui/src/assets/styles/styles.css index 57b6e0d6e..6d6899ef2 100644 --- a/platform/ui/src/assets/styles/styles.css +++ b/platform/ui/src/assets/styles/styles.css @@ -16,25 +16,12 @@ @apply bg-primary-main; } -/* INVISIBLE SCROLLBAR */ .invisible-scrollbar::-webkit-scrollbar { @apply hidden; } -.invisible-scrollbar::-webkit-scrollbar-track { - @apply hidden; -} - -.invisible-scrollbar::-webkit-scrollbar-thumb { - @apply hidden; -} - -.invisible-scrollbar::-webkit-scrollbar-thumb:window-inactive { - @apply hidden; -} - /* TOOLTIP WORKAROUND FOR ARROW UP */ -.tooltip::before { +.tooltip.tooltip-up::before { @apply bg-primary-dark absolute z-10; content: ''; width: 14px; @@ -43,3 +30,34 @@ left: 50%; transform: translateX(-50%); } + +.tooltip.tooltip-right { + left: calc(100% + 15px); +} + +.tooltip.tooltip-right::before { + @apply bg-black absolute z-10; + content: ''; + width: 2px; + height: 15px; + left: -1px; + top: 50%; + transform: translateY(-50%); +} + +/* TOOLTIP WORKAROUND FOR ARROW */ +.showTooltipOnHover .tooltip { + display: none; +} + +.showTooltipOnHover:hover .tooltip { + display: block; +} + +.showExcludeButtonOnHover .excludeButton { + display: none; +} + +.showExcludeButtonOnHover:hover .excludeButton { + display: block; +} diff --git a/platform/ui/src/components/Icon/getIcon.jsx b/platform/ui/src/components/Icon/getIcon.jsx index 7588b736c..4ac89ce9e 100644 --- a/platform/ui/src/components/Icon/getIcon.jsx +++ b/platform/ui/src/components/Icon/getIcon.jsx @@ -4,6 +4,8 @@ import React from 'react'; import arrowDown from './../../assets/icons/arrow-down.svg'; import calendar from './../../assets/icons/calendar.svg'; import cancel from './../../assets/icons/cancel.svg'; +import dottedCircle from './../../assets/icons/dotted-circle.svg'; +import circledCheckmark from './../../assets/icons/circled-checkmark.svg'; import chevronDown from './../../assets/icons/chevron-down.svg'; import chevronLeft from './../../assets/icons/chevron-left.svg'; import chevronRight from './../../assets/icons/chevron-right.svg'; @@ -24,6 +26,7 @@ import settings from './../../assets/icons/settings.svg'; 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'; /** Tools */ import toolZoom from './../../assets/icons/tool-zoom.svg'; @@ -41,6 +44,8 @@ const ICONS = { 'arrow-down': arrowDown, calendar: calendar, cancel: cancel, + 'dotted-circle': dottedCircle, + 'circled-checkmark': circledCheckmark, 'chevron-down': chevronDown, 'chevron-left': chevronLeft, 'chevron-right': chevronRight, @@ -61,6 +66,7 @@ const ICONS = { 'sorting-active-down': sortingActiveDown, 'sorting-active-up': sortingActiveUp, sorting: sorting, + tracked: tracked, /** Tools */ 'tool-zoom': toolZoom, diff --git a/platform/ui/src/components/SidePanel/SidePanel.jsx b/platform/ui/src/components/SidePanel/SidePanel.jsx index 2144bcf1e..c820181e9 100644 --- a/platform/ui/src/components/SidePanel/SidePanel.jsx +++ b/platform/ui/src/components/SidePanel/SidePanel.jsx @@ -77,7 +77,7 @@ const SidePanel = ({ onClick={() => { setIsOpen(false); }} - className="flex flex-row items-center border-b-2 border-secondary-main h-12 relative" + className="flex flex-row items-center border-b-2 border-secondary-light px-3 h-12" > { + const [studyGroupSelected, setStudyGroupSelected] = useState('Recent'); + const [studyActive, setStudyActive] = useState(null); + + return ( + +
+ + {studyGroupTypes.map((studyGroup) => { + const isActive = studyGroupSelected === studyGroup; + return ( + + ); + })} + +
+
+ {studyGroup[studyGroupSelected].map( + ({ + studyInstanceUid, + studyDate, + studyDescription, + instances, + modalities, + trackedSeries, + thumbnails, + }) => { + const isActive = studyActive === studyInstanceUid; + return ( + + { + setStudyActive(isActive ? null : studyInstanceUid); + }} + /> + {isActive && thumbnails && ( + + )} + + ); + } + )} +
+
+ ); +}; + +export default StudyBrowser; diff --git a/platform/ui/src/components/StudyBrowser/StudyBrowser.mdx b/platform/ui/src/components/StudyBrowser/StudyBrowser.mdx new file mode 100644 index 000000000..6a8f93205 --- /dev/null +++ b/platform/ui/src/components/StudyBrowser/StudyBrowser.mdx @@ -0,0 +1,26 @@ +--- +name: StudyBrowser +menu: General +route: components/studyBrowser +--- + +import { Playground, Props } from 'docz'; +import { StudyBrowser } from '@ohif/ui'; + +# StudyBrowser + +## Import + +```javascript +import { StudyBrowser } from '@ohif/ui'; +``` + +## Basic usage + + + + + +## Properties + + diff --git a/platform/ui/src/components/StudyBrowser/index.js b/platform/ui/src/components/StudyBrowser/index.js new file mode 100644 index 000000000..aa85fa066 --- /dev/null +++ b/platform/ui/src/components/StudyBrowser/index.js @@ -0,0 +1,2 @@ +import StudyBrowser from './StudyBrowser'; +export default StudyBrowser; diff --git a/platform/ui/src/components/StudyBrowser/mockData.js b/platform/ui/src/components/StudyBrowser/mockData.js new file mode 100644 index 000000000..337912300 --- /dev/null +++ b/platform/ui/src/components/StudyBrowser/mockData.js @@ -0,0 +1,56 @@ +const studyWithSR = { + studyInstanceUid: '1', + studyDate: '07-Sept-2010', + studyDescription: 'CHEST/ABD/PELVIS W/CONTRAST', + instances: 902, + modalities: 'CT,SR', + trackedSeries: 1, + thumbnails: [ + { + displaySetInstanceUid: 'f69f6asdasd48c-223e-db7f-c4af-b8906641a66e', + seriesDescription: 'Multiple line image series description lorem sit', + seriesNumber: 1, + instanceNumber: 68, + viewportIdentificator: 'A', + isTracked: true, + }, + { + displaySetInstanceUid: 'f69f648c-223e-db7f-c4asdasdaf-b8906641a66e', + seriesDescription: 'Multiple line image series description lorem sit', + seriesNumber: 1, + instanceNumber: 68, + viewportIdentificator: 'B', + }, + { + displaySetInstanceUid: 'f69f648c-223e-dasdasdb7f-c4af-b8906641a66e', + seriesDescription: 'Multiple line image series description lorem sit', + seriesNumber: 1, + instanceNumber: 68, + }, + { + displaySetInstanceUid: 'f69f648c-223e-db7f-c4afas-b8906asd641a66e', + seriesDescription: 'Multiple line description lorem ipsum dolor sit amet', + modality: 'SR', + seriesDate: '07-Sept-2010', + }, + ], +}; + +const studySimple = { + studyInstanceUid: '2', + studyDate: '07-Sept-2010', + studyDescription: 'CHEST/ABD/PELVIS W/CONTRAST', + instances: 902, + modalities: 'CT', + trackedSeries: 0, + thumbnails: [ + { + displaySetInstanceUid: 'f69f648c-223e-db7f-c4af-b8906641a66e', + seriesDescription: 'Multiple line image series description lorem sit', + seriesNumber: 1, + instanceNumber: 68, + }, + ], +}; + +export { studyWithSR, studySimple }; diff --git a/platform/ui/src/components/StudyItem/StudyItem.jsx b/platform/ui/src/components/StudyItem/StudyItem.jsx new file mode 100644 index 000000000..eaa85b10e --- /dev/null +++ b/platform/ui/src/components/StudyItem/StudyItem.jsx @@ -0,0 +1,72 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import classnames from 'classnames'; + +import { Icon } from '@ohif/ui'; + +const baseClasses = + 'first:border-0 border-t border-secondary-light cursor-pointer'; + +const StudyItem = ({ + studyDate, + studyDescription, + instances, + modalities, + trackedSeries, + isActive, + onClick, +}) => { + return ( +
+
+
+
{studyDate}
+
+ + {instances} +
+
+
+
{modalities}
+
{studyDescription}
+
+
+ {!!trackedSeries && ( +
+
+ + {trackedSeries} Tracked Series +
+
+ )} +
+ ); +}; + +StudyItem.propTypes = { + studyDate: PropTypes.any, + studyDescription: PropTypes.any, + modalities: PropTypes.any, + instances: PropTypes.any, + trackedSeries: PropTypes.any, + isActive: PropTypes.any, + onClick: PropTypes.func.isRequired, +}; + +export default StudyItem; diff --git a/platform/ui/src/components/StudyItem/index.js b/platform/ui/src/components/StudyItem/index.js new file mode 100644 index 000000000..0c9680c4f --- /dev/null +++ b/platform/ui/src/components/StudyItem/index.js @@ -0,0 +1,2 @@ +import StudyItem from './StudyItem'; +export default StudyItem; diff --git a/platform/ui/src/components/Thumbnail/Thumbnail.jsx b/platform/ui/src/components/Thumbnail/Thumbnail.jsx new file mode 100644 index 000000000..9f8cad358 --- /dev/null +++ b/platform/ui/src/components/Thumbnail/Thumbnail.jsx @@ -0,0 +1,117 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import classnames from 'classnames'; + +import { Icon } from '@ohif/ui'; + +const Thumbnail = ({ + seriesDescription, + seriesNumber, + instanceNumber, + viewportIdentificator, + isTracked, + isActive, + onClick, +}) => { + const trackedIcon = isTracked ? 'circled-checkmark' : 'dotted-circle'; + + return ( +
+
+
+ +
+ {viewportIdentificator} +
+
+
+
+ + Series is tracked + + {viewportIdentificator && ( + + in viewport + + {viewportIdentificator} + + + )} +
+
+ +
+
+ + + +
+
+ {isTracked && ( + + )} +
+
+
+ {'Thumbnail img'} +
+
+
+ {'S: '} + {seriesNumber} +
+
+ {instanceNumber} +
+
+
{seriesDescription}
+
+
+ ); +}; + +Thumbnail.propTypes = { + seriesDescription: PropTypes.string.isRequired, + seriesNumber: PropTypes.number.isRequired, + instanceNumber: PropTypes.number.isRequired, + isActive: PropTypes.bool.isRequired, + onClick: PropTypes.func.isRequired, + isTracked: PropTypes.bool, + viewportIdentificator: PropTypes.string, +}; + +export default Thumbnail; diff --git a/platform/ui/src/components/Thumbnail/index.js b/platform/ui/src/components/Thumbnail/index.js new file mode 100644 index 000000000..275561d01 --- /dev/null +++ b/platform/ui/src/components/Thumbnail/index.js @@ -0,0 +1,2 @@ +import Thumbnail from './Thumbnail'; +export default Thumbnail; diff --git a/platform/ui/src/components/ThumbnailList/ThumbnailList.jsx b/platform/ui/src/components/ThumbnailList/ThumbnailList.jsx new file mode 100644 index 000000000..ecafab2b0 --- /dev/null +++ b/platform/ui/src/components/ThumbnailList/ThumbnailList.jsx @@ -0,0 +1,73 @@ +import React, { useState } from 'react'; +import PropTypes from 'prop-types'; + +import { Thumbnail, ThumbnailSR } from '@ohif/ui'; + +const ThumbnailList = ({ thumbnails }) => { + const [thumbnailActive, setThumbnailActive] = useState(null); + + return ( +
+ {thumbnails.map( + ({ + displaySetInstanceUid, + seriesDescription, + seriesNumber, + instanceNumber, + modality, + seriesDate, + viewportIdentificator, + isTracked, + }) => { + const isSR = modality && modality.toLowerCase() === 'sr'; + const isActive = thumbnailActive === displaySetInstanceUid; + + if (isSR) { + return ( + + ); + } else { + return ( + { + setThumbnailActive( + thumbnailActive === displaySetInstanceUid + ? null + : displaySetInstanceUid + ); + }} + /> + ); + } + } + )} +
+ ); +}; + +ThumbnailList.propTypes = { + thumbnails: PropTypes.arrayOf( + PropTypes.shape({ + displaySetInstanceUid: PropTypes.string, + seriesDescription: PropTypes.string, + seriesNumber: PropTypes.number, + instanceNumber: PropTypes.number, + modality: PropTypes.string, + seriesDate: PropTypes.string, + }) + ), +}; + +export default ThumbnailList; diff --git a/platform/ui/src/components/ThumbnailList/index.js b/platform/ui/src/components/ThumbnailList/index.js new file mode 100644 index 000000000..e2cfade78 --- /dev/null +++ b/platform/ui/src/components/ThumbnailList/index.js @@ -0,0 +1,2 @@ +import ThumbnailList from './ThumbnailList'; +export default ThumbnailList; diff --git a/platform/ui/src/components/ThumbnailSR/ThumbnailSR.jsx b/platform/ui/src/components/ThumbnailSR/ThumbnailSR.jsx new file mode 100644 index 000000000..c3d949168 --- /dev/null +++ b/platform/ui/src/components/ThumbnailSR/ThumbnailSR.jsx @@ -0,0 +1,31 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import { Icon } from '@ohif/ui'; + +const ThumbnailSR = ({ seriesDescription, seriesDate }) => { + return ( +
+
+ +
+
+
+
+ SR +
+ {seriesDate} +
+
{seriesDescription}
+
+
+
+ ); +}; + +ThumbnailSR.propTypes = { + seriesDescription: PropTypes.string, + seriesDate: PropTypes.string, +}; + +export default ThumbnailSR; diff --git a/platform/ui/src/components/ThumbnailSR/index.js b/platform/ui/src/components/ThumbnailSR/index.js new file mode 100644 index 000000000..b9b2a119c --- /dev/null +++ b/platform/ui/src/components/ThumbnailSR/index.js @@ -0,0 +1,2 @@ +import ThumbnailSR from './ThumbnailSR'; +export default ThumbnailSR; diff --git a/platform/ui/src/components/index.js b/platform/ui/src/components/index.js index 05cb336f5..8bb14ec05 100644 --- a/platform/ui/src/components/index.js +++ b/platform/ui/src/components/index.js @@ -16,16 +16,21 @@ import NavBar from './NavBar'; import Select from './Select'; import SegmentationTable from './SegmentationTable'; import SidePanel from './SidePanel'; -import Svg from './Svg'; +import StudyBrowser from './StudyBrowser'; import StudyListExpandedRow from './StudyListExpandedRow'; import StudyListPagination from './StudyListPagination'; import StudyListTable from './StudyListTable'; +import Svg from './Svg'; +import StudyItem from './StudyItem'; import Table from './Table'; import TableBody from './TableBody'; import TableCell from './TableCell'; import TableHead from './TableHead'; import TableRow from './TableRow'; import ThemeWrapper from './ThemeWrapper/'; +import Thumbnail from './Thumbnail'; +import ThumbnailSR from './ThumbnailSR'; +import ThumbnailList from './ThumbnailList'; import Typography from './Typography'; export { @@ -47,15 +52,20 @@ export { Select, SegmentationTable, SidePanel, - Svg, + StudyBrowser, + StudyItem, StudyListExpandedRow, StudyListPagination, StudyListTable, + Svg, Table, TableBody, TableCell, TableHead, TableRow, ThemeWrapper, + Thumbnail, + ThumbnailSR, + ThumbnailList, Typography, }; diff --git a/platform/ui/src/views/ToolbarHeader/ToolbarHeader.mdx b/platform/ui/src/views/ToolbarHeader/ToolbarHeader.mdx index 0ed69f774..1b5abbe59 100644 --- a/platform/ui/src/views/ToolbarHeader/ToolbarHeader.mdx +++ b/platform/ui/src/views/ToolbarHeader/ToolbarHeader.mdx @@ -86,7 +86,7 @@ components. {shouldShowTooltip && (
{tool.label} diff --git a/platform/ui/src/views/Viewer/Viewer.js b/platform/ui/src/views/Viewer/Viewer.js index dc466fe1c..3df503696 100644 --- a/platform/ui/src/views/Viewer/Viewer.js +++ b/platform/ui/src/views/Viewer/Viewer.js @@ -1,5 +1,5 @@ import React from 'react'; -import { NavBar, SidePanel, Svg } from '@ohif/ui'; +import { NavBar, SidePanel, Svg, StudyBrowser } from '@ohif/ui'; const Viewer = () => { return ( @@ -18,11 +18,9 @@ const Viewer = () => { iconName="group-layers" iconLabel="Studies" componentLabel="Studies" - defaultIsOpen={false} + defaultIsOpen={true} > -
- panel placeholder -
+
CONTENT diff --git a/platform/ui/src/views/Viewer/Viewer.mdx b/platform/ui/src/views/Viewer/Viewer.mdx index 171baf086..f6b28b637 100644 --- a/platform/ui/src/views/Viewer/Viewer.mdx +++ b/platform/ui/src/views/Viewer/Viewer.mdx @@ -5,110 +5,32 @@ route: examples/viewer --- import { Playground, Props } from 'docz'; -import { - NavBar, - SidePanel, - Svg, - MeasurementTable, - SegmentationTable, - ButtonGroup, - Button, - Icon, - IconButton, -} from '@ohif/ui'; +import { NavBar, SidePanel, Svg, StudyBrowser } from '@ohif/ui'; # Viewer - {() => { - const measurementTableData = { - title: 'Measurements', - amount: 10, - data: new Array(10).fill(''), - }; - const segmentationTableData = { - title: 'Segments', - amount: 12, - data: new Array(12).fill(''), - }; - return ( -
- -
- -
-
-
- -
- panel placeholder -
-
-
- CONTENT -
- -
-
-
- 07-Sep-2010 - - CT - -
-
- - CHEST/ABD/PELVIS W CONTRAST - -
-
- - -
-
- alert('Export')}> - - - - - - -
-
-
+
+ +
+ +
+
+
+ + + +
+ CONTENT
); }}