From a9a4f3f82b35b686340b6da1600746888934f0ff Mon Sep 17 00:00:00 2001 From: Erik Ziegler Date: Tue, 7 Jun 2016 19:09:47 +0200 Subject: [PATCH] Lesion Tracker Redesign so far --- LesionTracker/.meteor/packages | 1 + LesionTracker/.meteor/versions | 1 + LesionTracker/client/body.styl | 3 - LesionTracker/client/components/init.js | 3 - LesionTracker/client/components/viewer.styl | 35 ---- .../components/viewerMain/viewerMain.js | 127 ------------ LesionTracker/client/head.html | 3 +- LesionTracker/client/routes.js | 18 +- OHIFViewer/.meteor/packages | 1 + OHIFViewer/.meteor/versions | 1 + OHIFViewer/client/head.html | 5 +- Packages/design/app.styl | 6 + .../roundedButtonGroup.html | 7 + .../roundedButtonGroup/roundedButtonGroup.js | 15 ++ .../roundedButtonGroup.styl | 46 +++++ Packages/design/package.js | 34 ++++ Packages/design/styles/global.styl | 10 + Packages/design/styles/mixins.styl | 13 ++ Packages/design/styles/spacings.styl | 39 ++++ Packages/design/styles/variables.styl | 34 ++++ Packages/design/styles/webfonts.styl | 2 + .../components/stageSortable/stageSortable.js | 4 + Packages/lesiontracker/assets/icons.svg | 186 ++++++++++++++++++ .../additionalMeasurements.html | 77 ++++++++ .../additionalMeasurements.js | 87 ++++++++ .../additionalMeasurements.styl | 74 +++++++ .../radioOptionGroup/radioOptionGroup.html | 16 ++ .../radioOptionGroup/radioOptionGroup.js | 15 ++ .../radioOptionGroup/radioOptionGroup.styl | 20 ++ .../components/caseProgress/caseProgress.html | 11 ++ .../components/caseProgress/caseProgress.js | 30 +++ .../components/caseProgress/caseProgress.styl | 3 + .../flexboxLayout/flexboxLayout.html | 20 ++ .../components/flexboxLayout/flexboxLayout.js | 66 +++++++ .../flexboxLayout/flexboxLayout.styl | 69 +++++++ .../components/hidingPanel/hidingPanel.html | 4 +- .../components/hidingPanel/hidingPanel.js | 13 +- .../components/lesionTable/lesionTable.html | 45 +++-- .../components/lesionTable/lesionTable.js | 99 +++++----- .../components/lesionTable/lesionTable.styl | 90 +-------- .../lesionTableHeaderRow.html | 22 +++ .../lesionTableHeaderRow.js | 38 ++++ .../lesionTableHeaderRow.styl | 58 ++++++ .../lesionTableRow/lesionTableRow.html | 45 ++++- .../lesionTableRow/lesionTableRow.styl | 42 +++- .../lesionTableTimepointCell.html | 12 +- .../lesionTableTimepointCell.styl | 12 +- .../lesionTableTimepointHeader.html | 12 +- .../lesionTableTimepointHeader.styl | 18 +- .../lesionTracker/lesionTracker.html | 35 ++++ .../components/lesionTracker/lesionTracker.js | 65 ++++++ .../lesionTracker/lesionTracker.styl | 47 +++++ .../lesionTrackerLayout.html | 9 - .../lesionTrackerLayout.js | 126 ------------ .../lesionTrackerLayout.styl | 38 ---- .../lesionTrackerViewportOverlay.styl | 103 +++++++++- .../lesionTrackerWorklistStudy.js | 10 +- .../radialProgressBar/radialProgressBar.html | 43 ++++ .../radialProgressBar/radialProgressBar.js | 13 ++ .../radialProgressBar/radialProgressBar.styl | 41 ++++ .../serverInformationModal.js | 12 +- .../studySeriesQuickSwitch.html | 29 +++ .../studySeriesQuickSwitch.js | 34 ++++ .../studySeriesQuickSwitch.styl | 68 +++++++ .../studyTimepointBrowser/studyTimepoint.html | 9 + .../studyTimepointBrowser/studyTimepoint.js | 58 ++++++ .../studyTimepointBrowser/studyTimepoint.styl | 8 + .../studyTimepointBrowser.html | 29 +++ .../studyTimepointBrowser.js | 46 +++++ .../studyTimepointBrowser.styl | 68 +++++++ .../studyTimepointStudy.html | 18 ++ .../studyTimepointStudy.js | 40 ++++ .../studyTimepointStudy.styl | 99 ++++++++++ .../timeoutCountdownDialog.html | 4 +- .../timeoutCountdownDialog.js | 44 ++++- .../toolbarSection/toolbarSection.html | 79 ++++++++ .../toolbarSection/toolbarSection.js | 133 +++++++++++++ .../toolbarSection/toolbarSection.styl | 151 ++++++++++++++ .../toolbarSectionButton.html | 12 ++ .../toolbarSectionButton.js | 0 .../toolbarSectionButton.styl | 0 .../userAccountMenu/userAccountMenu.html | 62 +++--- .../userAccountMenu/userAccountMenu.js | 74 +++++-- .../userAccountMenu/userAccountMenu.styl | 41 ++-- .../client/components/viewer}/viewer.html | 10 +- .../client/components/viewer}/viewer.js | 29 +-- .../client/components/viewer/viewer.styl | 26 ++- .../components/viewerMain/viewerMain.html | 1 - .../components/viewerMain/viewerMain.js | 10 + .../components/viewerMain/viewerMain.styl | 17 ++ .../lib/toggleLesionTrackerTools.js | 2 +- .../lib/worklist/openNewTabWithTimepoint.js | 15 +- .../lib/worklist/worklistModification.js | 38 +++- Packages/lesiontracker/package.js | 103 +++++++--- .../imageThumbnail/imageThumbnail.html | 3 +- .../imageThumbnail/imageThumbnail.js | 4 +- .../imageThumbnail/imageThumbnail.styl | 32 ++- .../thumbnailEntry/thumbnailEntry.html | 24 ++- .../thumbnailEntry/thumbnailEntry.js | 31 +-- .../thumbnailEntry/thumbnailEntry.styl | 80 ++++++-- .../imageViewerViewport.js | 2 + .../imageViewerViewport.styl | 5 +- .../viewportOverlay/viewportOverlay.styl | 5 +- Packages/viewerbase/lib/createStacks.js | 4 +- Packages/viewerbase/lib/helpers/logical.js | 35 ++++ Packages/viewerbase/lib/isImage.js | 59 ------ Packages/viewerbase/lib/sopClassDictionary.js | 115 ----------- Packages/viewerbase/package.js | 10 +- Packages/worklist/client/lib/switchToTab.js | 8 +- 109 files changed, 2972 insertions(+), 936 deletions(-) delete mode 100644 LesionTracker/client/components/viewer.styl delete mode 100644 LesionTracker/client/components/viewerMain/viewerMain.js create mode 100644 Packages/design/app.styl create mode 100644 Packages/design/components/roundedButtonGroup/roundedButtonGroup.html create mode 100644 Packages/design/components/roundedButtonGroup/roundedButtonGroup.js create mode 100644 Packages/design/components/roundedButtonGroup/roundedButtonGroup.styl create mode 100644 Packages/design/package.js create mode 100644 Packages/design/styles/global.styl create mode 100644 Packages/design/styles/mixins.styl create mode 100644 Packages/design/styles/spacings.styl create mode 100644 Packages/design/styles/variables.styl create mode 100644 Packages/design/styles/webfonts.styl create mode 100644 Packages/lesiontracker/assets/icons.svg create mode 100644 Packages/lesiontracker/client/components/additionalMeasurements/additionalMeasurements.html create mode 100644 Packages/lesiontracker/client/components/additionalMeasurements/additionalMeasurements.js create mode 100644 Packages/lesiontracker/client/components/additionalMeasurements/additionalMeasurements.styl create mode 100644 Packages/lesiontracker/client/components/additionalMeasurements/radioOptionGroup/radioOptionGroup.html create mode 100644 Packages/lesiontracker/client/components/additionalMeasurements/radioOptionGroup/radioOptionGroup.js create mode 100644 Packages/lesiontracker/client/components/additionalMeasurements/radioOptionGroup/radioOptionGroup.styl create mode 100644 Packages/lesiontracker/client/components/caseProgress/caseProgress.html create mode 100644 Packages/lesiontracker/client/components/caseProgress/caseProgress.js create mode 100644 Packages/lesiontracker/client/components/caseProgress/caseProgress.styl create mode 100644 Packages/lesiontracker/client/components/flexboxLayout/flexboxLayout.html create mode 100644 Packages/lesiontracker/client/components/flexboxLayout/flexboxLayout.js create mode 100644 Packages/lesiontracker/client/components/flexboxLayout/flexboxLayout.styl create mode 100644 Packages/lesiontracker/client/components/lesionTableHeaderRow/lesionTableHeaderRow.html create mode 100644 Packages/lesiontracker/client/components/lesionTableHeaderRow/lesionTableHeaderRow.js create mode 100644 Packages/lesiontracker/client/components/lesionTableHeaderRow/lesionTableHeaderRow.styl create mode 100644 Packages/lesiontracker/client/components/lesionTracker/lesionTracker.html create mode 100644 Packages/lesiontracker/client/components/lesionTracker/lesionTracker.js create mode 100644 Packages/lesiontracker/client/components/lesionTracker/lesionTracker.styl delete mode 100644 Packages/lesiontracker/client/components/lesionTrackerLayout/lesionTrackerLayout.js delete mode 100644 Packages/lesiontracker/client/components/lesionTrackerLayout/lesionTrackerLayout.styl create mode 100644 Packages/lesiontracker/client/components/radialProgressBar/radialProgressBar.html create mode 100644 Packages/lesiontracker/client/components/radialProgressBar/radialProgressBar.js create mode 100644 Packages/lesiontracker/client/components/radialProgressBar/radialProgressBar.styl create mode 100644 Packages/lesiontracker/client/components/studySeriesQuickSwitch/studySeriesQuickSwitch.html create mode 100644 Packages/lesiontracker/client/components/studySeriesQuickSwitch/studySeriesQuickSwitch.js create mode 100644 Packages/lesiontracker/client/components/studySeriesQuickSwitch/studySeriesQuickSwitch.styl create mode 100644 Packages/lesiontracker/client/components/studyTimepointBrowser/studyTimepoint.html create mode 100644 Packages/lesiontracker/client/components/studyTimepointBrowser/studyTimepoint.js create mode 100644 Packages/lesiontracker/client/components/studyTimepointBrowser/studyTimepoint.styl create mode 100644 Packages/lesiontracker/client/components/studyTimepointBrowser/studyTimepointBrowser.html create mode 100644 Packages/lesiontracker/client/components/studyTimepointBrowser/studyTimepointBrowser.js create mode 100644 Packages/lesiontracker/client/components/studyTimepointBrowser/studyTimepointBrowser.styl create mode 100644 Packages/lesiontracker/client/components/studyTimepointBrowser/studyTimepointStudy.html create mode 100644 Packages/lesiontracker/client/components/studyTimepointBrowser/studyTimepointStudy.js create mode 100644 Packages/lesiontracker/client/components/studyTimepointBrowser/studyTimepointStudy.styl create mode 100644 Packages/lesiontracker/client/components/toolbarSection/toolbarSection.html create mode 100644 Packages/lesiontracker/client/components/toolbarSection/toolbarSection.js create mode 100644 Packages/lesiontracker/client/components/toolbarSection/toolbarSection.styl create mode 100644 Packages/lesiontracker/client/components/toolbarSectionButton/toolbarSectionButton.html create mode 100644 Packages/lesiontracker/client/components/toolbarSectionButton/toolbarSectionButton.js create mode 100644 Packages/lesiontracker/client/components/toolbarSectionButton/toolbarSectionButton.styl rename {LesionTracker/client/components => Packages/lesiontracker/client/components/viewer}/viewer.html (62%) rename {LesionTracker/client/components => Packages/lesiontracker/client/components/viewer}/viewer.js (89%) rename LesionTracker/client/components/viewerMain/viewerMain.styl => Packages/lesiontracker/client/components/viewer/viewer.styl (51%) rename {LesionTracker => Packages/lesiontracker}/client/components/viewerMain/viewerMain.html (71%) create mode 100644 Packages/lesiontracker/client/components/viewerMain/viewerMain.js create mode 100644 Packages/lesiontracker/client/components/viewerMain/viewerMain.styl create mode 100644 Packages/viewerbase/lib/helpers/logical.js delete mode 100644 Packages/viewerbase/lib/isImage.js delete mode 100644 Packages/viewerbase/lib/sopClassDictionary.js diff --git a/LesionTracker/.meteor/packages b/LesionTracker/.meteor/packages index 982811d41..e9d5f691a 100755 --- a/LesionTracker/.meteor/packages +++ b/LesionTracker/.meteor/packages @@ -33,6 +33,7 @@ fortawesome:fontawesome random reactive-var reactive-dict +design lesiontracker practicalmeteor:loglevel momentjs:moment diff --git a/LesionTracker/.meteor/versions b/LesionTracker/.meteor/versions index c73db83d1..05f1cb45c 100644 --- a/LesionTracker/.meteor/versions +++ b/LesionTracker/.meteor/versions @@ -38,6 +38,7 @@ ddp-common@1.2.6 ddp-rate-limiter@1.0.5 ddp-server@1.2.9 deps@1.0.12 +design@0.0.1 dicomweb@0.0.1 diff-sequence@1.0.6 dimseservice@0.0.1 diff --git a/LesionTracker/client/body.styl b/LesionTracker/client/body.styl index 183a98060..c4b7c9834 100644 --- a/LesionTracker/client/body.styl +++ b/LesionTracker/client/body.styl @@ -1,9 +1,6 @@ body background-color: black -.tab-content - height: calc(100% - 40px) - /* Mobile devices */ @media only screen and (min-device-width : 375px) and (max-device-width : 768px) .logoContainer diff --git a/LesionTracker/client/components/init.js b/LesionTracker/client/components/init.js index c13cc2a7a..ad62626a0 100644 --- a/LesionTracker/client/components/init.js +++ b/LesionTracker/client/components/init.js @@ -34,7 +34,6 @@ Meteor.startup(function() { touch: cornerstoneTools.exToolTouch }); - var states = toolManager.getToolDefaultStates(); states.deactivate.push('bidirectional'); states.deactivate.push('nonTarget'); @@ -44,7 +43,5 @@ Meteor.startup(function() { states.deactivate.push('exTool'); states.activate.push('deleteLesionKeyboardTool'); - - states.enable.push('scaleOverlayTool'); toolManager.setToolDefaultStates(states); }); diff --git a/LesionTracker/client/components/viewer.styl b/LesionTracker/client/components/viewer.styl deleted file mode 100644 index 4bc2f6b4a..000000000 --- a/LesionTracker/client/components/viewer.styl +++ /dev/null @@ -1,35 +0,0 @@ -#viewer - width: 100% - height: 100% - margin: 0 - padding: 0 - background-color: black - -#viewportAndLesionTable - height: 100% - width: 99% - background-color: black - float: right - -o-transition: all 0.3s ease-out; - -ms-transition: all 0.3s ease-out; - -moz-transition: all 0.3s ease-out; - -webkit-transition: all 0.3s ease-out; - transition: all 0.3s ease-out; - -#imageViewerViewports - .viewportContainer - &.active - border: 2px solid #777 - - &:hover, &:hover.active - border: 2px solid white - -#btnCollapse - background-color: #333 - border-color: black - - &.active - border-color: #333 - -.btnCollapseIcon - color: white diff --git a/LesionTracker/client/components/viewerMain/viewerMain.js b/LesionTracker/client/components/viewerMain/viewerMain.js deleted file mode 100644 index 6eec6e550..000000000 --- a/LesionTracker/client/components/viewerMain/viewerMain.js +++ /dev/null @@ -1,127 +0,0 @@ -Template.viewerMain.helpers({ - toolbarOptions: function() { - var toolbarOptions = {}; - - var buttonData = []; - - var btnGroup = []; - - buttonData.push({ - id: 'resetViewport', - title: 'Reset Viewport', - classes: 'imageViewerCommand', - iconClasses: 'fa fa-undo' - }); - - buttonData.push({ - id: 'wwwc', - title: 'WW/WC', - classes: 'imageViewerTool', - iconClasses: 'fa fa-sun-o' - }); - - buttonData.push({ - id: 'zoom', - title: 'Zoom', - classes: 'imageViewerTool', - iconClasses: 'fa fa-search' - }); - - buttonData.push({ - id: 'pan', - title: 'Pan', - classes: 'imageViewerTool', - iconClasses: 'fa fa-arrows' - }); - - buttonData.push({ - id: 'stackScroll', - title: 'Stack Scroll', - classes: 'imageViewerTool', - iconClasses: 'fa fa-bars' - }); - - buttonData.push({ - id: 'length', - title: 'Length Measurement', - classes: 'imageViewerTool', - iconClasses: 'fa fa-arrows-v' - }); - - buttonData.push({ - id: 'ellipticalRoi', - title: 'Elliptical ROI Measurement', - classes: 'imageViewerTool', - iconClasses: 'fa fa-circle-o' - }); - - buttonData.push({ - id: 'bidirectional', - title: 'Target Tool', - classes: 'imageViewerTool', - iconClasses: 'fa fa-arrows-alt' - }); - - buttonData.push({ - id: 'nonTarget', - title: 'Non-Target Tool', - classes: 'imageViewerTool', - iconClasses: 'fa fa-long-arrow-up' - }); - - buttonData.push({ - id: 'clearTools', - title: 'Clear tools', - classes: 'imageViewerCommand', - iconClasses: 'fa fa-trash' - }); - - buttonData.push({ - id: 'toggleLesionTrackerTools', - title: 'Show / hide tools', - classes: 'imageViewerCommand', - iconClasses: 'fa fa-eye' - }); - - // CR/UN/EX Tools - var crunexToolsBtns = { - id: 'crunexTools', - tools: [{ - id: 'crTool', - title: 'CR Tool', - classes: 'imageViewerTool', - iconClasses: 'fa fa-cr' - }, { - id: 'unTool', - title: 'UN Tool', - classes: 'imageViewerTool', - iconClasses: 'fa fa-un' - }, { - id: 'exTool', - title: 'EX Tool', - classes: 'imageViewerTool', - iconClasses: 'fa fa-ex' - }], - title: 'CR/UN/EX', - groupIcon: 'fa fa-exchange' - }; - - btnGroup.push(crunexToolsBtns); - - toolbarOptions.buttonData = buttonData; - toolbarOptions.includePlayClipButton = false; - toolbarOptions.includeLayoutButton = false; - toolbarOptions.includeHangingProtocolButtons = false; - toolbarOptions.btnGroup = btnGroup; - return toolbarOptions; - } -}); - -Template.viewerMain.onRendered(function() { - var parentNode = document.getElementById('layoutManagerTarget'); - var studies = this.data.studies; - layoutManager = new LayoutManager(parentNode, studies); - - ProtocolEngine = new HP.ProtocolEngine(layoutManager, studies); - HP.setEngine(ProtocolEngine); -}); diff --git a/LesionTracker/client/head.html b/LesionTracker/client/head.html index 60600960b..bdacefdfe 100644 --- a/LesionTracker/client/head.html +++ b/LesionTracker/client/head.html @@ -13,5 +13,4 @@ - - \ No newline at end of file + diff --git a/LesionTracker/client/routes.js b/LesionTracker/client/routes.js index 3673788e2..4c5f51ec7 100644 --- a/LesionTracker/client/routes.js +++ b/LesionTracker/client/routes.js @@ -36,11 +36,11 @@ var routerOptions = { Router.route('/', function() { // Check user is logged in - if(Meteor.user() && Meteor.userId()) { + if (Meteor.user() && Meteor.userId()) { if (verifyEmail && Meteor.user().emails && !Meteor.user().emails[0].verified) { this.render('emailVerification', routerOptions); } else { - this.render('worklist', routerOptions); + this.render('lesionTracker', routerOptions); } } else { @@ -50,11 +50,11 @@ Router.route('/', function() { Router.route('/worklist', function() { // Check user is logged in - if(Meteor.user() && Meteor.userId()) { + if (Meteor.user() && Meteor.userId()) { if (verifyEmail && Meteor.user().emails && !Meteor.user().emails[0].verified) { this.render('emailVerification', routerOptions); } else { - this.render('worklist', routerOptions); + this.render('lesionTracker', routerOptions); } } else { @@ -77,15 +77,17 @@ Router.route('/viewer/timepoints/:_id', { return; } - this.render('worklist', routerOptions); + this.render('lesionTracker', routerOptions); openNewTabWithTimepoint(timepointId); } }); -Router.onBeforeAction(function () { - if (!Meteor.userId() && !Meteor.loggingIn()) { +Router.onBeforeAction(function() { + if (!Meteor.userId() && !Meteor.loggingIn()) { this.render('entrySignIn'); } else { this.next(); } -},{except: ['entrySignIn', 'entrySignUp', 'forgotPassword', 'resetPassword']}); +}, { + except: ['entrySignIn', 'entrySignUp', 'forgotPassword', 'resetPassword'] +}); diff --git a/OHIFViewer/.meteor/packages b/OHIFViewer/.meteor/packages index b6bdcb275..fd3a53c9a 100755 --- a/OHIFViewer/.meteor/packages +++ b/OHIFViewer/.meteor/packages @@ -26,6 +26,7 @@ spacebars check cornerstone dicomweb +design viewerbase worklist reactive-var diff --git a/OHIFViewer/.meteor/versions b/OHIFViewer/.meteor/versions index 6d4065cef..af7c2b89c 100644 --- a/OHIFViewer/.meteor/versions +++ b/OHIFViewer/.meteor/versions @@ -24,6 +24,7 @@ ddp-client@1.2.9 ddp-common@1.2.6 ddp-server@1.2.9 deps@1.0.12 +design@0.0.1 dicomweb@0.0.1 diff-sequence@1.0.6 dimseservice@0.0.1 diff --git a/OHIFViewer/client/head.html b/OHIFViewer/client/head.html index b3bffd530..7fc632e9a 100644 --- a/OHIFViewer/client/head.html +++ b/OHIFViewer/client/head.html @@ -1,12 +1,11 @@ OHIF Viewer - + - - \ No newline at end of file + diff --git a/Packages/design/app.styl b/Packages/design/app.styl new file mode 100644 index 000000000..4e35e6acb --- /dev/null +++ b/Packages/design/app.styl @@ -0,0 +1,6 @@ +@import "{design}/styles/webfonts" +@import "{design}/styles/variables" +@import "{design}/styles/mixins" +@import "{design}/styles/spacings" + +@import "{design}/styles/global" diff --git a/Packages/design/components/roundedButtonGroup/roundedButtonGroup.html b/Packages/design/components/roundedButtonGroup/roundedButtonGroup.html new file mode 100644 index 000000000..c5b8eaba9 --- /dev/null +++ b/Packages/design/components/roundedButtonGroup/roundedButtonGroup.html @@ -0,0 +1,7 @@ + diff --git a/Packages/design/components/roundedButtonGroup/roundedButtonGroup.js b/Packages/design/components/roundedButtonGroup/roundedButtonGroup.js new file mode 100644 index 000000000..445ac77b0 --- /dev/null +++ b/Packages/design/components/roundedButtonGroup/roundedButtonGroup.js @@ -0,0 +1,15 @@ +Template.roundedButtonGroup.onRendered(() => { + const instance = Template.instance(); + const value = instance.data.value; + if (!value.get()) { + value.set(instance.data.options[0].key); + } +}); + +Template.roundedButtonGroup.events({ + 'click [data-key]'(event, instance) { + const $element = $(event.currentTarget); + const key = $element.attr('data-key'); + instance.data.value.set(key); + } +}); diff --git a/Packages/design/components/roundedButtonGroup/roundedButtonGroup.styl b/Packages/design/components/roundedButtonGroup/roundedButtonGroup.styl new file mode 100644 index 000000000..01da85e96 --- /dev/null +++ b/Packages/design/components/roundedButtonGroup/roundedButtonGroup.styl @@ -0,0 +1,46 @@ +@import "{design}/app" + +$height = 25px +$borderColorActive = #3C8074 +$textColorActive = #2D2D2D + +.viewerRoundedButtonGroup + font-family: Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif + font-weight: 500 + position: relative + + .viewerRoundedButton + background: $darkUiColor + border: 1px $uiBorderColorDark solid + color: $textSecondaryColor + cursor: pointer + display: inline-block + margin-left: -1px + float: left + font-size: 11px + height: $height + line-height: $height + padding: 0 15px + text-decoration: none + text-transform: uppercase + transition: $sidebarTransition + -webkit-transition: $sidebarTransition + -ms-transition: $sidebarTransition + -o-transition: $sidebarTransition + -moz-transition: $sidebarTransition + z-index: 1 + + &.active + background: $activeColor + color: $textColorActive + border-color: $borderColorActive + z-index: 2 + + &:first-child + margin-left: 0 + border-bottom-left-radius: $height + border-top-left-radius: $height + + &:last-child + border-bottom-right-radius: $height + border-top-right-radius: $height diff --git a/Packages/design/package.js b/Packages/design/package.js new file mode 100644 index 000000000..291369121 --- /dev/null +++ b/Packages/design/package.js @@ -0,0 +1,34 @@ +Package.describe({ + name: 'design', + summary: 'OHIF Design styles and components', + version: '0.0.1' +}); + +Package.onUse(function(api) { + api.versionsFrom('1.2.1'); + + api.use('ecmascript'); + api.use('standard-app-packages'); + api.use('jquery'); + api.use('stylus'); + + // Importable colors / typography settings + api.addFiles([ + 'app.styl', + 'styles/global.styl', + 'styles/mixins.styl', + 'styles/spacings.styl', + 'styles/variables.styl', + 'styles/webfonts.styl' + ], 'client', { + isImport: true + }); + + // Rounded Button Group + api.addFiles([ + 'components/roundedButtonGroup/roundedButtonGroup.html', + 'components/roundedButtonGroup/roundedButtonGroup.styl', + 'components/roundedButtonGroup/roundedButtonGroup.js' + ], 'client'); + +}); diff --git a/Packages/design/styles/global.styl b/Packages/design/styles/global.styl new file mode 100644 index 000000000..554aefe23 --- /dev/null +++ b/Packages/design/styles/global.styl @@ -0,0 +1,10 @@ +html * + font-family: 'Roboto', sans-serif; + +hr + border-top: 1px solid $uiBorderColor; + +.center-table + display: table + margin-left: auto + margin-right: auto diff --git a/Packages/design/styles/mixins.styl b/Packages/design/styles/mixins.styl new file mode 100644 index 000000000..2ac665675 --- /dev/null +++ b/Packages/design/styles/mixins.styl @@ -0,0 +1,13 @@ +transition(t) + transition: t + -webkit-transition: t + -ms-transition: t + -o-transition: t + -moz-transition: t + +transform(t) + transform: t + -webkit-transform: t + -ms-transform: t + -o-transform: t + -moz-transform: t diff --git a/Packages/design/styles/spacings.styl b/Packages/design/styles/spacings.styl new file mode 100644 index 000000000..5511eb48c --- /dev/null +++ b/Packages/design/styles/spacings.styl @@ -0,0 +1,39 @@ +$spacer = 1rem + +$spacer-x = $spacer +$spacer-y = $spacer + +$spacerAxis = $spacer-x, + $spacer-y + +$spacings = 0 0, + 1 1, + 2 1.5, + 3 3g + +$properties = m margin, + p padding + +$sides = t top 1, + r right 0, + b bottom 1, + l left 0 + +$axes = x left right 0, + y top bottom 1 + +for $property in $properties + for $spacing in $spacings + .{$property[0]}-a-{$spacing[0]} + {$property[1]} $spacing[1]*$spacer !important + for $side in $sides + .{$property[0]}-{$side[0]}-{$spacing[0]} + {$property[1]}-{$side[1]} $spacing[1]*$spacerAxis[$side[2]] !important + for $axis in $axes + .{$property[0]}-{$axis[0]}-{$spacing[0]} + {$property[1]}-{$axis[1]} $spacing[1]*$spacerAxis[$axis[3]] !important + {$property[1]}-{$axis[2]} $spacing[1]*$spacerAxis[$axis[3]] !important + +.m-x-auto + margin-left: auto !important + margin-right: auto !important diff --git a/Packages/design/styles/variables.styl b/Packages/design/styles/variables.styl new file mode 100644 index 000000000..02e609b05 --- /dev/null +++ b/Packages/design/styles/variables.styl @@ -0,0 +1,34 @@ +// Interface UI Colors +$defaultColor = #94A8B3 +$hoverColor = #ffffff +$activeColor = #40D9B8 +$uiBorderColor = #436270 +$uiBorderColorDark = #3C5D80 +$darkUiColor = #16202B +$primaryBackgroundColor = #000000 + +// Text Colors +$textPrimaryColor = #ffffff +$textSecondaryColor = #91B9CD + +// Other Colors +$errorColor = #E29E4A +$menuBackgroundColor = #F2F2F2 + +// Sizes +$topBarHeight = 30px +$toolbarHeight = 50px + +// Thicknesses +$uiBorderThickness = 1px + +// Fonts +$logoFontFamily = "Sanchez" +$logoFontWeight = 300 + +// Transitions +$transitionDuration = 0.3s +$transitionEffect = ease +$sidebarTransition = all $transitionDuration $transitionEffect +$studiesSidebarMenuWidth = 307px +$lesionsSidebarMenuWidth = 317px diff --git a/Packages/design/styles/webfonts.styl b/Packages/design/styles/webfonts.styl new file mode 100644 index 000000000..e6e5f698e --- /dev/null +++ b/Packages/design/styles/webfonts.styl @@ -0,0 +1,2 @@ +@import url(http://fonts.googleapis.com/css?family=Roboto:400,100,100italic,300,300italic,400italic,500,500italic,700,700italic,900,900italic&subset=latin,latin-ext) +@import url(http://fonts.googleapis.com/css?family=Sanchez:400,700&subset=latin,latin-ext) diff --git a/Packages/hangingprotocols/client/components/stageSortable/stageSortable.js b/Packages/hangingprotocols/client/components/stageSortable/stageSortable.js index 0dce61294..f53d65a8c 100644 --- a/Packages/hangingprotocols/client/components/stageSortable/stageSortable.js +++ b/Packages/hangingprotocols/client/components/stageSortable/stageSortable.js @@ -24,6 +24,10 @@ Array.prototype.move = function(oldIndex, newIndex) { */ function getStageIndex(protocol, id) { var stageIndex; + if (!protocol || !protocol.stages) { + return; + } + protocol.stages.forEach(function(stage, index) { if (stage.id === id) { stageIndex = index; diff --git a/Packages/lesiontracker/assets/icons.svg b/Packages/lesiontracker/assets/icons.svg new file mode 100644 index 000000000..40b28f510 --- /dev/null +++ b/Packages/lesiontracker/assets/icons.svg @@ -0,0 +1,186 @@ + + + + HUD + + + + + + + + + + + + + Additional Measurements + + + + + + Lesions + + + + + + Image Count + + + + + + + Series Number + + + + + + Settings + + + + + + Complete + + + + + + Locked + + + + + + Studies + + + + + + Modality + + + + + + + + Window / Level + + + + + + + Link + + + + + + + Non-Target Measurement + + + + + + + + + + + + Target Measurement + + + + + + + Temporary Measurement + + + + + + + + + + More + + + + + + Pan + + + + + + + + + + + Zoom + + + + + + + Trial Information + + + + + + Expand + + + + + + Add + + + + + + Close + + + + + + Comment + + + + + + + Capture Screen + + + + + + + + + + Warning + + + + diff --git a/Packages/lesiontracker/client/components/additionalMeasurements/additionalMeasurements.html b/Packages/lesiontracker/client/components/additionalMeasurements/additionalMeasurements.html new file mode 100644 index 000000000..f0972d025 --- /dev/null +++ b/Packages/lesiontracker/client/components/additionalMeasurements/additionalMeasurements.html @@ -0,0 +1,77 @@ + \ No newline at end of file diff --git a/Packages/lesiontracker/client/components/additionalMeasurements/additionalMeasurements.js b/Packages/lesiontracker/client/components/additionalMeasurements/additionalMeasurements.js new file mode 100644 index 000000000..614c18e88 --- /dev/null +++ b/Packages/lesiontracker/client/components/additionalMeasurements/additionalMeasurements.js @@ -0,0 +1,87 @@ +var yesNoOptions = [{ + key: 'yes', + text: 'Yes' +}, { + key: 'no', + text: 'No' +}]; + +Template.additionalMeasurements.onCreated(function additionalMeasurementsOnCreated() { + const instance = this; + + instance.measurableDisease = new ReactiveVar(); + instance.numberOfBoneLesions = new ReactiveVar(); + instance.regionsOfMetastaticDisease = new ReactiveVar(); + instance.tracerRelatedToMetastaticDisease = new ReactiveVar(); + instance.acceptableImageQuality = new ReactiveVar(); + instance.adequateAnatomicalCoverage = new ReactiveVar(); + instance.presenceOfContrast = new ReactiveVar(); + instance.phaseOfContrast = new ReactiveVar(); + + instance.measurableDisease.set('absent'); + instance.tracerRelatedToMetastaticDisease.set('no'); + instance.acceptableImageQuality.set('yes'); + instance.adequateAnatomicalCoverage.set('yes'); + instance.presenceOfContrast.set('yes'); +}); + +Template.additionalMeasurements.onRendered(function additionalMeasurementsOnRendered() { + const instance = this; + + instance.$("#regionsOfMetastaticDisease").select2({ + multiple: true + }); +}); + +Template.additionalMeasurements.helpers({ + measurableDiseaseButtonData() { + const instance = Template.instance(); + return { + text: 'Measurable disease', + value: instance.measurableDisease, + options: [{ + key: 'present', + text: 'Present' + }, { + key: 'absent', + text: 'Absent' + }] + }; + }, + + metastaticDiseaseButtonData() { + const instance = Template.instance(); + return { + text: 'Tracer related to metastatic disease', + value: instance.tracerRelatedToMetastaticDisease, + options: yesNoOptions + }; + }, + + imageQualityButtonData() { + const instance = Template.instance(); + return { + text: 'Acceptable image quality', + value: instance.acceptableImageQuality, + options: yesNoOptions + }; + }, + + anatomicalCoverageButtonData() { + const instance = Template.instance(); + return { + text: 'Adequate anatomical coverage', + value: instance.adequateAnatomicalCoverage, + options: yesNoOptions + }; + }, + + presenceOfContrastButtonData() { + const instance = Template.instance(); + return { + text: 'Presence of contrast', + value: instance.presenceOfContrast, + options: yesNoOptions + }; + } +}); diff --git a/Packages/lesiontracker/client/components/additionalMeasurements/additionalMeasurements.styl b/Packages/lesiontracker/client/components/additionalMeasurements/additionalMeasurements.styl new file mode 100644 index 000000000..8e5a695a1 --- /dev/null +++ b/Packages/lesiontracker/client/components/additionalMeasurements/additionalMeasurements.styl @@ -0,0 +1,74 @@ +@import "{design}/app" + +#additionalMeasurements + width: 100% + height: 100% + background-color: #000000 + + .scrollArea + height: 100% + overflow-x: hidden + overflow-y: auto + margin-right: -16px + padding-bottom: 100px + padding-right: 16px + + -webkit-overflow-scrolling: touch + + &::-webkit-scrollbar + display: none + + .header + height: 33px + line-height: 33px + font-weight: 400 + font-size: 14px + padding: 0 10px + color: $textPrimaryColor + background-color: $darkUiColor + + .entrySection + #regionsOfMetastaticDisease + width: 285px + height: 30px + border-radius: 2px + background-color: #b6b6b6 + color: #3e3e3e + font-size: 14px + + .buttonSection + cursor: pointer + height: 100px + color: $defaultColor + + &:hover + color: $hoverColor + + .svgContainer + svg + fill: $hoverColor + + &:active + color: $activeColor + + .svgContainer + svg + fill: $activeColor + + div + float: left + display: inline-block + height: 80px + + .buttonLabel + line-height: 80px + padding: 0 30px + + .svgContainer + padding: 15px 5px + width: 40px + + svg + width: 40px + height: 40px + fill: $textPrimaryColor diff --git a/Packages/lesiontracker/client/components/additionalMeasurements/radioOptionGroup/radioOptionGroup.html b/Packages/lesiontracker/client/components/additionalMeasurements/radioOptionGroup/radioOptionGroup.html new file mode 100644 index 000000000..1cd931331 --- /dev/null +++ b/Packages/lesiontracker/client/components/additionalMeasurements/radioOptionGroup/radioOptionGroup.html @@ -0,0 +1,16 @@ + \ No newline at end of file diff --git a/Packages/lesiontracker/client/components/additionalMeasurements/radioOptionGroup/radioOptionGroup.js b/Packages/lesiontracker/client/components/additionalMeasurements/radioOptionGroup/radioOptionGroup.js new file mode 100644 index 000000000..410652dbd --- /dev/null +++ b/Packages/lesiontracker/client/components/additionalMeasurements/radioOptionGroup/radioOptionGroup.js @@ -0,0 +1,15 @@ +Template.radioOptionGroup.onRendered(() => { + const instance = Template.instance(); + const value = instance.data.value; + if (!value.get()) { + value.set(instance.data.options[0].key); + } +}); + +Template.radioOptionGroup.events({ + 'click [data-key]'(event, instance) { + const $element = $(event.currentTarget); + const key = $element.attr('data-key'); + instance.data.value.set(key); + } +}); \ No newline at end of file diff --git a/Packages/lesiontracker/client/components/additionalMeasurements/radioOptionGroup/radioOptionGroup.styl b/Packages/lesiontracker/client/components/additionalMeasurements/radioOptionGroup/radioOptionGroup.styl new file mode 100644 index 000000000..62edc98ca --- /dev/null +++ b/Packages/lesiontracker/client/components/additionalMeasurements/radioOptionGroup/radioOptionGroup.styl @@ -0,0 +1,20 @@ +@import "{design}/app" + +.entrySection + padding: 10px + color: $textPrimaryColor + + &:not(:last-child) + border-bottom: $uiBorderThickness solid $uiBorderColorDark + + h5 + font-weight: 300 + + label + font-size: 14px + font-weight: 100 + display: inline-block + padding: 5px + + input + margin: 5px diff --git a/Packages/lesiontracker/client/components/caseProgress/caseProgress.html b/Packages/lesiontracker/client/components/caseProgress/caseProgress.html new file mode 100644 index 000000000..e9d8f97e6 --- /dev/null +++ b/Packages/lesiontracker/client/components/caseProgress/caseProgress.html @@ -0,0 +1,11 @@ + \ No newline at end of file diff --git a/Packages/lesiontracker/client/components/caseProgress/caseProgress.js b/Packages/lesiontracker/client/components/caseProgress/caseProgress.js new file mode 100644 index 000000000..bea1195d8 --- /dev/null +++ b/Packages/lesiontracker/client/components/caseProgress/caseProgress.js @@ -0,0 +1,30 @@ +Template.caseProgress.onCreated(function caseProgressOnCreated() { + const instance = Template.instance(); + + instance.progressPercent = new ReactiveVar(); + instance.progressPercent.set(50); + + instance.progressText = new ReactiveVar(); + instance.progressText.set(5); + + instance.isLocked = new ReactiveVar(false); +}); + +Template.caseProgress.helpers({ + progressPercent() { + return Template.instance().progressPercent.get(); + }, + + progressText() { + return Template.instance().progressText.get(); + }, + + isLocked() { + return Template.instance().isLocked.get(); + }, + + progressComplete() { + var progressPercent = Template.instance().progressPercent.get(); + return progressPercent === 100; + } +}); diff --git a/Packages/lesiontracker/client/components/caseProgress/caseProgress.styl b/Packages/lesiontracker/client/components/caseProgress/caseProgress.styl new file mode 100644 index 000000000..668aaa8b4 --- /dev/null +++ b/Packages/lesiontracker/client/components/caseProgress/caseProgress.styl @@ -0,0 +1,3 @@ +@import "{design}/app" + +//.caseProgress diff --git a/Packages/lesiontracker/client/components/flexboxLayout/flexboxLayout.html b/Packages/lesiontracker/client/components/flexboxLayout/flexboxLayout.html new file mode 100644 index 000000000..61805a10c --- /dev/null +++ b/Packages/lesiontracker/client/components/flexboxLayout/flexboxLayout.html @@ -0,0 +1,20 @@ + diff --git a/Packages/lesiontracker/client/components/flexboxLayout/flexboxLayout.js b/Packages/lesiontracker/client/components/flexboxLayout/flexboxLayout.js new file mode 100644 index 000000000..cf5565bf5 --- /dev/null +++ b/Packages/lesiontracker/client/components/flexboxLayout/flexboxLayout.js @@ -0,0 +1,66 @@ +Template.flexboxLayout.onCreated(() => { + const instance = Template.instance(); + instance.state = instance.data.state; + instance.timepointViewType = new ReactiveVar(); +}); + +var resizeTimeout; +Template.flexboxLayout.onRendered(() => { + const instance = Template.instance(); + + instance.autorun(function() { + Meteor.clearTimeout(resizeTimeout); + + // Trigger a resize any time the layout state changes + var studySidebarOpen = instance.state.get('studySidebarOpen'); + var lesionSidebarOpen = instance.state.get('lesionSidebarOpen'); + + var additionalMeasurementsSidebarOpen = instance.state.get('additionalMeasurementsSidebarOpen'); + + resizeTimeout = Meteor.setTimeout(function() { + handleResize(); + }, 300); + }); +}); + +Template.flexboxLayout.helpers({ + timepointViewType() { + return Template.instance().timepointViewType; + }, + + buttonGroupData() { + const instance = Template.instance(); + return { + value: instance.timepointViewType, + options: [{ + key: 'key', + text: 'Key Timepoints' + }, { + key: 'all', + text: 'All Timepoints' + }] + }; + }, + + studySidebarOpen() { + const instance = Template.instance(); + return instance.state.get('studySidebarOpen'); + }, + + lesionSidebarOpen() { + const instance = Template.instance(); + return instance.state.get('lesionSidebarOpen'); + }, + + additionalMeasurementsSidebarOpen() { + const instance = Template.instance(); + return instance.state.get('additionalMeasurementsSidebarOpen'); + }, + + rightSidebarOpen() { + const instance = Template.instance(); + const lesionSidebarOpen = instance.data.state.get('lesionSidebarOpen'); + const additionalMeasurementsSidebarOpen = instance.data.state.get('additionalMeasurementsSidebarOpen'); + return lesionSidebarOpen || additionalMeasurementsSidebarOpen; + } +}); diff --git a/Packages/lesiontracker/client/components/flexboxLayout/flexboxLayout.styl b/Packages/lesiontracker/client/components/flexboxLayout/flexboxLayout.styl new file mode 100644 index 000000000..cde86b357 --- /dev/null +++ b/Packages/lesiontracker/client/components/flexboxLayout/flexboxLayout.styl @@ -0,0 +1,69 @@ +@import "{design}/app" + +.viewerSection + display: inline-flex + flex-flow: row nowrap + align-items: stretch + height: 'calc(100% - %s)' % $toolbarHeight + width: 100% + + .sidebarMenu + position: absolute + height: 100% + background: $primaryBackgroundColor + -webkit-transition: $sidebarTransition + -ms-transition: $sidebarTransition + -o-transition: $sidebarTransition + -moz-transition: $sidebarTransition + transition: $sidebarTransition + + .timepointButtonContainer + height: 55px + + .sidebar-left + width: $studiesSidebarMenuWidth + order: 1 + flex: 0 + left: - $studiesSidebarMenuWidth + border-right: $uiBorderThickness $uiBorderColor solid + + &.sidebar-open + left: 0 + + .mainContent + order: 2 + width: 100% + height: 100% + -webkit-transition: $sidebarTransition + -ms-transition: $sidebarTransition + -o-transition: $sidebarTransition + -moz-transition: $sidebarTransition + transition: $sidebarTransition + transform: translate3d(0,0,0); + + &.sidebar-left-open.sidebar-right-open + width: 'calc(100% - %s)' % ($studiesSidebarMenuWidth + $lesionsSidebarMenuWidth) + + &.sidebar-right-open + width: 'calc(100% - %s)' % $lesionsSidebarMenuWidth + + &.sidebar-left-open + width: 'calc(100% - %s)' % $studiesSidebarMenuWidth + + &.sidebar-left-open + transform: translate3d($studiesSidebarMenuWidth,0,0); + + .sidebar-right + width: $lesionsSidebarMenuWidth + order: 3 + flex: 0 + right: - $lesionsSidebarMenuWidth + border-left: $uiBorderThickness $uiBorderColor solid + + &.sidebar-open + right: 0 + + .studiesListedChanger + padding: 20px 10px + text-align: center + border-bottom: $uiBorderThickness $uiBorderColor solid diff --git a/Packages/lesiontracker/client/components/hidingPanel/hidingPanel.html b/Packages/lesiontracker/client/components/hidingPanel/hidingPanel.html index b3a3d8fe8..9839a1c7a 100644 --- a/Packages/lesiontracker/client/components/hidingPanel/hidingPanel.html +++ b/Packages/lesiontracker/client/components/hidingPanel/hidingPanel.html @@ -9,9 +9,7 @@
- {{#if studyDateIsShown}} - {{> studyDateList}} - {{/if}} + {{> studyDateList}}
{{> studyBrowser}}
diff --git a/Packages/lesiontracker/client/components/hidingPanel/hidingPanel.js b/Packages/lesiontracker/client/components/hidingPanel/hidingPanel.js index c4445a763..6689a26b6 100644 --- a/Packages/lesiontracker/client/components/hidingPanel/hidingPanel.js +++ b/Packages/lesiontracker/client/components/hidingPanel/hidingPanel.js @@ -1,6 +1,6 @@ function pinPanel() { - var hidingPanel = $(".hidingPanel"); + var hidingPanel = $('.hidingPanel'); hidingPanel.css({ width: '122px' }); @@ -12,12 +12,12 @@ function pinPanel() { // Calculate newWidth of viewportAndLesionTable var viewerWidth = $('#viewer').width(); var newWidth = 100 - 122 / viewerWidth * 100; - $("#viewportAndLesionTable").css('width', newWidth + '%'); + $('#viewportAndLesionTable').css('width', newWidth + '%'); } function unpinPanel() { -// Calculate newWidth of viewportAndLesionTable - $("#viewportAndLesionTable").css('width', '99%'); + // Calculate newWidth of viewportAndLesionTable + $('#viewportAndLesionTable').css('width', '99%'); } Template.hidingPanel.events({ @@ -90,9 +90,6 @@ Template.hidingPanel.events({ }); Template.hidingPanel.helpers({ - studyDateIsShown: function() { - return true; - }, isPinned: function() { return Template.instance().isPinned.get(); } @@ -117,7 +114,7 @@ Template.hidingPanel.onCreated(function() { if (!isPanelPinned) { isPanelPinned = false; } + this.isPinned = new ReactiveVar(isPanelPinned); - }); diff --git a/Packages/lesiontracker/client/components/lesionTable/lesionTable.html b/Packages/lesiontracker/client/components/lesionTable/lesionTable.html index 1fb5a7392..d2b5c4e7d 100644 --- a/Packages/lesiontracker/client/components/lesionTable/lesionTable.html +++ b/Packages/lesiontracker/client/components/lesionTable/lesionTable.html @@ -1,27 +1,30 @@ \ No newline at end of file diff --git a/Packages/lesiontracker/client/components/lesionTable/lesionTable.js b/Packages/lesiontracker/client/components/lesionTable/lesionTable.js index c76c0afab..8fdc0bb6d 100644 --- a/Packages/lesiontracker/client/components/lesionTable/lesionTable.js +++ b/Packages/lesiontracker/client/components/lesionTable/lesionTable.js @@ -1,9 +1,30 @@ Template.lesionTable.helpers({ - measurement: function() { + targets: function() { // All Targets shall be listed first followed by Non-Targets - return Measurements.find({}, { + return Measurements.find({ + isTarget: true + }, { + sort: { + lesionNumberAbsolute: 1 + } + }); + }, + nonTargets: function() { + // All Targets shall be listed first followed by Non-Targets + return Measurements.find({ + isTarget: false + }, { + sort: { + lesionNumberAbsolute: 1 + } + }); + }, + newLesions: function() { + // All Targets shall be listed first followed by Non-Targets + return Measurements.find({ + saved: false + }, { sort: { - isTarget: -1, lesionNumberAbsolute: 1 } }); @@ -14,59 +35,43 @@ Template.lesionTable.helpers({ latestDate: 1 } }); + }, + + buttonGroupData() { + const instance = Template.instance(); + return { + value: instance.lesionTableLayout, + options: [{ + key: 'comparison', + text: 'Comparison' + }, { + key: 'key', + text: 'Key Timepoints' + }] + }; } }); Template.lesionTable.events({ -// 'click table#tblLesion tbody tr': function(e, template) { - // Retrieve the lesion id from the DOM data for this row - // var measurementId = $(e.currentTarget).data('measurementid'); - - // activateLesion(measurementId, template.data); - //}, - 'mousedown div#dragbar': function(e, template) { - var pY = e.pageY; - var draggableParent = $(e.currentTarget).parent(); - var startHeight = draggableParent.height(); - template.dragging.set(true); - - $(document).on('mouseup', function(e) { - template.dragging.set(false); - $(document).off('mouseup').off('mousemove'); - }); - - $(document).on('mousemove', function(e) { - var topPosition = e.pageY - pY; - var newHeight = startHeight - topPosition; - - // Min lesion table height = 5px - if (newHeight < 5) { - return; - } - - draggableParent.css({ - top: topPosition, - height: newHeight - }); - - var viewportAndLesionTableHeight = $('#viewportAndLesionTable').height(); - var newPercentageHeightofLesionTable = (startHeight - topPosition) / viewportAndLesionTableHeight * 100; - var newPercentageHeightofViewermain = 100 - newPercentageHeightofLesionTable; - $('.viewerMain').height(newPercentageHeightofViewermain + '%'); - - // Resize viewport - resizeViewportElements(); - }); - } -}); - -Template.lesionTable.onCreated(function() { - this.dragging = new ReactiveVar(false); + /** + * Retrieve the lesion id from the DOM data for this row + */ + /*'click table#tblLesion tbody tr': function(e, template) { + var measurementId = $(e.currentTarget).data('measurementid'); + activateLesion(measurementId, template.data); + },*/ }); // Temporary until we have a real window manager with events for series/study changed Session.setDefault('NewSeriesLoaded', false); +Template.lesionTable.onCreated(function() { + var instance = this; + + instance.lesionTableLayout = new ReactiveVar(); + instance.lesionTableLayout.set('comparisonTimepoints'); +}); + Template.lesionTable.onRendered(function() { // Find the first measurement by Lesion Number var firstLesion = Measurements.findOne({}, { diff --git a/Packages/lesiontracker/client/components/lesionTable/lesionTable.styl b/Packages/lesiontracker/client/components/lesionTable/lesionTable.styl index d17d3df04..ff8094569 100644 --- a/Packages/lesiontracker/client/components/lesionTable/lesionTable.styl +++ b/Packages/lesiontracker/client/components/lesionTable/lesionTable.styl @@ -1,93 +1,23 @@ +@import "{design}/app" + #lesionTableContainer width: 100% height: 16% background-color: #000000 - #dragbar - width: 100% - height: 3px - top: 0 - left: 0 - right: 0 - bottom: 0 - background-color: #666666 - cursor: row-resize - #lesionTableWrapper position: relative width: 100% height: 100% margin: 0 auto - .lesionTable +.lesionTableTimepointHeaderRow + display: flex - td, th - border: 1px solid #FFFFFF - border-collapse: collapse - width: 100px + .spacer + width: 20px - td.lesionNumber, th#thNumber - width: 70px - - td.location, th#thLocation - width: 250px - - td.target, th#thTarget - width: 70px - - - thead - width: 100% - height: 40px - display: block - - tr - width: 100% - height: 40px - - th - border-bottom-width: 0 - font-weight: normal - color: #FFFFFF - line-height: 15px - text-align: center - margin: 0 - padding: 5px - background-color: #424242 - vertical-align: middle - - tbody - display: block - position: absolute - top: 40px - left: 0 - right: 0 - bottom: 0 - overflow: auto - width: 100% - padding-bottom: 20px - - tr.selectedRow - background: #F5F5F5 !important - color: #000000 !important - - tr.lesionTableRow - width: 100% - - &:nth-child(even) - background: #888 - &:nth-child(odd) - background: #BBB - &:hover - cursor: pointer - background-color: #009BD2 - color: white - - td - border-bottom-width: 0 - margin: 0 - padding: 5px - line-height: 15px - - &.lesionNumber, &.target, &.lesionTableTimepointCell - text-align: center +.lesionTableLayoutChanger + padding: 5px + text-align: center + margin: 0 auto diff --git a/Packages/lesiontracker/client/components/lesionTableHeaderRow/lesionTableHeaderRow.html b/Packages/lesiontracker/client/components/lesionTableHeaderRow/lesionTableHeaderRow.html new file mode 100644 index 000000000..935e9e7a3 --- /dev/null +++ b/Packages/lesiontracker/client/components/lesionTableHeaderRow/lesionTableHeaderRow.html @@ -0,0 +1,22 @@ + diff --git a/Packages/lesiontracker/client/components/lesionTableHeaderRow/lesionTableHeaderRow.js b/Packages/lesiontracker/client/components/lesionTableHeaderRow/lesionTableHeaderRow.js new file mode 100644 index 000000000..437f801fb --- /dev/null +++ b/Packages/lesiontracker/client/components/lesionTableHeaderRow/lesionTableHeaderRow.js @@ -0,0 +1,38 @@ +Template.lesionTableHeaderRow.helpers({ + numberOfLesions: function() { + var instance = Template.instance(); + return instance.data.measurements.count(); + }, + maxNumLesions: function() { + var instance = Template.instance(); + var lesionType = instance.id; + + // TODO: Check what we are annotating + var timepointType = 'baseline'; + + // Identify which Trial Conformance Criteria are currently being used + // Note that there may be more than one. + var criteriaTypes = TrialCriteriaTypes.find({ + selected: true + }).map(function(criteria) { + return criteria.id; + }); + + var currentConstraints = getTrialCriteriaConstraints(criteriaTypes); + if (!currentConstraints) { + // For testing + return 10; + } + + var criteria = currentConstraints[timepointType][lesionType]; + + return criteria.group.totalNumberOfLesions; + } +}); + +Template.lesionTableHeaderRow.events({ + 'click .add': function() { + console.log('Add was clicked'); + // TODO: Set active tool to new type + } +}); \ No newline at end of file diff --git a/Packages/lesiontracker/client/components/lesionTableHeaderRow/lesionTableHeaderRow.styl b/Packages/lesiontracker/client/components/lesionTableHeaderRow/lesionTableHeaderRow.styl new file mode 100644 index 000000000..266d67dd5 --- /dev/null +++ b/Packages/lesiontracker/client/components/lesionTableHeaderRow/lesionTableHeaderRow.styl @@ -0,0 +1,58 @@ +@import "{design}/app" + +$headerRowHeight = 63px + +.lesionTableHeaderRow + background: $darkUiColor + color: $textSecondaryColor + fill: $textSecondaryColor + width: 100% + height: $headerRowHeight + display: flex + line-height: $headerRowHeight + + div + flex: 1 + justify-content: space-around + align-items: stretch + text-align: center + + .add + padding: 0 10px + max-width: 20px + cursor: pointer + + svg + height: $headerRowHeight + max-width: 11px + + .type + cursor: pointer + font-weight: 200 + height: $headerRowHeight + font-size: 13pt + + .numberOfLesions + float: right + font-weight: 100 + font-size: 40px + max-width: 30px + margin-right: 5px + height: $headerRowHeight + line-height: $headerRowHeight + + .max + height: $headerRowHeight + background: $darkUiColor + + .maxNumLesions + margin: 21px auto + height: 20px + line-height: 20px + width: 47px + font-size: 12px + text-transform: uppercase + font-weight: 500 + border-radius: 3px + color: black + background-color: $textSecondaryColor; diff --git a/Packages/lesiontracker/client/components/lesionTableRow/lesionTableRow.html b/Packages/lesiontracker/client/components/lesionTableRow/lesionTableRow.html index 73bf1e1a4..bfeae9ce1 100644 --- a/Packages/lesiontracker/client/components/lesionTableRow/lesionTableRow.html +++ b/Packages/lesiontracker/client/components/lesionTableRow/lesionTableRow.html @@ -1,11 +1,38 @@ diff --git a/Packages/lesiontracker/client/components/lesionTableRow/lesionTableRow.styl b/Packages/lesiontracker/client/components/lesionTableRow/lesionTableRow.styl index c41d8d124..c736068ac 100644 --- a/Packages/lesiontracker/client/components/lesionTableRow/lesionTableRow.styl +++ b/Packages/lesiontracker/client/components/lesionTableRow/lesionTableRow.styl @@ -1,3 +1,39 @@ -.lesionTableRow .location - &:hover - font-weight: bold \ No newline at end of file +@import "{design}/app" + +.lesionTableRow + display: flex + width: 100% + + .lesionRowSidebar + flex: 1 + color: $textSecondaryColor + background: $darkUiColor + max-width: 20px + height: 60px + + .lesionNumber + padding: 3px + + &.active + border-left: 4px $activeColor solid + + .lesionDetails + flex: 1 + display: flex + flex-direction: column + align-items: stretch + + .location + flex: 1 + text-align: center + color: $textSecondaryColor + height: 20px + width: 100% + + .timepointData + display: flex + + div + flex: 1 + text-align: center + justify-content: space-around diff --git a/Packages/lesiontracker/client/components/lesionTableTimepointCell/lesionTableTimepointCell.html b/Packages/lesiontracker/client/components/lesionTableTimepointCell/lesionTableTimepointCell.html index 85887ed19..d39d2195d 100644 --- a/Packages/lesiontracker/client/components/lesionTableTimepointCell/lesionTableTimepointCell.html +++ b/Packages/lesiontracker/client/components/lesionTableTimepointCell/lesionTableTimepointCell.html @@ -1,11 +1,11 @@ \ No newline at end of file diff --git a/Packages/lesiontracker/client/components/lesionTableTimepointCell/lesionTableTimepointCell.styl b/Packages/lesiontracker/client/components/lesionTableTimepointCell/lesionTableTimepointCell.styl index d868f9bff..0457b0c3f 100644 --- a/Packages/lesiontracker/client/components/lesionTableTimepointCell/lesionTableTimepointCell.styl +++ b/Packages/lesiontracker/client/components/lesionTableTimepointCell/lesionTableTimepointCell.styl @@ -1,8 +1,6 @@ +@import "{design}/app" + .lesionTableTimepointCell - &:hover - font-weight: bold - .nonTargetCell - &:hover - color: red - - + border-left: $uiBorderThickness $uiBorderColor solid + color: $textPrimaryColor + padding: 0 10px diff --git a/Packages/lesiontracker/client/components/lesionTableTimepointHeader/lesionTableTimepointHeader.html b/Packages/lesiontracker/client/components/lesionTableTimepointHeader/lesionTableTimepointHeader.html index 25ef19639..a9f86852f 100644 --- a/Packages/lesiontracker/client/components/lesionTableTimepointHeader/lesionTableTimepointHeader.html +++ b/Packages/lesiontracker/client/components/lesionTableTimepointHeader/lesionTableTimepointHeader.html @@ -1,6 +1,10 @@ \ No newline at end of file diff --git a/Packages/lesiontracker/client/components/lesionTableTimepointHeader/lesionTableTimepointHeader.styl b/Packages/lesiontracker/client/components/lesionTableTimepointHeader/lesionTableTimepointHeader.styl index 4c555bbd0..bb6218d65 100644 --- a/Packages/lesiontracker/client/components/lesionTableTimepointHeader/lesionTableTimepointHeader.styl +++ b/Packages/lesiontracker/client/components/lesionTableTimepointHeader/lesionTableTimepointHeader.styl @@ -1,3 +1,15 @@ -#lesionTableContainer .lesionTable thead tr th.lesionTableTimepointCell.loaded - //font-weight: bold - color: #4fbfff \ No newline at end of file +@import "{design}/app" + +.lesionTableTimepointHeader + flex: 1 + justify-content: space-around + + div + text-align: center + + .timepointName + color: $textSecondaryColor + + .timepointDate + color: $textPrimaryColor + border-left: $uiBorderColor $uiBorderThickness solid diff --git a/Packages/lesiontracker/client/components/lesionTracker/lesionTracker.html b/Packages/lesiontracker/client/components/lesionTracker/lesionTracker.html new file mode 100644 index 000000000..05b460a2f --- /dev/null +++ b/Packages/lesiontracker/client/components/lesionTracker/lesionTracker.html @@ -0,0 +1,35 @@ + \ No newline at end of file diff --git a/Packages/lesiontracker/client/components/lesionTracker/lesionTracker.js b/Packages/lesiontracker/client/components/lesionTracker/lesionTracker.js new file mode 100644 index 000000000..d2153f7c8 --- /dev/null +++ b/Packages/lesiontracker/client/components/lesionTracker/lesionTracker.js @@ -0,0 +1,65 @@ +// Define the ViewerData global object +// If there is currently any Session data for this object, +// use this to repopulate the variable +ViewerData = Session.get('ViewerData') || {}; + +Template.lesionTracker.onCreated(function() { + // showViewer to go to viewer from audit + this.showWorklistMenu = new ReactiveVar(true); + // Get url and check worklist + var currentRoute = Router.current(); + if (!currentRoute || !currentRoute.route) { + return; + } + + var currentPath = currentRoute.route.path(this); + if (currentPath === '/' || currentPath === '/worklist') { + this.showWorklistMenu.set(false); + } +}); + +Template.lesionTracker.onRendered(function() { + var templateData = Template.currentData(); + if (templateData && templateData.studyInstanceUid) { + var studyInstanceUid = templateData.studyInstanceUid; + openNewTab(studyInstanceUid, studyInstanceUid); + } else { + // If there is a tab set as active in the Session, + // switch to that now. + var contentId = Session.get('activeContentId'); + + // TODO: FIx this it seems to be forcing two switches + switchToTab(contentId); + } + + Meteor.subscribe('hangingprotocols'); +}); + +Template.lesionTracker.events({ + 'click #tablist a[data-toggle="tab"]': function(e) { + // If this tab is already active, do nothing + var tabButton = $(e.currentTarget); + var tabTitle = tabButton.parents('.tabTitle'); + if (tabTitle.hasClass('active')) { + return; + } + + // Otherwise, switch to the tab + var contentId = tabButton.data('target').replace('#', ''); + switchToTab(contentId); + }, + 'click #loadStudyList': function() { + switchToTab('worklistTab'); + } +}); + +Session.set('defaultSignInMessage', 'Tumor tracking in your browser.'); + +Template.lesionTracker.helpers({ + showWorklistMenu: function() { + return Template.instance().showWorklistMenu.get(); + }, + onStudyList: function() { + return (Session.get('activeContentId') === 'worklistTab'); + } +}); diff --git a/Packages/lesiontracker/client/components/lesionTracker/lesionTracker.styl b/Packages/lesiontracker/client/components/lesionTracker/lesionTracker.styl new file mode 100644 index 000000000..36b8d374d --- /dev/null +++ b/Packages/lesiontracker/client/components/lesionTracker/lesionTracker.styl @@ -0,0 +1,47 @@ +@import "{design}/app" + +.topBar + height: $topBarHeight + color: $textPrimaryColor + background-color: $primaryBackgroundColor + + .brandSection + height: 100% + display: inline-block + + img.logoImage + height: 100% + margin: 0 5px + + .logoText + display: inline-block + font-family: $logoFontFamily + font-weight: $logoFontWeight + color: $textPrimaryColor + + .studyListLinkSection + border-left: $uiBorderThickness solid $uiBorderColor; + padding: 5px + margin: 0 10px + + a + text-decoration: none + cursor: pointer + line-height: 100% + + .userAccountSection + margin: 0 10px + +#worklistTabs + height: "calc(100% - %s)" % $topBarHeight + +// Override Viewerbase's borders +#imageViewerViewports .viewportContainer + border: none !important + outline: 0 !important // Prevent blue outline in Chrome + + &:hover + &.active + &:hover.active + border: none !important + outline: 0 !important // Prevent blue outline in Chrome diff --git a/Packages/lesiontracker/client/components/lesionTrackerLayout/lesionTrackerLayout.html b/Packages/lesiontracker/client/components/lesionTrackerLayout/lesionTrackerLayout.html index c2f96eff8..5f62f3035 100644 --- a/Packages/lesiontracker/client/components/lesionTrackerLayout/lesionTrackerLayout.html +++ b/Packages/lesiontracker/client/components/lesionTrackerLayout/lesionTrackerLayout.html @@ -1,12 +1,3 @@ \ No newline at end of file diff --git a/Packages/lesiontracker/client/components/lesionTrackerLayout/lesionTrackerLayout.js b/Packages/lesiontracker/client/components/lesionTrackerLayout/lesionTrackerLayout.js deleted file mode 100644 index 4012f453d..000000000 --- a/Packages/lesiontracker/client/components/lesionTrackerLayout/lesionTrackerLayout.js +++ /dev/null @@ -1,126 +0,0 @@ -Session.set('defaultSignInMessage', 'Tumor tracking in your browser.'); - -Template.lesionTrackerLayout.helpers({ - fullName: function() { - return Meteor.user().profile.fullName; - }, - - showWorklistMenu: function() { - return Template.instance().showWorklistMenu.get(); - }, - - currentUser: function() { - var verifyEmail = Meteor.settings && Meteor.settings.public && Meteor.settings.public.verifyEmail || false; - - if (!Meteor.user() || !Meteor.userId()) { - return false; - } - - if (!verifyEmail) { - return true; - } - - if (Meteor.user().emails[0].verified) { - return true; - } - - return false; - } -}); - -Template.lesionTrackerLayout.onCreated(function() { - // showViewer to go to viewer from audit - this.showWorklistMenu = new ReactiveVar(true); - // Get url and check worklist - var currentRoute = Router.current(); - if (!currentRoute || !currentRoute.route) { - return; - } - - var currentPath = currentRoute.route.path(this); - if (currentPath === '/' || currentPath === '/worklist') { - this.showWorklistMenu.set(false); - } - - // Show countdown dialog - var handle; - $(document).on('TriggerOpenTimeoutCountdownDialog', function(e, leftTime) { - // Show modal dialog - handle = setInterval(function() { - leftTime--; - // Set countdownDialogLeftTime session - Session.set('countdownDialogLeftTime', leftTime); - - // Show dialog - var dialog = $('#timeoutCountdownDialog'); - if (dialog.css('display') === 'none') { - dialog.css('display', 'block'); - } - }, 1000); - - }); - - $(document).on('TriggerCloseTimeoutCountdownDialog', function(e) { - var dialog = $('#timeoutCountdownDialog'); - dialog.css('display', 'none'); - if (handle) { - clearInterval(handle); - // Close the dialog - dialog.css('display', 'none'); - - // Remove reviewers info for the user - Meteor.call('removeUserFromReviewers', Meteor.userId()); - } - }); -}); - -Template.lesionTrackerLayout.onRendered(function() { - var oldUserId = undefined; - var userName; - var lastLoginModalInterval; - - Tracker.autorun(function() { - // Hook login/logout - var newUserId = Meteor.userId(); - if (oldUserId === null && newUserId) { - Session.set('showLastLoginModal', true); - userName = Meteor.user().profile.fullName; - // Log - HipaaLogger.logEvent({ - eventType: 'init', - userId: Meteor.userId(), - userName: userName - }); - } else if (newUserId === null && oldUserId) { - // Set showLastLoginModal as null - Session.set('showLastLoginModal', null); - // Destroy interval for last login modal - Meteor.clearInterval(lastLoginModalInterval); - console.log('The user logged out'); - - // Log - // TODO: eventype is not defined for logout in hipaa-audit-log - /*HipaaLogger.logEvent({ - eventType: 'logout', - userId: oldUserId, - userName: userName - });*/ - - // Remove the user from Reviewers - Meteor.call('removeUserFromReviewers', oldUserId); - } - oldUserId = Meteor.userId(); - - // Trigger last login date popup - if (Session.get('showLastLoginModal')) { - Modal.show('lastLoginModal'); - lastLoginModalInterval = Meteor.setInterval( function() { - Modal.hide('lastLoginModal'); - Session.set("showLastLoginModal", null); - }, 3000); - return true; - } - }); -}); - - diff --git a/Packages/lesiontracker/client/components/lesionTrackerLayout/lesionTrackerLayout.styl b/Packages/lesiontracker/client/components/lesionTrackerLayout/lesionTrackerLayout.styl deleted file mode 100644 index e53826299..000000000 --- a/Packages/lesiontracker/client/components/lesionTrackerLayout/lesionTrackerLayout.styl +++ /dev/null @@ -1,38 +0,0 @@ -.logoContainer - position: absolute - height: 40px - top: 0 - right: 0 - padding: 0 10px - color: white - @media only screen and (min-device-width : 375px) and (max-device-width : 667px) and (orientation : portrait) - width: 15% - - .navbar-brand - display: inline-block - padding: 0 - margin-right: 5px - height: 100% - - img - height: 30px - float: left - margin-right: 10px - margin-top: 5px - /* Media queries for iPhone6*/ - @media only screen and (min-device-width : 375px) and (max-device-width : 667px) and (orientation : portrait) - height: 20px - margin-top: 10px - margin-bottom: 10px - margin-right: 5px - - h4.name - float: left - margin: 10px 0 - font-family: "Sanchez" - color: white - @media only screen and (min-device-width : 375px) and (max-device-width : 667px) and (orientation : portrait) - font-size: 0.3em - font-size: 0.3em - width: 50% - diff --git a/Packages/lesiontracker/client/components/lesionTrackerViewportOverlay/lesionTrackerViewportOverlay.styl b/Packages/lesiontracker/client/components/lesionTrackerViewportOverlay/lesionTrackerViewportOverlay.styl index 5b317c2be..8d12debf7 100644 --- a/Packages/lesiontracker/client/components/lesionTrackerViewportOverlay/lesionTrackerViewportOverlay.styl +++ b/Packages/lesiontracker/client/components/lesionTrackerViewportOverlay/lesionTrackerViewportOverlay.styl @@ -1,3 +1,102 @@ +@import "{design}/app" + +$viewportTagPadding = 20px +$imageSliderBorderRadius = 57px +$imageSliderColor = #22374d +$imageSliderTrackColor = rgba(0,0,0,0) +$imageSliderBorder = none + +// Note that these are backwards due to the magic needed to make a cross-browser vertical slider +$imageSliderWidth = 12px +$imageSliderHeight = 39px +$imageSliderCursor = grab + .imageViewerViewportOverlay - .timepointName - color: #32BFFF \ No newline at end of file + color: $textSecondaryColor + + .dicomTag + font-weight: 200 + + .topleft + top: $viewportTagPadding + left: $viewportTagPadding + + .topright + top: $viewportTagPadding + right: $viewportTagPadding + text-align: right + + .bottomleft + bottom: $viewportTagPadding + left: $viewportTagPadding + + .bottomright + bottom: $viewportTagPadding + right: $viewportTagPadding + text-align: right + + .imageControls + #scrollbar + #imageSlider + -webkit-appearance: none + background-color: $imageSliderTrackColor + + // --- Style the range track --- // + &::-webkit-slider-runnable-track + height: 5px + border: none + cursor: pointer + background-color: $imageSliderTrackColor + z-index: 6 + + &::-moz-range-track + height: 2px + border: none + cursor: pointer + background-color: $imageSliderTrackColor + z-index: 6 + + &::-ms-track + width: 100% + height: 2px + border: none + cursor: pointer + animate: 0.2s + background: transparent + border-width: 15px 0 + color: $imageSliderTrackColor + + // Hide any fill IE tries to add + &::-ms-fill-lower + background: $imageSliderTrackColor + + &::-ms-fill-upper + background: $imageSliderTrackColor + + // --- Style the range thumb --- // + &::-webkit-slider-thumb + -webkit-appearance: none !important + background-color: $imageSliderColor + border-radius: $imageSliderBorderRadius + border: $imageSliderBorder + height: $imageSliderWidth + width: $imageSliderHeight + cursor: $imageSliderCursor + margin-top: -7px + + &::-moz-range-thumb + background-color: $imageSliderColor + border-radius: $imageSliderBorderRadius + border: $imageSliderBorder + height: $imageSliderWidth + width: $imageSliderHeight + cursor: $imageSliderCursor + z-index: 7 + + &::-ms-thumb + background-color: $imageSliderColor + border-radius: $imageSliderBorderRadius + border: $imageSliderBorder + height: $imageSliderWidth + width: $imageSliderHeight + cursor: $imageSliderCursor diff --git a/Packages/lesiontracker/client/components/lesionTrackerWorklistStudy/lesionTrackerWorklistStudy.js b/Packages/lesiontracker/client/components/lesionTrackerWorklistStudy/lesionTrackerWorklistStudy.js index dbaccfe2e..70d9e848d 100644 --- a/Packages/lesiontracker/client/components/lesionTrackerWorklistStudy/lesionTrackerWorklistStudy.js +++ b/Packages/lesiontracker/client/components/lesionTrackerWorklistStudy/lesionTrackerWorklistStudy.js @@ -45,7 +45,10 @@ Template[defaultTemplate].helpers({ return; } - var timepointReviewers = Reviewers.findOne({timepointId: timepoint.timepointId}); + var timepointReviewers = Reviewers.findOne({ + timepointId: timepoint.timepointId + }); + if (!timepointReviewers) { return; } @@ -59,7 +62,7 @@ function getReviewerTipText(reviewers) { return; } - var newReviewers = reviewers.filter(function ( reviewer ) { + var newReviewers = reviewers.filter(function(reviewer) { return reviewer.userId !== Meteor.userId(); }); @@ -76,6 +79,7 @@ function getReviewerTipText(reviewers) { if (index > 0) { tipText += ','; } + tipText += reviewer.userName; }); @@ -96,4 +100,4 @@ WorklistTabs.find().observe({ }); } } -}); \ No newline at end of file +}); diff --git a/Packages/lesiontracker/client/components/radialProgressBar/radialProgressBar.html b/Packages/lesiontracker/client/components/radialProgressBar/radialProgressBar.html new file mode 100644 index 000000000..e9d854c6d --- /dev/null +++ b/Packages/lesiontracker/client/components/radialProgressBar/radialProgressBar.html @@ -0,0 +1,43 @@ + \ No newline at end of file diff --git a/Packages/lesiontracker/client/components/radialProgressBar/radialProgressBar.js b/Packages/lesiontracker/client/components/radialProgressBar/radialProgressBar.js new file mode 100644 index 000000000..4f7e97a99 --- /dev/null +++ b/Packages/lesiontracker/client/components/radialProgressBar/radialProgressBar.js @@ -0,0 +1,13 @@ +Template.radialProgressBar.helpers({ + progressComplete() { + const instance = Template.instance(); + return instance.data.progressPercent === 100; + }, + + progressRadius() { + var radius = 13 * 2 * Math.PI; + const instance = Template.instance(); + var percentLeft = (100 - instance.data.progressPercent) / 100; + return percentLeft * radius; + } +}); diff --git a/Packages/lesiontracker/client/components/radialProgressBar/radialProgressBar.styl b/Packages/lesiontracker/client/components/radialProgressBar/radialProgressBar.styl new file mode 100644 index 000000000..caacf3d54 --- /dev/null +++ b/Packages/lesiontracker/client/components/radialProgressBar/radialProgressBar.styl @@ -0,0 +1,41 @@ +@import "{design}/app" + +$circleSize = 46px +$progressTextColor = #6fbde2 + +.radialProgress + height: $circleSize + width: $circleSize + box-shadow: 0 0 1em black + border-radius: 100% + position: relative + + #svg circle + stroke-dashoffset: 0 + transition: stroke-dashoffset 1s linear + stroke: #203244 + stroke-width: 3px + + #svg #bar + stroke: $activeColor + -ms-transform: rotate(270deg); /* IE 9 */ + -ms-transform-origin: center center; /* IE 9 */ + -webkit-transform: rotate(270deg); /* Chrome, Safari, Opera */ + -webkit-transform-origin: center center; /* Chrome, Safari, Opera */ + transform: rotate(270deg); + transform-origin: center center; + + .progressText + position: absolute; + width: $circleSize; + height: $circleSize; + line-height: $circleSize; + font-family: Roboto-Bold; + font-size: 17px; + text-align: center; + color: $progressTextColor; + top: 0; + + svg + width: 17px + height: 17px diff --git a/Packages/lesiontracker/client/components/serverInformationModal/serverInformationModal.js b/Packages/lesiontracker/client/components/serverInformationModal/serverInformationModal.js index beb449252..50c95b4af 100644 --- a/Packages/lesiontracker/client/components/serverInformationModal/serverInformationModal.js +++ b/Packages/lesiontracker/client/components/serverInformationModal/serverInformationModal.js @@ -1,4 +1,3 @@ - function parseUrl(url) { var parser = document.createElement('a'); parser.href = url; @@ -11,14 +10,19 @@ Template.serverInformationModal.helpers({ var serviceInfo = Meteor.settings[defaultServiceType]; if (defaultServiceType === 'dicomWeb') { var serverInformationDicom = []; - var endpoints = serviceInfo["endpoints"]; + var endpoints = serviceInfo['endpoints']; endpoints.forEach(function(endpoint) { var parsedUrl = parseUrl(endpoint.qidoRoot); - serverInformationDicom.push({host: parsedUrl.hostname, port: parsedUrl.port, aeTitle: endpoint.name}); + serverInformationDicom.push({ + host: parsedUrl.hostname, + port: parsedUrl.port, + aeTitle: endpoint.name + }); }); return serverInformationDicom; } + return serviceInfo; } -}); \ No newline at end of file +}); diff --git a/Packages/lesiontracker/client/components/studySeriesQuickSwitch/studySeriesQuickSwitch.html b/Packages/lesiontracker/client/components/studySeriesQuickSwitch/studySeriesQuickSwitch.html new file mode 100644 index 000000000..80f566532 --- /dev/null +++ b/Packages/lesiontracker/client/components/studySeriesQuickSwitch/studySeriesQuickSwitch.html @@ -0,0 +1,29 @@ + diff --git a/Packages/lesiontracker/client/components/studySeriesQuickSwitch/studySeriesQuickSwitch.js b/Packages/lesiontracker/client/components/studySeriesQuickSwitch/studySeriesQuickSwitch.js new file mode 100644 index 000000000..99a00673f --- /dev/null +++ b/Packages/lesiontracker/client/components/studySeriesQuickSwitch/studySeriesQuickSwitch.js @@ -0,0 +1,34 @@ +Template.studySeriesQuickSwitch.onCreated(function studySeriesQuickSwitchOnCreated() { + const instance = this; + + instance.seriesOpen = new ReactiveVar(false); + instance.studiesOpen = new ReactiveVar(false); +}); + +Template.studySeriesQuickSwitch.events({ + 'mouseenter .js-show-series'(event, instance) { + instance.seriesOpen.set(true); + }, + + 'mouseenter .js-show-studies'(event, instance) { + instance.studiesOpen.set(true); + }, + + 'mouseleave .js-hide-series'(event, instance) { + instance.seriesOpen.set(false); + }, + + 'mouseleave .js-hide-studies'(event, instance) { + instance.studiesOpen.set(false); + } +}); + +Template.studySeriesQuickSwitch.helpers({ + seriesOpen() { + return Template.instance().seriesOpen.get(); + }, + + studiesOpen() { + return Template.instance().studiesOpen.get(); + } +}); \ No newline at end of file diff --git a/Packages/lesiontracker/client/components/studySeriesQuickSwitch/studySeriesQuickSwitch.styl b/Packages/lesiontracker/client/components/studySeriesQuickSwitch/studySeriesQuickSwitch.styl new file mode 100644 index 000000000..a85a9eb8e --- /dev/null +++ b/Packages/lesiontracker/client/components/studySeriesQuickSwitch/studySeriesQuickSwitch.styl @@ -0,0 +1,68 @@ +@import "{design}/app" + +$switchSize = 50px + +.quickSwitch + position: absolute + width: 140px + height: 80px + top: 5px + + div.switchSection + float: left + display: inline-block + margin: 0 5px + + .label + display: block + text-transform: uppercase + text-align: center + font-size: 12px + font-weight: 100 + color: $textSecondaryColor + + .studySwitch + display: block + margin: 0 auto + width: $switchSize + height: $switchSize + border-radius: 11px + background-color: $darkUiColor + border: solid 1px $activeColor + + .seriesSwitch + display: block + width: $switchSize + height: $switchSize + + svg + width: $switchSize + height: $switchSize + +.mirror-x + transform(scaleX(-1)); + +.studyHover + position: absolute + z-index: 10000 + top: 70px + left: -350px + width: 400px + height: 500px + background: red + transition(all 0.3s ease) + +.seriesHover + position: absolute + z-index: 10000 + top: 70px + left: 50px + width: 700px + height: 900px + background: blue + transition(all 0.3s ease) + + &.tiny + width: 0 + height: 0 + opacity: 0 diff --git a/Packages/lesiontracker/client/components/studyTimepointBrowser/studyTimepoint.html b/Packages/lesiontracker/client/components/studyTimepointBrowser/studyTimepoint.html new file mode 100644 index 000000000..aa5a777e7 --- /dev/null +++ b/Packages/lesiontracker/client/components/studyTimepointBrowser/studyTimepoint.html @@ -0,0 +1,9 @@ + diff --git a/Packages/lesiontracker/client/components/studyTimepointBrowser/studyTimepoint.js b/Packages/lesiontracker/client/components/studyTimepointBrowser/studyTimepoint.js new file mode 100644 index 000000000..ab8dcb386 --- /dev/null +++ b/Packages/lesiontracker/client/components/studyTimepointBrowser/studyTimepoint.js @@ -0,0 +1,58 @@ +// Initialize the timepoint wrapper max-height to enable CSS transition +Template.studyTimepoint.onRendered(() => { + const instance = Template.instance(); + + const $studies = instance.$('.studyTimepoint'); + const $wrapper = $studies.closest('.studyTimepointWrapper'); + const $timepoint = $wrapper.closest('.timepointEntry'); + const studiesVisible = $studies.is(':visible'); + + if (!studiesVisible) { + $timepoint.addClass('active'); + } + + // Recalculates the timepoint height to make CSS transition smoother + $studies.trigger('displayStateChanged'); + + if (!studiesVisible) { + $timepoint.removeClass('active'); + } +}); + +Template.studyTimepoint.events({ + // Changes the selected study + 'selectionChanged .studyTimepoint'(event, instance, selection) { + const $selection = $(selection); + const $thumbnails = $selection.find('.studyTimepointThumbnails'); + const $timepoint = instance.$('.studyTimepoint'); + + // Set the max-height to inherit to be able to expand the wrapper on its full height + instance.$('.studyTimepointWrapper').css('max-height', 'inherit'); + + // Removes selected state from all studies but the triggered study + instance.$('.studyTimepointStudy').not(selection).removeClass('active'); + + // Toggle selected state for the triggered study + $selection.toggleClass('active'); + + // Recalculates the timepoint height to make CSS transition smoother + $thumbnails.one('transitionend', () => $timepoint.trigger('displayStateChanged')); + }, + // It should be triggered when the timepoint height is changed + 'displayStateChanged .studyTimepoint'(event, instance) { + const $timepoint = $(event.currentTarget); + const $wrapper = $timepoint.closest('.studyTimepointWrapper'); + + // Set the max-height for the wrapper to make CSS transition smoother + $wrapper.css('max-height', $timepoint.height()); + } +}); + +Template.studyTimepoint.helpers({ + studies: function(timepoint) { + const query = { + selected: true + }; + return ViewerStudies.find(query); + } +}); diff --git a/Packages/lesiontracker/client/components/studyTimepointBrowser/studyTimepoint.styl b/Packages/lesiontracker/client/components/studyTimepointBrowser/studyTimepoint.styl new file mode 100644 index 000000000..86d535520 --- /dev/null +++ b/Packages/lesiontracker/client/components/studyTimepointBrowser/studyTimepoint.styl @@ -0,0 +1,8 @@ +@import "{design}/app" + +.studyTimepointWrapper + overflow: hidden + transition($sidebarTransition) + +.timepointEntry:not(.active) .studyTimepointWrapper + max-height: 0 !important diff --git a/Packages/lesiontracker/client/components/studyTimepointBrowser/studyTimepointBrowser.html b/Packages/lesiontracker/client/components/studyTimepointBrowser/studyTimepointBrowser.html new file mode 100644 index 000000000..c74a91c2d --- /dev/null +++ b/Packages/lesiontracker/client/components/studyTimepointBrowser/studyTimepointBrowser.html @@ -0,0 +1,29 @@ + diff --git a/Packages/lesiontracker/client/components/studyTimepointBrowser/studyTimepointBrowser.js b/Packages/lesiontracker/client/components/studyTimepointBrowser/studyTimepointBrowser.js new file mode 100644 index 000000000..7e835f98d --- /dev/null +++ b/Packages/lesiontracker/client/components/studyTimepointBrowser/studyTimepointBrowser.js @@ -0,0 +1,46 @@ +Template.studyTimepointBrowser.events({ + 'click .timepointHeader'(event, instance) { + const $timepoint = $(event.currentTarget).closest('.timepointEntry'); + + // Recalculates the timepoint height to make CSS transition smoother + $timepoint.find('.studyTimepoint').trigger('displayStateChanged'); + + // Toggle active class to group/ungroup timepoint studies + $timepoint.toggleClass('active'); + } +}); + +Template.studyTimepointBrowser.helpers({ + timepoints: function() { + const sort = { + sort: { + earliestDate: -1 + } + }; + return Timepoints.find({}, sort); + }, + shouldShowTimepoint(timepoint, index) { + const instance = Template.instance(); + if (instance.data.timepointViewType.get() === 'all') { + return true; + } + + return index < 4 || timepoint.timepointType === 'baseline'; + }, + // Build the timepoint title based on its date + timepointTitle(timepoint, total, index) { + if (timepoint.timepointType === 'baseline') { + return 'Baseline'; + } + + const states = { + 0: '(Current)', + 1: '(Prior)', + 2: '(Nadir)' + }; + // TODO: [design] find out how to define the nadir timepoint + const followUp = total - index - 1; + const parenthesis = states[index] || ''; + return `Follow-up ${followUp} ${parenthesis}`; + } +}); diff --git a/Packages/lesiontracker/client/components/studyTimepointBrowser/studyTimepointBrowser.styl b/Packages/lesiontracker/client/components/studyTimepointBrowser/studyTimepointBrowser.styl new file mode 100644 index 000000000..479264f9b --- /dev/null +++ b/Packages/lesiontracker/client/components/studyTimepointBrowser/studyTimepointBrowser.styl @@ -0,0 +1,68 @@ +@import "{design}/app" + +.studyTimepointBrowser + background-color: $primaryBackgroundColor + float: left + height: calc(100% - 55px - 50px - 30px) + overflow: hidden + width: 100% + + .timepointEntry + &.active + .timepointModalities + height: 0 + .timepointDate + display: none + .expandIcon i + transform: rotateX(180deg) + .timepointFollowupTitle + font-size: 14px + color: $textSecondaryColor + text-transform: uppercase + + .timepointHeader + cursor: pointer + font-size: 13px + + & + .expandIcon i + .timepointFollowupTitle + .timepointDate + .timepointModalities + color: $textSecondaryColor + transition($sidebarTransition) + + &:hover + & + .timepointFollowupTitle + .expandIcon i + .timepointDate + .timepointModalities + color: $textPrimaryColor !important + + .timepointDetails + .timepointModalities + line-height: 25px + + .timepointModalities + height: 25px + overflow: hidden + + .expandIcon i + color: $defaultColor + + .timepointFollowupTitle + color: $textPrimaryColor + font-family: Roboto + padding-top: 2px + + .studyTimepointScrollArea + height: 100% + overflow-x: hidden + overflow-y: auto + margin-right: -16px + padding-bottom: 20px + padding-right: 16px + + &::-webkit-scrollbar + display: none diff --git a/Packages/lesiontracker/client/components/studyTimepointBrowser/studyTimepointStudy.html b/Packages/lesiontracker/client/components/studyTimepointBrowser/studyTimepointStudy.html new file mode 100644 index 000000000..12edb7158 --- /dev/null +++ b/Packages/lesiontracker/client/components/studyTimepointBrowser/studyTimepointStudy.html @@ -0,0 +1,18 @@ + diff --git a/Packages/lesiontracker/client/components/studyTimepointBrowser/studyTimepointStudy.js b/Packages/lesiontracker/client/components/studyTimepointBrowser/studyTimepointStudy.js new file mode 100644 index 000000000..3de9606a1 --- /dev/null +++ b/Packages/lesiontracker/client/components/studyTimepointBrowser/studyTimepointStudy.js @@ -0,0 +1,40 @@ +// Initialize the study wrapper max-height to enable CSS transition +Template.studyTimepointStudy.onRendered(() => { + const instance = Template.instance(); + const $study = instance.$('.studyTimepointStudy'); + const $thumbnails = instance.$('.studyTimepointThumbnails'); + $study.addClass('active'); + $thumbnails.css('max-height', $thumbnails.height()); + $study.removeClass('active'); +}); + +Template.studyTimepointStudy.events({ + // Recalculates the timepoint height to make CSS transition smoother + 'transitionend .studyTimepointThumbnails'(event, instance) { + if (event.target === event.currentTarget) { + $(event.currentTarget).closest('.studyTimepoint').trigger('displayStateChanged'); + } + }, + // Changes the current study selection for the clicked study + 'click .studyModality'(event, instance) { + const $study = $(event.currentTarget).closest('.studyTimepointStudy'); + const $timepoint = $study.closest('.studyTimepoint'); + const study = $study[0]; + $timepoint.trigger('selectionChanged', [study]); + } +}); + +Template.studyTimepointStudy.helpers({ + thumbnails: function(study) { + var stacks = createStacks(study); + + var array = []; + stacks.forEach(function(stack, index) { + array.push({ + thumbnailIndex: index, + stack: stack + }); + }); + return array; + } +}); diff --git a/Packages/lesiontracker/client/components/studyTimepointBrowser/studyTimepointStudy.styl b/Packages/lesiontracker/client/components/studyTimepointBrowser/studyTimepointStudy.styl new file mode 100644 index 000000000..996151426 --- /dev/null +++ b/Packages/lesiontracker/client/components/studyTimepointBrowser/studyTimepointStudy.styl @@ -0,0 +1,99 @@ +@import "{design}/app" + +$boxBackgroundColor = #3E5975 +$boxBorderColor = transparent +$boxHoverBackgroundColor = #14191E +$boxHoverBorderColor = #2d4660 +$boxActiveBackgroundColor = $activeColor +$boxActiveTextColor = $primaryBackgroundColor +$boxActiveBorderColor = #131D29 +$boxWidth = 50px +$borderThickness = 2px +$boxBorderRadius = 8px +$nestingMargin = 6px +$spacerX = 7px +$spacerY = 12px + +.studyTimepointStudy + + &.active + .studyModality + box-shadow: inset 0 0 0 3px $activeColor + .studyModalityBox + color: $boxActiveTextColor + & + &:before + &:after + background-color: $boxActiveBackgroundColor + border-color: $boxActiveBorderColor + + &:not(.active) .studyTimepointThumbnails + max-height: 0 !important + + .studyTimepointThumbnails + overflow: hidden + transition($sidebarTransition) + + .studyModality + border: 1px solid $boxBorderColor + border-radius: 12px + cursor: pointer + font-family: Roboto + padding: $spacerY $spacerX ($spacerY - 1) + position: relative + transition($sidebarTransition) + z-index: 1 + + &:hover + background-color: $boxHoverBackgroundColor + border-color: $boxHoverBorderColor + + .studyModalityText + font-size: 13px + left: ($spacerX * 3) + $boxWidth + ($nestingMargin * 3) + line-height: 14px + position: absolute + right: $spacerX + top: $spacerY + .studyModalityDate + margin-top: 8px + color: $textSecondaryColor + .studyModalityDescription + margin-top: 8px + color: $textPrimaryColor + + .studyModalityBox + color: $textSecondaryColor + font-size: 22px + line-height: $boxWidth + margin-left: $nestingMargin * 2 + margin-top: $nestingMargin * 2 + position: relative + text-align: center + text-transform: uppercase + + & + &:before + &:after + background-color: $boxBackgroundColor + border: $borderThickness solid $primaryBackgroundColor + border-radius: $boxBorderRadius + height: $boxWidth + ($borderThickness * 2) + transition($sidebarTransition) + width: $boxWidth + ($borderThickness * 2) + + &:before + &:after + display: block + content: '' + position: absolute + + &:before + top: -($borderThickness + $nestingMargin) + left: -($borderThickness + $nestingMargin) + z-index: -1 + + &:after + top: -($borderThickness + ($nestingMargin * 2)) + left: -($borderThickness + ($nestingMargin * 2)) + z-index: -2 diff --git a/Packages/lesiontracker/client/components/timeoutCountdownDialog/timeoutCountdownDialog.html b/Packages/lesiontracker/client/components/timeoutCountdownDialog/timeoutCountdownDialog.html index 6aefad20a..2f4a15dab 100644 --- a/Packages/lesiontracker/client/components/timeoutCountdownDialog/timeoutCountdownDialog.html +++ b/Packages/lesiontracker/client/components/timeoutCountdownDialog/timeoutCountdownDialog.html @@ -1,10 +1,10 @@ \ No newline at end of file diff --git a/Packages/lesiontracker/client/components/timeoutCountdownDialog/timeoutCountdownDialog.js b/Packages/lesiontracker/client/components/timeoutCountdownDialog/timeoutCountdownDialog.js index 7ef21124d..a6bbbbc47 100644 --- a/Packages/lesiontracker/client/components/timeoutCountdownDialog/timeoutCountdownDialog.js +++ b/Packages/lesiontracker/client/components/timeoutCountdownDialog/timeoutCountdownDialog.js @@ -1,9 +1,39 @@ - Template.timeoutCountdownDialog.helpers({ - leftTime: function(e) { - return Session.get("countdownDialogLeftTime"); - }, - secondsText: function() { - return Session.get("countdownDialogLeftTime") > 1? "seconds": "second"; + timeLeft: function() { + var timeLeft = Session.get('countdownDialogLeftTime'); + var suffix = timeLeft > 1 ? 'seconds': 'second'; + return timeLeft + suffix; } -}); \ No newline at end of file +}); + +Template.timeoutCountdownDialog.onRendered(function() { + // Show countdown dialog + var timeoutCountdownInterval; + $(document).on('TriggerOpenTimeoutCountdownDialog', function(e, leftTime) { + // Show modal dialog + timeoutCountdownInterval = setInterval(function() { + // Set countdownDialogLeftTime session + Session.set('countdownDialogLeftTime', --leftTime); + + var dialog = $('#timeoutCountdownDialog'); + + // Show dialog + dialog.css('display', 'block'); + }, 1000); + + }); + + $(document).on('TriggerCloseTimeoutCountdownDialog', function() { + var dialog = $('#timeoutCountdownDialog'); + + // Close the dialog + dialog.css('display', 'none'); + + if (timeoutCountdownInterval) { + clearInterval(timeoutCountdownInterval); + + // Remove reviewer info for the user + Meteor.call('removeUserFromReviewers', Meteor.userId()); + } + }); +}); diff --git a/Packages/lesiontracker/client/components/toolbarSection/toolbarSection.html b/Packages/lesiontracker/client/components/toolbarSection/toolbarSection.html new file mode 100644 index 000000000..244aa320b --- /dev/null +++ b/Packages/lesiontracker/client/components/toolbarSection/toolbarSection.html @@ -0,0 +1,79 @@ + diff --git a/Packages/lesiontracker/client/components/toolbarSection/toolbarSection.js b/Packages/lesiontracker/client/components/toolbarSection/toolbarSection.js new file mode 100644 index 000000000..fbd1baa12 --- /dev/null +++ b/Packages/lesiontracker/client/components/toolbarSection/toolbarSection.js @@ -0,0 +1,133 @@ +Template.toolbarSection.onRendered(function() { + var instance = this; + + instance.state = instance.data.state; +}); + +Template.toolbarSection.helpers({ + toolbarButtons: function() { + var buttonData = []; + buttonData.push({ + id: 'zoom', + title: 'Zoom', + classes: 'imageViewerTool', + svgLink: '/packages/lesiontracker/assets/icons.svg#icon-tools-zoom' + }); + + buttonData.push({ + id: 'wwwc', + title: 'Levels', + classes: 'imageViewerTool', + svgLink: '/packages/lesiontracker/assets/icons.svg#icon-tools-levels' + }); + + buttonData.push({ + id: 'pan', + title: 'Pan', + classes: 'imageViewerTool', + svgLink: '/packages/lesiontracker/assets/icons.svg#icon-tools-pan' + }); + + buttonData.push({ + id: 'link', + title: 'Link', + classes: 'imageViewerCommand toolbarSectionButton', + svgLink: '/packages/lesiontracker/assets/icons.svg#icon-tools-link' + }); + + buttonData.push({ + id: 'bidirectional', + title: 'Target', + classes: 'imageViewerTool', + svgLink: '/packages/lesiontracker/assets/icons.svg#icon-tools-measure-target' + }); + + buttonData.push({ + id: 'nonTarget', + title: 'Non-Target', + classes: 'imageViewerTool toolbarSectionButton', + svgLink: '/packages/lesiontracker/assets/icons.svg#icon-tools-measure-non-target' + }); + + buttonData.push({ + id: 'length', + title: 'Temp', + classes: 'imageViewerTool toolbarSectionButton', + svgLink: '/packages/lesiontracker/assets/icons.svg#icon-tools-measure-temp' + }); + + return buttonData; + } +}); + +Template.toolbarSection.events({ + 'click #studySidebarToggle': function(event, instance) { + var isOpen = instance.data.state.get('studySidebarOpen'); + instance.data.state.set('studySidebarOpen', !isOpen); + }, + 'click #lesionSidebarToggle': function(event, instance) { + var isOpen = instance.data.state.get('lesionSidebarOpen'); + instance.data.state.set('lesionSidebarOpen', !isOpen); + instance.data.state.set('additionalMeasurementsSidebarOpen', false); + }, + 'click #additionalMeasurementsSidebarToggle': function(event, instance) { + var isOpen = instance.data.state.get('additionalMeasurementsSidebarOpen'); + instance.data.state.set('additionalMeasurementsSidebarOpen', !isOpen); + instance.data.state.set('lesionSidebarOpen', false); + }, + // TODO: Inherit these from toolbar template somehow + 'click .imageViewerTool': function(e) { + $(e.currentTarget).tooltip('hide'); + + var tool = e.currentTarget.id; + + var elements = $('.imageViewerViewport'); + + var activeTool = toolManager.getActiveTool(); + $('.toolbarSectionButton').removeClass('active'); + if (tool === activeTool) { + var defaultTool = toolManager.getDefaultTool(); + console.log('Setting active tool to: ' + defaultTool); + toolManager.setActiveTool(defaultTool, elements); + } else { + console.log('Setting active tool to: ' + tool); + toolManager.setActiveTool(tool, elements); + } + }, + 'click .imageViewerCommand': function(e) { + $(e.currentTarget).tooltip('hide'); + + var command = e.currentTarget.id; + if (!OHIF.viewer.functionList.hasOwnProperty(command)) { + return; + } + + var activeViewport = Session.get('activeViewport'); + var element = $('.imageViewerViewport').get(activeViewport); + OHIF.viewer.functionList[command](element); + } +}); + +Template.toolbarSection.onRendered(function() { + var tooltipButtons = $('[data-toggle="tooltip"]'); + tooltipButtons.tooltip(OHIF.viewer.tooltipConfig); + + // Enable tooltips for the layout button + var extraTooltipButtons = $('[rel="tooltip"]'); + extraTooltipButtons.tooltip(OHIF.viewer.tooltipConfig); + + // Set disabled/enabled tool buttons that are set in toolManager + var states = toolManager.getToolDefaultStates(); + var disabledToolButtons = states.disabledToolButtons; + var allToolbarButtons = $('#toolbar').find('button'); + if (disabledToolButtons && disabledToolButtons.length > 0) { + for (var i = 0; i < allToolbarButtons.length; i++) { + var toolbarButton = allToolbarButtons[i]; + $(toolbarButton).prop('disabled', false); + var index = disabledToolButtons.indexOf($(toolbarButton).attr('id')); + if (index !== -1) { + $(toolbarButton).prop('disabled', true); + } + } + } +}); diff --git a/Packages/lesiontracker/client/components/toolbarSection/toolbarSection.styl b/Packages/lesiontracker/client/components/toolbarSection/toolbarSection.styl new file mode 100644 index 000000000..0eeeaf7d0 --- /dev/null +++ b/Packages/lesiontracker/client/components/toolbarSection/toolbarSection.styl @@ -0,0 +1,151 @@ +@import "{design}/app" + +.toolbarSection + height: $toolbarHeight + width: 100% + border-bottom: $uiBorderColor $uiBorderThickness solid + + .capsule-group + display: inline-block + + div.capsule-entry + display: inline-block + color: $defaultColor + fill: $defaultColor + stroke: $defaultColor + height: $toolbarHeight + min-width: 30px + cursor: pointer + text-align: center + margin: 0 + + div.capsule + width: 66px + height: 26px + + &:first-child + .capsule + border-radius: 57px + background-color: #16202b + border: solid 1px #3c5d80 + border-right: none + border-top-right-radius: 0 + border-bottom-right-radius: 0 + + &:last-child + .capsule + border-radius: 57px + background-color: #16202b + border: solid 1px #3c5d80 + border-left: none + border-top-left-radius: 0 + border-bottom-left-radius: 0 + + &.disabled + opacity: 0.5 + cursor: not-allowed + + .svgContainer + margin: 0 auto + text-align: center + + .capsule + width: 62px + height: 26px + border-radius: 100px + background-color: #16202b + border: solid 1px #3c5d80 + + svg + background-color: transparent + margin: 5px + width: 17px + height: 17px + + &:active, &.active + color: $activeColor + + svg + fill: $activeColor + stroke: $activeColor + + &:hover + color: $hoverColor + + svg + fill: $hoverColor + stroke: $hoverColor + + + .toolbarSectionButton + display: inline-block + color: $defaultColor + fill: $defaultColor + stroke: $defaultColor + padding: 0 10px + height: $toolbarHeight + min-width: 30px + cursor: pointer + text-align: center + + &.disabled + opacity: 0.5 + cursor: not-allowed + + .buttonLabel + font-size: 12px + + .svgContainer + margin: 0 auto + text-align: center + + .capsule + width: 62px + height: 26px + border-radius: 100px + background-color: #16202b + border: solid 1px #3c5d80 + + svg + background-color: transparent + margin: 2px + width: 21px + height: 21px + + &:active, &.active + color: $activeColor + + svg + fill: $activeColor + stroke: $activeColor + + &:hover + color: $hoverColor + + svg + fill: $hoverColor + stroke: $hoverColor + + .toolbarSectionEntry + display: inline-block + color: $defaultColor + fill: $defaultColor + stroke: $defaultColor + padding: 0 10px + height: $toolbarHeight + min-width: 30px + cursor: pointer + text-align: center + + .quickSwitch + position: absolute + + &.middle + left: calc(50% - 60px) + + &.left + left: calc(50% - 160px) + + &.right + left: 50% + margin-left: 20px diff --git a/Packages/lesiontracker/client/components/toolbarSectionButton/toolbarSectionButton.html b/Packages/lesiontracker/client/components/toolbarSectionButton/toolbarSectionButton.html new file mode 100644 index 000000000..f4640c3a4 --- /dev/null +++ b/Packages/lesiontracker/client/components/toolbarSectionButton/toolbarSectionButton.html @@ -0,0 +1,12 @@ + \ No newline at end of file diff --git a/Packages/lesiontracker/client/components/toolbarSectionButton/toolbarSectionButton.js b/Packages/lesiontracker/client/components/toolbarSectionButton/toolbarSectionButton.js new file mode 100644 index 000000000..e69de29bb diff --git a/Packages/lesiontracker/client/components/toolbarSectionButton/toolbarSectionButton.styl b/Packages/lesiontracker/client/components/toolbarSectionButton/toolbarSectionButton.styl new file mode 100644 index 000000000..e69de29bb diff --git a/Packages/lesiontracker/client/components/userAccountMenu/userAccountMenu.html b/Packages/lesiontracker/client/components/userAccountMenu/userAccountMenu.html index 7049a6acb..72b2af201 100644 --- a/Packages/lesiontracker/client/components/userAccountMenu/userAccountMenu.html +++ b/Packages/lesiontracker/client/components/userAccountMenu/userAccountMenu.html @@ -1,38 +1,46 @@ \ No newline at end of file diff --git a/Packages/lesiontracker/client/components/userAccountMenu/userAccountMenu.js b/Packages/lesiontracker/client/components/userAccountMenu/userAccountMenu.js index 3fbaf0487..2c71c0632 100644 --- a/Packages/lesiontracker/client/components/userAccountMenu/userAccountMenu.js +++ b/Packages/lesiontracker/client/components/userAccountMenu/userAccountMenu.js @@ -1,12 +1,15 @@ Template.userAccountMenu.helpers({ - fullName: function() { - return Meteor.user().profile.fullName; + name: function() { + var nameSplit = Meteor.user().profile.fullName.split(' '); + var lastName = nameSplit[nameSplit.length - 1]; + nameSplit[nameSplit.length - 1] = lastName.substr(0, 1) + '.'; + return nameSplit.join(' '); }, currentUser: function() { var verifyEmail = Meteor.settings && Meteor.settings.public && Meteor.settings.public.verifyEmail || false; if (!Meteor.user() || !Meteor.userId()) { - return false; + return; } if (!verifyEmail) { @@ -17,18 +20,13 @@ Template.userAccountMenu.helpers({ return true; } - if (Meteor.user().emails[0].verified) { - return true; - } - - return false; + return !!Meteor.user().emails[0].verified; }, showWorklistMenu: function() { var currentPath = Router.current().route.path(this); - if (currentPath === '/' || currentPath === '/worklist') { - return false; + if (currentPath !== '/' && currentPath !== '/worklist') { + return true; } - return true; } }); @@ -39,8 +37,62 @@ Template.userAccountMenu.events({ 'click #logoutButton': function() { // Remove reviewers info for the user Meteor.call('removeUserFromReviewers', Meteor.userId()); + Meteor.logout(function() { Router.go('/entrySignIn'); }); } +}); + +Template.userAccountMenu.onRendered(function() { + var oldUserId; + var lastLoginModalInterval; + + this.autorun(function() { + // Hook login/logout + var user = Meteor.user(); + var newUserId = user._id; + + if (oldUserId === null && newUserId) { + Session.set('showLastLoginModal', true); + + // Log + HipaaLogger.logEvent({ + eventType: 'init', + userId: user._id, + userName: user.fullName + }); + + } else if (newUserId === null && oldUserId) { + // Set showLastLoginModal as null + Session.set('showLastLoginModal', null); + + // Destroy interval for last login modal + Meteor.clearInterval(lastLoginModalInterval); + console.log('The user logged out'); + + // Log + // TODO: eventType is not defined for logout in hipaa-audit-log + /*HipaaLogger.logEvent({ + eventType: 'logout', + userId: oldUserId, + userName: userName + });*/ + + // Remove the user from Reviewers + Meteor.call('removeUserFromReviewers', oldUserId); + } + + oldUserId = Meteor.userId(); + + // Trigger last login date popup + if (Session.get('showLastLoginModal')) { + Modal.show('lastLoginModal'); + + lastLoginModalInterval = Meteor.setInterval(function() { + Modal.hide('lastLoginModal'); + Session.set('showLastLoginModal', null); + }, 3000); + } + }); }); \ No newline at end of file diff --git a/Packages/lesiontracker/client/components/userAccountMenu/userAccountMenu.styl b/Packages/lesiontracker/client/components/userAccountMenu/userAccountMenu.styl index 1c9116ae3..ce4eab0d5 100644 --- a/Packages/lesiontracker/client/components/userAccountMenu/userAccountMenu.styl +++ b/Packages/lesiontracker/client/components/userAccountMenu/userAccountMenu.styl @@ -1,34 +1,15 @@ +@import "{design}/app" + #userAccountMenu - position: relative - float: right + padding: 5px height: 100% - line-height: 40px - z-index: 999 - .list-group - background-color: transparent - color: white - line-height: 35px - margin-left: 10px - .list-group-item - padding: 5px 10px - width: 200px - a#userName - padding: 0 - background-color: transparent - color: white - border: solid 1px transparent - text-align: right - display: inline-block - width: auto - #userMenu - position: absolute - right: 0 - .externalItem - a - color: #555 + .dropdown-toggle + cursor: pointer + text-decoration: none + background-color: $primaryBackgroundColor + color: $textSecondaryColor - .list-group-submenu - .sub-sub-item - padding-left: 20px - padding-right: 20px \ No newline at end of file + ul.dropdown-menu + li + cursor: pointer diff --git a/LesionTracker/client/components/viewer.html b/Packages/lesiontracker/client/components/viewer/viewer.html similarity index 62% rename from LesionTracker/client/components/viewer.html rename to Packages/lesiontracker/client/components/viewer/viewer.html index 88fd828f3..d0b9f7875 100644 --- a/LesionTracker/client/components/viewer.html +++ b/Packages/lesiontracker/client/components/viewer/viewer.html @@ -5,12 +5,10 @@ {{>lesionLocationDialog}} {{>nonTargetLesionDialog}} {{>nonTargetResponseDialog}} - {{>conformanceCheckFeedback}} - {{>hidingPanel}} -
- {{>viewerMain }} - {{>lesionTable }} -
+ + + {{ >toolbarSection }} + {{ >flexboxLayout }}
{{else}} {{>loadingText}} diff --git a/LesionTracker/client/components/viewer.js b/Packages/lesiontracker/client/components/viewer/viewer.js similarity index 89% rename from LesionTracker/client/components/viewer.js rename to Packages/lesiontracker/client/components/viewer/viewer.js index 732712c2c..2a1f9f4e7 100644 --- a/LesionTracker/client/components/viewer.js +++ b/Packages/lesiontracker/client/components/viewer/viewer.js @@ -1,4 +1,7 @@ Session.setDefault('activeViewport', false); +Session.setDefault('studySidebarOpen', false); +Session.setDefault('lesionSidebarOpen', false); +Session.setDefault('additionalMeasurementsSidebarOpen', false); Template.viewer.onCreated(function() { // Attach the Window resize listener @@ -6,10 +9,15 @@ Template.viewer.onCreated(function() { ValidationErrors.remove({}); - var self = this; + var instance = this; + instance.data.state = new ReactiveDict(); + instance.data.state.set('studySidebarOpen', Session.get('studySidebarOpen')); + instance.data.state.set('lesionSidebarOpen', Session.get('lesionSidebarOpen')); + instance.data.state.set('additionalMeasurementsSidebarOpen', Session.get('additionalMeasurementsSidebarOpen')); + var contentId = this.data.contentId; - OHIF = OHIF || { + OHIF = OHIF || window.OHIF || { viewer: {} }; @@ -100,7 +108,7 @@ Template.viewer.onCreated(function() { Session.set('activeViewport', ViewerData[contentId].activeViewport || false); // Set lesion tool buttons as disabled if pixel spacing is not available for active element - self.autorun(pixelSpacingAutorunCheck); + instance.autorun(pixelSpacingAutorunCheck); // Update the ViewerStudies collection with the loaded studies ViewerStudies.remove({}); @@ -113,18 +121,17 @@ Template.viewer.onCreated(function() { var patientId = this.data.studies[0].patientId; Session.set('patientId', patientId); - self.autorun(function() { + instance.autorun(function() { var dataContext = Template.currentData(); - self.subscribe('singlePatientAssociatedStudies', dataContext.studies[0].patientId); - self.subscribe('singlePatientTimepoints', dataContext.studies[0].patientId); - self.subscribe('singlePatientMeasurements', dataContext.studies[0].patientId); - self.subscribe('singlePatientImageMeasurements', dataContext.studies[0].patientId); + instance.subscribe('singlePatientAssociatedStudies', dataContext.studies[0].patientId); + instance.subscribe('singlePatientTimepoints', dataContext.studies[0].patientId); + instance.subscribe('singlePatientMeasurements', dataContext.studies[0].patientId); + instance.subscribe('singlePatientImageMeasurements', dataContext.studies[0].patientId); - var subscriptionsReady = self.subscriptionsReady(); + var subscriptionsReady = instance.subscriptionsReady(); log.info('autorun viewer.js. Ready: ' + subscriptionsReady); if (subscriptionsReady) { - // Set buttons as enabled/disabled when Timepoints collection is ready timepointAutoCheck(dataContext); @@ -241,7 +248,7 @@ Template.viewer.onCreated(function() { // Set reviewer for this timepoint if (data.timepoints[timepointId].studyInstanceUid) { - Meteor.call('setReviewer',data.timepoints[timepointId].studyInstanceUid); + Meteor.call('setReviewer', data.timepoints[timepointId].studyInstanceUid); } }); diff --git a/LesionTracker/client/components/viewerMain/viewerMain.styl b/Packages/lesiontracker/client/components/viewer/viewer.styl similarity index 51% rename from LesionTracker/client/components/viewerMain/viewerMain.styl rename to Packages/lesiontracker/client/components/viewer/viewer.styl index f29c98428..9931e54a5 100644 --- a/LesionTracker/client/components/viewerMain/viewerMain.styl +++ b/Packages/lesiontracker/client/components/viewer/viewer.styl @@ -1,10 +1,12 @@ -.viewerMain - width: 100% - height: 84% +@import "{design}/app" + +#viewer + width: 100% + height: 100% + margin: 0 + padding: 0 + background-color: black - #layoutManagerTarget - width: 100% - height: calc(100% - 30px) //font awesome icons .fa-cr:before @@ -20,4 +22,14 @@ .fa-ex:before content: 'EX' font-weight: bold - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif \ No newline at end of file + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif + +#btnCollapse + background-color: #333 + border-color: black + + &.active + border-color: #333 + +.btnCollapseIcon + color: white diff --git a/LesionTracker/client/components/viewerMain/viewerMain.html b/Packages/lesiontracker/client/components/viewerMain/viewerMain.html similarity index 71% rename from LesionTracker/client/components/viewerMain/viewerMain.html rename to Packages/lesiontracker/client/components/viewerMain/viewerMain.html index dd8f44d2d..dbf874b7c 100644 --- a/LesionTracker/client/components/viewerMain/viewerMain.html +++ b/Packages/lesiontracker/client/components/viewerMain/viewerMain.html @@ -1,6 +1,5 @@ diff --git a/Packages/viewerbase/client/components/studyBrowser/imageThumbnail/imageThumbnail.js b/Packages/viewerbase/client/components/studyBrowser/imageThumbnail/imageThumbnail.js index c1cd5a630..72de39669 100644 --- a/Packages/viewerbase/client/components/studyBrowser/imageThumbnail/imageThumbnail.js +++ b/Packages/viewerbase/client/components/studyBrowser/imageThumbnail/imageThumbnail.js @@ -1,6 +1,6 @@ Template.imageThumbnail.onRendered(function() { var instance = this.data.stack.instances[0]; - var element = this.find('.imageThumbnail'); + var element = this.find('.imageThumbnailCanvas'); cornerstone.disable(element); $(element).find('canvas').remove(); @@ -12,7 +12,7 @@ Template.imageThumbnail.onRendered(function() { var elem = $(element); elem.find('.imageThumbnailLoadingIndicator').css('display', 'block'); - var thumbnailIndex = $('.imageThumbnail').index(element); + var thumbnailIndex = $('.imageThumbnailCanvas').index(element); ThumbnailLoading[thumbnailIndex] = imageId; this.data.thumbnailIndex = thumbnailIndex; diff --git a/Packages/viewerbase/client/components/studyBrowser/imageThumbnail/imageThumbnail.styl b/Packages/viewerbase/client/components/studyBrowser/imageThumbnail/imageThumbnail.styl index cf1750626..52e4a6777 100644 --- a/Packages/viewerbase/client/components/studyBrowser/imageThumbnail/imageThumbnail.styl +++ b/Packages/viewerbase/client/components/studyBrowser/imageThumbnail/imageThumbnail.styl @@ -1,10 +1,30 @@ +@import "{design}/app" + +.thumbnailEntry.active .imageThumbnail + border-color: $activeColor + box-shadow: none + transition($sidebarTransition) + .imageThumbnail - width: 90px - height: 90px - background-color: black + background-color: $primaryBackgroundColor + border: 5px solid transparent + border-radius: 12px + box-shadow: inset 0 0 0 1px $uiBorderColorDark + height: 135px margin: 0 auto + padding 1px 7px position: relative - + transition($sidebarTransition) + width: 217px + + &:hover + border-color: $hoverColor + box-shadow: none + +.imageThumbnailCanvas + height: 100% + overflow: hidden + .thumbnailLoadingIndicator display: none pointer-events: none @@ -17,7 +37,7 @@ bottom: 0 margin: auto position: absolute - + p text-align: center - font-size: 10pt \ No newline at end of file + font-size: 10pt diff --git a/Packages/viewerbase/client/components/studyBrowser/thumbnailEntry/thumbnailEntry.html b/Packages/viewerbase/client/components/studyBrowser/thumbnailEntry/thumbnailEntry.html index f0e3f2b38..08e1aa1c9 100644 --- a/Packages/viewerbase/client/components/studyBrowser/thumbnailEntry/thumbnailEntry.html +++ b/Packages/viewerbase/client/components/studyBrowser/thumbnailEntry/thumbnailEntry.html @@ -1,6 +1,22 @@ \ No newline at end of file + diff --git a/Packages/viewerbase/client/components/studyBrowser/thumbnailEntry/thumbnailEntry.js b/Packages/viewerbase/client/components/studyBrowser/thumbnailEntry/thumbnailEntry.js index 0ef836f4a..5c3f0f8ec 100644 --- a/Packages/viewerbase/client/components/studyBrowser/thumbnailEntry/thumbnailEntry.js +++ b/Packages/viewerbase/client/components/studyBrowser/thumbnailEntry/thumbnailEntry.js @@ -24,7 +24,7 @@ function thumbnailDragStartHandler(e, data) { // Identify the current study and series index from the thumbnail's DOM position var targetThumbnail = e.currentTarget; var imageThumbnail = $(targetThumbnail); - + // Store this data for use during drag and drop OHIF.viewer.dragAndDropData = data; @@ -32,7 +32,7 @@ function thumbnailDragStartHandler(e, data) { var targetId = targetThumbnail.id + 'DragClone'; var clone = cloneElement(targetThumbnail, targetId); clone.classList.add('imageThumbnailClone'); - + // Set pointerEvents to pass through the clone DOM element // This is necessary in order to identify what is below it // when using document.elementFromPoint @@ -40,7 +40,7 @@ function thumbnailDragStartHandler(e, data) { // Append the clone to the parent of the target targetThumbnail.parentNode.appendChild(clone); - + // Set the cursor x and y positions from the current touch/mouse coordinates // Handle touchStart cases if (e.type === 'touchstart') { @@ -61,7 +61,7 @@ function thumbnailDragStartHandler(e, data) { // This block gets the current position of the touch/mouse // relative to the thumbnail itself - // + // // i.e. Where did the user grab it from? var position = imageThumbnail.position(); var left = position.left; @@ -70,7 +70,7 @@ function thumbnailDragStartHandler(e, data) { // This difference is saved for later so the element movement looks normal diffX = cursorX - left; diffY = cursorY - top; - + // This sets the default style properties of the cloned element so it is // ready to be dragged around the page $(clone).css({ @@ -103,7 +103,7 @@ function thumbnailDragHandler(e, target) { 'z-index': 100, visibility: 'visible' }); - + // Identify the element below the current cursor position var elemBelow = document.elementFromPoint(cursorX + diffX, cursorY + diffY); @@ -156,7 +156,7 @@ function thumbnailDragEndHandler(e, target) { // Remove any current faded effects on viewports $('.imageViewerViewport canvas').removeClass('faded'); - + // If none exists, stop here if (!elemBelow) { return; @@ -186,8 +186,19 @@ function thumbnailDragEndHandler(e, target) { var viewportIndex = $('.imageViewerViewport').index(element); + // Removes active class from previous selected series in viewport + const previousUid = layoutManager.viewportData[viewportIndex].seriesInstanceUid; + if (previousUid) { + $(`.thumbnailEntry[data-uid="${previousUid}"]`).removeClass('active'); + } + // Rerender the viewport using the drag and drop data layoutManager.rerenderViewportWithNewSeries(viewportIndex, OHIF.viewer.dragAndDropData); + + // Set active state for dragged thumbnail entry + const newUid = OHIF.viewer.dragAndDropData.seriesInstanceUid + $(`.thumbnailEntry[data-uid="${newUid}"]`).addClass('active'); + return false; } @@ -213,9 +224,3 @@ Template.thumbnailEntry.events({ thumbnailDragEndHandler(e, e.currentTarget); } }); - -Template.thumbnailEntry.helpers({ - seriesDescription: function() { - return this.stack.seriesDescription || ''; - } -}); diff --git a/Packages/viewerbase/client/components/studyBrowser/thumbnailEntry/thumbnailEntry.styl b/Packages/viewerbase/client/components/studyBrowser/thumbnailEntry/thumbnailEntry.styl index e5d4d5fad..cc7b22178 100644 --- a/Packages/viewerbase/client/components/studyBrowser/thumbnailEntry/thumbnailEntry.styl +++ b/Packages/viewerbase/client/components/studyBrowser/thumbnailEntry/thumbnailEntry.styl @@ -1,24 +1,66 @@ +@import "{design}/app" + +$seriesCountBackgroundColor = #678696 + .thumbnailEntry - width: 105px - padding: 5px - background-color: black - border: 1px solid #424242 - margin: 0 auto cursor: copy cursor: -webkit-grab cursor: -moz-grab - - .seriesDescription - text-align: center - color: #555 + display: table + margin: 0 auto + font-family: Roboto + + .seriesDetails + color: $textPrimaryColor + font-size: 14px + line-height: 1.3em + min-height: 36px + position: relative word-wrap: break-word - - &:hover - background-color: white - - &.active - background-color: #424242 - border-color: #4e4e4e - - .seriesDescription - color: white \ No newline at end of file + + .seriesDescription + margin-right: 54px + + .seriesInformation + position: absolute + right: 0 + top: 0 + + .seriesInformationNumbers + .seriesInformationIcons + margin-right: 4px + + .seriesInformationNumbers + color: $textSecondaryColor + float: left + font-size: 12px + + .seriesInformationIcons + float: left + text-align: right + + strong + color: $activeColor + font-size: 10px + // TODO: [design] try to find out what is the font-weight for Roboto-Black + font-weight: 500 + + div + background-color: $seriesCountBackgroundColor + margin-top: 6px + position: relative + + &:after + background-color: $activeColor + box-shadow: 1px 1px rgba(0, 0, 0, .115) + left: -5px + position: absolute + top: -5px + + & + &:after + border: 1px solid $primaryBackgroundColor + content: '' + display: block + height: 11px + width: 11px diff --git a/Packages/viewerbase/client/components/viewer/imageViewerViewport/imageViewerViewport.js b/Packages/viewerbase/client/components/viewer/imageViewerViewport/imageViewerViewport.js index 1b53d6cdd..f9879697c 100644 --- a/Packages/viewerbase/client/components/viewer/imageViewerViewport/imageViewerViewport.js +++ b/Packages/viewerbase/client/components/viewer/imageViewerViewport/imageViewerViewport.js @@ -21,6 +21,8 @@ function loadSeriesIntoViewport(data, templateData) { // The viewport index is often used to store information about a viewport element var element = data.element; var viewportIndex = $('.imageViewerViewport').index(element); + + layoutManager = layoutManager || window.layoutManager; layoutManager.viewportData[viewportIndex].viewportIndex = viewportIndex; // Get the contentID of the current worklist tab, if the viewport is running diff --git a/Packages/viewerbase/client/components/viewer/imageViewerViewport/imageViewerViewport.styl b/Packages/viewerbase/client/components/viewer/imageViewerViewport/imageViewerViewport.styl index 2182b6540..6aba8643d 100644 --- a/Packages/viewerbase/client/components/viewer/imageViewerViewport/imageViewerViewport.styl +++ b/Packages/viewerbase/client/components/viewer/imageViewerViewport/imageViewerViewport.styl @@ -2,7 +2,10 @@ width: 100% height: 100% background-color: black - + + // Prevent the blue outline in Chrome when a viewport is selected + outline: 0 !important; + // Prevents the entire page from getting larger // when the magnify tool is near the sides/corners of the page overflow: hidden diff --git a/Packages/viewerbase/client/components/viewer/viewportOverlay/viewportOverlay.styl b/Packages/viewerbase/client/components/viewer/viewportOverlay/viewportOverlay.styl index 56f5bd031..d97902739 100644 --- a/Packages/viewerbase/client/components/viewer/viewportOverlay/viewportOverlay.styl +++ b/Packages/viewerbase/client/components/viewer/viewportOverlay/viewportOverlay.styl @@ -1,8 +1,7 @@ .imageViewerViewportOverlay - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif - font-size: 13px + font-size: 18px color: #979797 - line-height: 18px + line-height: 1.4 display: none // Shown when an image is loaded .dicomTag diff --git a/Packages/viewerbase/lib/createStacks.js b/Packages/viewerbase/lib/createStacks.js index f50fa7e47..d3f9f43b7 100644 --- a/Packages/viewerbase/lib/createStacks.js +++ b/Packages/viewerbase/lib/createStacks.js @@ -22,9 +22,9 @@ createStacks = function(study) { } // Don't display thumbnails for non-image modalities - // All imaging modalities must have a valid value for sopClassUid or rows + // All imaging modalities must have a valid value for rows (or columns) var anInstance = series.instances[0]; - if (!anInstance || (!isImage(anInstance.sopClassUid) && !anInstance.rows)) { + if (!anInstance || !anInstance.rows) { return; } diff --git a/Packages/viewerbase/lib/helpers/logical.js b/Packages/viewerbase/lib/helpers/logical.js new file mode 100644 index 000000000..ac941f127 --- /dev/null +++ b/Packages/viewerbase/lib/helpers/logical.js @@ -0,0 +1,35 @@ +Template.registerHelper('choose', function() { + let result; + _.each(_.initial(arguments, 1), function(value) { + return value && (result = value); + }); + return result; +}); + +Template.registerHelper('bool', function(value) { + return !!value; +}); + +Template.registerHelper('equals', function(a, b) { + return a === b; +}); + +Template.registerHelper('not', function(value) { + return !value; +}); + +Template.registerHelper('and', function() { + let result = true; + _.each(_.initial(arguments, 1), function(value) { + return value || (result = false); + }); + return result; +}); + +Template.registerHelper('or', function() { + let result = false; + _.each(_.initial(arguments, 1), function(value) { + return value && (result = true); + }); + return result; +}); \ No newline at end of file diff --git a/Packages/viewerbase/lib/isImage.js b/Packages/viewerbase/lib/isImage.js deleted file mode 100644 index 55238e272..000000000 --- a/Packages/viewerbase/lib/isImage.js +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Checks whether dicom files with specified SOP Class UID have image data - * @param {string} sopClassUid - SOP Class UID to be checked - * @returns {boolean} - true if it has image data - */ -isImage = function(sopClassUid) { - if (sopClassUid == sopClassDictionary.ComputedRadiographyImageStorage - || sopClassUid == sopClassDictionary.DigitalXRayImageStorageForPresentation - || sopClassUid == sopClassDictionary.DigitalXRayImageStorageForProcessing - || sopClassUid == sopClassDictionary.DigitalMammographyXRayImageStorageForPresentation - || sopClassUid == sopClassDictionary.DigitalMammographyXRayImageStorageForProcessing - || sopClassUid == sopClassDictionary.DigitalIntraOralXRayImageStorageForPresentation - || sopClassUid == sopClassDictionary.DigitalIntraOralXRayImageStorageForProcessing - || sopClassUid == sopClassDictionary.CTImageStorage - || sopClassUid == sopClassDictionary.EnhancedCTImageStorage - || sopClassUid == sopClassDictionary.LegacyConvertedEnhancedCTImageStorage - || sopClassUid == sopClassDictionary.UltrasoundMultiframeImageStorage - || sopClassUid == sopClassDictionary.MRImageStorage - || sopClassUid == sopClassDictionary.EnhancedMRImageStorage - || sopClassUid == sopClassDictionary.EnhancedMRColorImageStorage - || sopClassUid == sopClassDictionary.LegacyConvertedEnhancedMRImageStorage - || sopClassUid == sopClassDictionary.UltrasoundImageStorage - || sopClassUid == sopClassDictionary.SecondaryCaptureImageStorage - || sopClassUid == sopClassDictionary.MultiframeSingleBitSecondaryCaptureImageStorage - || sopClassUid == sopClassDictionary.MultiframeGrayscaleByteSecondaryCaptureImageStorage - || sopClassUid == sopClassDictionary.MultiframeGrayscaleWordSecondaryCaptureImageStorage - || sopClassUid == sopClassDictionary.MultiframeTrueColorSecondaryCaptureImageStorage - || sopClassUid == sopClassDictionary.XRayAngiographicImageStorage - || sopClassUid == sopClassDictionary.EnhancedXAImageStorage - || sopClassUid == sopClassDictionary.XRayRadiofluoroscopicImageStorage - || sopClassUid == sopClassDictionary.EnhancedXRFImageStorage - || sopClassUid == sopClassDictionary.XRay3DAngiographicImageStorage - || sopClassUid == sopClassDictionary.XRay3DCraniofacialImageStorage - || sopClassUid == sopClassDictionary.BreastTomosynthesisImageStorage - || sopClassUid == sopClassDictionary.BreastProjectionXRayImageStorageForPresentation - || sopClassUid == sopClassDictionary.BreastProjectionXRayImageStorageForProcessing - || sopClassUid == sopClassDictionary.IntravascularOpticalCoherenceTomographyImageStorageForPresentation - || sopClassUid == sopClassDictionary.IntravascularOpticalCoherenceTomographyImageStorageForProcessing - || sopClassUid == sopClassDictionary.NuclearMedicineImageStorage - || sopClassUid == sopClassDictionary.VLEndoscopicImageStorage - || sopClassUid == sopClassDictionary.VideoEndoscopicImageStorage - || sopClassUid == sopClassDictionary.VLMicroscopicImageStorage - || sopClassUid == sopClassDictionary.VideoMicroscopicImageStorage - || sopClassUid == sopClassDictionary.VLSlideCoordinatesMicroscopicImageStorage - || sopClassUid == sopClassDictionary.VLPhotographicImageStorage - || sopClassUid == sopClassDictionary.VideoPhotographicImageStorage - || sopClassUid == sopClassDictionary.OphthalmicPhotography8BitImageStorage - || sopClassUid == sopClassDictionary.OphthalmicPhotography16BitImageStorage - || sopClassUid == sopClassDictionary.OphthalmicTomographyImageStorage - || sopClassUid == sopClassDictionary.VLWholeSlideMicroscopyImageStorage - || sopClassUid == sopClassDictionary.PositronEmissionTomographyImageStorage - || sopClassUid == sopClassDictionary.EnhancedPETImageStorage - || sopClassUid == sopClassDictionary.LegacyConvertedEnhancedPETImageStorage - || sopClassUid == sopClassDictionary.RTImageStorage) { - return true; - } - - return false; -}; \ No newline at end of file diff --git a/Packages/viewerbase/lib/sopClassDictionary.js b/Packages/viewerbase/lib/sopClassDictionary.js deleted file mode 100644 index 3d0ab24c0..000000000 --- a/Packages/viewerbase/lib/sopClassDictionary.js +++ /dev/null @@ -1,115 +0,0 @@ -sopClassDictionary = { - ComputedRadiographyImageStorage: "1.2.840.10008.5.1.4.1.1.1", - DigitalXRayImageStorageForPresentation: "1.2.840.10008.5.1.4.1.1.1.1", - DigitalXRayImageStorageForProcessing: "1.2.840.10008.5.1.4.1.1.1.1.1", - DigitalMammographyXRayImageStorageForPresentation: "1.2.840.10008.5.1.4.1.1.1.2", - DigitalMammographyXRayImageStorageForProcessing: "1.2.840.10008.5.1.4.1.1.1.2.1", - DigitalIntraOralXRayImageStorageForPresentation: "1.2.840.10008.5.1.4.1.1.1.3", - DigitalIntraOralXRayImageStorageForProcessing: "1.2.840.10008.5.1.4.1.1.1.3.1", - CTImageStorage: "1.2.840.10008.5.1.4.1.1.2", - EnhancedCTImageStorage: "1.2.840.10008.5.1.4.1.1.2.1", - LegacyConvertedEnhancedCTImageStorage: "1.2.840.10008.5.1.4.1.1.2.2", - UltrasoundMultiframeImageStorage: "1.2.840.10008.5.1.4.1.1.3.1", - MRImageStorage: "1.2.840.10008.5.1.4.1.1.4", - EnhancedMRImageStorage: "1.2.840.10008.5.1.4.1.1.4.1", - MRSpectroscopyStorage: "1.2.840.10008.5.1.4.1.1.4.2", - EnhancedMRColorImageStorage: "1.2.840.10008.5.1.4.1.1.4.3", - LegacyConvertedEnhancedMRImageStorage: "1.2.840.10008.5.1.4.1.1.4.4", - UltrasoundImageStorage: "1.2.840.10008.5.1.4.1.1.6.1", - EnhancedUSVolumeStorage: "1.2.840.10008.5.1.4.1.1.6.2", - SecondaryCaptureImageStorage: "1.2.840.10008.5.1.4.1.1.7", - MultiframeSingleBitSecondaryCaptureImageStorage: "1.2.840.10008.5.1.4.1.1.7.1", - MultiframeGrayscaleByteSecondaryCaptureImageStorage: "1.2.840.10008.5.1.4.1.1.7.2", - MultiframeGrayscaleWordSecondaryCaptureImageStorage: "1.2.840.10008.5.1.4.1.1.7.3", - MultiframeTrueColorSecondaryCaptureImageStorage: "1.2.840.10008.5.1.4.1.1.7.4", - Sop12LeadECGWaveformStorage: "1.2.840.10008.5.1.4.1.1.9.1.1", - GeneralECGWaveformStorage: "1.2.840.10008.5.1.4.1.1.9.1.2", - AmbulatoryECGWaveformStorage: "1.2.840.10008.5.1.4.1.1.9.1.3", - HemodynamicWaveformStorage: "1.2.840.10008.5.1.4.1.1.9.2.1", - CardiacElectrophysiologyWaveformStorage: "1.2.840.10008.5.1.4.1.1.9.3.1", - BasicVoiceAudioWaveformStorage: "1.2.840.10008.5.1.4.1.1.9.4.1", - GeneralAudioWaveformStorage: "1.2.840.10008.5.1.4.1.1.9.4.2", - ArterialPulseWaveformStorage: "1.2.840.10008.5.1.4.1.1.9.5.1", - RespiratoryWaveformStorage: "1.2.840.10008.5.1.4.1.1.9.6.1", - GrayscaleSoftcopyPresentationStateStorage: "1.2.840.10008.5.1.4.1.1.11.1", - ColorSoftcopyPresentationStateStorage: "1.2.840.10008.5.1.4.1.1.11.2", - PseudoColorSoftcopyPresentationStateStorage: "1.2.840.10008.5.1.4.1.1.11.3", - BlendingSoftcopyPresentationStateStorage: "1.2.840.10008.5.1.4.1.1.11.4", - XAXRFGrayscaleSoftcopyPresentationStateStorage: "1.2.840.10008.5.1.4.1.1.11.5", - XRayAngiographicImageStorage: "1.2.840.10008.5.1.4.1.1.12.1", - EnhancedXAImageStorage: "1.2.840.10008.5.1.4.1.1.12.1.1", - XRayRadiofluoroscopicImageStorage: "1.2.840.10008.5.1.4.1.1.12.2", - EnhancedXRFImageStorage: "1.2.840.10008.5.1.4.1.1.12.2.1", - XRay3DAngiographicImageStorage: "1.2.840.10008.5.1.4.1.1.13.1.1", - XRay3DCraniofacialImageStorage: "1.2.840.10008.5.1.4.1.1.13.1.2", - BreastTomosynthesisImageStorage: "1.2.840.10008.5.1.4.1.1.13.1.3", - BreastProjectionXRayImageStorageForPresentation: "1.2.840.10008.5.1.4.1.1.13.1.4", - BreastProjectionXRayImageStorageForProcessing: "1.2.840.10008.5.1.4.1.1.13.1.5", - IntravascularOpticalCoherenceTomographyImageStorageForPresentation: "1.2.840.10008.5.1.4.1.1.14.1", - IntravascularOpticalCoherenceTomographyImageStorageForProcessing: "1.2.840.10008.5.1.4.1.1.14.2", - NuclearMedicineImageStorage: "1.2.840.10008.5.1.4.1.1.20", - RawDataStorage: "1.2.840.10008.5.1.4.1.1.66", - SpatialRegistrationStorage: "1.2.840.10008.5.1.4.1.1.66.1", - SpatialFiducialsStorage: "1.2.840.10008.5.1.4.1.1.66.2", - DeformableSpatialRegistrationStorage: "1.2.840.10008.5.1.4.1.1.66.3", - SegmentationStorage: "1.2.840.10008.5.1.4.1.1.66.4", - SurfaceSegmentationStorage: "1.2.840.10008.5.1.4.1.1.66.5", - RealWorldValueMappingStorage: "1.2.840.10008.5.1.4.1.1.67", - SurfaceScanMeshStorage: "1.2.840.10008.5.1.4.1.1.68.1", - SurfaceScanPointCloudStorage: "1.2.840.10008.5.1.4.1.1.68.2", - VLEndoscopicImageStorage: "1.2.840.10008.5.1.4.1.1.77.1.1", - VideoEndoscopicImageStorage: "1.2.840.10008.5.1.4.1.1.77.1.1.1", - VLMicroscopicImageStorage: "1.2.840.10008.5.1.4.1.1.77.1.2", - VideoMicroscopicImageStorage: "1.2.840.10008.5.1.4.1.1.77.1.2.1", - VLSlideCoordinatesMicroscopicImageStorage: "1.2.840.10008.5.1.4.1.1.77.1.3", - VLPhotographicImageStorage: "1.2.840.10008.5.1.4.1.1.77.1.4", - VideoPhotographicImageStorage: "1.2.840.10008.5.1.4.1.1.77.1.4.1", - OphthalmicPhotography8BitImageStorage: "1.2.840.10008.5.1.4.1.1.77.1.5.1", - OphthalmicPhotography16BitImageStorage: "1.2.840.10008.5.1.4.1.1.77.1.5.2", - StereometricRelationshipStorage: "1.2.840.10008.5.1.4.1.1.77.1.5.3", - OphthalmicTomographyImageStorage: "1.2.840.10008.5.1.4.1.1.77.1.5.4", - VLWholeSlideMicroscopyImageStorage: "1.2.840.10008.5.1.4.1.1.77.1.6", - LensometryMeasurementsStorage: "1.2.840.10008.5.1.4.1.1.78.1", - AutorefractionMeasurementsStorage: "1.2.840.10008.5.1.4.1.1.78.2", - KeratometryMeasurementsStorage: "1.2.840.10008.5.1.4.1.1.78.3", - SubjectiveRefractionMeasurementsStorage: "1.2.840.10008.5.1.4.1.1.78.4", - VisualAcuityMeasurementsStorage: "1.2.840.10008.5.1.4.1.1.78.5", - SpectaclePrescriptionReportStorage: "1.2.840.10008.5.1.4.1.1.78.6", - OphthalmicAxialMeasurementsStorage: "1.2.840.10008.5.1.4.1.1.78.7", - IntraocularLensCalculationsStorage: "1.2.840.10008.5.1.4.1.1.78.8", - MacularGridThicknessandVolumeReport: "1.2.840.10008.5.1.4.1.1.79.1", - OphthalmicVisualFieldStaticPerimetryMeasurementsStorage: "1.2.840.10008.5.1.4.1.1.80.1", - OphthalmicThicknessMapStorage: "1.2.840.10008.5.1.4.1.1.81.1", - CornealTopographyMapStorage: "1.2.840.10008.5.1.4.1.1.82.1", - BasicTextSR: "1.2.840.10008.5.1.4.1.1.88.11", - EnhancedSR: "1.2.840.10008.5.1.4.1.1.88.22", - ComprehensiveSR: "1.2.840.10008.5.1.4.1.1.88.33", - Comprehensive3DSR: "1.2.840.10008.5.1.4.1.1.88.34", - ProcedureLog: "1.2.840.10008.5.1.4.1.1.88.40", - MammographyCADSR: "1.2.840.10008.5.1.4.1.1.88.50", - KeyObjectSelection: "1.2.840.10008.5.1.4.1.1.88.59", - ChestCADSR: "1.2.840.10008.5.1.4.1.1.88.65", - XRayRadiationDoseSR: "1.2.840.10008.5.1.4.1.1.88.67", - RadiopharmaceuticalRadiationDoseSR: "1.2.840.10008.5.1.4.1.1.88.68", - ColonCADSR: "1.2.840.10008.5.1.4.1.1.88.69", - ImplantationPlanSRDocumentStorage: "1.2.840.10008.5.1.4.1.1.88.70", - EncapsulatedPDFStorage: "1.2.840.10008.5.1.4.1.1.104.1", - EncapsulatedCDAStorage: "1.2.840.10008.5.1.4.1.1.104.2", - PositronEmissionTomographyImageStorage: "1.2.840.10008.5.1.4.1.1.128", - EnhancedPETImageStorage: "1.2.840.10008.5.1.4.1.1.130", - LegacyConvertedEnhancedPETImageStorage: "1.2.840.10008.5.1.4.1.1.128.1", - BasicStructuredDisplayStorage: "1.2.840.10008.5.1.4.1.1.131", - RTImageStorage: "1.2.840.10008.5.1.4.1.1.481.1", - RTDoseStorage: "1.2.840.10008.5.1.4.1.1.481.2", - RTStructureSetStorage: "1.2.840.10008.5.1.4.1.1.481.3", - RTBeamsTreatmentRecordStorage: "1.2.840.10008.5.1.4.1.1.481.4", - RTPlanStorage: "1.2.840.10008.5.1.4.1.1.481.5", - RTBrachyTreatmentRecordStorage: "1.2.840.10008.5.1.4.1.1.481.6", - RTTreatmentSummaryRecordStorage: "1.2.840.10008.5.1.4.1.1.481.7", - RTIonPlanStorage: "1.2.840.10008.5.1.4.1.1.481.8", - RTIonBeamsTreatmentRecordStorage: "1.2.840.10008.5.1.4.1.1.481.9", - RTBeamsDeliveryInstructionStorage: "1.2.840.10008.5.1.4.34.7", - GenericImplantTemplateStorage: "1.2.840.10008.5.1.4.43.1", - ImplantAssemblyTemplateStorage: "1.2.840.10008.5.1.4.44.1", - ImplantTemplateGroupStorage: "1.2.840.10008.5.1.4.45.1" -}; \ No newline at end of file diff --git a/Packages/viewerbase/package.js b/Packages/viewerbase/package.js index a70eb3546..d319134a3 100644 --- a/Packages/viewerbase/package.js +++ b/Packages/viewerbase/package.js @@ -13,9 +13,9 @@ Package.onUse(function(api) { api.use('jquery'); api.use('stylus'); api.use('practicalmeteor:loglevel'); - api.use('momentjs:moment'); - + api.use('momentjs:moment'); api.use('validatejs'); + api.use('design'); // Our custom packages api.use('cornerstone'); @@ -133,8 +133,6 @@ Package.onUse(function(api) { //api.addFiles('lib/validators.js', 'client'); api.addFiles('lib/instanceClassSpecificViewport.js', 'client'); api.addFiles('lib/setMammogramViewportAlignment.js', 'client'); - api.addFiles('lib/isImage.js', 'client'); - api.addFiles('lib/sopClassDictionary.js', 'client'); api.export('resizeViewportElements', 'client'); api.export('handleResize', 'client'); @@ -158,8 +156,6 @@ Package.onUse(function(api) { api.export('getInstanceClassDefaultViewport', 'client'); api.export('showConfirmDialog', 'client'); api.export('applyWLPreset', 'client'); - api.export('isImage', 'client'); - api.export('sopClassDictionary', 'client'); // Export the ValidateJS Library with our added validators //api.export('validate', 'client'); @@ -177,6 +173,7 @@ Package.onUse(function(api) { // UI Helpers api.addFiles('lib/helpers/formatDA.js', 'client'); + api.addFiles('lib/helpers/logical.js', 'client'); api.addFiles('lib/helpers/formatJSDate.js', 'client'); api.addFiles('lib/helpers/jsDateFromNow.js', 'client'); api.addFiles('lib/helpers/formatNumberPrecision.js', 'client'); @@ -205,4 +202,3 @@ Package.onUse(function(api) { api.addFiles('server/collections.js', 'server'); api.export('StudyImportStatus', [ 'client', 'server' ]); }); - diff --git a/Packages/worklist/client/lib/switchToTab.js b/Packages/worklist/client/lib/switchToTab.js index 7555f3b12..8a50f75c4 100644 --- a/Packages/worklist/client/lib/switchToTab.js +++ b/Packages/worklist/client/lib/switchToTab.js @@ -11,9 +11,11 @@ switchToTab = function(contentId) { log.info('Switching to tab: ' + contentId); - // Use Bootstrap's Tab JavaScript to show the contents of the current tab - // Unless it is the worklist, it is currently an empty div - $('.tabTitle a[data-target="#' + contentId + '"]').tab('show'); + $('.tabTitle').removeClass('active'); + $('.tabTitle a[data-target="#' + contentId + '"]').addClass('active'); + + $('.tab-content .tab-pane').removeClass('active'); + $('.tab-content .tab-pane#' + contentId).addClass('active'); // Remove any previous Viewers from the DOM $('.viewerContainer').remove();