Skip to content

Commit

Permalink
feat: Custom loader (#46)
Browse files Browse the repository at this point in the history
* Custom loader prop, evt listener fix, example updated.

* Proptypes updated.
  • Loading branch information
amrita-syn authored and dannyrb committed Nov 14, 2019
1 parent 1ed97ab commit 2b6735d
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 34 deletions.
4 changes: 2 additions & 2 deletions examples/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,10 @@ function Index() {
text: 'How to render multiple viewports and track the "active viewport".',
},
{
title: 'Custom Overlay Component',
title: 'Custom Overlay and Loader Component',
url: '/custom-overlay',
text:
'Provide an alternative React Component to use in place of the built in overlay-text component.',
'Provide an alternative React Component to use in place of the built in overlay-text and loading indicator components.',
},
{
title: 'Escape Hatch',
Expand Down
44 changes: 44 additions & 0 deletions examples/ExamplePageCustomOverlay.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,26 @@ class CustomOverlay extends Component {
}
}

class CustomLoader extends Component {
render() {
return (
<div
className="lds-ripple"
style={{
position: 'absolute',
top: '47%',
left: '47%',
width: '100%',
height: '100%',
color: 'white',
}}
>
<div />
<div />
</div>
);
}
}
class ExamplePageCustomOverlay extends Component {
render() {
return (
Expand All @@ -66,6 +86,7 @@ class ExamplePageCustomOverlay extends Component {
'dicomweb://s3.amazonaws.com/lury/PTCTStudy/1.3.6.1.4.1.25403.52237031786.3872.20100510032220.12.dcm',
]}
viewportOverlayComponent={CustomOverlay}
loadingIndicatorComponent={CustomLoader}
style={{ minWidth: '100%', height: '512px', flex: '1' }}
/>
</div>
Expand Down Expand Up @@ -114,6 +135,28 @@ class ExamplePageCustomOverlay extends Component {
}
}
class CustomLoader extends Component {
render() {
return (
<div
className="lds-ripple"
style={{
position: 'absolute',
top: '47%',
left: '47%',
width: '100%',
height: '100%',
color: 'white',
}}
>
<div></div>
<div></div>
</div>
);
}
}
{/* RENDER */}
<CornerstoneViewport
tools={[
Expand All @@ -128,6 +171,7 @@ imageIds={[
'dicomweb://s3.amazonaws.com/lury/PTCTStudy/1.3.6.1.4.1.25403.52237031786.3872.20100510032220.12.dcm',
]}
viewportOverlayComponent={CustomOverlay}
loadingIndicatorComponent={CustomLoader}
style={{ minWidth: '100%', height: '512px', flex: '1' }}
/>`}
</SyntaxHighlighter>
Expand Down
43 changes: 41 additions & 2 deletions examples/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@
html {
box-sizing: border-box;
}

*,
*:before,
*:after {
box-sizing: inherit;
}

body {
margin: 0;
padding: 0;
margin : 0;
padding : 0;
font-family: sans-serif;
}

Expand All @@ -24,3 +25,41 @@ body {
.viewport-wrapper.active {
border: 2px solid dodgerblue;
}

/* Custom Loader with animation */
.lds-ripple {
display : inline-block;
position: relative;
width : 64px;
height : 64px;
}

.lds-ripple div {
position : absolute;
border : 4px solid blue;
opacity : 1;
border-radius: 50%;
animation : lds-ripple 1s cubic-bezier(0, 0.2, 0.8, 1) infinite;
}

.lds-ripple div:nth-child(2) {
animation-delay: -0.5s;
}

@keyframes lds-ripple {
0% {
top : 28px;
left : 28px;
width : 0;
height : 0;
opacity: 1;
}

100% {
top : -1px;
left : -1px;
width : 58px;
height : 58px;
opacity: 0;
}
}
69 changes: 47 additions & 22 deletions src/CornerstoneViewport/CornerstoneViewport.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,6 @@ const scrollToIndex = cornerstoneTools.importInternal('util/scrollToIndex');
const { loadHandlerManager } = cornerstoneTools;

class CornerstoneViewport extends Component {
static defaultProps = {
// Watch
imageIdIndex: 0,
isPlaying: false,
cineFrameRate: 24,
viewportOverlayComponent: ViewportOverlay,
imageIds: ['no-id://'],
// Init
cornerstoneOptions: {},
isStackPrefetchEnabled: false,
loadIndicatorDelay: 45,
resizeThrottleMs: 200,
tools: [],
};

static propTypes = {
imageIds: PropTypes.arrayOf(PropTypes.string).isRequired,
imageIdIndex: PropTypes.number,
Expand Down Expand Up @@ -79,13 +64,33 @@ class CornerstoneViewport extends Component {
startLoadHandler: PropTypes.func,
endLoadHandler: PropTypes.func,
loadIndicatorDelay: PropTypes.number,
loadingIndicatorComponent: PropTypes.oneOfType([
PropTypes.element,
PropTypes.func,
]),
resizeThrottleMs: PropTypes.number, // 0 to disable
//
style: PropTypes.object,
className: PropTypes.string,
isOverlayVisible: PropTypes.bool,
};

static defaultProps = {
// Watch
imageIdIndex: 0,
isPlaying: false,
cineFrameRate: 24,
viewportOverlayComponent: ViewportOverlay,
imageIds: ['no-id://'],
// Init
cornerstoneOptions: {},
isStackPrefetchEnabled: false,
loadIndicatorDelay: 45,
loadingIndicatorComponent: LoadingIndicator,
resizeThrottleMs: 200,
tools: [],
};

constructor(props) {
super(props);

Expand All @@ -97,6 +102,7 @@ class CornerstoneViewport extends Component {
// We can probs grab this once and hold on to? (updated on newImage)
imageId,
imageIdIndex, // Maybe
imageProgress: 0,
isLoading: true,
numImagesLoaded: 0,
error: null,
Expand Down Expand Up @@ -172,7 +178,6 @@ class CornerstoneViewport extends Component {
_trySetActiveTool(this.element, this.props.activeTool);
this.setState({ isLoading: false });
} catch (error) {
console.error(error);
this.setState({ error, isLoading: false });
}
}
Expand Down Expand Up @@ -299,6 +304,17 @@ class CornerstoneViewport extends Component {
cornerstone.disable(this.element);
}

/**
* @returns Component
* @memberof CornerstoneViewport
*/
getLoadingIndicator() {
const { loadingIndicatorComponent: Component } = this.props;
const { error, imageProgress } = this.state;

return <Component error={error} percentComplete={imageProgress} />;
}

/**
*
*
Expand Down Expand Up @@ -388,9 +404,14 @@ class CornerstoneViewport extends Component {
this.onNewImage
);

// Update our "Images Loaded" count.
// Better than nothing?
this.element[addOrRemoveEventListener](
// Update image load progress
cornerstone.events[addOrRemoveEventListener](
'cornerstoneimageloadprogress',
this.onImageProgress
);

// Update number of images loaded
cornerstone.events[addOrRemoveEventListener](
cornerstone.EVENTS.IMAGE_LOADED,
this.onImageLoaded
);
Expand Down Expand Up @@ -581,6 +602,12 @@ class CornerstoneViewport extends Component {
});
};

onImageProgress = e => {
this.setState({
imageProgress: e.detail.percentComplete,
});
};

imageSliderOnInputCallback = value => {
this.setViewportActive();

Expand Down Expand Up @@ -624,9 +651,7 @@ class CornerstoneViewport extends Component {
this.element = input;
}}
>
{displayLoadingIndicator && (
<LoadingIndicator error={this.state.error} />
)}
{displayLoadingIndicator && this.getLoadingIndicator()}
{/* This classname is important in that it tells `cornerstone` to not
* create a new canvas element when we "enable" the `viewport-element`
*/}
Expand Down
7 changes: 6 additions & 1 deletion src/LoadingIndicator/LoadingIndicator.css
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
.imageViewerLoadingIndicator {
color: #91b9cd;
}

.faded {
opacity: 0.5;
}

.imageViewerErrorLoadingIndicator {
color: #e29e4a;
}

.imageViewerErrorLoadingIndicator p,
.imageViewerErrorLoadingIndicator h4 {
padding: 4px 0;
text-align: center;
word-wrap: break-word;
}

.imageViewerErrorLoadingIndicator p {
font-size: 11pt;
}

.loadingIndicator {
background-color: rgba(0, 0, 0, 0.75);
font-size: 18px;
Expand All @@ -27,8 +32,8 @@
width: 100%;
z-index: 1;
}

.loadingIndicator .indicatorContents {
font-size: 30px;
font-weight: 300;
position: absolute;
text-align: center;
Expand Down
14 changes: 7 additions & 7 deletions src/LoadingIndicator/LoadingIndicator.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,8 @@ class LoadingIndicator extends PureComponent {
};

render() {
let percComplete;
if (this.props.percentComplete && this.props.percentComplete !== 100) {
percComplete = `${this.props.percentComplete}%`;
}
const pc = this.props.percentComplete;
const percComplete = `${pc}%`;

return (
<React.Fragment>
Expand All @@ -33,10 +31,12 @@ class LoadingIndicator extends PureComponent {
) : (
<div className="imageViewerLoadingIndicator loadingIndicator">
<div className="indicatorContents">
<p>
Loading... <i className="fa fa-spin fa-circle-o-notch fa-fw" />{' '}
<h2>
{pc < 100 ? 'Loading...' : 'Loaded -'}
<i className="fa fa-spin fa-circle-o-notch fa-fw" />{' '}
{percComplete}
</p>
</h2>
{pc === 100 && <p>Processing...</p>}
</div>
</div>
)}
Expand Down

0 comments on commit 2b6735d

Please sign in to comment.