neiki-gallery 2.1.0 → 3.0.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/LICENSE +21 -21
- package/README.md +232 -33
- package/dist/neiki-gallery.css +663 -2
- package/dist/neiki-gallery.js +1419 -25
- package/dist/neiki-gallery.min.css +1 -1
- package/dist/neiki-gallery.min.js +2 -2
- package/package.json +54 -45
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 neikiri
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 neikiri
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
<img src="https://img.shields.io/badge/css-%23663399.svg?style=for-the-badge&logo=css&logoColor=white" alt="CSS">
|
|
11
11
|
<br>
|
|
12
12
|
<img src="https://img.shields.io/badge/License-MIT-2563EB?style=for-the-badge&logo=open-source-initiative&logoColor=white&labelColor=000F15&logoWidth=20" alt="License">
|
|
13
|
-
<img src="https://img.shields.io/badge/Version-
|
|
13
|
+
<img src="https://img.shields.io/badge/Version-3.0.0-2563EB?style=for-the-badge&logo=semantic-release&logoColor=white&labelColor=000F15&logoWidth=20" alt="Version">
|
|
14
14
|
</p>
|
|
15
15
|
|
|
16
16
|
<p align="center">
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
- **Multiple instances** — independent galleries on one page
|
|
56
56
|
- **Dark & light themes** — CSS custom properties + accent color system (`--neiki-accent`)
|
|
57
57
|
- **Accessibility** — ARIA attributes, focus management, `prefers-reduced-motion`
|
|
58
|
-
- **Event system** — `open`, `close`, `change`, `filter`, `select`, `slideshowStart`, `slideshowStop`
|
|
58
|
+
- **Event system** — `open`, `close`, `change`, `filter`, `select`, `slideshowStart`, `slideshowStop`, `favorite`, `unfavorite`, `infoOpen`, `infoClose`, `print`, `editorOpen`, `editorExport`, `annotateOpen`, `annotateExport`, `append`, `remove`
|
|
59
59
|
- **Blurhash placeholders** — `data-blurhash` attribute decoded into blurred preview (replaces shimmer)
|
|
60
60
|
- **Story mode** — Instagram-like vertical fullscreen viewer with progress bars and tap navigation
|
|
61
61
|
- **Picture-in-Picture** — minimize lightbox to a resizable corner window
|
|
@@ -68,7 +68,21 @@
|
|
|
68
68
|
- **Drag & drop reorder** — HTML5 drag to reorder grid items
|
|
69
69
|
- **Aspect-ratio skeleton** — `data-width`/`data-height` for placeholder sizing
|
|
70
70
|
- **Web Share API** — native mobile sharing with clipboard fallback
|
|
71
|
-
- **
|
|
71
|
+
- **Video & embeds** — auto-detect MP4/WebM/Ogg/MOV/M4V, YouTube and Vimeo URLs in lightbox
|
|
72
|
+
- **Plugin system** — `NeikiGallery.registerPlugin()` with `init`/`open`/`change`/`close`/`destroy` lifecycle hooks
|
|
73
|
+
- **Album groups** — `data-group="album"` links galleries; navigation traverses across all
|
|
74
|
+
- **Favorites** — heart button + localStorage persistence + `B` key shortcut
|
|
75
|
+
- **Info panel** — slide-out sidebar with metadata, EXIF, source URL (`I` key)
|
|
76
|
+
- **Image editor** — rotate / flip / export to PNG via `<canvas>`
|
|
77
|
+
- **Annotation layer** — freehand drawing with color picker, brush size, undo, clear, export
|
|
78
|
+
- **Print** — print current image with caption (`P` key)
|
|
79
|
+
- **Right-click context menu** — open original, copy link, download, share, print, favorite
|
|
80
|
+
- **Infinite scroll** — `loadMore` callback + `append()` / `remove()` API
|
|
81
|
+
- **Keyboard shortcuts overlay** — press `?` to view all shortcuts
|
|
82
|
+
- **System theme detection** — `theme: 'system'` follows OS `prefers-color-scheme`
|
|
83
|
+
- **Kenburns slideshow** — slow zoom-and-pan animation per slide
|
|
84
|
+
- **Configurable counter** — `counterFormat` with `{current}`/`{total}`/`{percent}` tokens
|
|
85
|
+
- **Cross-browser** — Chrome 80+, Firefox 75+, Safari 14+, Edge 80+
|
|
72
86
|
|
|
73
87
|
## 🚀 Quick Start
|
|
74
88
|
|
|
@@ -103,7 +117,7 @@ That's it — galleries with `data-neiki-gallery` auto-initialize on page load.
|
|
|
103
117
|
|
|
104
118
|
> **Pin a specific version** to avoid unexpected changes:
|
|
105
119
|
> ```
|
|
106
|
-
> https://cdn.neiki.eu/neiki-gallery/
|
|
120
|
+
> https://cdn.neiki.eu/neiki-gallery/3.0.0/neiki-gallery.min.js
|
|
107
121
|
> ```
|
|
108
122
|
|
|
109
123
|
### Self-Hosting (Local)
|
|
@@ -147,26 +161,45 @@ const gallery = new NeikiGallery('#my-gallery', {
|
|
|
147
161
|
contextualZoom: false, // zoom to click point
|
|
148
162
|
fullscreen: true,
|
|
149
163
|
transition: 'fade', // 'fade' | 'slide'
|
|
150
|
-
theme: '
|
|
164
|
+
theme: 'system', // 'dark' | 'light' | 'system' (auto-detect) ← v3 default
|
|
151
165
|
hashNavigation: true,
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
166
|
+
counter: true,
|
|
167
|
+
counterFormat: '{current} / {total}', // v3 — tokens: {current}, {total}, {percent}
|
|
168
|
+
stagger: true,
|
|
169
|
+
slideshow: { // v3 — nested config (boolean still works)
|
|
170
|
+
interval: 4000,
|
|
171
|
+
pauseOnHover: true,
|
|
172
|
+
kenburns: false,
|
|
173
|
+
direction: 'forward' // or 'reverse'
|
|
174
|
+
},
|
|
175
|
+
share: true,
|
|
176
|
+
filter: false,
|
|
177
|
+
batchSelect: false,
|
|
158
178
|
// v2.1.0
|
|
159
|
-
focusPoint: true,
|
|
160
|
-
blurhash: true,
|
|
161
|
-
exif: false,
|
|
162
|
-
storyMode: false,
|
|
163
|
-
pip: false,
|
|
164
|
-
virtualScroll: false,
|
|
165
|
-
dragReorder: false,
|
|
166
|
-
backdropTint: false,
|
|
167
|
-
morphTransition: false,
|
|
168
|
-
colorPalette: false,
|
|
169
|
-
aspectSkeleton: true,
|
|
179
|
+
focusPoint: true,
|
|
180
|
+
blurhash: true,
|
|
181
|
+
exif: false,
|
|
182
|
+
storyMode: false,
|
|
183
|
+
pip: false,
|
|
184
|
+
virtualScroll: false,
|
|
185
|
+
dragReorder: false,
|
|
186
|
+
backdropTint: false,
|
|
187
|
+
morphTransition: false,
|
|
188
|
+
colorPalette: false,
|
|
189
|
+
aspectSkeleton: true,
|
|
190
|
+
// v3.0.0
|
|
191
|
+
video: true, // detect MP4 / YouTube / Vimeo
|
|
192
|
+
plugins: ['watermark', { name: 'analytics', trackingId: 'X' }],
|
|
193
|
+
group: '', // album group name (or use data-group)
|
|
194
|
+
favorites: false, // ❤️ button + localStorage
|
|
195
|
+
favoritesKey: '', // custom localStorage key suffix
|
|
196
|
+
infoPanel: false, // sidebar with metadata (I key)
|
|
197
|
+
contextMenu: false, // right-click menu on items
|
|
198
|
+
shortcutsHelp: true, // overlay on '?' key
|
|
199
|
+
infiniteScroll: false, // auto-load more
|
|
200
|
+
loadMore: null, // function(currentLength) → array | Promise<array>
|
|
201
|
+
editor: false, // crop/rotate/flip toolbar
|
|
202
|
+
annotate: false // freehand drawing layer
|
|
170
203
|
});
|
|
171
204
|
```
|
|
172
205
|
|
|
@@ -177,15 +210,50 @@ const gallery = new NeikiGallery('#my-gallery', {
|
|
|
177
210
|
```js
|
|
178
211
|
gallery.open(index); // Open lightbox at image index
|
|
179
212
|
gallery.close(); // Close lightbox
|
|
180
|
-
gallery.next(); // Go to next image
|
|
181
|
-
gallery.prev(); // Go to previous image
|
|
213
|
+
gallery.next(); // Go to next image (group-aware)
|
|
214
|
+
gallery.prev(); // Go to previous image (group-aware)
|
|
182
215
|
gallery.startSlideshow(); // Start autoplay
|
|
183
216
|
gallery.stopSlideshow(); // Stop autoplay
|
|
217
|
+
gallery.pauseSlideshow(); // v3 — pause without emitting stop
|
|
184
218
|
gallery.toggleSlideshow(); // Toggle autoplay
|
|
185
219
|
gallery.filter('landscape'); // Filter by tag (null = show all)
|
|
186
220
|
gallery.getSelected(); // Get batch-selected items
|
|
187
221
|
gallery.clearSelection(); // Clear batch selection
|
|
188
222
|
gallery.getOrder(); // Get current item order (after drag reorder)
|
|
223
|
+
|
|
224
|
+
// v3.0.0 — Favorites
|
|
225
|
+
gallery.toggleFavorite(); // Toggle favorite for current image
|
|
226
|
+
gallery.isFavorite(index); // Check if image is favorited
|
|
227
|
+
gallery.getFavorites(); // Get array of favorited image src URLs
|
|
228
|
+
gallery.clearFavorites(); // Remove all favorites
|
|
229
|
+
|
|
230
|
+
// v3.0.0 — Info panel / overlays
|
|
231
|
+
gallery.toggleInfoPanel(force); // Toggle info sidebar (force = bool)
|
|
232
|
+
gallery.toggleShortcutsHelp(force); // Toggle keyboard shortcuts overlay
|
|
233
|
+
|
|
234
|
+
// v3.0.0 — Editor & annotation
|
|
235
|
+
gallery.openEditor(); // Open crop/rotate/flip editor
|
|
236
|
+
gallery.closeEditor();
|
|
237
|
+
gallery.getEditedBlob(); // Last exported edited image as Blob
|
|
238
|
+
|
|
239
|
+
gallery.openAnnotate(); // Open drawing layer
|
|
240
|
+
gallery.closeAnnotate();
|
|
241
|
+
gallery.getAnnotatedBlob();
|
|
242
|
+
|
|
243
|
+
// v3.0.0 — Print & dynamic content
|
|
244
|
+
gallery.print(index); // Print current or specified image
|
|
245
|
+
gallery.append([{src,...}]); // Append new items at runtime
|
|
246
|
+
gallery.remove(index); // Remove item by index
|
|
247
|
+
|
|
248
|
+
// v3.0.0 — Plugin access
|
|
249
|
+
gallery.plugin('watermark'); // Get plugin instance by name
|
|
250
|
+
|
|
251
|
+
// v3.0.0 — Event helpers
|
|
252
|
+
gallery.on('change', fn);
|
|
253
|
+
gallery.off('change', fn); // Remove specific listener
|
|
254
|
+
gallery.off('change'); // Remove all listeners for event
|
|
255
|
+
gallery.once('open', fn); // Auto-removed after first fire
|
|
256
|
+
|
|
189
257
|
gallery.destroy(); // Remove gallery, clean up all listeners & DOM
|
|
190
258
|
```
|
|
191
259
|
|
|
@@ -221,8 +289,57 @@ NeikiGallery.parseExif('photo.jpg', (tags) => {
|
|
|
221
289
|
|
|
222
290
|
// Blurhash decoding (v2.1.0)
|
|
223
291
|
const pixels = NeikiGallery.decodeBlurhash('LEHV6nWB2y...', 32, 32);
|
|
292
|
+
|
|
293
|
+
// Media type detection (v3.0.0)
|
|
294
|
+
NeikiGallery.detectMediaType('https://youtube.com/watch?v=abc'); // 'youtube'
|
|
295
|
+
NeikiGallery.detectMediaType('clip.mp4'); // 'video'
|
|
296
|
+
NeikiGallery.detectMediaType('photo.jpg'); // 'image'
|
|
297
|
+
|
|
298
|
+
// Plugin registration (v3.0.0)
|
|
299
|
+
NeikiGallery.registerPlugin('watermark', (gallery, options) => ({
|
|
300
|
+
init() { /* set up DOM, listeners */ },
|
|
301
|
+
open() { /* on lightbox open */ },
|
|
302
|
+
change(d){ /* on slide change, d = {index, item, prevIndex} */ },
|
|
303
|
+
close() { /* on lightbox close */ },
|
|
304
|
+
destroy(){ /* cleanup */ }
|
|
305
|
+
}));
|
|
306
|
+
|
|
307
|
+
NeikiGallery.unregisterPlugin('watermark');
|
|
308
|
+
NeikiGallery.getRegisteredPlugins(); // ['watermark', ...]
|
|
309
|
+
|
|
310
|
+
// Version
|
|
311
|
+
console.log(NeikiGallery.version); // '3.0.0'
|
|
224
312
|
```
|
|
225
313
|
|
|
314
|
+
### Plugin System
|
|
315
|
+
|
|
316
|
+
A plugin is a factory function that receives the gallery instance and an options
|
|
317
|
+
object, and returns an object with optional lifecycle methods:
|
|
318
|
+
|
|
319
|
+
```js
|
|
320
|
+
NeikiGallery.registerPlugin('analytics', (gallery, opts) => ({
|
|
321
|
+
init() {
|
|
322
|
+
console.log('[analytics] gallery initialized');
|
|
323
|
+
},
|
|
324
|
+
open(data) {
|
|
325
|
+
track('lightbox_open', { index: data.index });
|
|
326
|
+
},
|
|
327
|
+
change(data) {
|
|
328
|
+
track('slide_view', { src: data.item.src, index: data.index });
|
|
329
|
+
},
|
|
330
|
+
close() {
|
|
331
|
+
track('lightbox_close');
|
|
332
|
+
},
|
|
333
|
+
destroy() {
|
|
334
|
+
console.log('[analytics] cleanup');
|
|
335
|
+
}
|
|
336
|
+
}));
|
|
337
|
+
|
|
338
|
+
new NeikiGallery('#g', { plugins: [{ name: 'analytics', sampleRate: 0.5 }] });
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
You can access plugin instances on a gallery via `gallery.plugin('analytics')`.
|
|
342
|
+
|
|
226
343
|
### Events
|
|
227
344
|
|
|
228
345
|
```js
|
|
@@ -256,6 +373,16 @@ gallery.on('reorder', (order) => {
|
|
|
256
373
|
|
|
257
374
|
gallery.on('pipEnter', () => console.log('PiP on'));
|
|
258
375
|
gallery.on('storyEnter', () => console.log('Story opened'));
|
|
376
|
+
|
|
377
|
+
// v3.0.0 events
|
|
378
|
+
gallery.on('favorite', ({ index, src }) => console.log('★ favorited', src));
|
|
379
|
+
gallery.on('unfavorite', ({ index, src }) => console.log('☆ removed'));
|
|
380
|
+
gallery.on('infoOpen', () => console.log('info panel opened'));
|
|
381
|
+
gallery.on('print', ({ index, src }) => console.log('printed', src));
|
|
382
|
+
gallery.on('editorExport', ({ blob, url }) => console.log('edited blob', url));
|
|
383
|
+
gallery.on('annotateExport', ({ blob, url }) => console.log('annotated', url));
|
|
384
|
+
gallery.on('append', (items) => console.log('appended', items.length));
|
|
385
|
+
gallery.on('remove', ({ index }) => console.log('removed item', index));
|
|
259
386
|
```
|
|
260
387
|
|
|
261
388
|
### Options
|
|
@@ -269,15 +396,15 @@ gallery.on('storyEnter', () => console.log('Story opened'));
|
|
|
269
396
|
| `contextualZoom` | `boolean` | `false` | Zoom to cursor point instead of center |
|
|
270
397
|
| `fullscreen` | `boolean` | `true` | Show fullscreen button |
|
|
271
398
|
| `transition` | `string` | `'fade'` | `'fade'` or `'slide'` |
|
|
272
|
-
| `theme` | `string` | `'
|
|
399
|
+
| `theme` | `string` | `'system'` | `'dark'` · `'light'` · `'system'` (auto-detect) |
|
|
273
400
|
| `hashNavigation` | `boolean` | `true` | URL hash deep linking |
|
|
274
401
|
| `counter` | `boolean` | `true` | Show image counter |
|
|
402
|
+
| `counterFormat` | `string` | `'{current} / {total}'` | Counter format with `{current}`, `{total}`, `{percent}` tokens |
|
|
275
403
|
| `captions` | `boolean` | `true` | Show image captions |
|
|
276
404
|
| `preload` | `number` | `1` | Adjacent images to preload |
|
|
277
405
|
| `lazyLoad` | `boolean` | `true` | Lazy load grid thumbnails |
|
|
278
406
|
| `stagger` | `boolean` | `true` | Staggered entrance animation |
|
|
279
|
-
| `slideshow` | `boolean` | `false` |
|
|
280
|
-
| `slideshowInterval` | `number` | `4000` | Slideshow interval in ms |
|
|
407
|
+
| `slideshow` | `boolean \| object` | `false` | `true` or `{ interval, pauseOnHover, kenburns, direction }` |
|
|
281
408
|
| `share` | `boolean` | `true` | Show share button in lightbox |
|
|
282
409
|
| `filter` | `boolean` | `false` | Enable tag filtering bar |
|
|
283
410
|
| `batchSelect` | `boolean` | `false` | Enable Shift+click multi-select |
|
|
@@ -292,6 +419,18 @@ gallery.on('storyEnter', () => console.log('Story opened'));
|
|
|
292
419
|
| `morphTransition` | `boolean` | `false` | FLIP morph from grid to lightbox |
|
|
293
420
|
| `colorPalette` | `boolean` | `false` | Show extracted color palette |
|
|
294
421
|
| `aspectSkeleton` | `boolean` | `true` | Use `data-width`/`data-height` for skeleton |
|
|
422
|
+
| `video` | `boolean` | `true` | Auto-detect MP4 / YouTube / Vimeo URLs |
|
|
423
|
+
| `plugins` | `array` | `null` | Plugins to instantiate; e.g. `['name', { name, ...opts }]` |
|
|
424
|
+
| `group` | `string` | `''` | Album group name (also via `data-group`) |
|
|
425
|
+
| `favorites` | `boolean` | `false` | Heart button + localStorage |
|
|
426
|
+
| `favoritesKey` | `string` | `''` | Custom suffix for localStorage key |
|
|
427
|
+
| `infoPanel` | `boolean` | `false` | Slide-out metadata sidebar |
|
|
428
|
+
| `contextMenu` | `boolean` | `false` | Right-click menu on items |
|
|
429
|
+
| `shortcutsHelp` | `boolean` | `true` | `?` key shortcuts overlay |
|
|
430
|
+
| `infiniteScroll` | `boolean` | `false` | Auto-load more on scroll (with `loadMore`) |
|
|
431
|
+
| `loadMore` | `function` | `null` | `(currentLength) => array \| Promise<array>` |
|
|
432
|
+
| `editor` | `boolean` | `false` | Crop/rotate/flip toolbar |
|
|
433
|
+
| `annotate` | `boolean` | `false` | Freehand drawing layer |
|
|
295
434
|
|
|
296
435
|
### Data Attributes
|
|
297
436
|
|
|
@@ -306,6 +445,9 @@ All options can also be set via `data-` attributes on the container:
|
|
|
306
445
|
data-stagger="true"
|
|
307
446
|
data-slideshow="false"
|
|
308
447
|
data-slideshow-interval="5000"
|
|
448
|
+
data-slideshow-pause-on-hover="true"
|
|
449
|
+
data-slideshow-kenburns="true"
|
|
450
|
+
data-counter-format="{current} of {total}"
|
|
309
451
|
data-share="true"
|
|
310
452
|
data-filter="true"
|
|
311
453
|
data-batch-select="false"
|
|
@@ -318,8 +460,16 @@ All options can also be set via `data-` attributes on the container:
|
|
|
318
460
|
data-color-palette="false"
|
|
319
461
|
data-drag-reorder="false"
|
|
320
462
|
data-virtual-scroll="false"
|
|
321
|
-
data-aspect-skeleton="true"
|
|
322
|
-
|
|
463
|
+
data-aspect-skeleton="true"
|
|
464
|
+
data-group="vacation"
|
|
465
|
+
data-favorites="true"
|
|
466
|
+
data-info-panel="true"
|
|
467
|
+
data-context-menu="true"
|
|
468
|
+
data-shortcuts-help="true"
|
|
469
|
+
data-editor="true"
|
|
470
|
+
data-annotate="true"
|
|
471
|
+
data-video="true">
|
|
472
|
+
<a href="full.jpg" data-tags="landscape,nature" data-size="large" data-width="1200" data-height="800" data-poster="poster.jpg" data-type="image">
|
|
323
473
|
<img src="thumb.jpg" alt="Photo" data-focus="0.5 0.3" data-blurhash="LEHV6nWB2y...">
|
|
324
474
|
</a>
|
|
325
475
|
</div>
|
|
@@ -357,10 +507,59 @@ Switch themes at runtime by setting `data-theme="light"` or `data-theme="dark"`
|
|
|
357
507
|
|
|
358
508
|
| Browser | Version |
|
|
359
509
|
|---------|---------|
|
|
360
|
-
| Chrome |
|
|
361
|
-
| Firefox |
|
|
362
|
-
| Safari |
|
|
363
|
-
| Edge |
|
|
510
|
+
| Chrome | 80+ |
|
|
511
|
+
| Firefox | 75+ |
|
|
512
|
+
| Safari | 14+ |
|
|
513
|
+
| Edge | 80+ |
|
|
514
|
+
|
|
515
|
+
> **v3.0.0** raised the minimum to take advantage of `aspect-ratio`, `backdrop-filter`,
|
|
516
|
+
> stable `IntersectionObserver`, and modern Canvas/Blob APIs. If you need older
|
|
517
|
+
> browser support, pin to v2.1.0.
|
|
518
|
+
|
|
519
|
+
## 🔁 Migration from v2.x to v3.0.0
|
|
520
|
+
|
|
521
|
+
**v3.0.0 contains breaking changes.** Most galleries will continue to work, but
|
|
522
|
+
review the following:
|
|
523
|
+
|
|
524
|
+
### Slideshow config is nested
|
|
525
|
+
|
|
526
|
+
```js
|
|
527
|
+
// v2.x
|
|
528
|
+
new NeikiGallery('#g', { slideshow: true, slideshowInterval: 5000 });
|
|
529
|
+
|
|
530
|
+
// v3.0.0 — preferred (fine-grained control)
|
|
531
|
+
new NeikiGallery('#g', {
|
|
532
|
+
slideshow: { interval: 5000, pauseOnHover: true, kenburns: true }
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
// v3.0.0 — still works (uses defaults)
|
|
536
|
+
new NeikiGallery('#g', { slideshow: true });
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
### Default theme is `'system'`
|
|
540
|
+
|
|
541
|
+
If you relied on the old default `'dark'`, set it explicitly:
|
|
542
|
+
|
|
543
|
+
```js
|
|
544
|
+
new NeikiGallery('#g', { theme: 'dark' });
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
### Hash format changed
|
|
548
|
+
|
|
549
|
+
Old: `#neiki-1=3` → New: `#gallery-id-or-group-name/3`
|
|
550
|
+
|
|
551
|
+
If you have external links to the old format, update them or set
|
|
552
|
+
`hashNavigation: false` and implement custom routing.
|
|
553
|
+
|
|
554
|
+
### Counter format
|
|
555
|
+
|
|
556
|
+
Default output (`"3 / 12"`) is unchanged, but it's now formatted via
|
|
557
|
+
`counterFormat: '{current} / {total}'`. Override with custom tokens.
|
|
558
|
+
|
|
559
|
+
### Browser support
|
|
560
|
+
|
|
561
|
+
Minimum Chrome 80+ / Firefox 75+ / Safari 14+ / Edge 80+. If you need older
|
|
562
|
+
browser support, stay on v2.1.0.
|
|
364
563
|
|
|
365
564
|
## 📄 License
|
|
366
565
|
|