wip on react transition 4

This commit is contained in:
Erik Ziegler 2018-11-30 16:12:04 -05:00
parent 895fb01a02
commit 5568490819
22 changed files with 70 additions and 287 deletions

View File

@ -1,15 +1,5 @@
import { Viewerbase } from 'meteor/ohif:viewerbase'; import { Viewerbase } from 'meteor/ohif:viewerbase';
Template.flexboxLayout.events({
'transitionend .sidebarMenu'(event) {
if (!event.target.classList.contains('sidebarMenu')) {
return;
}
window.ResizeViewportManager.handleResize();
}
});
Template.flexboxLayout.helpers({ Template.flexboxLayout.helpers({
leftSidebarOpen() { leftSidebarOpen() {
return Template.instance().data.state.get('leftSidebar'); return Template.instance().data.state.get('leftSidebar');

View File

@ -1,5 +0,0 @@
.CornerstoneViewport {
width: 100%;
height: 100%;
position: relative; /*Necessary so that the viewportOverlay is on top of the viewports */
}

View File

@ -1,20 +1,21 @@
import { Component } from 'react'; import { Component } from 'react';
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import cloneDeep from 'lodash.clonedeep';
import debounce from 'lodash.debounce';
//import * as cornerstone from 'cornerstone-core'; //import * as cornerstone from 'cornerstone-core';
//import * as cornerstoneTools from 'cornerstone-tools'; //import * as cornerstoneTools from 'cornerstone-tools';
import { cornerstone, cornerstoneTools } from 'meteor/ohif:cornerstone'; import { cornerstone, cornerstoneTools } from 'meteor/ohif:cornerstone';
//import './lib/initCornerstone.js'; //import './lib/initCornerstone.js';
import ImageScrollbar from './ImageScrollbar.js'; import ImageScrollbar from '../ImageScrollbar/ImageScrollbar.js';
import ViewportOverlay from './ViewportOverlay.js'; import ViewportOverlay from '../ViewportOverlay/ViewportOverlay.js';
import ToolContextMenu from './ToolContextMenu.js'; import ToolContextMenu from '../ToolContextMenu/ToolContextMenu.js';
import LoadingIndicator from './LoadingIndicator/LoadingIndicator.js'; import LoadingIndicator from '../LoadingIndicator/LoadingIndicator.js';
import './CornerstoneViewport.css'; import ViewportOrientationMarkers from '../ViewportOrientationMarkers/ViewportOrientationMarkers.js';
import { StackManager } from "../../../lib/StackManager";
import cloneDeep from 'lodash.clonedeep'; import './CornerstoneViewport.styl';
import debounce from 'lodash.debounce';
import {StackManager} from "../../lib/StackManager";
const EVENT_RESIZE = 'resize'; const EVENT_RESIZE = 'resize';
const loadIndicatorDelay = 45; const loadIndicatorDelay = 45;
@ -92,6 +93,13 @@ class CornerstoneViewport extends Component {
//loadHandlerManager.setEndLoadHandler(this.doneLoadingHandler); //loadHandlerManager.setEndLoadHandler(this.doneLoadingHandler);
this.debouncedResize = debounce(() => { this.debouncedResize = debounce(() => {
try {
cornerstone.getEnabledElement(this.element);
} catch (error) {
console.error(error);
return;
}
cornerstone.resize(this.element, true); cornerstone.resize(this.element, true);
this.setState({ this.setState({
@ -117,6 +125,9 @@ class CornerstoneViewport extends Component {
<div <div
className="CornerstoneViewport viewportElement" className="CornerstoneViewport viewportElement"
onContextMenu={this.onContextMenu} onContextMenu={this.onContextMenu}
tabIndex='0'
unselectable='on'
onSelectStart='return false;'
ref={input => { ref={input => {
this.element = input; this.element = input;
}} }}
@ -129,6 +140,10 @@ class CornerstoneViewport extends Component {
imageId={this.state.imageId} imageId={this.state.imageId}
numImagesLoaded={this.state.numImagesLoaded} numImagesLoaded={this.state.numImagesLoaded}
/> />
<ViewportOrientationMarkers/>
</div>
<div className='viewportInstructions'>
Please drag a stack here to view images.
</div> </div>
{this.displayScrollbar && ( {this.displayScrollbar && (
<ImageScrollbar <ImageScrollbar
@ -191,8 +206,6 @@ class CornerstoneViewport extends Component {
this.setState({ this.setState({
viewport viewport
}); });
debugger;
} }
onNewImage() { onNewImage() {
@ -452,7 +465,7 @@ class CornerstoneViewport extends Component {
}); });
} }
cornerstone.resize(this.element); this.debouncedResize();
} }
setActiveTool = activeTool => { setActiveTool = activeTool => {

View File

@ -1,8 +1,9 @@
@import "{ohif:design}/app" @import "{ohif:design}/app"
.imageViewerViewport .CornerstoneViewport
width: 100% width: 100%
height: 100% height: 100%
position: relative;
background-color: black background-color: black
// Prevent the blue outline in Chrome when a viewport is selected // Prevent the blue outline in Chrome when a viewport is selected

View File

@ -2,8 +2,7 @@
<div class='imageViewerViewport' <div class='imageViewerViewport'
oncontextmenu='return false;' oncontextmenu='return false;'
unselectable='on' unselectable='on'
onselectstart='return false;' onselectstart='return false;'>
tabindex='0'>
</div> </div>
<div class='viewportInstructions'> <div class='viewportInstructions'>
Please drag a stack here to view images. Please drag a stack here to view images.

View File

@ -2,7 +2,7 @@ import { Component } from 'react';
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { OHIF } from 'meteor/ohif:core'; import { OHIF } from 'meteor/ohif:core';
import CornerstoneViewport from '../CornerstoneViewport.js'; import CornerstoneViewport from '../CornerstoneViewport/CornerstoneViewport.js';
import './GridLayout.styl'; import './GridLayout.styl';
const TOP_CLASS = 'top'; const TOP_CLASS = 'top';

View File

@ -1,49 +0,0 @@
@value globals: "../GlobalStyles.css";
@value activeColor, imageSliderColor from globals;
.scroll {
position: absolute;
top: 0;
right: 0;
width: 15px;
height: 100%;
z-index: 2;
}
.scroll .scroll-holder {
position: relative;
height: 100%;
}
.scroll > .scroll-holder > .imageSlider {
transform: rotate(90deg);
transform-origin: top left;
position: absolute;
width: 100%; /* Note: This width does not have an effect. */
margin-left: 10px;
-webkit-appearance: none;
background-color: rgba(0, 0, 0, 0);
outline: none;
}
input.imageSlider:focus {
outline: none;
}
input.imageSlider::-moz-focus-outer {
border: none;
}
input.imageSlider::-webkit-slider-thumb {
cursor: -webkit-grab;
background: imageSliderColor;
-webkit-appearance: none;
width: 45px;
height: 20px;
border-radius: 10px;
}
input.imageSlider::-webkit-slider-thumb:active {
background: activeColor;
cursor: -webkit-grabbing;
}

View File

@ -1,13 +1,14 @@
import { Component } from 'react'; import { Component } from 'react';
import React from 'react'; import React from 'react';
import './ImageScrollbar.css';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './ImageScrollbar.styl';
class ImageScrollbar extends Component { class ImageScrollbar extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.onChange = this.onChange.bind(this); this.onChange = this.onChange.bind(this);
this.onKeyDown = this.onKeyDown.bind(this);
} }
render() { render() {
@ -27,6 +28,7 @@ class ImageScrollbar extends Component {
step="1" step="1"
value={this.props.value} value={this.props.value}
onChange={this.onChange} onChange={this.onChange}
onKeyDown={this.onKeyDown}
/> />
</div> </div>
</div> </div>
@ -37,6 +39,25 @@ class ImageScrollbar extends Component {
const intValue = parseInt(event.target.value, 10); const intValue = parseInt(event.target.value, 10);
this.props.onInputCallback(intValue); this.props.onInputCallback(intValue);
} }
onKeyDown(event) {
// We don't allow direct keyboard up/down input on the
// image sliders since the natural direction is reversed (0 is at the top)
// Store the KeyCodes in an object for readability
const keys = {
DOWN: 40,
UP: 38
};
if (event.which === keys.DOWN) {
OHIF.commands.run('scrollDown');
event.preventDefault();
} else if (event.which === keys.UP) {
OHIF.commands.run('scrollUp');
event.preventDefault();
}
}
} }
ImageScrollbar.propTypes = { ImageScrollbar.propTypes = {

View File

@ -8,14 +8,14 @@ $imageSliderBorder = none
$imageSliderWidth = 12px $imageSliderWidth = 12px
$imageSliderHeight = 39px $imageSliderHeight = 39px
.imageControls .scroll
height: 100% height: 100%
padding: 5px padding: 5px
position: absolute position: absolute
right: 0 right: 0
top: 0 top: 0
.scrollbar .scroll-holder
height: calc(100% - 20px) height: calc(100% - 20px)
margin-top: 5px margin-top: 5px
position: relative position: relative

View File

@ -1,6 +1,5 @@
import { Component } from 'react'; import { Component } from 'react';
import React from 'react'; import React from 'react';
import './ViewportOverlay.css';
/*import * as cornerstone from 'cornerstone-core'; /*import * as cornerstone from 'cornerstone-core';
import * as cornerstoneTools from 'cornerstone-tools';*/ import * as cornerstoneTools from 'cornerstone-tools';*/
import { cornerstone, cornerstoneTools } from 'meteor/ohif:cornerstone'; import { cornerstone, cornerstoneTools } from 'meteor/ohif:cornerstone';

View File

@ -1,38 +0,0 @@
.ViewportOverlay {
width: 100%;
height: 100%;
position: absolute;
top: 0px;
left: 0px;
z-index: 2;
}
.ViewportOverlay .overlay-element {
position: absolute;
color: lightblue;
text-shadow: 1px 1px black;
}
.ViewportOverlay .overlay-element > span {
display: block;
}
.ViewportOverlay .top-right {
top: 10px;
right: 25px; /* room for scrollbar */
}
.ViewportOverlay .top-left {
top: 10px;
left: 10px;
}
.ViewportOverlay .bottom-right {
bottom: 10px;
right: 30px;
}
.ViewportOverlay .bottom-left {
bottom: 10px;
left: 10px;
}

View File

@ -1,7 +1,7 @@
import { Component } from 'react'; import { Component } from 'react';
import React from 'react'; import React from 'react';
import './ViewportOverlay.css';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './ViewportOverlay.styl';
class ViewportOverlay extends Component { class ViewportOverlay extends Component {
render() { render() {

View File

@ -8,40 +8,36 @@ $viewportTagPadding = 20px
.imageViewerViewportOverlay .imageViewerViewportOverlay
theme('color', '$textSecondaryColor') theme('color', '$textSecondaryColor')
.dicomTag .overlay-element
position: absolute position: absolute
font-weight: 400 font-weight: 400
text-shadow: 1px 1px black text-shadow: 1px 1px black
pointer-events: none pointer-events: none
.topleft .top-left
top: $viewportTagPadding top: $viewportTagPadding
left: $viewportTagPadding left: $viewportTagPadding
.topcenter .top-center
top: $viewportTagPadding top: $viewportTagPadding
padding-top: $viewportTagPadding padding-top: $viewportTagPadding
width: 100% width: 100%
text-align: center text-align: center
.topright .top-right
top: $viewportTagPadding top: $viewportTagPadding
right: $viewportTagPadding right: $viewportTagPadding
text-align: right text-align: right
.bottomleft .bottom-left
bottom: $viewportTagPadding bottom: $viewportTagPadding
left: $viewportTagPadding left: $viewportTagPadding
.bottomright .bottom-right
bottom: $viewportTagPadding bottom: $viewportTagPadding
right: $viewportTagPadding right: $viewportTagPadding
text-align: right text-align: right
.priorIndicator
font-weight: bold
color: yellow
&.controlsVisible &.controlsVisible
.topright, .bottomright .topright, .bottomright
right: "calc(%s + 19px)" % $viewportTagPadding right: "calc(%s + 19px)" % $viewportTagPadding

View File

@ -242,39 +242,4 @@ Template.viewportOverlay.helpers({
return stack.imageIds.length; return stack.imageIds.length;
}, },
prior() {
// This helper is updated whenever a new image is displayed in the viewport
Session.get('CornerstoneNewImage' + this.viewportIndex);
if (!this.imageId) {
return;
}
// @TypeSafeStudies
// Make sure there are more than two studies loaded in the viewer
const viewportStudies = OHIF.viewer.Studies.all();
if (viewportStudies.length < 2) {
return;
}
// Here we sort the collection in ascending order by study date, so
// that we can obtain the oldest study as the first element of the array
//
// TODO= Find out if we should encode studyDate as a Date in the OHIF.viewer.Studies Collection
const viewportStudiesArray = _.sortBy(viewportStudies, function(study) {
return viewportOverlayUtils.formatDateTime(study.studyDate, study.studyTime);
});
// Get study data
const study = cornerstone.metaData.get('study', this.imageId);
if (!study) {
return;
}
const oldestStudy = viewportStudiesArray[0];
if (viewportOverlayUtils.formatDateTime(study.studyDate, study.studyTime) <= viewportOverlayUtils.formatDateTime(oldestStudy.studyDate, oldestStudy.studyTime)) {
return 'Prior';
}
}
}); });

View File

@ -4,7 +4,6 @@
<div class="topleft dicomTag"> <div class="topleft dicomTag">
<div>{{formatPN patientName}}</div> <div>{{formatPN patientName}}</div>
<div>{{patientId}}</div> <div>{{patientId}}</div>
<div class='priorIndicator'>{{prior}}</div>
</div> </div>
<div class="topright dicomTag"> <div class="topright dicomTag">
<div>{{studyDescription}}</div> <div>{{studyDescription}}</div>
@ -33,7 +32,6 @@
<div class="topright dicomTag"> <div class="topright dicomTag">
<div>{{formatPN patientName}}</div> <div>{{formatPN patientName}}</div>
<div>{{patientId}}</div> <div>{{patientId}}</div>
<div class='priorIndicator'>{{prior}}</div>
<div>{{studyDescription}}</div> <div>{{studyDescription}}</div>
<div>{{formatDA studyDate}} {{formatTM studyTime}}</div> <div>{{formatDA studyDate}} {{formatTM studyTime}}</div>
</div> </div>
@ -53,7 +51,6 @@
<div class="topleft dicomTag"> <div class="topleft dicomTag">
<div>{{formatPN patientName}}</div> <div>{{formatPN patientName}}</div>
<div>{{patientId}}</div> <div>{{patientId}}</div>
<div class='priorIndicator'>{{prior}}</div>
<div>{{studyDescription}}</div> <div>{{studyDescription}}</div>
<div>{{formatDA studyDate}} {{formatTM studyTime}}</div> <div>{{formatDA studyDate}} {{formatTM studyTime}}</div>
</div> </div>

View File

@ -1,7 +0,0 @@
<template name="imageControls">
<div class="imageControls">
<div class="scrollbar">
<input class="imageSlider" type="range" min="1" value="{{imageIndex}}" max="{{numImages}}"/>
</div>
</div>
</template>

View File

@ -1,83 +0,0 @@
import { Template } from 'meteor/templating';
import { $ } from 'meteor/jquery';
import { _ } from 'meteor/underscore';
import { OHIF } from 'meteor/ohif:core';
import { setActiveViewport } from '../../../lib/setActiveViewport';
import { switchToImageByIndex } from '../../../lib/switchToImageByIndex';
import { Component } from 'react';
import React from 'react';
const slideTimeoutTime = 40;
let slideTimeout;
Template.imageControls.onRendered(() => {
const instance = Template.instance();
// Set the current imageSlider width to its parent's height
// (because webkit is stupid and can't style vertical sliders)
const $slider = instance.$('.imageSlider');
const $viewport = $slider.closest('.imageViewerViewportOverlay').siblings('.imageViewerViewport');
instance.handleResize = _.throttle(() => {
const viewportHeight = $viewport.height();
$slider.width(viewportHeight - 20);
}, 150);
instance.handleResize();
$(window).on('resize', instance.handleResize);
});
Template.imageControls.onDestroyed(() => {
const instance = Template.instance();
if (instance.handleResize) {
$(window).off('resize', instance.handleResize);
}
});
Template.imageControls.events({
'rescale .scrollbar'(event, instance) {
instance.handleResize();
},
'keydown input[type=range]'(event) {
// We don't allow direct keyboard up/down input on the
// image sliders since the natural direction is reversed (0 is at the top)
// Store the KeyCodes in an object for readability
const keys = {
DOWN: 40,
UP: 38
};
if (event.which === keys.DOWN) {
OHIF.commands.run('scrollDown');
event.preventDefault();
} else if (event.which === keys.UP) {
OHIF.commands.run('scrollUp');
event.preventDefault();
}
},
'input input[type=range], change input[type=range]'(event) {
// Note that we throttle requests to prevent the
// user's ultrafast scrolling from firing requests too quickly.
clearTimeout(slideTimeout);
slideTimeout = setTimeout(() => {
// Using the slider in an inactive viewport
// should cause that viewport to become active
const $slider = $(event.currentTarget);
const viewportContainer = $slider.parents('.viewportContainer');
setActiveViewport(viewportContainer);
// Subtract 1 here since the slider goes from 1 to N images
// But the stack indexing starts at 0
const newImageIdIndex = parseInt($slider.val(), 10) - 1;
switchToImageByIndex(newImageIdIndex);
}, slideTimeoutTime);
return false;
}
});

View File

@ -1,10 +1,11 @@
import ViewerMain from './ViewerMain/ViewerMain.js'; import ViewerMain from './ViewerMain/ViewerMain.js';
import GridLayout from './GridLayout/GridLayout.js'; import GridLayout from './GridLayout/GridLayout.js';
import LoadingIndicator from './LoadingIndicator/LoadingIndicator.js'; import LoadingIndicator from './LoadingIndicator/LoadingIndicator.js';
import CornerstoneViewport from './CornerstoneViewport.js'; import CornerstoneViewport from './CornerstoneViewport/CornerstoneViewport.js';
import ImageScrollbar from './ImageScrollbar.js'; import ImageScrollbar from './ImageScrollbar/ImageScrollbar.js';
import ToolContextMenu from './ToolContextMenu.js'; import ToolContextMenu from './ToolContextMenu/ToolContextMenu.js';
import ViewportOverlay from './ViewportOverlay.js'; import ViewportOverlay from './ViewportOverlay/ViewportOverlay.js';
import ViewportOrientationMarkers from './ViewportOrientationMarkers/ViewportOrientationMarkers.js';
const viewer = { const viewer = {
ViewerMain, ViewerMain,
@ -14,7 +15,6 @@ const viewer = {
ImageScrollbar, ImageScrollbar,
ToolContextMenu, ToolContextMenu,
ViewportOverlay, ViewportOverlay,
test: 'test2'
}; };
export default viewer; export default viewer;

View File

@ -121,26 +121,10 @@ Package.onUse(function(api) {
api.addFiles('client/components/studyBrowser/imageThumbnail/imageThumbnail.styl', 'client'); api.addFiles('client/components/studyBrowser/imageThumbnail/imageThumbnail.styl', 'client');
// Viewer components // Viewer components
api.addFiles('client/components/viewer/imageViewerViewport/imageViewerViewport.html', 'client');
api.addFiles('client/components/viewer/imageViewerViewport/imageViewerViewport.js', 'client');
api.addFiles('client/components/viewer/imageViewerViewport/imageViewerViewport.styl', 'client');
api.addFiles('client/components/viewer/annotationDialogs/annotationDialogs.html', 'client'); api.addFiles('client/components/viewer/annotationDialogs/annotationDialogs.html', 'client');
api.addFiles('client/components/viewer/annotationDialogs/annotationDialogs.js', 'client'); api.addFiles('client/components/viewer/annotationDialogs/annotationDialogs.js', 'client');
api.addFiles('client/components/viewer/annotationDialogs/annotationDialogs.styl', 'client'); api.addFiles('client/components/viewer/annotationDialogs/annotationDialogs.styl', 'client');
api.addFiles('client/components/viewer/viewportOrientationMarkers/viewportOrientationMarkers.styl', 'client');
api.addFiles('client/components/viewer/viewportOverlay/viewportOverlay.html', 'client');
api.addFiles('client/components/viewer/viewportOverlay/viewportOverlay.js', 'client');
api.addFiles('client/components/viewer/viewportOverlay/viewportOverlay.styl', 'client');
api.addFiles('client/components/viewer/toolContextMenu/toolContextMenu.js', 'client');
api.addFiles('client/components/viewer/imageControls/imageControls.html', 'client');
api.addFiles('client/components/viewer/imageControls/imageControls.js', 'client');
api.addFiles('client/components/viewer/imageControls/imageControls.styl', 'client');
api.addFiles('client/components/viewer/layoutButton/layoutButton.html', 'client'); api.addFiles('client/components/viewer/layoutButton/layoutButton.html', 'client');
api.addFiles('client/components/viewer/layoutButton/layoutButton.js', 'client'); api.addFiles('client/components/viewer/layoutButton/layoutButton.js', 'client');