@@ -2,6 +2,7 @@ import PropTypes from 'prop-types';
2
2
import React , { Children , Component } from 'react' ;
3
3
import { StyleSheet , css } from 'aphrodite' ;
4
4
import ScrollLock from 'react-scrolllock' ;
5
+ import { BounceLoader } from 'react-spinners' ;
5
6
import { StyleSheet as StyleSheet$1 , css as css$1 } from 'aphrodite/no-important' ;
6
7
import { CSSTransitionGroup } from 'react-transition-group' ;
7
8
import { render } from 'react-dom' ;
@@ -948,7 +949,9 @@ var Lightbox = function (_Component) {
948
949
949
950
_this . theme = deepMerge ( theme , props . theme ) ;
950
951
_this . classes = StyleSheet . create ( deepMerge ( defaultStyles , _this . theme ) ) ;
951
- bindFunctions . call ( _this , [ 'gotoNext' , 'gotoPrev' , 'closeBackdrop' , 'handleKeyboardInput' ] ) ;
952
+ _this . state = { imageLoaded : false } ;
953
+
954
+ bindFunctions . call ( _this , [ 'gotoNext' , 'gotoPrev' , 'closeBackdrop' , 'handleKeyboardInput' , 'handleImageLoaded' ] ) ;
952
955
return _this ;
953
956
}
954
957
@@ -994,6 +997,12 @@ var Lightbox = function (_Component) {
994
997
}
995
998
}
996
999
1000
+ // preload current image
1001
+ if ( this . props . currentImage !== nextProps . currentImage || ! this . props . isOpen && nextProps . isOpen ) {
1002
+ var img = this . preloadImage ( nextProps . currentImage , this . handleImageLoaded ) ;
1003
+ this . setState ( { imageLoaded : img . complete } ) ;
1004
+ }
1005
+
997
1006
// add/remove event listeners
998
1007
if ( ! this . props . isOpen && nextProps . isOpen && nextProps . enableKeyboardInput ) {
999
1008
window . addEventListener ( 'keydown' , this . handleKeyboardInput ) ;
@@ -1016,33 +1025,54 @@ var Lightbox = function (_Component) {
1016
1025
1017
1026
} , {
1018
1027
key : 'preloadImage' ,
1019
- value : function preloadImage ( idx ) {
1028
+ value : function preloadImage ( idx , onload ) {
1020
1029
var image = this . props . images [ idx ] ;
1021
1030
if ( ! image ) return ;
1022
1031
1023
1032
var img = new Image ( ) ;
1024
1033
1034
+ // TODO: add error handling for missing images
1035
+ img . onerror = onload ;
1036
+ img . onload = onload ;
1025
1037
img . src = image . src ;
1026
1038
img . srcSet = image . srcSet || image . srcset ;
1039
+
1040
+ if ( img . srcSet ) img . setAttribute ( 'srcset' , img . srcSet ) ;
1041
+
1042
+ return img ;
1027
1043
}
1028
1044
} , {
1029
1045
key : 'gotoNext' ,
1030
1046
value : function gotoNext ( event ) {
1031
- if ( this . props . currentImage === this . props . images . length - 1 ) return ;
1047
+ var _props = this . props ,
1048
+ currentImage = _props . currentImage ,
1049
+ images = _props . images ;
1050
+ var imageLoaded = this . state . imageLoaded ;
1051
+
1052
+
1053
+ if ( ! imageLoaded || currentImage === images . length - 1 ) return ;
1054
+
1032
1055
if ( event ) {
1033
1056
event . preventDefault ( ) ;
1034
1057
event . stopPropagation ( ) ;
1035
1058
}
1059
+
1036
1060
this . props . onClickNext ( ) ;
1037
1061
}
1038
1062
} , {
1039
1063
key : 'gotoPrev' ,
1040
1064
value : function gotoPrev ( event ) {
1041
- if ( this . props . currentImage === 0 ) return ;
1065
+ var currentImage = this . props . currentImage ;
1066
+ var imageLoaded = this . state . imageLoaded ;
1067
+
1068
+
1069
+ if ( ! imageLoaded || currentImage === 0 ) return ;
1070
+
1042
1071
if ( event ) {
1043
1072
event . preventDefault ( ) ;
1044
1073
event . stopPropagation ( ) ;
1045
1074
}
1075
+
1046
1076
this . props . onClickPrev ( ) ;
1047
1077
}
1048
1078
} , {
@@ -1072,6 +1102,11 @@ var Lightbox = function (_Component) {
1072
1102
}
1073
1103
return false ;
1074
1104
}
1105
+ } , {
1106
+ key : 'handleImageLoaded' ,
1107
+ value : function handleImageLoaded ( ) {
1108
+ this . setState ( { imageLoaded : true } ) ;
1109
+ }
1075
1110
1076
1111
// ==============================
1077
1112
// RENDERERS
@@ -1106,14 +1141,12 @@ var Lightbox = function (_Component) {
1106
1141
} , {
1107
1142
key : 'renderDialog' ,
1108
1143
value : function renderDialog ( ) {
1109
- var _props = this . props ,
1110
- backdropClosesModal = _props . backdropClosesModal ,
1111
- customControls = _props . customControls ,
1112
- isOpen = _props . isOpen ,
1113
- onClose = _props . onClose ,
1114
- showCloseButton = _props . showCloseButton ,
1115
- showThumbnails = _props . showThumbnails ,
1116
- width = _props . width ;
1144
+ var _props2 = this . props ,
1145
+ backdropClosesModal = _props2 . backdropClosesModal ,
1146
+ isOpen = _props2 . isOpen ,
1147
+ showThumbnails = _props2 . showThumbnails ,
1148
+ width = _props2 . width ;
1149
+ var imageLoaded = this . state . imageLoaded ;
1117
1150
1118
1151
1119
1152
if ( ! isOpen ) return React . createElement ( 'span' , { key : 'closed' } ) ;
@@ -1132,31 +1165,31 @@ var Lightbox = function (_Component) {
1132
1165
} ,
1133
1166
React . createElement (
1134
1167
'div' ,
1135
- { className : css ( this . classes . content ) , style : { marginBottom : offsetThumbnails , maxWidth : width } } ,
1136
- React . createElement ( Header , {
1137
- customControls : customControls ,
1138
- onClose : onClose ,
1139
- showCloseButton : showCloseButton ,
1140
- closeButtonTitle : this . props . closeButtonTitle
1141
- } ) ,
1142
- this . renderImages ( )
1143
- ) ,
1144
- this . renderThumbnails ( ) ,
1145
- this . renderArrowPrev ( ) ,
1146
- this . renderArrowNext ( ) ,
1147
- React . createElement ( ScrollLock , null )
1168
+ null ,
1169
+ React . createElement (
1170
+ 'div' ,
1171
+ { className : css ( this . classes . content ) , style : { marginBottom : offsetThumbnails , maxWidth : width } } ,
1172
+ imageLoaded && this . renderHeader ( ) ,
1173
+ this . renderImages ( ) ,
1174
+ this . renderSpinner ( ) ,
1175
+ imageLoaded && this . renderFooter ( )
1176
+ ) ,
1177
+ imageLoaded && this . renderThumbnails ( ) ,
1178
+ imageLoaded && this . renderArrowPrev ( ) ,
1179
+ imageLoaded && this . renderArrowNext ( ) ,
1180
+ React . createElement ( ScrollLock , null )
1181
+ )
1148
1182
) ;
1149
1183
}
1150
1184
} , {
1151
1185
key : 'renderImages' ,
1152
1186
value : function renderImages ( ) {
1153
- var _props2 = this . props ,
1154
- currentImage = _props2 . currentImage ,
1155
- images = _props2 . images ,
1156
- imageCountSeparator = _props2 . imageCountSeparator ,
1157
- onClickImage = _props2 . onClickImage ,
1158
- showImageCount = _props2 . showImageCount ,
1159
- showThumbnails = _props2 . showThumbnails ;
1187
+ var _props3 = this . props ,
1188
+ currentImage = _props3 . currentImage ,
1189
+ images = _props3 . images ,
1190
+ onClickImage = _props3 . onClickImage ,
1191
+ showThumbnails = _props3 . showThumbnails ;
1192
+ var imageLoaded = this . state . imageLoaded ;
1160
1193
1161
1194
1162
1195
if ( ! images || ! images . length ) return null ;
@@ -1179,7 +1212,7 @@ var Lightbox = function (_Component) {
1179
1212
'figure' ,
1180
1213
{ className : css ( this . classes . figure ) } ,
1181
1214
React . createElement ( 'img' , {
1182
- className : css ( this . classes . image ) ,
1215
+ className : css ( this . classes . image , imageLoaded && this . classes . imageLoaded ) ,
1183
1216
onClick : onClickImage ,
1184
1217
sizes : sizes ,
1185
1218
alt : image . alt ,
@@ -1189,25 +1222,18 @@ var Lightbox = function (_Component) {
1189
1222
cursor : onClickImage ? 'pointer' : 'auto' ,
1190
1223
maxHeight : 'calc(100vh - ' + heightOffset + ')'
1191
1224
}
1192
- } ) ,
1193
- React . createElement ( Footer , {
1194
- caption : images [ currentImage ] . caption ,
1195
- countCurrent : currentImage + 1 ,
1196
- countSeparator : imageCountSeparator ,
1197
- countTotal : images . length ,
1198
- showCount : showImageCount
1199
1225
} )
1200
1226
) ;
1201
1227
}
1202
1228
} , {
1203
1229
key : 'renderThumbnails' ,
1204
1230
value : function renderThumbnails ( ) {
1205
- var _props3 = this . props ,
1206
- images = _props3 . images ,
1207
- currentImage = _props3 . currentImage ,
1208
- onClickThumbnail = _props3 . onClickThumbnail ,
1209
- showThumbnails = _props3 . showThumbnails ,
1210
- thumbnailOffset = _props3 . thumbnailOffset ;
1231
+ var _props4 = this . props ,
1232
+ images = _props4 . images ,
1233
+ currentImage = _props4 . currentImage ,
1234
+ onClickThumbnail = _props4 . onClickThumbnail ,
1235
+ showThumbnails = _props4 . showThumbnails ,
1236
+ thumbnailOffset = _props4 . thumbnailOffset ;
1211
1237
1212
1238
1213
1239
if ( ! showThumbnails ) return ;
@@ -1219,6 +1245,63 @@ var Lightbox = function (_Component) {
1219
1245
onClickThumbnail : onClickThumbnail
1220
1246
} ) ;
1221
1247
}
1248
+ } , {
1249
+ key : 'renderHeader' ,
1250
+ value : function renderHeader ( ) {
1251
+ var _props5 = this . props ,
1252
+ closeButtonTitle = _props5 . closeButtonTitle ,
1253
+ customControls = _props5 . customControls ,
1254
+ onClose = _props5 . onClose ,
1255
+ showCloseButton = _props5 . showCloseButton ;
1256
+
1257
+
1258
+ return React . createElement ( Header , {
1259
+ customControls : customControls ,
1260
+ onClose : onClose ,
1261
+ showCloseButton : showCloseButton ,
1262
+ closeButtonTitle : closeButtonTitle
1263
+ } ) ;
1264
+ }
1265
+ } , {
1266
+ key : 'renderFooter' ,
1267
+ value : function renderFooter ( ) {
1268
+ var _props6 = this . props ,
1269
+ currentImage = _props6 . currentImage ,
1270
+ images = _props6 . images ,
1271
+ imageCountSeparator = _props6 . imageCountSeparator ,
1272
+ showImageCount = _props6 . showImageCount ;
1273
+
1274
+
1275
+ if ( ! images || ! images . length ) return null ;
1276
+
1277
+ return React . createElement ( Footer , {
1278
+ caption : images [ currentImage ] . caption ,
1279
+ countCurrent : currentImage + 1 ,
1280
+ countSeparator : imageCountSeparator ,
1281
+ countTotal : images . length ,
1282
+ showCount : showImageCount
1283
+ } ) ;
1284
+ }
1285
+ } , {
1286
+ key : 'renderSpinner' ,
1287
+ value : function renderSpinner ( ) {
1288
+ var _props7 = this . props ,
1289
+ spinner = _props7 . spinner ,
1290
+ spinnerColor = _props7 . spinnerColor ,
1291
+ spinnerSize = _props7 . spinnerSize ;
1292
+ var imageLoaded = this . state . imageLoaded ;
1293
+
1294
+ var Spinner = spinner ;
1295
+
1296
+ return React . createElement (
1297
+ 'div' ,
1298
+ { className : css ( this . classes . spinner , ! imageLoaded && this . classes . spinnerActive ) } ,
1299
+ React . createElement ( Spinner , {
1300
+ color : spinnerColor ,
1301
+ size : spinnerSize
1302
+ } )
1303
+ ) ;
1304
+ }
1222
1305
} , {
1223
1306
key : 'render' ,
1224
1307
value : function render$$1 ( ) {
@@ -1232,6 +1315,10 @@ var Lightbox = function (_Component) {
1232
1315
return Lightbox ;
1233
1316
} ( Component ) ;
1234
1317
1318
+ var DefaultSpinner = function DefaultSpinner ( props ) {
1319
+ return React . createElement ( BounceLoader , props ) ;
1320
+ } ;
1321
+
1235
1322
Lightbox . propTypes = {
1236
1323
backdropClosesModal : PropTypes . bool ,
1237
1324
closeButtonTitle : PropTypes . string ,
@@ -1256,6 +1343,9 @@ Lightbox.propTypes = {
1256
1343
showCloseButton : PropTypes . bool ,
1257
1344
showImageCount : PropTypes . bool ,
1258
1345
showThumbnails : PropTypes . bool ,
1346
+ spinner : PropTypes . func ,
1347
+ spinnerColor : PropTypes . string ,
1348
+ spinnerSize : PropTypes . number ,
1259
1349
theme : PropTypes . object ,
1260
1350
thumbnailOffset : PropTypes . number ,
1261
1351
width : PropTypes . number
@@ -1271,6 +1361,9 @@ Lightbox.defaultProps = {
1271
1361
rightArrowTitle : 'Next (Right arrow key)' ,
1272
1362
showCloseButton : true ,
1273
1363
showImageCount : true ,
1364
+ spinner : DefaultSpinner ,
1365
+ spinnerColor : 'white' ,
1366
+ spinnerSize : 100 ,
1274
1367
theme : { } ,
1275
1368
thumbnailOffset : 2 ,
1276
1369
width : 1024
@@ -1294,7 +1387,27 @@ var defaultStyles = {
1294
1387
1295
1388
// disable user select
1296
1389
WebkitTouchCallout : 'none' ,
1297
- userSelect : 'none'
1390
+ userSelect : 'none' ,
1391
+
1392
+ // opacity animation on image load
1393
+ opacity : 0 ,
1394
+ transition : 'opacity 0.3s'
1395
+ } ,
1396
+ imageLoaded : {
1397
+ opacity : 1
1398
+ } ,
1399
+ spinner : {
1400
+ position : 'absolute' ,
1401
+ top : '50%' ,
1402
+ left : '50%' ,
1403
+ transform : 'translate(-50%, -50%)' ,
1404
+
1405
+ // opacity animation to make spinner appear with delay
1406
+ opacity : 0 ,
1407
+ transition : 'opacity 0.3s'
1408
+ } ,
1409
+ spinnerActive : {
1410
+ opacity : 1
1298
1411
}
1299
1412
} ;
1300
1413
0 commit comments