Creating sidebar templates for studies/timepoints browsers
This commit is contained in:
parent
d787ed2cb9
commit
3b629f9816
@ -1,9 +1,15 @@
|
|||||||
<template name="viewerSection">
|
<template name="viewerSection">
|
||||||
<div class="viewerSection">
|
<div class="viewerSection">
|
||||||
<div class="sidebarMenu sidebar-left {{#if leftSidebarOpen}}sidebar-open{{/if}}">
|
<div class="sidebarMenu sidebar-left {{#if leftSidebarOpen}}sidebar-open{{/if}}">
|
||||||
{{>timepointBrowserList (extend timepointBrowserData scrollableClass='p-a-1')}}
|
{{#if isTimepointBrowser}}
|
||||||
<!-- <hr> -->
|
{{>timepointBrowserSidebar (clone this)}}
|
||||||
<!-- {{>studyTimepointBrowser (clone this)}} -->
|
{{else}}
|
||||||
|
{{#if instance.loading.get}}
|
||||||
|
{{>loadingText}}
|
||||||
|
{{else}}
|
||||||
|
{{>studyBrowserSidebar (clone this studiesInformation=instance.studiesInformation.get)}}
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
<div class="mainContent {{#if leftSidebarOpen}}sidebar-left-open{{/if}} {{#if rightSidebarOpen}}sidebar-right-open{{/if}}">
|
<div class="mainContent {{#if leftSidebarOpen}}sidebar-left-open{{/if}} {{#if rightSidebarOpen}}sidebar-right-open{{/if}}">
|
||||||
{{>viewerMain (clone this)}}
|
{{>viewerMain (clone this)}}
|
||||||
|
|||||||
@ -1,41 +1,33 @@
|
|||||||
import { Meteor } from 'meteor/meteor';
|
|
||||||
import { Template } from 'meteor/templating';
|
import { Template } from 'meteor/templating';
|
||||||
|
import { ReactiveVar } from 'meteor/reactive-var';
|
||||||
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
|
|
||||||
|
Template.viewerSection.onCreated(() => {
|
||||||
|
const instance = Template.instance();
|
||||||
|
|
||||||
|
instance.isTimepointBrowser = () => !!OHIF.viewer.data.currentTimepointId;
|
||||||
|
|
||||||
|
if (!instance.isTimepointBrowser()) {
|
||||||
|
instance.loading = new ReactiveVar(true);
|
||||||
|
instance.studiesInformation = new ReactiveVar([]);
|
||||||
|
const filter = { studyInstanceUid: OHIF.viewer.data.studyInstanceUids };
|
||||||
|
OHIF.studies.searchStudies(filter).then(studiesData => {
|
||||||
|
instance.loading.set(false);
|
||||||
|
instance.studiesInformation.set(studiesData);
|
||||||
|
}).catch(error => {
|
||||||
|
instance.loading.set(false);
|
||||||
|
const text = 'An error has occurred while retrieving studies information';
|
||||||
|
OHIF.ui.notifications.danger({ text });
|
||||||
|
OHIF.log.error(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Template.viewerSection.events({
|
Template.viewerSection.events({
|
||||||
'transitionend .sidebarMenu'(event) {
|
'transitionend .sidebarMenu'(event) {
|
||||||
if (!event.target.classList.contains('sidebarMenu')) {
|
if (!event.target.classList.contains('sidebarMenu')) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.ResizeViewportManager.handleResize();
|
window.ResizeViewportManager.handleResize();
|
||||||
},
|
|
||||||
|
|
||||||
'ohif.studies.study.click'(event, instance) {
|
|
||||||
const $element = $(event.currentTarget);
|
|
||||||
|
|
||||||
// Defer the active class toggling to wait for child template rendering
|
|
||||||
Meteor.defer(() => {
|
|
||||||
// Remove max height restriction from studies browser
|
|
||||||
const $studiesBrowser = $element.closest('.timepoint-browser-studies');
|
|
||||||
$studiesBrowser.css('max-height', '');
|
|
||||||
|
|
||||||
// Remove active class from sibling studies
|
|
||||||
$element.siblings().removeClass('active');
|
|
||||||
|
|
||||||
// Toggle the active class on clicked study
|
|
||||||
$element.toggleClass('active');
|
|
||||||
|
|
||||||
// Adjust the max height for studiesBrowser when series transition is finished
|
|
||||||
const $seriesBrowser = $element.find('.study-browser-series');
|
|
||||||
$seriesBrowser.one('transitionend', () => $studiesBrowser.adjustMax('height'));
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
'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'));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -48,15 +40,11 @@ Template.viewerSection.helpers({
|
|||||||
return Template.instance().data.state.get('rightSidebar');
|
return Template.instance().data.state.get('rightSidebar');
|
||||||
},
|
},
|
||||||
|
|
||||||
timepointBrowserData() {
|
isTimepointBrowser() {
|
||||||
const instance = Template.instance();
|
return Template.instance().isTimepointBrowser();
|
||||||
const { timepointApi } = instance.data;
|
},
|
||||||
const timepoints = timepointApi.all();
|
|
||||||
return {
|
studiesInformation() {
|
||||||
timepointApi,
|
return Template.instance().studiesInformation.get();
|
||||||
timepoints,
|
|
||||||
timepointChildTemplate: 'timepointBrowserStudies',
|
|
||||||
studyChildTemplate: 'studyBrowserSeries'
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -37,6 +37,10 @@
|
|||||||
margin-left: 0
|
margin-left: 0
|
||||||
visibility: visible
|
visibility: visible
|
||||||
|
|
||||||
|
.loadingTextDiv
|
||||||
|
theme('color', '$textSecondaryColor')
|
||||||
|
font-size: 32px
|
||||||
|
|
||||||
.mainContent
|
.mainContent
|
||||||
flex: 1
|
flex: 1
|
||||||
height: 100%
|
height: 100%
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
import './item.html';
|
import './item.html';
|
||||||
import './item.js';
|
import './item.js';
|
||||||
import './item.styl';
|
|
||||||
import './list.html';
|
import './list.html';
|
||||||
|
import './list.styl';
|
||||||
|
import './sidebar.html';
|
||||||
|
import './sidebar.js';
|
||||||
import './studies.html';
|
import './studies.html';
|
||||||
import './studies.js';
|
import './studies.js';
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import { Meteor } from 'meteor/meteor';
|
|
||||||
import { Template } from 'meteor/templating';
|
import { Template } from 'meteor/templating';
|
||||||
import { ReactiveVar } from 'meteor/reactive-var';
|
import { ReactiveVar } from 'meteor/reactive-var';
|
||||||
import { Tracker } from 'meteor/tracker';
|
import { Tracker } from 'meteor/tracker';
|
||||||
@ -28,23 +27,16 @@ Template.timepointBrowserItem.onCreated(() => {
|
|||||||
instance.summary.set(summary.join(', '));
|
instance.summary.set(summary.join(', '));
|
||||||
};
|
};
|
||||||
|
|
||||||
instance.loadStudies = () => new Promise((resolve, reject) => {
|
const filter = { studyInstanceUid: timepoint.studyInstanceUids };
|
||||||
const filter = { studyInstanceUid: timepoint.studyInstanceUids };
|
instance.loadStudies = () => OHIF.studies.searchStudies(filter).then(studiesData => {
|
||||||
instance.summary.set('Loading...');
|
timepoint.studiesData = studiesData;
|
||||||
Meteor.call('StudyListSearch', filter, (error, studiesData) => {
|
timepointApi.timepoints.update(timepoint._id, { $set: { studiesData } });
|
||||||
if (error) {
|
instance.loaded = true;
|
||||||
const text = 'An error has occurred while retrieving studies information';
|
instance.loading.set(false);
|
||||||
OHIF.ui.notifications.danger({ text });
|
}).catch(error => {
|
||||||
OHIF.log.error(error);
|
const text = 'An error has occurred while retrieving studies information';
|
||||||
reject(error);
|
OHIF.ui.notifications.danger({ text });
|
||||||
} else {
|
OHIF.log.error(error);
|
||||||
timepoint.studiesData = studiesData;
|
|
||||||
timepointApi.timepoints.update(timepoint._id, { $set: { studiesData } });
|
|
||||||
instance.loaded = true;
|
|
||||||
instance.loading.set(false);
|
|
||||||
resolve(studiesData);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (instance.loaded) {
|
if (instance.loaded) {
|
||||||
@ -62,6 +54,7 @@ Template.timepointBrowserItem.events({
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (!instance.loaded) {
|
if (!instance.loaded) {
|
||||||
|
instance.summary.set('Loading...');
|
||||||
instance.loadStudies().then(() => {
|
instance.loadStudies().then(() => {
|
||||||
Tracker.afterFlush(triggerClick);
|
Tracker.afterFlush(triggerClick);
|
||||||
instance.setModalitiesSummary();
|
instance.setModalitiesSummary();
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
@require '{ohif:design}/app'
|
@require '{ohif:design}/app'
|
||||||
|
|
||||||
|
.timepoint-switch
|
||||||
|
theme('border-bottom', '1px solid $uiBorderColor')
|
||||||
|
|
||||||
.timepoint-browser-item
|
.timepoint-browser-item
|
||||||
|
|
||||||
.timepoint-item
|
.timepoint-item
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
<template name="timepointBrowserSidebar">
|
||||||
|
<div class="full-height flex-v">
|
||||||
|
<div class="p-x-1">
|
||||||
|
<div class="timepoint-switch p-y-2">
|
||||||
|
{{>roundedButtonGroup viewTypeButtonGroupData}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{#scrollArea class='flex-grow' scrollableClass='p-a-1'}}
|
||||||
|
{{>timepointBrowserList (extend timepointBrowserData)}}
|
||||||
|
{{/scrollArea}}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@ -0,0 +1,93 @@
|
|||||||
|
import { Meteor } from 'meteor/meteor';
|
||||||
|
import { Template } from 'meteor/templating';
|
||||||
|
import { ReactiveVar } from 'meteor/reactive-var';
|
||||||
|
|
||||||
|
Template.timepointBrowserSidebar.onCreated(() => {
|
||||||
|
const instance = Template.instance();
|
||||||
|
|
||||||
|
// Reactive variable to control the view type for all or key timepoints
|
||||||
|
instance.timepointViewType = new ReactiveVar(instance.data.timepointViewType);
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.timepointBrowserSidebar.onRendered(() => {
|
||||||
|
const instance = Template.instance();
|
||||||
|
|
||||||
|
// Collapse all timepoints but first when timepoint view type changes
|
||||||
|
instance.autorun(() => {
|
||||||
|
// Runs this computation every time the timepointViewType is changed
|
||||||
|
const type = instance.timepointViewType.get();
|
||||||
|
|
||||||
|
// Removes all active classes to collapse the timepoints and studies
|
||||||
|
instance.$('.timepoint-browser-item, .study-browser-item').removeClass('active');
|
||||||
|
if (type === 'key') {
|
||||||
|
// Show only first timepoint expanded for key timepoints
|
||||||
|
instance.$('.timepoint-browser-item:first .timepoint-item').trigger('click');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.timepointBrowserSidebar.events({
|
||||||
|
'ohif.studies.study.click'(event, instance) {
|
||||||
|
const $element = $(event.currentTarget);
|
||||||
|
|
||||||
|
// Defer the active class toggling to wait for child template rendering
|
||||||
|
Meteor.defer(() => {
|
||||||
|
// Remove max height restriction from studies browser
|
||||||
|
const $studiesBrowser = $element.closest('.timepoint-browser-studies');
|
||||||
|
$studiesBrowser.css('max-height', '');
|
||||||
|
|
||||||
|
// Remove active class from sibling studies
|
||||||
|
$element.siblings().removeClass('active');
|
||||||
|
|
||||||
|
// Toggle the active class on clicked study
|
||||||
|
$element.toggleClass('active');
|
||||||
|
|
||||||
|
// Adjust the max height for studiesBrowser when series transition is finished
|
||||||
|
const $seriesBrowser = $element.find('.study-browser-series');
|
||||||
|
$seriesBrowser.one('transitionend', () => $studiesBrowser.adjustMax('height'));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
'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'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.timepointBrowserSidebar.helpers({
|
||||||
|
viewTypeButtonGroupData() {
|
||||||
|
return {
|
||||||
|
value: Template.instance().timepointViewType,
|
||||||
|
options: [{
|
||||||
|
value: 'key',
|
||||||
|
text: 'Key Timepoints'
|
||||||
|
}, {
|
||||||
|
value: 'all',
|
||||||
|
text: 'All Timepoints'
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
timepointBrowserData() {
|
||||||
|
const instance = Template.instance();
|
||||||
|
const { timepointApi } = instance.data;
|
||||||
|
|
||||||
|
const currentTimepoint = timepointApi.current();
|
||||||
|
const { patientId } = currentTimepoint;
|
||||||
|
let timepoints = [];
|
||||||
|
if (instance.timepointViewType.get() === 'key') {
|
||||||
|
timepoints = timepointApi.key();
|
||||||
|
} else {
|
||||||
|
timepoints = timepointApi.all({ patientId });
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
timepointApi,
|
||||||
|
timepoints,
|
||||||
|
timepointChildTemplate: 'timepointBrowserStudies',
|
||||||
|
studyChildTemplate: 'studyBrowserSeries'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -12,6 +12,10 @@ measurementTools.forEach(tool => {
|
|||||||
const Timepoints = new Mongo.Collection('timepoints');
|
const Timepoints = new Mongo.Collection('timepoints');
|
||||||
Timepoints._debugName = 'Timepoints';
|
Timepoints._debugName = 'Timepoints';
|
||||||
|
|
||||||
|
Meteor.publish('timepoints', function() {
|
||||||
|
return Timepoints.find();
|
||||||
|
});
|
||||||
|
|
||||||
// TODO: Make storage use update instead of clearing the entire collection and
|
// TODO: Make storage use update instead of clearing the entire collection and
|
||||||
// re-inserting everything.
|
// re-inserting everything.
|
||||||
Meteor.methods({
|
Meteor.methods({
|
||||||
|
|||||||
@ -1,37 +0,0 @@
|
|||||||
import { Meteor } from 'meteor/meteor';
|
|
||||||
|
|
||||||
Meteor.publish('timepoints', function() {
|
|
||||||
return Timepoints.find();
|
|
||||||
});
|
|
||||||
|
|
||||||
Meteor.publish('singlePatientTimepoints', function(patientId) {
|
|
||||||
return Timepoints.find({
|
|
||||||
patientId: patientId
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
Meteor.publish('studies', function() {
|
|
||||||
return Studies.find();
|
|
||||||
});
|
|
||||||
|
|
||||||
Meteor.publish('singlePatientAssociatedStudies', function(patientId) {
|
|
||||||
return Studies.find({
|
|
||||||
patientId: patientId
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
Meteor.publish('singlePatientMeasurements', function(patientId) {
|
|
||||||
return Measurements.find({
|
|
||||||
patientId: patientId
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
Meteor.publish('singlePatientImageMeasurements', function(patientId) {
|
|
||||||
return ImageMeasurements.find({
|
|
||||||
patientId: patientId
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
Meteor.publish('reviewers', function() {
|
|
||||||
return Reviewers.find();
|
|
||||||
});
|
|
||||||
@ -120,8 +120,8 @@ class TimepointApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return all timepoints
|
// Return all timepoints
|
||||||
all() {
|
all(filter={}) {
|
||||||
return this.timepoints.find({}, {
|
return this.timepoints.find(filter, {
|
||||||
sort: {
|
sort: {
|
||||||
latestDate: -1
|
latestDate: -1
|
||||||
},
|
},
|
||||||
|
|||||||
@ -186,6 +186,11 @@ export const UISettings = new SimpleSchema({
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
label: 'Define if the middle instance of a series will be used as thumbnail. If not, the first instance will be used.',
|
label: 'Define if the middle instance of a series will be used as thumbnail. If not, the first instance will be used.',
|
||||||
defaultValue: true
|
defaultValue: true
|
||||||
|
},
|
||||||
|
displaySeriesQuickSwitch: {
|
||||||
|
type: Boolean,
|
||||||
|
label: 'Define if the studies and series quick switch should be displayed above the viewer viewport',
|
||||||
|
defaultValue: false
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
import './list.html';
|
import './list.html';
|
||||||
|
import './list.styl';
|
||||||
import './item.html';
|
import './item.html';
|
||||||
import './item.js';
|
import './item.js';
|
||||||
import './item.styl';
|
|
||||||
import './series.html';
|
import './series.html';
|
||||||
import './series.js';
|
import './series.js';
|
||||||
|
import './sidebar.html';
|
||||||
|
import './sidebar.js';
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { Template } from 'meteor/templating';
|
|||||||
import { ReactiveVar } from 'meteor/reactive-var';
|
import { ReactiveVar } from 'meteor/reactive-var';
|
||||||
import { Tracker } from 'meteor/tracker';
|
import { Tracker } from 'meteor/tracker';
|
||||||
import { $ } from 'meteor/jquery';
|
import { $ } from 'meteor/jquery';
|
||||||
|
import { _ } from 'meteor/underscore';
|
||||||
import { OHIF } from 'meteor/ohif:core';
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
|
|
||||||
Template.studyBrowserItem.onCreated(() => {
|
Template.studyBrowserItem.onCreated(() => {
|
||||||
@ -11,7 +12,7 @@ Template.studyBrowserItem.onCreated(() => {
|
|||||||
instance.loading = new ReactiveVar(false);
|
instance.loading = new ReactiveVar(false);
|
||||||
|
|
||||||
const modalities = instance.data.studyInformation.modalities || 'UN';
|
const modalities = instance.data.studyInformation.modalities || 'UN';
|
||||||
instance.modalities = modalities.replace(/\\/g, ' ');
|
instance.modalities = _.uniq(modalities.split(/[^A-Za-z]+/g)).join(' ');
|
||||||
});
|
});
|
||||||
|
|
||||||
Template.studyBrowserItem.events({
|
Template.studyBrowserItem.events({
|
||||||
@ -27,7 +28,7 @@ Template.studyBrowserItem.events({
|
|||||||
triggerClick();
|
triggerClick();
|
||||||
} else {
|
} else {
|
||||||
instance.loading.set(true);
|
instance.loading.set(true);
|
||||||
OHIF.studies.retrieveStudyMetadata(studyInformation.studyInstanceUid).then(() => {
|
OHIF.studies.loadStudy(studyInformation.studyInstanceUid).then(() => {
|
||||||
instance.loaded = true;
|
instance.loaded = true;
|
||||||
instance.loading.set(false);
|
instance.loading.set(false);
|
||||||
$element.trigger('ohif.studies.study.load', studyInformation);
|
$element.trigger('ohif.studies.study.load', studyInformation);
|
||||||
@ -43,22 +44,19 @@ Template.studyBrowserItem.helpers({
|
|||||||
},
|
},
|
||||||
|
|
||||||
modalityStyle() {
|
modalityStyle() {
|
||||||
// Responsively styles the Modality Acronyms for studies
|
// Responsively styles the Modality Acronyms for studies with more than one modality
|
||||||
// with more than one modality
|
|
||||||
const instance = Template.instance();
|
const instance = Template.instance();
|
||||||
const numModalities = instance.modalities.split(/\s/g).length;
|
const numModalities = instance.modalities.split(/\s/g).length;
|
||||||
|
|
||||||
if (numModalities === 1) {
|
if (numModalities === 1) {
|
||||||
// If we have only one modality, it should take up the whole div.
|
// If we have only one modality, it should take up the whole div
|
||||||
return 'font-size: 1em';
|
return 'font-size:1em';
|
||||||
} else if (numModalities === 2) {
|
} else if (numModalities === 2) {
|
||||||
// If we have two, let them sit side-by-side
|
// If we have two modalities, let them sit side-by-side
|
||||||
return 'font-size: 0.75em';
|
return 'font-size:0.7em';
|
||||||
} else {
|
} else {
|
||||||
// If we have more than two modalities, change the line height to display multiple rows,
|
// If we have more than two modalities, change the line height to display multiple rows
|
||||||
// depending on the number of modalities we need to display.
|
return 'font-size:0.7em;line-height:1.4em;padding-top:0.5em';
|
||||||
const lineHeight = Math.ceil(numModalities / 2) * 1.2;
|
|
||||||
return 'line-height: ' + lineHeight + 'em';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -17,14 +17,16 @@ $spacerY = 12px
|
|||||||
transform(scale(1))
|
transform(scale(1))
|
||||||
|
|
||||||
.loadingTextDiv
|
.loadingTextDiv
|
||||||
padding: 25px
|
theme('color', '$textSecondaryColor')
|
||||||
border-radius: 12px
|
|
||||||
position: absolute
|
|
||||||
opacity: 0
|
|
||||||
z-index: 100
|
|
||||||
background-color: $boxHoverBackgroundColor
|
background-color: $boxHoverBackgroundColor
|
||||||
pointer-events: none
|
border-radius: 12px
|
||||||
|
font-size: 32px
|
||||||
|
opacity: 0
|
||||||
margin: 0
|
margin: 0
|
||||||
|
padding: 25px
|
||||||
|
position: absolute
|
||||||
|
pointer-events: none
|
||||||
|
z-index: 100
|
||||||
|
|
||||||
&.loading
|
&.loading
|
||||||
.loadingTextDiv
|
.loadingTextDiv
|
||||||
@ -111,9 +113,10 @@ $spacerY = 12px
|
|||||||
position: relative
|
position: relative
|
||||||
|
|
||||||
.study-modality-text
|
.study-modality-text
|
||||||
|
height: 100%
|
||||||
|
overflow: hidden
|
||||||
text-align: center
|
text-align: center
|
||||||
text-transform: uppercase
|
text-transform: uppercase
|
||||||
height: 100%
|
|
||||||
|
|
||||||
&
|
&
|
||||||
&:before
|
&:before
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
<template name="studyBrowserSidebar">
|
||||||
|
{{#scrollArea class='full-height' scrollableClass='p-a-1'}}
|
||||||
|
{{>studyBrowserList (clone this studyChildTemplate='studyBrowserSeries')}}
|
||||||
|
{{/scrollArea}}
|
||||||
|
</template>
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
import { Template } from 'meteor/templating';
|
||||||
|
|
||||||
|
Template.studyBrowserSidebar.events({
|
||||||
|
'ohif.studies.study.click'(event, instance) {
|
||||||
|
const $element = $(event.currentTarget);
|
||||||
|
|
||||||
|
// Remove active class from sibling studies
|
||||||
|
$element.siblings().removeClass('active');
|
||||||
|
|
||||||
|
// Toggle the active class on clicked study
|
||||||
|
$element.toggleClass('active');
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -1,2 +1,4 @@
|
|||||||
|
import './loadStudy';
|
||||||
import './retrieveStudiesMetadata';
|
import './retrieveStudiesMetadata';
|
||||||
import './retrieveStudyMetadata';
|
import './retrieveStudyMetadata';
|
||||||
|
import './searchStudies';
|
||||||
|
|||||||
20
Packages/ohif-studies/imports/client/lib/loadStudy.js
Normal file
20
Packages/ohif-studies/imports/client/lib/loadStudy.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the study metadata and store its information locally
|
||||||
|
*
|
||||||
|
* @param {String} studyInstanceUid The UID of the Study to be loaded
|
||||||
|
* @returns {Promise} that will be resolved with the study metadata or rejected with an error
|
||||||
|
*/
|
||||||
|
OHIF.studies.loadStudy = studyInstanceUid => new Promise((resolve, reject) => {
|
||||||
|
OHIF.studies.retrieveStudyMetadata(studyInstanceUid).then(study => {
|
||||||
|
// Double check to make sure this study wasn't already inserted into OHIF.viewer.Studies
|
||||||
|
// so we don't cause duplicate entry errors
|
||||||
|
const loaded = OHIF.viewer.Studies.findBy({ studyInstanceUid: study.studyInstanceUid });
|
||||||
|
if (!loaded) {
|
||||||
|
OHIF.viewer.Studies.insert(study);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(study);
|
||||||
|
}).catch(reject);
|
||||||
|
});
|
||||||
18
Packages/ohif-studies/imports/client/lib/searchStudies.js
Normal file
18
Packages/ohif-studies/imports/client/lib/searchStudies.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { Meteor } from 'meteor/meteor';
|
||||||
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for studies information by the given filter
|
||||||
|
*
|
||||||
|
* @param {Object} filter Filter that will be used on search
|
||||||
|
* @returns {Promise} resolved with an array of studies information or rejected with an error
|
||||||
|
*/
|
||||||
|
OHIF.studies.searchStudies = filter => new Promise((resolve, reject) => {
|
||||||
|
Meteor.call('StudyListSearch', filter, (error, studiesData) => {
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
} else {
|
||||||
|
resolve(studiesData);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -30,9 +30,10 @@ $seriesCountBackgroundColor = #678696
|
|||||||
|
|
||||||
.seriesInformation
|
.seriesInformation
|
||||||
display: flex
|
display: flex
|
||||||
|
flex-grow: 1
|
||||||
float: none
|
float: none
|
||||||
max-width: none
|
max-width: none
|
||||||
padding-right: none
|
padding-right: 0
|
||||||
|
|
||||||
.item
|
.item
|
||||||
flex: 1
|
flex: 1
|
||||||
|
|||||||
@ -27,7 +27,8 @@
|
|||||||
"leftSidebarOpen": false,
|
"leftSidebarOpen": false,
|
||||||
"displaySetNavigationLoopOverSeries": false,
|
"displaySetNavigationLoopOverSeries": false,
|
||||||
"displaySetNavigationMultipleViewports": true,
|
"displaySetNavigationMultipleViewports": true,
|
||||||
"autoPositionMeasurementsTextCallOuts": "TRLB"
|
"autoPositionMeasurementsTextCallOuts": "TRLB",
|
||||||
|
"displaySeriesQuickSwitch": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"proxy": {
|
"proxy": {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user