reorganize packages, start converting overall app to react (very broken at the moment)
This commit is contained in:
1 parent
024fe1a448
commit
5f30f7bd35
479 files changed
+5364
-11611
No files matched your search
+12
@@ -0,0 +1,12 @@
|
||||
<template name="caseProgress">
|
||||
{{#form (extend class='caseProgress' api=instance.api)}}
|
||||
{{#if instance.isFollowUp.get}}
|
||||
{{>radialProgressBar isLocked=isLocked progressPercent=progressPercent progressText=progressText}}
|
||||
{{#if isLocked}}
|
||||
<div class="caseProgressStatus">
|
||||
<h5>Locked</h5>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/form}}
|
||||
</template>
|
||||
@@ -0,0 +1,143 @@
|
||||
import { Template } from 'meteor/templating';
|
||||
import { ReactiveVar } from 'meteor/reactive-var';
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
|
||||
Template.caseProgress.onCreated(() => {
|
||||
const instance = Template.instance();
|
||||
|
||||
instance.progressPercent = new ReactiveVar();
|
||||
instance.progressText = new ReactiveVar();
|
||||
instance.isLocked = new ReactiveVar(false);
|
||||
instance.isFollowUp = new ReactiveVar(false);
|
||||
});
|
||||
|
||||
Template.caseProgress.onRendered(() => {
|
||||
const instance = Template.instance();
|
||||
const { timepointApi, measurementApi, timepointId } = instance.data;
|
||||
|
||||
// Stop here if we have no current timepoint ID (and therefore no defined timepointAPI)
|
||||
if (!timepointApi) {
|
||||
instance.progressPercent.set(100);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the current and prior timepoints
|
||||
const current = timepointApi.timepoints.findOne({ timepointId });
|
||||
const priorFilter = {
|
||||
latestDate: { $lt: current.latestDate },
|
||||
patientId: current.patientId
|
||||
};
|
||||
const priorSorting = { sort: { latestDate: -1 } };
|
||||
const prior = timepointApi.timepoints.findOne(priorFilter, priorSorting);
|
||||
|
||||
// Stop here if timepoint is locked
|
||||
if (current && current.isLocked) {
|
||||
return instance.isLocked.set(true);
|
||||
} else {
|
||||
instance.isLocked.set(false);
|
||||
}
|
||||
|
||||
// Stop here if no current or prior timepoint was found
|
||||
if (!current || !prior || !current.timepointId) {
|
||||
return instance.progressPercent.set(100);
|
||||
}
|
||||
|
||||
// Retrieve the initial number of targets left to measure at this
|
||||
// follow-up. Note that this is done outside of the reactive function
|
||||
// below so that new lesions don't change the initial target count.
|
||||
|
||||
const config = OHIF.measurements.MeasurementApi.getConfiguration();
|
||||
const toolGroups = config.measurementTools;
|
||||
|
||||
const toolIds = [];
|
||||
toolGroups.forEach(toolGroup => toolGroup.childTools.forEach(tool => {
|
||||
const option = 'options.caseProgress.include';
|
||||
if (OHIF.utils.ObjectPath.get(tool, option)) {
|
||||
toolIds.push(tool.id);
|
||||
}
|
||||
}));
|
||||
|
||||
const getTimepointFilter = timepointId => ({
|
||||
timepointId,
|
||||
toolId: { $in: toolIds }
|
||||
});
|
||||
|
||||
const getNumMeasurementsAtTimepoint = timepointId => {
|
||||
OHIF.log.info('getNumMeasurementsAtTimepoint');
|
||||
const filter = getTimepointFilter(timepointId);
|
||||
|
||||
let count = 0;
|
||||
toolGroups.forEach(toolGroup => {
|
||||
count += measurementApi.fetch(toolGroup.id, filter).length;
|
||||
});
|
||||
|
||||
return count;
|
||||
};
|
||||
|
||||
const getNumRemainingBetweenTimepoints = (currentTimepointId, priorTimepointId) => {
|
||||
const currentFilter = getTimepointFilter(currentTimepointId);
|
||||
const priorFilter = getTimepointFilter(priorTimepointId);
|
||||
|
||||
let totalRemaining = 0;
|
||||
toolGroups.forEach(toolGroup => {
|
||||
const toolGroupId = toolGroup.id;
|
||||
const numCurrent = measurementApi.fetch(toolGroupId, currentFilter).length;
|
||||
const numPrior = measurementApi.fetch(toolGroupId, priorFilter).length;
|
||||
const remaining = Math.max(numPrior - numCurrent, 0);
|
||||
totalRemaining += remaining;
|
||||
});
|
||||
|
||||
return totalRemaining;
|
||||
};
|
||||
|
||||
// If we're currently reviewing a Baseline timepoint, don't do any
|
||||
// progress measurement.
|
||||
if (current.timepointType === 'baseline') {
|
||||
instance.progressPercent.set(100);
|
||||
instance.isFollowUp.set(false);
|
||||
} else {
|
||||
instance.isFollowUp.set(true);
|
||||
// Setup a reactive function to update the progress whenever
|
||||
// a measurement is made
|
||||
instance.autorun(() => {
|
||||
measurementApi.changeObserver.depend();
|
||||
// Obtain the number of Measurements for which the current Timepoint has
|
||||
// no Measurement data
|
||||
const totalMeasurements = getNumMeasurementsAtTimepoint(prior.timepointId);
|
||||
const numRemainingMeasurements = getNumRemainingBetweenTimepoints(current.timepointId, prior.timepointId);
|
||||
const numMeasurementsMade = totalMeasurements - numRemainingMeasurements;
|
||||
|
||||
// Update the Case Progress text with the remaining measurement count
|
||||
instance.progressText.set(numRemainingMeasurements);
|
||||
|
||||
// Calculate the Case Progress as a percentage in order to update the
|
||||
// radial progress bar
|
||||
const progressPercent = Math.min(100, Math.round(100 * numMeasurementsMade / totalMeasurements));
|
||||
instance.progressPercent.set(progressPercent);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Template.caseProgress.helpers({
|
||||
progressPercent() {
|
||||
return Template.instance().progressPercent.get();
|
||||
},
|
||||
|
||||
progressText() {
|
||||
return Template.instance().progressText.get();
|
||||
},
|
||||
|
||||
isLocked() {
|
||||
return Template.instance().isLocked.get();
|
||||
},
|
||||
|
||||
progressComplete() {
|
||||
const instance = Template.instance();
|
||||
if (!instance.data.timepointApi) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const progressPercent = instance.progressPercent.get();
|
||||
return progressPercent === 100;
|
||||
}
|
||||
});
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
@require '{ohif:viewerbase}/app'
|
||||
|
||||
.caseProgress
|
||||
transition(all 0.3 ease)
|
||||
|
||||
.radialProgress
|
||||
display: inline-block
|
||||
float:left
|
||||
|
||||
.caseProgressStatus
|
||||
display: inline-block
|
||||
float: left
|
||||
margin-left: 22px
|
||||
|
||||
h5
|
||||
theme('color', '$textPrimaryColor')
|
||||
line-height: 40px
|
||||
margin: 0
|
||||
font-size: 15px
|
||||
font-weight: bold
|
||||
@@ -0,0 +1,8 @@
|
||||
// Case Progress imports
|
||||
import './caseProgress.html';
|
||||
import './caseProgress.styl';
|
||||
import './caseProgress.js';
|
||||
|
||||
import './radialProgressBar/radialProgressBar.html';
|
||||
import './radialProgressBar/radialProgressBar.styl';
|
||||
import './radialProgressBar/radialProgressBar.js';
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
<template name="radialProgressBar">
|
||||
<div class="radialProgress">
|
||||
<svg id="svg"
|
||||
width="26"
|
||||
height="26"
|
||||
viewPort="0 0 26 26"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
|
||||
<!-- stroke-dasharray is 2 * PI * radius !-->
|
||||
<circle r="11"
|
||||
cx="13"
|
||||
cy="13"
|
||||
fill="transparent"
|
||||
stroke-dasharray="69.11503837897544"
|
||||
stroke-dashoffset="0">
|
||||
</circle>
|
||||
{{#unless isLocked}}
|
||||
<circle id="bar"
|
||||
r="11"
|
||||
cx="13"
|
||||
cy="13"
|
||||
fill="transparent"
|
||||
stroke-dasharray="69.11503837897544"
|
||||
style="stroke-dashoffset: {{progressRadius}}px;">
|
||||
</circle>
|
||||
{{/unless}}
|
||||
</svg>
|
||||
<div class="progressArea {{#if isLocked}}locked{{else}}{{#if progressComplete}}complete{{/if}}{{/if}}">
|
||||
{{#if isLocked}}
|
||||
<svg>
|
||||
<use xlink:href={{absoluteUrl "packages/ohif_viewerbase/assets/icons.svg#icon-status-lock"}}></use>
|
||||
</svg>
|
||||
{{else}}
|
||||
{{#if progressComplete}}
|
||||
<svg>
|
||||
<use xlink:href={{absoluteUrl "packages/ohif_viewerbase/assets/icons.svg#icon-status-complete"}}></use>
|
||||
</svg>
|
||||
{{else}}
|
||||
{{progressText}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import { Template } from 'meteor/templating';
|
||||
|
||||
Template.radialProgressBar.helpers({
|
||||
progressComplete() {
|
||||
const instance = Template.instance();
|
||||
return instance.data.progressPercent === 100;
|
||||
},
|
||||
|
||||
progressRadius() {
|
||||
const instance = Template.instance();
|
||||
const radius = 11 * 2 * Math.PI;
|
||||
const percentLeft = (100 - instance.data.progressPercent) / 100;
|
||||
return percentLeft * radius;
|
||||
}
|
||||
});
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
@require '{ohif:viewerbase}/app'
|
||||
|
||||
$circleSize = 26px
|
||||
|
||||
.radialProgress
|
||||
border-radius: 100%
|
||||
height: $circleSize
|
||||
margin-top: 3px
|
||||
margin-right: 4px
|
||||
position: relative
|
||||
vendorize(box-shadow, 0 0 1em black)
|
||||
width: $circleSize
|
||||
|
||||
#svg circle
|
||||
stroke-dashoffset: 0
|
||||
theme('stroke', '$uiBorderColorDark')
|
||||
stroke-width: 3px
|
||||
vendorize(transition, stroke-dashoffset 1s linear)
|
||||
|
||||
#svg #bar
|
||||
theme('stroke', '$textSecondaryColor')
|
||||
vendorize(transform, rotate(270deg))
|
||||
vendorize(transform-origin, center center)
|
||||
|
||||
.progressArea
|
||||
theme('color', '$textSecondaryColor')
|
||||
display: table
|
||||
font-weight: 700
|
||||
font-size: 12px
|
||||
height: $circleSize
|
||||
line-height: $circleSize
|
||||
left: 50%
|
||||
position: absolute
|
||||
text-align: center
|
||||
top: 50%
|
||||
transform(translate(-50%, -50%))
|
||||
width: $circleSize
|
||||
|
||||
&.locked
|
||||
line-height: 0
|
||||
|
||||
&, & svg
|
||||
height: 12px
|
||||
width: 10px
|
||||
|
||||
&.complete
|
||||
line-height: 0
|
||||
|
||||
&, & svg
|
||||
height: 16px
|
||||
width: 16px
|
||||
|
||||
svg
|
||||
theme('fill', '$textSecondaryColor')
|
||||
theme('stroke', '$textSecondaryColor')
|
||||
Reference in new issue
Block a user