Skip to content

Commit 2b6735d

Browse files
amrita-syndannyrb
authored andcommitted
feat: Custom loader (#46)
* Custom loader prop, evt listener fix, example updated. * Proptypes updated.
1 parent 1ed97ab commit 2b6735d

File tree

6 files changed

+147
-34
lines changed

6 files changed

+147
-34
lines changed

examples/App.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,10 @@ function Index() {
6363
text: 'How to render multiple viewports and track the "active viewport".',
6464
},
6565
{
66-
title: 'Custom Overlay Component',
66+
title: 'Custom Overlay and Loader Component',
6767
url: '/custom-overlay',
6868
text:
69-
'Provide an alternative React Component to use in place of the built in overlay-text component.',
69+
'Provide an alternative React Component to use in place of the built in overlay-text and loading indicator components.',
7070
},
7171
{
7272
title: 'Escape Hatch',

examples/ExamplePageCustomOverlay.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,26 @@ class CustomOverlay extends Component {
4343
}
4444
}
4545

46+
class CustomLoader extends Component {
47+
render() {
48+
return (
49+
<div
50+
className="lds-ripple"
51+
style={{
52+
position: 'absolute',
53+
top: '47%',
54+
left: '47%',
55+
width: '100%',
56+
height: '100%',
57+
color: 'white',
58+
}}
59+
>
60+
<div />
61+
<div />
62+
</div>
63+
);
64+
}
65+
}
4666
class ExamplePageCustomOverlay extends Component {
4767
render() {
4868
return (
@@ -66,6 +86,7 @@ class ExamplePageCustomOverlay extends Component {
6686
'dicomweb://s3.amazonaws.com/lury/PTCTStudy/1.3.6.1.4.1.25403.52237031786.3872.20100510032220.12.dcm',
6787
]}
6888
viewportOverlayComponent={CustomOverlay}
89+
loadingIndicatorComponent={CustomLoader}
6990
style={{ minWidth: '100%', height: '512px', flex: '1' }}
7091
/>
7192
</div>
@@ -114,6 +135,28 @@ class ExamplePageCustomOverlay extends Component {
114135
}
115136
}
116137
138+
class CustomLoader extends Component {
139+
render() {
140+
return (
141+
<div
142+
className="lds-ripple"
143+
style={{
144+
position: 'absolute',
145+
top: '47%',
146+
left: '47%',
147+
width: '100%',
148+
height: '100%',
149+
color: 'white',
150+
}}
151+
>
152+
<div></div>
153+
<div></div>
154+
</div>
155+
);
156+
}
157+
}
158+
159+
117160
{/* RENDER */}
118161
<CornerstoneViewport
119162
tools={[
@@ -128,6 +171,7 @@ imageIds={[
128171
'dicomweb://s3.amazonaws.com/lury/PTCTStudy/1.3.6.1.4.1.25403.52237031786.3872.20100510032220.12.dcm',
129172
]}
130173
viewportOverlayComponent={CustomOverlay}
174+
loadingIndicatorComponent={CustomLoader}
131175
style={{ minWidth: '100%', height: '512px', flex: '1' }}
132176
/>`}
133177
</SyntaxHighlighter>

examples/index.css

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,16 @@
55
html {
66
box-sizing: border-box;
77
}
8+
89
*,
910
*:before,
1011
*:after {
1112
box-sizing: inherit;
1213
}
1314

1415
body {
15-
margin: 0;
16-
padding: 0;
16+
margin : 0;
17+
padding : 0;
1718
font-family: sans-serif;
1819
}
1920

@@ -24,3 +25,41 @@ body {
2425
.viewport-wrapper.active {
2526
border: 2px solid dodgerblue;
2627
}
28+
29+
/* Custom Loader with animation */
30+
.lds-ripple {
31+
display : inline-block;
32+
position: relative;
33+
width : 64px;
34+
height : 64px;
35+
}
36+
37+
.lds-ripple div {
38+
position : absolute;
39+
border : 4px solid blue;
40+
opacity : 1;
41+
border-radius: 50%;
42+
animation : lds-ripple 1s cubic-bezier(0, 0.2, 0.8, 1) infinite;
43+
}
44+
45+
.lds-ripple div:nth-child(2) {
46+
animation-delay: -0.5s;
47+
}
48+
49+
@keyframes lds-ripple {
50+
0% {
51+
top : 28px;
52+
left : 28px;
53+
width : 0;
54+
height : 0;
55+
opacity: 1;
56+
}
57+
58+
100% {
59+
top : -1px;
60+
left : -1px;
61+
width : 58px;
62+
height : 58px;
63+
opacity: 0;
64+
}
65+
}

src/CornerstoneViewport/CornerstoneViewport.js

Lines changed: 47 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,6 @@ const scrollToIndex = cornerstoneTools.importInternal('util/scrollToIndex');
1919
const { loadHandlerManager } = cornerstoneTools;
2020

2121
class CornerstoneViewport extends Component {
22-
static defaultProps = {
23-
// Watch
24-
imageIdIndex: 0,
25-
isPlaying: false,
26-
cineFrameRate: 24,
27-
viewportOverlayComponent: ViewportOverlay,
28-
imageIds: ['no-id://'],
29-
// Init
30-
cornerstoneOptions: {},
31-
isStackPrefetchEnabled: false,
32-
loadIndicatorDelay: 45,
33-
resizeThrottleMs: 200,
34-
tools: [],
35-
};
36-
3722
static propTypes = {
3823
imageIds: PropTypes.arrayOf(PropTypes.string).isRequired,
3924
imageIdIndex: PropTypes.number,
@@ -79,13 +64,33 @@ class CornerstoneViewport extends Component {
7964
startLoadHandler: PropTypes.func,
8065
endLoadHandler: PropTypes.func,
8166
loadIndicatorDelay: PropTypes.number,
67+
loadingIndicatorComponent: PropTypes.oneOfType([
68+
PropTypes.element,
69+
PropTypes.func,
70+
]),
8271
resizeThrottleMs: PropTypes.number, // 0 to disable
8372
//
8473
style: PropTypes.object,
8574
className: PropTypes.string,
8675
isOverlayVisible: PropTypes.bool,
8776
};
8877

78+
static defaultProps = {
79+
// Watch
80+
imageIdIndex: 0,
81+
isPlaying: false,
82+
cineFrameRate: 24,
83+
viewportOverlayComponent: ViewportOverlay,
84+
imageIds: ['no-id://'],
85+
// Init
86+
cornerstoneOptions: {},
87+
isStackPrefetchEnabled: false,
88+
loadIndicatorDelay: 45,
89+
loadingIndicatorComponent: LoadingIndicator,
90+
resizeThrottleMs: 200,
91+
tools: [],
92+
};
93+
8994
constructor(props) {
9095
super(props);
9196

@@ -97,6 +102,7 @@ class CornerstoneViewport extends Component {
97102
// We can probs grab this once and hold on to? (updated on newImage)
98103
imageId,
99104
imageIdIndex, // Maybe
105+
imageProgress: 0,
100106
isLoading: true,
101107
numImagesLoaded: 0,
102108
error: null,
@@ -172,7 +178,6 @@ class CornerstoneViewport extends Component {
172178
_trySetActiveTool(this.element, this.props.activeTool);
173179
this.setState({ isLoading: false });
174180
} catch (error) {
175-
console.error(error);
176181
this.setState({ error, isLoading: false });
177182
}
178183
}
@@ -299,6 +304,17 @@ class CornerstoneViewport extends Component {
299304
cornerstone.disable(this.element);
300305
}
301306

307+
/**
308+
* @returns Component
309+
* @memberof CornerstoneViewport
310+
*/
311+
getLoadingIndicator() {
312+
const { loadingIndicatorComponent: Component } = this.props;
313+
const { error, imageProgress } = this.state;
314+
315+
return <Component error={error} percentComplete={imageProgress} />;
316+
}
317+
302318
/**
303319
*
304320
*
@@ -388,9 +404,14 @@ class CornerstoneViewport extends Component {
388404
this.onNewImage
389405
);
390406

391-
// Update our "Images Loaded" count.
392-
// Better than nothing?
393-
this.element[addOrRemoveEventListener](
407+
// Update image load progress
408+
cornerstone.events[addOrRemoveEventListener](
409+
'cornerstoneimageloadprogress',
410+
this.onImageProgress
411+
);
412+
413+
// Update number of images loaded
414+
cornerstone.events[addOrRemoveEventListener](
394415
cornerstone.EVENTS.IMAGE_LOADED,
395416
this.onImageLoaded
396417
);
@@ -581,6 +602,12 @@ class CornerstoneViewport extends Component {
581602
});
582603
};
583604

605+
onImageProgress = e => {
606+
this.setState({
607+
imageProgress: e.detail.percentComplete,
608+
});
609+
};
610+
584611
imageSliderOnInputCallback = value => {
585612
this.setViewportActive();
586613

@@ -624,9 +651,7 @@ class CornerstoneViewport extends Component {
624651
this.element = input;
625652
}}
626653
>
627-
{displayLoadingIndicator && (
628-
<LoadingIndicator error={this.state.error} />
629-
)}
654+
{displayLoadingIndicator && this.getLoadingIndicator()}
630655
{/* This classname is important in that it tells `cornerstone` to not
631656
* create a new canvas element when we "enable" the `viewport-element`
632657
*/}

src/LoadingIndicator/LoadingIndicator.css

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,26 @@
11
.imageViewerLoadingIndicator {
22
color: #91b9cd;
33
}
4+
45
.faded {
56
opacity: 0.5;
67
}
8+
79
.imageViewerErrorLoadingIndicator {
810
color: #e29e4a;
911
}
12+
1013
.imageViewerErrorLoadingIndicator p,
1114
.imageViewerErrorLoadingIndicator h4 {
1215
padding: 4px 0;
1316
text-align: center;
1417
word-wrap: break-word;
1518
}
19+
1620
.imageViewerErrorLoadingIndicator p {
1721
font-size: 11pt;
1822
}
23+
1924
.loadingIndicator {
2025
background-color: rgba(0, 0, 0, 0.75);
2126
font-size: 18px;
@@ -27,8 +32,8 @@
2732
width: 100%;
2833
z-index: 1;
2934
}
35+
3036
.loadingIndicator .indicatorContents {
31-
font-size: 30px;
3237
font-weight: 300;
3338
position: absolute;
3439
text-align: center;

src/LoadingIndicator/LoadingIndicator.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,8 @@ class LoadingIndicator extends PureComponent {
1515
};
1616

1717
render() {
18-
let percComplete;
19-
if (this.props.percentComplete && this.props.percentComplete !== 100) {
20-
percComplete = `${this.props.percentComplete}%`;
21-
}
18+
const pc = this.props.percentComplete;
19+
const percComplete = `${pc}%`;
2220

2321
return (
2422
<React.Fragment>
@@ -33,10 +31,12 @@ class LoadingIndicator extends PureComponent {
3331
) : (
3432
<div className="imageViewerLoadingIndicator loadingIndicator">
3533
<div className="indicatorContents">
36-
<p>
37-
Loading... <i className="fa fa-spin fa-circle-o-notch fa-fw" />{' '}
34+
<h2>
35+
{pc < 100 ? 'Loading...' : 'Loaded -'}
36+
<i className="fa fa-spin fa-circle-o-notch fa-fw" />{' '}
3837
{percComplete}
39-
</p>
38+
</h2>
39+
{pc === 100 && <p>Processing...</p>}
4040
</div>
4141
</div>
4242
)}

0 commit comments

Comments
 (0)