zoomora 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +75 -15
- package/dist/zoomora.es.js +94 -28
- package/dist/zoomora.es.js.map +1 -1
- package/dist/zoomora.es.min.js +4 -4
- package/dist/zoomora.es.min.js.map +1 -1
- package/dist/zoomora.umd.js +94 -28
- package/dist/zoomora.umd.js.map +1 -1
- package/dist/zoomora.umd.min.js +4 -4
- package/dist/zoomora.umd.min.js.map +1 -1
- package/package.json +1 -1
- package/src/zoomora.js +160 -47
package/src/zoomora.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Zoomora Lightbox Plugin v1.
|
|
2
|
+
* Zoomora Lightbox Plugin v1.2.0
|
|
3
3
|
* A modern, responsive lightbox plugin with zoom, fullscreen, and gallery features
|
|
4
4
|
*
|
|
5
|
-
* Copyright (c)
|
|
5
|
+
* Copyright (c) 2026 Faruk Ahmed (FrontTheme)
|
|
6
6
|
* Licensed under MIT License
|
|
7
7
|
*/
|
|
8
8
|
|
|
@@ -46,6 +46,7 @@ import './styles/zoomora.scss';
|
|
|
46
46
|
maxZoomScale: 3, // Maximum zoom scale
|
|
47
47
|
zoomStep: 0.1, // Zoom step for scroll wheel
|
|
48
48
|
animationDuration: 300, // Animation duration in ms
|
|
49
|
+
closeOnBackgroundClick: true, // Close lightbox when clicking on background
|
|
49
50
|
onOpen: null, // Callback when lightbox opens
|
|
50
51
|
onClose: null, // Callback when lightbox closes
|
|
51
52
|
onNext: null, // Callback when navigating to next item
|
|
@@ -247,7 +248,7 @@ import './styles/zoomora.scss';
|
|
|
247
248
|
// Lightbox click to close
|
|
248
249
|
this.addEventListener(this.zoomora, 'click', this.boundMethods.handleZoomoraClick);
|
|
249
250
|
|
|
250
|
-
// Content click for zoom toggle
|
|
251
|
+
// Content click for zoom toggle and background click
|
|
251
252
|
this.addEventListener(this.content, 'click', this.boundMethods.handleContentClick);
|
|
252
253
|
|
|
253
254
|
// Mouse drag events
|
|
@@ -344,16 +345,25 @@ import './styles/zoomora.scss';
|
|
|
344
345
|
* Handle fullscreen change events
|
|
345
346
|
*/
|
|
346
347
|
handleFullscreenChange() {
|
|
347
|
-
const isInFullscreen =
|
|
348
|
+
const isInFullscreen = !!(
|
|
349
|
+
document.fullscreenElement ||
|
|
348
350
|
document.webkitFullscreenElement ||
|
|
349
351
|
document.mozFullScreenElement ||
|
|
350
|
-
document.msFullscreenElement
|
|
352
|
+
document.msFullscreenElement
|
|
353
|
+
);
|
|
351
354
|
|
|
355
|
+
// Only update if state changed from fullscreen to not fullscreen
|
|
352
356
|
if (!isInFullscreen && this.isFullscreen) {
|
|
353
357
|
this.isFullscreen = false;
|
|
354
358
|
this.zoomora.classList.remove('fullscreen');
|
|
355
359
|
this.updateButtons();
|
|
356
360
|
}
|
|
361
|
+
// Update if entering fullscreen
|
|
362
|
+
else if (isInFullscreen && !this.isFullscreen) {
|
|
363
|
+
this.isFullscreen = true;
|
|
364
|
+
this.zoomora.classList.add('fullscreen');
|
|
365
|
+
this.updateButtons();
|
|
366
|
+
}
|
|
357
367
|
}
|
|
358
368
|
|
|
359
369
|
/**
|
|
@@ -361,7 +371,7 @@ import './styles/zoomora.scss';
|
|
|
361
371
|
* @param {Event} e - Click event
|
|
362
372
|
*/
|
|
363
373
|
handleZoomoraClick(e) {
|
|
364
|
-
if (e.target === this.zoomora) {
|
|
374
|
+
if (e.target === this.zoomora && this.options.closeOnBackgroundClick) {
|
|
365
375
|
this.close();
|
|
366
376
|
}
|
|
367
377
|
}
|
|
@@ -376,9 +386,60 @@ import './styles/zoomora.scss';
|
|
|
376
386
|
return;
|
|
377
387
|
}
|
|
378
388
|
|
|
379
|
-
|
|
380
|
-
if (
|
|
381
|
-
|
|
389
|
+
// Don't close on header clicks
|
|
390
|
+
if (this.header && this.header.contains(e.target)) {
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Don't close on button clicks
|
|
395
|
+
if (e.target.closest('button')) {
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Check if click is directly on actual media element (img, video, iframe)
|
|
400
|
+
const mediaElement = e.target.closest('.zoomora-media, .zoomora-video, .zoomora-iframe');
|
|
401
|
+
|
|
402
|
+
if (mediaElement) {
|
|
403
|
+
// Only toggle zoom for images
|
|
404
|
+
if (mediaElement.classList.contains('zoomora-media') && !mediaElement.classList.contains('no-zoom')) {
|
|
405
|
+
this.toggleZoom();
|
|
406
|
+
}
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Check if clicking inside video container but not on video itself
|
|
411
|
+
const videoContainer = e.target.closest('.zoomora-video-container');
|
|
412
|
+
if (videoContainer) {
|
|
413
|
+
// If clicking on the play button, don't close
|
|
414
|
+
if (e.target.closest('.zoomora-play-button')) {
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
// If clicking on background area of video container, close
|
|
418
|
+
if (this.options.closeOnBackgroundClick) {
|
|
419
|
+
this.close();
|
|
420
|
+
}
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Check if clicking on background elements (including when zoomed)
|
|
425
|
+
// This checks the actual target element, not just closest()
|
|
426
|
+
const clickedElement = e.target;
|
|
427
|
+
const isBackgroundElement =
|
|
428
|
+
clickedElement.classList.contains('zoomora-content') ||
|
|
429
|
+
clickedElement.classList.contains('zoomora-slide-container') ||
|
|
430
|
+
clickedElement.classList.contains('zoomora-slide') ||
|
|
431
|
+
clickedElement.classList.contains('zoomora-slides-container') ||
|
|
432
|
+
clickedElement.id === 'zoomoraContent' ||
|
|
433
|
+
clickedElement.id === 'zoomoraSlidesContainer';
|
|
434
|
+
|
|
435
|
+
if (isBackgroundElement && this.options.closeOnBackgroundClick) {
|
|
436
|
+
this.close();
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// If we reach here and no media was clicked, close
|
|
441
|
+
if (this.options.closeOnBackgroundClick) {
|
|
442
|
+
this.close();
|
|
382
443
|
}
|
|
383
444
|
}
|
|
384
445
|
|
|
@@ -970,11 +1031,6 @@ import './styles/zoomora.scss';
|
|
|
970
1031
|
}
|
|
971
1032
|
}
|
|
972
1033
|
|
|
973
|
-
/**
|
|
974
|
-
* Calculate zoom levels for an image
|
|
975
|
-
* @param {HTMLImageElement} img - Image element
|
|
976
|
-
* @returns {Object} Zoom configuration
|
|
977
|
-
*/
|
|
978
1034
|
/**
|
|
979
1035
|
* Calculate zoom levels for an image
|
|
980
1036
|
* @param {HTMLImageElement} img - Image element
|
|
@@ -1151,7 +1207,6 @@ import './styles/zoomora.scss';
|
|
|
1151
1207
|
const zoomConfig = this.calculateZoomLevels(media);
|
|
1152
1208
|
|
|
1153
1209
|
if (!zoomConfig.canZoom) {
|
|
1154
|
-
console.log('Image cannot be zoomed (already at full size or larger)');
|
|
1155
1210
|
return;
|
|
1156
1211
|
}
|
|
1157
1212
|
|
|
@@ -1193,8 +1248,6 @@ import './styles/zoomora.scss';
|
|
|
1193
1248
|
media.style.transform = `translate(0px, 0px) scale(${targetScale})`;
|
|
1194
1249
|
media.style.cursor = 'grab';
|
|
1195
1250
|
media.classList.add('zoomed');
|
|
1196
|
-
|
|
1197
|
-
console.log(`Zoomed to level ${currentLevel}/${zoomConfig.levels.length - 1} (${(targetScale * 100).toFixed(0)}%)`);
|
|
1198
1251
|
}
|
|
1199
1252
|
}
|
|
1200
1253
|
|
|
@@ -1231,21 +1284,50 @@ import './styles/zoomora.scss';
|
|
|
1231
1284
|
*/
|
|
1232
1285
|
enterFullscreen() {
|
|
1233
1286
|
const element = this.zoomora;
|
|
1234
|
-
const requestFullscreen = element.requestFullscreen ||
|
|
1235
|
-
element.webkitRequestFullscreen ||
|
|
1236
|
-
element.mozRequestFullScreen ||
|
|
1237
|
-
element.msRequestFullscreen;
|
|
1238
1287
|
|
|
1239
|
-
|
|
1240
|
-
|
|
1288
|
+
try {
|
|
1289
|
+
// Try standard API first
|
|
1290
|
+
if (element.requestFullscreen) {
|
|
1291
|
+
element.requestFullscreen()
|
|
1292
|
+
.then(() => {
|
|
1293
|
+
this.isFullscreen = true;
|
|
1294
|
+
this.zoomora.classList.add('fullscreen');
|
|
1295
|
+
this.updateButtons();
|
|
1296
|
+
})
|
|
1297
|
+
.catch(err => {
|
|
1298
|
+
console.warn('Zoomora: Fullscreen request failed:', err.message);
|
|
1299
|
+
this.fallbackFullscreen();
|
|
1300
|
+
});
|
|
1301
|
+
}
|
|
1302
|
+
// Try webkit (Safari, older Chrome)
|
|
1303
|
+
else if (element.webkitRequestFullscreen) {
|
|
1304
|
+
element.webkitRequestFullscreen();
|
|
1305
|
+
// Webkit doesn't return a promise, so set state immediately
|
|
1306
|
+
this.isFullscreen = true;
|
|
1307
|
+
this.zoomora.classList.add('fullscreen');
|
|
1308
|
+
this.updateButtons();
|
|
1309
|
+
}
|
|
1310
|
+
// Try moz (Firefox)
|
|
1311
|
+
else if (element.mozRequestFullScreen) {
|
|
1312
|
+
element.mozRequestFullScreen();
|
|
1313
|
+
this.isFullscreen = true;
|
|
1314
|
+
this.zoomora.classList.add('fullscreen');
|
|
1315
|
+
this.updateButtons();
|
|
1316
|
+
}
|
|
1317
|
+
// Try ms (IE11)
|
|
1318
|
+
else if (element.msRequestFullscreen) {
|
|
1319
|
+
element.msRequestFullscreen();
|
|
1241
1320
|
this.isFullscreen = true;
|
|
1242
1321
|
this.zoomora.classList.add('fullscreen');
|
|
1243
1322
|
this.updateButtons();
|
|
1244
|
-
}
|
|
1245
|
-
|
|
1323
|
+
}
|
|
1324
|
+
// Fallback to CSS fullscreen
|
|
1325
|
+
else {
|
|
1326
|
+
console.warn('Zoomora: Fullscreen API not supported, using CSS fallback');
|
|
1246
1327
|
this.fallbackFullscreen();
|
|
1247
|
-
}
|
|
1248
|
-
}
|
|
1328
|
+
}
|
|
1329
|
+
} catch (error) {
|
|
1330
|
+
console.error('Zoomora: Error entering fullscreen:', error);
|
|
1249
1331
|
this.fallbackFullscreen();
|
|
1250
1332
|
}
|
|
1251
1333
|
}
|
|
@@ -1254,30 +1336,61 @@ import './styles/zoomora.scss';
|
|
|
1254
1336
|
* Exit fullscreen mode
|
|
1255
1337
|
*/
|
|
1256
1338
|
exitFullscreen() {
|
|
1257
|
-
|
|
1339
|
+
// First check if we're actually in fullscreen
|
|
1340
|
+
const isInFullscreen = !!(
|
|
1341
|
+
document.fullscreenElement ||
|
|
1258
1342
|
document.webkitFullscreenElement ||
|
|
1259
1343
|
document.mozFullScreenElement ||
|
|
1260
|
-
document.msFullscreenElement
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1344
|
+
document.msFullscreenElement
|
|
1345
|
+
);
|
|
1346
|
+
|
|
1347
|
+
if (!isInFullscreen) {
|
|
1348
|
+
// Not in real fullscreen, just exit CSS fullscreen
|
|
1349
|
+
this.fallbackExitFullscreen();
|
|
1350
|
+
return;
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
try {
|
|
1354
|
+
// Try standard API first
|
|
1355
|
+
if (document.exitFullscreen) {
|
|
1356
|
+
document.exitFullscreen()
|
|
1357
|
+
.then(() => {
|
|
1358
|
+
this.isFullscreen = false;
|
|
1359
|
+
this.zoomora.classList.remove('fullscreen');
|
|
1360
|
+
this.updateButtons();
|
|
1361
|
+
})
|
|
1362
|
+
.catch(err => {
|
|
1363
|
+
console.warn('Zoomora: Exit fullscreen failed:', err.message);
|
|
1364
|
+
this.fallbackExitFullscreen();
|
|
1365
|
+
});
|
|
1366
|
+
}
|
|
1367
|
+
// Try webkit
|
|
1368
|
+
else if (document.webkitExitFullscreen) {
|
|
1369
|
+
document.webkitExitFullscreen();
|
|
1370
|
+
this.isFullscreen = false;
|
|
1371
|
+
this.zoomora.classList.remove('fullscreen');
|
|
1372
|
+
this.updateButtons();
|
|
1373
|
+
}
|
|
1374
|
+
// Try moz
|
|
1375
|
+
else if (document.mozCancelFullScreen) {
|
|
1376
|
+
document.mozCancelFullScreen();
|
|
1377
|
+
this.isFullscreen = false;
|
|
1378
|
+
this.zoomora.classList.remove('fullscreen');
|
|
1379
|
+
this.updateButtons();
|
|
1380
|
+
}
|
|
1381
|
+
// Try ms
|
|
1382
|
+
else if (document.msExitFullscreen) {
|
|
1383
|
+
document.msExitFullscreen();
|
|
1384
|
+
this.isFullscreen = false;
|
|
1385
|
+
this.zoomora.classList.remove('fullscreen');
|
|
1386
|
+
this.updateButtons();
|
|
1387
|
+
}
|
|
1388
|
+
// Fallback
|
|
1389
|
+
else {
|
|
1278
1390
|
this.fallbackExitFullscreen();
|
|
1279
1391
|
}
|
|
1280
|
-
}
|
|
1392
|
+
} catch (error) {
|
|
1393
|
+
console.error('Zoomora: Error exiting fullscreen:', error);
|
|
1281
1394
|
this.fallbackExitFullscreen();
|
|
1282
1395
|
}
|
|
1283
1396
|
}
|