diff --git a/LesionTracker/client/components/toolbarSection/toolbarSection.html b/LesionTracker/client/components/toolbarSection/toolbarSection.html index 0654390c6..45e1098d2 100644 --- a/LesionTracker/client/components/toolbarSection/toolbarSection.html +++ b/LesionTracker/client/components/toolbarSection/toolbarSection.html @@ -7,13 +7,6 @@ {{>toolbarSectionTools toolbarButtons=toolbarButtons}} - {{#if splitView}} - {{>studySeriesQuickSwitch (clone this side="right" viewportIndex=1)}} - {{>studySeriesQuickSwitch (clone this side="left" viewportIndex=0)}} - {{else}} - {{>studySeriesQuickSwitch (clone this side="middle" viewportIndex=0)}} - {{/if}} -
{{>caseProgress}}
diff --git a/LesionTracker/client/components/toolbarSection/toolbarSection.js b/LesionTracker/client/components/toolbarSection/toolbarSection.js index 5ed1d8af0..c5e3827b6 100644 --- a/LesionTracker/client/components/toolbarSection/toolbarSection.js +++ b/LesionTracker/client/components/toolbarSection/toolbarSection.js @@ -4,19 +4,6 @@ import { OHIF } from 'meteor/ohif:core'; import { Viewerbase } from 'meteor/ohif:viewerbase'; Template.toolbarSection.helpers({ - // Returns true if the view shall be split in two viewports - splitView() { - // Run this computation every time the viewports are updated - Session.get('LayoutManagerUpdated'); - - // Stops here if layout manager is not defined yet - if (!Viewerbase.layoutManager) { - return; - } - - return Viewerbase.layoutManager.viewportData.length > 1; - }, - leftSidebarToggleButtonData() { const instance = Template.instance(); return { diff --git a/Packages/ohif-core/client/components/scrollArea/scrollArea.js b/Packages/ohif-core/client/components/scrollArea/scrollArea.js index d81bfaf7a..1657d0ca8 100644 --- a/Packages/ohif-core/client/components/scrollArea/scrollArea.js +++ b/Packages/ohif-core/client/components/scrollArea/scrollArea.js @@ -1,5 +1,6 @@ import { Template } from 'meteor/templating'; import { _ } from 'meteor/underscore'; +import { OHIF } from 'meteor/ohif:core'; Template.scrollArea.onCreated(() => { const instance = Template.instance(); @@ -16,17 +17,20 @@ Template.scrollArea.onCreated(() => { Template.scrollArea.onRendered(() => { const instance = Template.instance(); - const { config } = instance; - if (config.hideScrollbar) { - const $scrollable = instance.$('.scrollable').first(); - const scrollable = $scrollable[0]; - const x = config.scrollX ? 1 : 0; - const y = config.scrollY ? 1 : 0; - $scrollable.css({ - 'margin-right': 0 - (scrollable.offsetWidth - scrollable.clientWidth) * y, - 'margin-bottom': 0 - (scrollable.offsetHeight - scrollable.clientHeight) * x - }); - } + + instance.adjustMargins = _.throttle(() => { + const { config } = instance; + if (config.hideScrollbar) { + const $scrollable = instance.$('.scrollable').first(); + const x = config.scrollX ? 1 : 0; + const y = config.scrollY ? 1 : 0; + const scrollbarSize = OHIF.ui.getScrollbarSize(); + $scrollable.css({ + 'margin-right': 0 - (scrollbarSize[0]) * y, + 'margin-bottom': 0 - (scrollbarSize[1]) * x + }); + } + }, 150); instance.$scrollable = instance.$('.scrollable').first(); instance.scrollHandler = _.throttle(event => { @@ -50,9 +54,17 @@ Template.scrollArea.onRendered(() => { if (scrollTop + height < scrollHeight) { $scrollArea.addClass('can-scroll-down'); } - }, 300); + }, 150); instance.scrollHandler(); + + instance.adjustMargins(); + $(window).on('resize', instance.adjustMargins); +}); + +Template.scrollArea.onDestroyed(() => { + const instance = Template.instance(); + $(window).off('resize', instance.adjustMargins); }); Template.scrollArea.events({ diff --git a/Packages/ohif-core/client/components/scrollArea/scrollArea.styl b/Packages/ohif-core/client/components/scrollArea/scrollArea.styl index ef0e02b19..86cfe6749 100644 --- a/Packages/ohif-core/client/components/scrollArea/scrollArea.styl +++ b/Packages/ohif-core/client/components/scrollArea/scrollArea.styl @@ -9,6 +9,7 @@ $scrollNavSize = 24px .scrollable max-height: inherit overflow: hidden + zoom: 1 &.scroll-x overflow-x: scroll diff --git a/Packages/ohif-core/client/lib/ui.js b/Packages/ohif-core/client/lib/ui.js index 6f3b210a7..86aaa57ba 100644 --- a/Packages/ohif-core/client/lib/ui.js +++ b/Packages/ohif-core/client/lib/ui.js @@ -26,3 +26,45 @@ OHIF.ui.getOffset = element => { top }; }; + +/** + * Get the vertical and horizontal scrollbar sizes + * Got from https://stackoverflow.com/questions/986937/how-can-i-get-the-browsers-scrollbar-sizes + * + * @returns {Array} Array containing the scrollbar horizontal and vertical sizes + */ +OHIF.ui.getScrollbarSize = () => { + const inner = document.createElement('p'); + inner.style.width = '100%'; + inner.style.height = '100%'; + + const outer = document.createElement('div'); + outer.style.position = 'absolute'; + outer.style.top = '0px'; + outer.style.left = '0px'; + outer.style.visibility = 'hidden'; + outer.style.width = '100px'; + outer.style.height = '100px'; + outer.style.overflow = 'hidden'; + outer.appendChild(inner); + + document.body.appendChild(outer); + + const w1 = inner.offsetWidth; + const h1 = inner.offsetHeight; + outer.style.overflow = 'scroll'; + let w2 = inner.offsetWidth; + let h2 = inner.offsetHeight; + + if (w1 === w2) { + w2 = outer.clientWidth; + } + + if (h1 === h2) { + h2 = outer.clientHeight; + } + + document.body.removeChild(outer); + + return [(w1 - w2), (h1 - h2)]; +}; diff --git a/Packages/ohif-lesiontracker/client/components/timepointBrowser/item.html b/Packages/ohif-lesiontracker/client/components/timepointBrowser/item.html index 9263dde8e..9b1a4d81a 100644 --- a/Packages/ohif-lesiontracker/client/components/timepointBrowser/item.html +++ b/Packages/ohif-lesiontracker/client/components/timepointBrowser/item.html @@ -13,8 +13,8 @@
{{choose instance.summary.get 'Click to load'}}
{{#if this.timepointChildTemplate}} - {{#if and (not instance.loading.get) instance.loaded}} - {{>Template.dynamic template=this.timepointChildTemplate data=(clone this studiesInformation=timepoint.studiesData)}} + {{#if instance.studiesData.get}} + {{>Template.dynamic template=this.timepointChildTemplate data=(clone this studiesInformation=instance.studiesData.get)}} {{/if}} {{/if}} diff --git a/Packages/ohif-lesiontracker/client/components/timepointBrowser/item.js b/Packages/ohif-lesiontracker/client/components/timepointBrowser/item.js index 5eaa78693..e39c96429 100644 --- a/Packages/ohif-lesiontracker/client/components/timepointBrowser/item.js +++ b/Packages/ohif-lesiontracker/client/components/timepointBrowser/item.js @@ -8,15 +8,28 @@ import { OHIF } from 'meteor/ohif:core'; Template.timepointBrowserItem.onCreated(() => { const instance = Template.instance(); const { timepoint, timepointApi } = instance.data; + const { timepointId } = timepoint; + const hasStudiesData = !!(timepoint.studiesData && timepoint.studiesData.length); instance.summary = new ReactiveVar(''); - instance.loading = new ReactiveVar(false); - instance.loaded = !!(timepoint.studiesData && timepoint.studiesData.length); + instance.studiesData = new ReactiveVar(hasStudiesData ? timepoint.studiesData : null); + + const updateStudiesData = newDocument => { + const newTimepoint = newDocument || timepointApi.timepoints.findOne({ timepointId }); + if (newTimepoint && newTimepoint.studiesData && newTimepoint.studiesData.length) { + instance.studiesData.set(newTimepoint.studiesData); + } + }; + + timepointApi.timepoints.find({ timepointId }).observe({ changed: updateStudiesData }); // Build the modalities summary of all timepoint's studies instance.setModalitiesSummary = () => { + const studiesData = instance.studiesData.get(); + if (!studiesData) return; + const modalities = {}; - timepoint.studiesData.forEach(study => { + studiesData.forEach(study => { const modality = study.modalities || 'UN'; modalities[modality] = modalities[modality] + 1 || 1; }); @@ -29,20 +42,22 @@ Template.timepointBrowserItem.onCreated(() => { const filter = { studyInstanceUid: timepoint.studyInstanceUids }; instance.loadStudies = () => OHIF.studies.searchStudies(filter).then(studiesData => { - timepoint.studiesData = studiesData; + instance.studiesData.set(studiesData); timepointApi.timepoints.update(timepoint._id, { $set: { studiesData } }); - instance.loaded = true; instance.setModalitiesSummary(); - instance.loading.set(false); }).catch(error => { const text = 'An error has occurred while retrieving studies information'; OHIF.ui.notifications.danger({ text }); OHIF.log.error(error); }); - if (instance.loaded) { - instance.setModalitiesSummary(); - } + updateStudiesData(); + instance.autorun(() => { + const studiesData = instance.studiesData.get(); + if (studiesData) { + instance.setModalitiesSummary(); + } + }); }); Template.timepointBrowserItem.events({ @@ -58,7 +73,7 @@ Template.timepointBrowserItem.events({ $element.trigger('ohif.lesiontracker.timepoint.click', instance.data.timepoint); }; - if (!instance.loaded) { + if (!instance.studiesData.get()) { instance.summary.set('Loading...'); instance.loadStudies().then(() => Tracker.afterFlush(triggerClick)); } else { diff --git a/Packages/ohif-lesiontracker/client/components/timepointBrowser/list.styl b/Packages/ohif-lesiontracker/client/components/timepointBrowser/list.styl index 5397f7485..653575aa3 100644 --- a/Packages/ohif-lesiontracker/client/components/timepointBrowser/list.styl +++ b/Packages/ohif-lesiontracker/client/components/timepointBrowser/list.styl @@ -77,3 +77,9 @@ .timepoint-title theme('color', '$textPrimaryColor') padding-top: 2px + +.series-quick-switch .timepoint-browser-list + padding: 0 10px + + div.timepoint-title + text-transform: none diff --git a/Packages/ohif-lesiontracker/client/components/timepointBrowser/quickSwitch.html b/Packages/ohif-lesiontracker/client/components/timepointBrowser/quickSwitch.html index 5e7d638cc..99e11f257 100644 --- a/Packages/ohif-lesiontracker/client/components/timepointBrowser/quickSwitch.html +++ b/Packages/ohif-lesiontracker/client/components/timepointBrowser/quickSwitch.html @@ -1,6 +1,6 @@ diff --git a/Packages/ohif-lesiontracker/client/components/timepointBrowser/quickSwitch.js b/Packages/ohif-lesiontracker/client/components/timepointBrowser/quickSwitch.js index 51ce41925..52949ed90 100644 --- a/Packages/ohif-lesiontracker/client/components/timepointBrowser/quickSwitch.js +++ b/Packages/ohif-lesiontracker/client/components/timepointBrowser/quickSwitch.js @@ -2,45 +2,66 @@ import { Meteor } from 'meteor/meteor'; import { Template } from 'meteor/templating'; import { ReactiveVar } from 'meteor/reactive-var'; import { Tracker } from 'meteor/tracker'; +import { _ } from 'meteor/underscore'; import { OHIF } from 'meteor/ohif:core'; Template.timepointBrowserQuickSwitch.onCreated(() => { const instance = Template.instance(); - instance.showAdditional = new ReactiveVar(false); - instance.currentTimepoint = new ReactiveVar(); + const { timepointApi } = OHIF.viewer; - instance.updateCurrentTimepoint = studyInstanceUid => { - const currentTimepoint = OHIF.viewer.timepointApi.study(studyInstanceUid)[0]; - instance.currentTimepoint.set(currentTimepoint); + instance.hasAdditional = new ReactiveVar(false); + instance.showAdditional = new ReactiveVar(false); + instance.selectedTimepoint = new ReactiveVar(); + instance.timepoints = new ReactiveVar([]); + + instance.updateSelectedTimepoint = studyInstanceUid => { + const selectedTimepoint = timepointApi.study(studyInstanceUid)[0]; + instance.selectedTimepoint.set(selectedTimepoint); }; + const currentTimepoint = timepointApi.current(); + const filter = { latestDate: { $lte: currentTimepoint.latestDate } }; + instance.keyTimepoints = timepointApi.key(filter); + const { viewportIndex } = instance.data; instance.autorun(() => { OHIF.viewerbase.layoutManager.observer.depend(); const viewportData = OHIF.viewerbase.layoutManager.viewportData[viewportIndex]; - instance.updateCurrentTimepoint(viewportData.studyInstanceUid); + instance.updateSelectedTimepoint(viewportData.studyInstanceUid); }); instance.autorun(() => { instance.data.currentStudy.dep.depend(); instance.showAdditional.set(false); }); + + instance.autorun(() => { + const showAdditional = instance.showAdditional.get(); + instance.hasAdditional.set(!showAdditional && instance.keyTimepoints.length > 1); + }); + + instance.autorun(() => { + const selectedTimepoint = instance.selectedTimepoint.get(); + const showAdditional = instance.showAdditional.get(); + const timepoints = showAdditional ? instance.keyTimepoints : [selectedTimepoint]; + instance.timepoints.set(timepoints); + }); }); Template.timepointBrowserQuickSwitch.onRendered(() => { const instance = Template.instance(); instance.autorun(() => { - const currentTimepoint = instance.currentTimepoint.get(); - const currentTimepointId = (currentTimepoint && currentTimepoint.timepointId) || ''; + const selectedTimepoint = instance.selectedTimepoint.get(); + const selectedTimepointId = (selectedTimepoint && selectedTimepoint.timepointId) || ''; const $allBrowserItems = instance.$('.timepoint-browser-item'); - const $browserItem = $allBrowserItems.filter(`[data-id=${currentTimepointId}]`); + const $browserItem = $allBrowserItems.filter(`[data-id=${selectedTimepointId}]`); if (!$browserItem.hasClass('active')) { $browserItem.find('.timepoint-item').trigger('click'); } }); - instance.autorun(() => { + instance.updateActiveStudy = () => { const currentStudy = instance.data.currentStudy.get(); const studyInstanceUid = (currentStudy && currentStudy.studyInstanceUid) || ''; Tracker.afterFlush(() => { @@ -48,15 +69,16 @@ Template.timepointBrowserQuickSwitch.onRendered(() => { $studyBrowserItems.removeClass('active'); $studyBrowserItems.filter(`[data-uid="${studyInstanceUid}"]`).addClass('active'); }); - }); + }; + + instance.autorun(instance.updateActiveStudy); }); Template.timepointBrowserQuickSwitch.events({ 'ohif.lesiontracker.timepoint.click'(event, instance) { const $element = $(event.currentTarget); - - // Defer the active class toggling to wait for child template rendering - Meteor.defer(() => $element.toggleClass('active')); + $element.toggleClass('active'); + instance.updateActiveStudy(); }, 'ohif.studies.study.click'(event, instance, studyInformation) { @@ -65,29 +87,39 @@ Template.timepointBrowserQuickSwitch.events({ instance.data.currentStudy.set(study); const $studySwitch = $(event.currentTarget).closest('.study-switch'); $studySwitch.siblings('.series-switch').trigger('rescale'); - instance.updateCurrentTimepoint(studyInformation.studyInstanceUid); + instance.updateSelectedTimepoint(studyInformation.studyInstanceUid); + + // Create a hover bridge to prevent quick switch from closing due to scroll height + Meteor.defer(() => { + const $scrollable = $studySwitch.find('.study-browser>.scrollable'); + const offsetY = $scrollable.offset().top + $scrollable.outerHeight(); + if (event.clientY > offsetY) { + const hoverHandler = _.throttle(event => { + if (event.clientY <= offsetY) { + $scrollable.css('padding-bottom', ''); + $scrollable.off('mousemove', hoverHandler); + $scrollable.off('mouseleave', hoverHandler); + } + }, 100); + $scrollable.css('padding-bottom', event.clientY - offsetY + 20); + $scrollable.on('mousemove', hoverHandler); + $scrollable.one('mouseleave', () => $scrollable.off('mousemove', hoverHandler)); + } + }); }, - 'click .show-additional'(event, instance) { + 'click .study-item-box.additional'(event, instance) { instance.showAdditional.set(true); - }, + } }); Template.timepointBrowserQuickSwitch.helpers({ timepointBrowserData() { const instance = Template.instance(); const { timepointApi } = OHIF.viewer; - const currentTimepoint = instance.currentTimepoint.get(); - let timepoints; - if (instance.showAdditional.get()) { - timepoints = timepointApi.key(); - } else { - timepoints = [currentTimepoint]; - } - return { timepointApi, - timepoints, + timepoints: instance.timepoints.get(), timepointChildTemplate: 'timepointBrowserStudies' }; } diff --git a/Packages/ohif-measurements/both/configuration/timepoints.js b/Packages/ohif-measurements/both/configuration/timepoints.js index a76ebc370..c28320a42 100644 --- a/Packages/ohif-measurements/both/configuration/timepoints.js +++ b/Packages/ohif-measurements/both/configuration/timepoints.js @@ -192,11 +192,11 @@ class TimepointApi { } // Return only the key timepoints (current, prior, nadir and baseline) - key() { + key(filter={}) { const result = []; // Get all the timepoints - const all = this.all(); + const all = this.all(filter); // Iterate over each timepoint and insert the key ones in the result _.each(all, (timepoint, index) => { diff --git a/Packages/ohif-studies/imports/client/components/browser/item.js b/Packages/ohif-studies/imports/client/components/browser/item.js index ab4f84d7c..6d8cc0d46 100644 --- a/Packages/ohif-studies/imports/client/components/browser/item.js +++ b/Packages/ohif-studies/imports/client/components/browser/item.js @@ -22,7 +22,13 @@ Template.studyBrowserItem.events({ const { studyInformation } = instance.data; const element = event.currentTarget.parentElement; const $element = $(element); - const triggerClick = () => $element.trigger('ohif.studies.study.click', studyInformation); + const triggerClick = () => { + const cloneEvent = _.clone(event); + delete cloneEvent.type; + cloneEvent.currentTarget = cloneEvent.target = element; + const newEvent = $.Event('ohif.studies.study.click', cloneEvent); + $element.trigger(newEvent, studyInformation); + }; if (instance.loaded) { triggerClick(); diff --git a/Packages/ohif-viewerbase/client/components/viewer/seriesQuickSwitch/seriesQuickSwitch.js b/Packages/ohif-viewerbase/client/components/viewer/seriesQuickSwitch/seriesQuickSwitch.js index 023b6172d..fa87c3643 100644 --- a/Packages/ohif-viewerbase/client/components/viewer/seriesQuickSwitch/seriesQuickSwitch.js +++ b/Packages/ohif-viewerbase/client/components/viewer/seriesQuickSwitch/seriesQuickSwitch.js @@ -3,10 +3,6 @@ import { ReactiveVar } from 'meteor/reactive-var'; import { $ } from 'meteor/jquery'; import { OHIF } from 'meteor/ohif:core'; -// FIXME >>>> REMOVE -$(document.body).on('keydown', e => (e.keyCode === 69 && $('.quickSwitchWrapper').toggle()) || 1); -OHIF.cornerstone.renderer = ''; - Template.seriesQuickSwitch.onCreated(() => { const instance = Template.instance();