popupable 1.5.2 → 1.6.1

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 CHANGED
@@ -19,6 +19,7 @@ Just add `data-popupable` to any image!
19
19
  * Thumbnail strip and image counter
20
20
  * Pinch-to-zoom on touch, with optional click/tap-to-zoom support
21
21
  * Checkerboard background for transparent images
22
+ * Attribute inheritance - set options on a parent to apply to all children
22
23
  * Customizable via CSS variables
23
24
  * Works in Vue, React, Svelte, and more
24
25
 
@@ -60,13 +61,35 @@ Close by clicking again, or pressing Escape, Backspace, or Delete.
60
61
  | `data-popupable-no-upscale` | Prevents the popup from scaling the image beyond its native resolution. |
61
62
  | `data-popupable-zoomable` | Enables click/tap-to-zoom and scroll wheel zoom. |
62
63
  | `data-popupable-anim="name"` | Sets the open/close animation style. |
63
- | `data-popupable-group="name"` | Groups images into a navigable gallery. |
64
- | `data-popupable-counter` | Shows a "1 / N" counter when in a group. Read from the clicked image. |
65
- | `data-popupable-thumbnails` | Shows a thumbnail strip when in a group. Read from the clicked image. |
64
+ | `data-popupable-group="name"` | Groups images into a navigable gallery. Can be placed on a container element to group all `data-popupable` children. |
65
+ | `data-popupable-counter` | Shows a "1 / N" counter when in a group. |
66
+ | `data-popupable-thumbnails` | Shows a thumbnail strip when in a group. |
66
67
  | `data-popupable-order="..."` | Controls the order of UI elements. |
67
68
 
69
+ ### Inheritance
70
+
71
+ All `data-popupable-*` attributes except `data-popupable` itself are **inherited**. If an attribute isn't on the element, popupable walks up the DOM tree to find it on a parent. This lets you set options once on a container instead of repeating them on every child.
72
+
73
+ Set an attribute to `"false"` to explicitly unset an inherited value:
74
+
75
+ ```html
76
+ <div data-popupable-anim="pop" data-popupable-counter data-popupable-thumbnails>
77
+ <img src="photo1.jpg" data-popupable>
78
+ <img src="photo2.jpg" data-popupable data-popupable-counter="false">
79
+ </div>
80
+ ```
81
+
68
82
  ## Advanced Usage
69
83
 
84
+ ### Non-image elements
85
+
86
+ Any element can be made popupable. Use `src` or `data-popupable-src` to specify the image to display:
87
+
88
+ ```html
89
+ <div src="photo.jpg" style="background-image: url(photo.jpg)" data-popupable></div>
90
+ <div style="background-image: url(photo.jpg)" data-popupable data-popupable-src="photo.jpg"></div>
91
+ ```
92
+
70
93
  ### Hi-res source
71
94
 
72
95
  Use `data-popupable-src` to display a different (e.g. higher resolution) image when the popup is open:
@@ -113,20 +136,32 @@ When zoomed in, pan by dragging and zoom in/out with the scroll wheel. Click the
113
136
 
114
137
  Group multiple images together with `data-popupable-group`. Users can navigate with arrow buttons, swipe gestures, scroll wheel, or keyboard shortcuts.
115
138
 
139
+ Place `data-popupable-group` directly on elements or on a container to group all `data-popupable` children:
140
+
116
141
  ```html
142
+ <!-- On each element -->
117
143
  <img src="photo1.jpg" data-popupable data-popupable-group="holiday">
118
144
  <img src="photo2.jpg" data-popupable data-popupable-group="holiday">
119
145
  <img src="photo3.jpg" data-popupable data-popupable-group="holiday">
146
+
147
+ <!-- Or on a container (inherited) -->
148
+ <div data-popupable-group="holiday">
149
+ <img src="photo1.jpg" data-popupable>
150
+ <img src="photo2.jpg" data-popupable>
151
+ <img src="photo3.jpg" data-popupable>
152
+ </div>
120
153
  ```
121
154
 
122
155
  ### Counter and thumbnails
123
156
 
124
- Add `data-popupable-counter` and/or `data-popupable-thumbnails` to individual group members. The attributes are read from whichever image is clicked to open the gallery, so add them to every image that should show these elements:
157
+ Add `data-popupable-counter` and/or `data-popupable-thumbnails` to show those UI elements in a gallery. These inherit, so placing them on the group container applies them to all members:
125
158
 
126
159
  ```html
127
- <img src="photo1.jpg" data-popupable data-popupable-group="holiday" data-popupable-counter data-popupable-thumbnails>
128
- <img src="photo2.jpg" data-popupable data-popupable-group="holiday" data-popupable-counter data-popupable-thumbnails>
129
- <img src="photo3.jpg" data-popupable data-popupable-group="holiday" data-popupable-counter data-popupable-thumbnails>
160
+ <div data-popupable-group="holiday" data-popupable-counter data-popupable-thumbnails>
161
+ <img src="photo1.jpg" data-popupable>
162
+ <img src="photo2.jpg" data-popupable>
163
+ <img src="photo3.jpg" data-popupable>
164
+ </div>
130
165
  ```
131
166
 
132
167
  ### Custom UI order
@@ -185,26 +220,28 @@ The popup container receives a `popupable-anim-{name}` class (e.g. `popupable-an
185
220
 
186
221
  #### Custom animation styles
187
222
 
188
- Register your own animation style by adding a function to `window.popupableAnimTypes`. The function receives the original element and the final expanded rect `{ top, left, width, height }`, and returns an object describing the starting rect for the open animation (which is also the ending rect for close).
223
+ Register your own animation style by adding an object to `window.popupableAnimTypes`. The object must have a `position` method that receives the original element and the final expanded rect, and returns the starting rect for the open animation (which is also the ending rect for close). Additional static flags control the animation behaviour.
189
224
 
190
- | Return property | Type | Description |
225
+ | Property | Type | Description |
191
226
  |---|---|---|
192
- | `top` | number | Starting top position in pixels |
193
- | `left` | number | Starting left position in pixels |
194
- | `width` | number | Starting width in pixels |
195
- | `height` | number | Starting height in pixels |
227
+ | `position(el, rect)` | function | Returns `{ top, left, width, height }` for the start of the open animation |
228
+ | `styles` | boolean | Copies border, outline, and box-shadow from the element to the popup clone, then transitions them out |
196
229
  | `fade` | boolean | Fades opacity in/out alongside the geometry |
197
230
  | `crossfade` | boolean | Crossfades between the thumbnail and alternate source image as it opens |
198
231
  | `hideSource` | boolean | Hides the original element while the popup is open |
199
232
 
200
233
  ```js
201
- window.popupableAnimTypes.myAnim = (original, rect) => ({
202
- top: rect.top + rect.height / 2,
203
- left: rect.left,
204
- width: rect.width,
205
- height: 0,
206
- fade: true
207
- })
234
+ window.popupableAnimTypes.myAnim = {
235
+ fade: true,
236
+ position(original, rect) {
237
+ return {
238
+ top: rect.top + rect.height / 2,
239
+ left: rect.left,
240
+ width: rect.width,
241
+ height: 0
242
+ }
243
+ }
244
+ }
208
245
  ```
209
246
 
210
247
  ## Customization
@@ -1,7 +1,7 @@
1
1
  /*!
2
2
  * popupable
3
- * Version : 1.5.2
3
+ * Version : 1.6.1
4
4
  * License : MIT
5
5
  * Copyright: 2026 Ewan Howell
6
6
  */
7
- @property --popupable-screen-padding{syntax:"<length>";inherits:true;initial-value:40px}@property --popupable-background{syntax:"<color>";inherits:true;initial-value:#000B}@property --popupable-ui-background{syntax:"<color>";inherits:true;initial-value:#0008}@property --popupable-blur{syntax:"<length>";inherits:true;initial-value:6px}@property --popupable-open-duration{syntax:"<time>";inherits:true;initial-value:.25s}@property --popupable-open-easing{syntax:"*";inherits:true;initial-value:ease}@property --popupable-switch-duration{syntax:"<time>";inherits:true;initial-value:.25s}@property --popupable-switch-easing{syntax:"*";inherits:true;initial-value:ease}@property --popupable-text-color{syntax:"<color>";inherits:true;initial-value:#fff}[data-popupable],[data-popupable] *{cursor:pointer!important}.popupable-hide{visibility:hidden!important}.popupable-loading,.popupable-loading *{cursor:wait!important}.popupable-block-touch *{touch-action:none!important}.popupable-container{position:fixed;inset:0;transition:background var(--popupable-open-duration) ease,backdrop-filter var(--popupable-open-duration) ease;z-index:2147483646;user-select:none;pointer-events:none;touch-action:none}.popupable-container.popupable-active{z-index:2147483647;background:var(--popupable-background);backdrop-filter:blur(var(--popupable-blur))}.popupable-container>*{pointer-events:none}.popupable-container *{box-sizing:border-box;color:var(--popupable-text-color);-webkit-tap-highlight-color:transparent}.popupable-container.popupable-open,.popupable-container.popupable-open>*{pointer-events:initial}.popupable-clones{transition:transform var(--popupable-switch-duration) var(--popupable-switch-easing)}.popupable-clone-container{position:fixed;transition:all var(--popupable-open-duration) var(--popupable-open-easing);pointer-events:initial;overflow:hidden;transform:translateX(calc(var(--popupable-view-width) * var(--popupable-offset-multiplier)));transform-origin:0 0}.popupable-clone-container.popupable-transparent::before{content:"";position:absolute;inset:0;background-image:conic-gradient(#313131 .25turn,#1e1e1e .25turn .5turn,#313131 .5turn .75turn,#1e1e1e .75turn);background-size:32px 32px;background-attachment:fixed;z-index:-1;opacity:0;transition:opacity var(--popupable-open-duration) var(--popupable-open-easing);image-rendering:pixelated}.popupable-fade .popupable-clone-container{opacity:0}.popupable-container.popupable-active .popupable-clone-container{border-radius:0!important;opacity:1}.popupable-container.popupable-active .popupable-clone-container.popupable-transparent::before{opacity:1}.popupable-container.popupable-open .popupable-clone-container{transition:transform var(--popupable-switch-duration) var(--popupable-switch-easing),translate var(--popupable-switch-duration) var(--popupable-switch-easing)}.popupable-clone,.popupable-clone-layer{width:100%;height:100%;position:absolute;inset:0;transition:opacity var(--popupable-open-duration) var(--popupable-open-easing)}.popupable-clone-layer{opacity:0;object-fit:cover}.popupable-crossfade.popupable-open .popupable-clone:not(:last-child){opacity:0}.popupable-container:not(.popupable-crossfade) .popupable-clone-layer,.popupable-crossfade.popupable-active .popupable-clone-layer{opacity:1}.popupable-viewport{position:fixed;left:var(--popupable-vv-left,0);top:var(--popupable-vv-top,0);width:calc(var(--popupable-vv-width,100vw) * var(--popupable-vv-scale,1));height:calc(var(--popupable-vv-height,100vh) * var(--popupable-vv-scale,1));transform-origin:top left;transform:scale(var(--popupable-vv-ui-scale,1));pointer-events:none!important}.popupable-button{width:40px;height:40px;display:flex;align-items:center;justify-content:center;transition:opacity var(--popupable-open-duration) var(--popupable-open-easing),transform var(--popupable-open-duration) var(--popupable-open-easing);cursor:pointer;position:relative;opacity:0}.popupable-button::before{content:"";position:absolute;inset:0;opacity:.5;background:var(--popupable-ui-background);transition:opacity .25s;border-radius:50%}.popupable-button svg{width:24px;height:24px;z-index:1}.popupable-container.popupable-open .popupable-button{transition:background .25s,opacity .25s,transform .25s}.popupable-button-container:hover .popupable-button::before,.popupable-button:hover::before{opacity:1}.popupable-button-container{position:absolute;padding:40px;cursor:pointer;z-index:1;pointer-events:auto}.popupable-next-container{top:50%;transform:translateY(-50%);right:calc(var(--popupable-screen-padding)/ 2)}.popupable-prev-container{top:50%;transform:translateY(-50%);left:calc(var(--popupable-screen-padding)/ 2)}.popupable-close-container{position:absolute;bottom:0;right:0;padding:20px;transform:translateY(100%)}.popupable-next{transform:translateX(40px)}.popupable-prev{transform:translateX(-40px)}.popupable-close{transform:translateY(40px)}.popupable-container.popupable-active .popupable-button{transform:initial}.popupable-container:not(.popupable-locked).popupable-active .popupable-button{opacity:1}.popupable-next-container:hover .popupable-button{transform:translateX(4px)}.popupable-prev-container:hover .popupable-button{transform:translateX(-4px)}.popupable-button-disabled,.popupable-button-inactive,.popupable-locked .popupable-button-container{pointer-events:none}.popupable-locked .popupable-button-container .popupable-button,:is(.popupable-button-disabled,.popupable-button-inactive) .popupable-button{opacity:0!important}.popupable-locked .popupable-next-container .popupable-next,.popupable-next-container:is(.popupable-button-disabled,.popupable-button-inactive) .popupable-next{transform:translateX(40px)}.popupable-locked .popupable-prev-container .popupable-prev,.popupable-prev-container:is(.popupable-button-disabled,.popupable-button-inactive) .popupable-prev{transform:translateX(-40px)}.popupable-footer,.popupable-header{position:absolute;top:0;left:0;right:0;opacity:0;transition:opacity var(--popupable-open-duration) var(--popupable-open-easing),transform var(--popupable-open-duration) var(--popupable-open-easing);transform:translateY(-40px);background:var(--popupable-ui-background);pointer-events:auto}.popupable-footer{top:initial;bottom:0;transform:translateY(40px)}.popupable-container.popupable-active:not(.popupable-locked) .popupable-footer,.popupable-container.popupable-active:not(.popupable-locked) .popupable-header{opacity:1;transform:initial}.popupable-counter{padding:10px;text-align:center}.popupable-counter+.popupable-thumbnails,.popupable-thumbnails+.popupable-counter{border-top:1px solid #fff3}.popupable-content-container{display:grid;align-items:flex-end;transition:height var(--popupable-switch-duration) var(--popupable-switch-easing);overflow:hidden;position:relative}.popupable-content{grid-column:1;grid-row:1;text-align:center;padding:16px;display:flex;flex-direction:column;gap:8px;user-select:text;transition:opacity var(--popupable-switch-duration) var(--popupable-switch-easing),transform var(--popupable-switch-duration) var(--popupable-switch-easing);position:absolute;bottom:0;left:50%;transform:translateX(-50%);max-width:100%;width:max-content}.popupable-title{font-size:32px;font-weight:600}.popupable-content-before{pointer-events:none;opacity:0;transform:translateX(calc(-50% - 80px))}.popupable-content-after{pointer-events:none;opacity:0;transform:translateX(calc(-50% + 80px))}.popupable-content-container:not(:first-child) .popupable-content{padding-top:17px}.popupable-content-container:not(:last-child) .popupable-content{padding-bottom:17px}.popupable-content-container:not(:first-child)::before{content:"";position:absolute;height:1px;top:0;left:0;right:0;background-color:#fff3}.popupable-footer .popupable-content-container:not(:last-child)::after,.popupable-header .popupable-content-container:not(:nth-last-child(2))::after{content:"";position:absolute;height:1px;bottom:0;left:0;right:0;background-color:#fff3}.popupable-thumbnails{display:flex;gap:10px;padding:10px;justify-content:safe center;overflow:hidden;scrollbar-width:none;touch-action:pan-x}.popupable-thumbnails.popupable-thumbnails-dragging{cursor:grabbing}.popupable-thumbnail{width:64px;height:64px;object-fit:cover;opacity:.65;cursor:pointer;transition:opacity .2s,outline-color .2s;outline:2px solid #0000;outline-offset:-2px;flex:0 0 auto}.popupable-thumbnail:hover{opacity:1}.popupable-thumbnail.popupable-thumbnail-active{opacity:1;outline-color:#fffa}.popupable-zoomable{cursor:zoom-in}.popupable-zoomed{cursor:zoom-out}.popupable-block-transitions,.popupable-block-transitions *{transition:initial!important}@media (max-width:768px){:root{--popupable-screen-padding:14px}.popupable-button{width:42px;height:42px}.popupable-next-container,.popupable-prev-container{padding:36px}.popupable-button svg{width:25px;height:25px}.popupable-counter{padding:8px}.popupable-content{padding:14px;gap:6px}.popupable-content-container:not(:first-child) .popupable-content{padding-top:15px}.popupable-content-container:not(:last-child) .popupable-content{padding-bottom:15px}.popupable-title{font-size:28px}.popupable-thumbnail{width:48px;height:48px}}
7
+ @property --popupable-screen-padding{syntax:"<length>";inherits:true;initial-value:40px}@property --popupable-background{syntax:"<color>";inherits:true;initial-value:#000B}@property --popupable-ui-background{syntax:"<color>";inherits:true;initial-value:#0008}@property --popupable-blur{syntax:"<length>";inherits:true;initial-value:6px}@property --popupable-open-duration{syntax:"<time>";inherits:true;initial-value:.25s}@property --popupable-open-easing{syntax:"*";inherits:true;initial-value:ease}@property --popupable-switch-duration{syntax:"<time>";inherits:true;initial-value:.25s}@property --popupable-switch-easing{syntax:"*";inherits:true;initial-value:ease}@property --popupable-text-color{syntax:"<color>";inherits:true;initial-value:#fff}[data-popupable],[data-popupable] *{cursor:pointer!important}.popupable-hide{visibility:hidden!important}.popupable-loading,.popupable-loading *{cursor:wait!important}.popupable-block-touch *{touch-action:none!important}.popupable-container{position:fixed;inset:0;transition:background var(--popupable-open-duration) ease,backdrop-filter var(--popupable-open-duration) ease;z-index:2147483646;user-select:none;pointer-events:none;touch-action:none}.popupable-container.popupable-active{z-index:2147483647;background:var(--popupable-background);backdrop-filter:blur(var(--popupable-blur))}.popupable-container>*{pointer-events:none}.popupable-container *{box-sizing:border-box;color:var(--popupable-text-color);-webkit-tap-highlight-color:transparent}.popupable-container.popupable-open,.popupable-container.popupable-open>*{pointer-events:initial}.popupable-clones{transition:transform var(--popupable-switch-duration) var(--popupable-switch-easing)}.popupable-clone-container{position:fixed;transition:all var(--popupable-open-duration) var(--popupable-open-easing);pointer-events:initial;overflow:hidden;transform:translateX(calc(var(--popupable-view-width) * var(--popupable-offset-multiplier)));transform-origin:0 0}.popupable-clone-container.popupable-transparent::before{content:"";position:absolute;inset:0;background-image:conic-gradient(#313131 .25turn,#1e1e1e .25turn .5turn,#313131 .5turn .75turn,#1e1e1e .75turn);background-size:32px 32px;background-attachment:fixed;z-index:-1;opacity:0;transition:opacity var(--popupable-open-duration) var(--popupable-open-easing);image-rendering:pixelated}.popupable-fade .popupable-clone-container{opacity:0}.popupable-container.popupable-active .popupable-clone-container{border-radius:0!important;border-width:0!important;outline-width:0!important;box-shadow:none!important;opacity:1}.popupable-container.popupable-active .popupable-clone-container.popupable-transparent::before{opacity:1}.popupable-container.popupable-open .popupable-clone-container{transition:transform var(--popupable-switch-duration) var(--popupable-switch-easing),translate var(--popupable-switch-duration) var(--popupable-switch-easing)}.popupable-clone,.popupable-clone-layer{width:100%;height:100%;position:absolute;inset:0;transition:opacity var(--popupable-open-duration) var(--popupable-open-easing)}.popupable-clone-layer{opacity:0;object-fit:cover}.popupable-crossfade.popupable-open .popupable-clone:not(:last-child){opacity:0}.popupable-container:not(.popupable-crossfade) .popupable-clone-layer,.popupable-crossfade.popupable-active .popupable-clone-layer{opacity:1}.popupable-viewport{position:fixed;left:var(--popupable-vv-left,0);top:var(--popupable-vv-top,0);width:calc(var(--popupable-vv-width,100vw) * var(--popupable-vv-scale,1));height:calc(var(--popupable-vv-height,100vh) * var(--popupable-vv-scale,1));transform-origin:top left;transform:scale(var(--popupable-vv-ui-scale,1));pointer-events:none!important}.popupable-button{width:40px;height:40px;display:flex;align-items:center;justify-content:center;transition:opacity var(--popupable-open-duration) var(--popupable-open-easing),transform var(--popupable-open-duration) var(--popupable-open-easing);cursor:pointer;position:relative;opacity:0}.popupable-button::before{content:"";position:absolute;inset:0;opacity:.5;background:var(--popupable-ui-background);transition:opacity .25s;border-radius:50%}.popupable-button svg{width:24px;height:24px;z-index:1}.popupable-container.popupable-open .popupable-button{transition:background .25s,opacity .25s,transform .25s}.popupable-button-container:hover .popupable-button::before,.popupable-button:hover::before{opacity:1}.popupable-button-container{position:absolute;padding:40px;cursor:pointer;z-index:1;pointer-events:auto}.popupable-next-container{top:50%;transform:translateY(-50%);right:calc(var(--popupable-screen-padding)/ 2)}.popupable-prev-container{top:50%;transform:translateY(-50%);left:calc(var(--popupable-screen-padding)/ 2)}.popupable-close-container{position:absolute;bottom:0;right:0;padding:20px;transform:translateY(100%)}.popupable-next{transform:translateX(40px)}.popupable-prev{transform:translateX(-40px)}.popupable-close{transform:translateY(40px)}.popupable-container.popupable-active .popupable-button{transform:initial}.popupable-container:not(.popupable-locked).popupable-active .popupable-button{opacity:1}.popupable-next-container:hover .popupable-button{transform:translateX(4px)}.popupable-prev-container:hover .popupable-button{transform:translateX(-4px)}.popupable-button-disabled,.popupable-button-inactive,.popupable-locked .popupable-button-container{pointer-events:none}.popupable-locked .popupable-button-container .popupable-button,:is(.popupable-button-disabled,.popupable-button-inactive) .popupable-button{opacity:0!important}.popupable-locked .popupable-next-container .popupable-next,.popupable-next-container:is(.popupable-button-disabled,.popupable-button-inactive) .popupable-next{transform:translateX(40px)}.popupable-locked .popupable-prev-container .popupable-prev,.popupable-prev-container:is(.popupable-button-disabled,.popupable-button-inactive) .popupable-prev{transform:translateX(-40px)}.popupable-footer,.popupable-header{position:absolute;top:0;left:0;right:0;opacity:0;transition:opacity var(--popupable-open-duration) var(--popupable-open-easing),transform var(--popupable-open-duration) var(--popupable-open-easing);transform:translateY(-40px);background:var(--popupable-ui-background);pointer-events:auto}.popupable-footer{top:initial;bottom:0;transform:translateY(40px)}.popupable-container.popupable-active:not(.popupable-locked) .popupable-footer,.popupable-container.popupable-active:not(.popupable-locked) .popupable-header{opacity:1;transform:initial}.popupable-counter{padding:10px;text-align:center}.popupable-counter+.popupable-thumbnails,.popupable-thumbnails+.popupable-counter{border-top:1px solid #fff3}.popupable-content-container{display:grid;align-items:flex-end;transition:height var(--popupable-switch-duration) var(--popupable-switch-easing);overflow:hidden;position:relative}.popupable-content{grid-column:1;grid-row:1;text-align:center;padding:16px;display:flex;flex-direction:column;gap:8px;user-select:text;transition:opacity var(--popupable-switch-duration) var(--popupable-switch-easing),transform var(--popupable-switch-duration) var(--popupable-switch-easing);position:absolute;bottom:0;left:50%;transform:translateX(-50%);max-width:100%;width:max-content}.popupable-title{font-size:32px;font-weight:600}.popupable-content-before{pointer-events:none;opacity:0;transform:translateX(calc(-50% - 80px))}.popupable-content-after{pointer-events:none;opacity:0;transform:translateX(calc(-50% + 80px))}.popupable-content-container:not(:first-child) .popupable-content{padding-top:17px}.popupable-content-container:not(:last-child) .popupable-content{padding-bottom:17px}.popupable-content-container:not(:first-child)::before{content:"";position:absolute;height:1px;top:0;left:0;right:0;background-color:#fff3}.popupable-footer .popupable-content-container:not(:last-child)::after,.popupable-header .popupable-content-container:not(:nth-last-child(2))::after{content:"";position:absolute;height:1px;bottom:0;left:0;right:0;background-color:#fff3}.popupable-thumbnails{display:flex;gap:10px;padding:10px;justify-content:safe center;overflow:hidden;scrollbar-width:none;touch-action:pan-x}.popupable-thumbnails.popupable-thumbnails-dragging{cursor:grabbing}.popupable-thumbnail{width:64px;height:64px;object-fit:cover;opacity:.65;cursor:pointer;transition:opacity .2s,outline-color .2s;outline:2px solid #0000;outline-offset:-2px;flex:0 0 auto}.popupable-thumbnail:hover{opacity:1}.popupable-thumbnail.popupable-thumbnail-active{opacity:1;outline-color:#fffa}.popupable-zoomable{cursor:zoom-in}.popupable-zoomed{cursor:zoom-out}@media (max-width:768px){:root{--popupable-screen-padding:14px}.popupable-button{width:42px;height:42px}.popupable-next-container,.popupable-prev-container{padding:36px}.popupable-button svg{width:25px;height:25px}.popupable-counter{padding:8px}.popupable-content{padding:14px;gap:6px}.popupable-content-container:not(:first-child) .popupable-content{padding-top:15px}.popupable-content-container:not(:last-child) .popupable-content{padding-bottom:15px}.popupable-title{font-size:28px}.popupable-thumbnail{width:48px;height:48px}}
@@ -1,7 +1,7 @@
1
1
  /*!
2
2
  * popupable
3
- * Version : 1.5.2
3
+ * Version : 1.6.1
4
4
  * License : MIT
5
5
  * Copyright: 2026 Ewan Howell
6
6
  */
7
- {let e,t,n,o=0;const a=3;let i,r,s,l;function triggerHaptic(){if("function"!=typeof navigator.vibrate){if(!i){i=document.createElement("label"),i.style.cssText="position:fixed;left:-9999px;top:-9999px;opacity:0;pointer-events:none";const e=document.createElement("input");e.type="checkbox",e.setAttribute("switch",""),i.append(e),document.body.append(i)}i.click()}else navigator.vibrate([10])}function disableScroll(){window.addEventListener("wheel",prevent,{passive:!1}),window.addEventListener("touchmove",prevent,{passive:!1}),window.addEventListener("keydown",blockKeys,!0)}function enableScroll(){window.removeEventListener("wheel",prevent),window.removeEventListener("touchmove",prevent),window.removeEventListener("keydown",blockKeys,!0)}function prevent(e){e.preventDefault()}function blockKeys(e){["ArrowUp","ArrowDown","PageUp","PageDown","Home","End"," "].includes(e.key)&&e.preventDefault()}function calcExpandedRect(t){const n=t.original,o=visualViewport?.width||window.innerWidth,a=visualViewport?.height||window.innerHeight,i=visualViewport?.offsetTop||0,r=visualViewport?.offsetLeft||0,s=visualViewport?.scale||1,l=1/s,p=e.popup,c=(parseFloat(getComputedStyle(p).getPropertyValue("--popupable-screen-padding"))||40)/s,u=Math.max(0,o-2*c),d=a-2*c,b=Math.max(0,d);let f;if(t.maintainAspect){const e=n.getBoundingClientRect();f=e.width/e.height}else{const e=t.cloneLayer||n;f=e.naturalWidth/e.naturalHeight||1}let m=0,h=0;if(e.orderPlacement){const n=e.popup.querySelector(".popupable-counter"),o=n?n.getBoundingClientRect().height/l:0,a=e.thumbnailsContainer?e.thumbnailsContainer.getBoundingClientRect().height/l:0,i=e.contentContainer,r=t.content?t.content.getBoundingClientRect().height/l:i?.previousElementSibling&&i?.nextElementSibling&&parseFloat(getComputedStyle(i,"::after").height)||0,{counterTop:s,contentTop:p,thumbnailsTop:c,counterBottom:u,contentBottom:d,thumbnailsBottom:b}=e.orderPlacement;m=(s?o:0)+(p?r:0)+(c?a:0),h=(u?o:0)+(d?r:0)+(b?a:0)}const v=Math.max(0,a-m-h-2*c);let g=u,y=g/f;if(y>b&&(y=b,g=y*f),y>v&&(y=v,g=y*f),t.noUpscale){const e=t.cloneLayer||n,o=e.naturalWidth,a=e.naturalHeight;if(o&&a){const e=o/s,t=a/s,n=Math.min(1,e/g,t/y);n<1&&(g*=n,y*=n)}}let w=i+c+(b-y)/2;const L=i+a-h-c-y;return w=Math.min(w,L),w=Math.max(w,i+m+c),{top:w,left:r+c+(u-g)/2,width:g,height:y}}function setCloneToOriginalRect(t,n){const o=calcExpandedRect(e),a=popupableAnimTypes[e.animation](n,o);return t.style.top=a.top+"px",t.style.left=a.left+"px",t.style.width=a.width+"px",t.style.height=a.height+"px",a}function openPopupable(e){if("open"===e.state)return;e.state="open",triggerHaptic();const{cloneContainer:t,popup:n,transition:o,group:a,listeners:i}=e;n.classList.add("popupable-active"),e.clearNavInactive?.(),updateExpandedSize(),e.closingContainer?.removeEventListener("transitionend",o.listener);const r=e.closingContainer??t;o.listener=t=>{if(!t||t.target===t.currentTarget){if(r.removeEventListener("transitionend",o.listener),n.classList.add("popupable-open"),a)for(const e of a)e.cloneContainer.style.display=null;for(const e of i)e.target.addEventListener(e.event,e.func,e.args);e.scheduleNavHide?.()}},o.duration?r.addEventListener("transitionend",o.listener):o.listener()}function closePopupable(){if(!e||"close"===e.state)return;o++,e.state="close",document.body.classList.add("popupable-block-touch"),setTimeout(()=>document.body.classList.remove("popupable-block-touch"),300);const{cloneContainer:n,original:a,popup:i,transition:r,group:s,listeners:l}=e;a.classList.remove("popupable-loading"),i.classList.remove("popupable-active"),i.classList.remove("popupable-open");const p=s?s[s.currentIndex].cloneContainer:n;if(e.closingContainer=p,setCloneToOriginalRect(p,a),s)for(const e of s)e.cloneContainer!==p&&(e.cloneContainer.style.display="none");for(const e of l)e.target.removeEventListener(e.event,e.func);n.removeEventListener("transitionend",r.listener);const c=e;r.listener=n=>{n&&n.target!==n.currentTarget||(p.removeEventListener("transitionend",r.listener),a.classList.remove("popupable-hide"),i.remove(),c===e&&(enableScroll(),t=e,e=null))},r.duration?p.addEventListener("transitionend",r.listener):r.listener()}function updateExpandedSize(){if(!e||"close"===e.state)return;const t=visualViewport?.width||window.innerWidth,n=visualViewport?.height||window.innerHeight,o=visualViewport?.offsetTop||0,a=visualViewport?.offsetLeft||0,i=visualViewport?.scale||1;document.documentElement.style.setProperty("--popupable-view-width",t+"px"),e.popup.style.setProperty("--popupable-vv-width",t+"px"),e.popup.style.setProperty("--popupable-vv-height",n+"px"),e.popup.style.setProperty("--popupable-vv-top",o+"px"),e.popup.style.setProperty("--popupable-vv-left",a+"px"),e.popup.style.setProperty("--popupable-vv-scale",i),e.popup.style.setProperty("--popupable-vv-ui-scale",1/i);const r=1/i;let s;s=e.group?e.group:[e];for(const e of s){const{top:t,left:n,width:o,height:a}=calcExpandedRect(e);e.cloneContainer.style.top=t+"px",e.cloneContainer.style.left=n+"px",e.cloneContainer.style.width=o+"px",e.cloneContainer.style.height=a+"px"}if(e.contentContainer){let t;if(t=e.group?e.group[e.group.currentIndex]:e,t.content){const n=t.content.getBoundingClientRect();e.contentContainer.style.height=n.height/r+"px"}else{const t=e.contentContainer,n=t.previousElementSibling&&t.nextElementSibling&&parseFloat(getComputedStyle(t,"::after").height)||0;t.style.height=n+"px"}}}function parsePopupableOrder(e){const t=["counter","image","content","thumbnails"],n=new Set(t),o=[];if(e)for(const t of e.split(",")){const e=t.trim().toLowerCase();e&&n.has(e)&&!o.includes(e)&&o.push(e)}for(const e of t)o.includes(e)||o.push(e);const a=o.indexOf("image");return{top:o.slice(0,a),bottom:o.slice(a+1)}}function cloneElement(e,t){const n=document.createElement("div");n.className="popupable-clone-container",e.hasAttribute("data-popupable-transparent")&&n.classList.add("popupable-transparent");const o=e.getAttribute("currentSrc")||e.getAttribute("src"),a=new Image;a.className="popupable-clone",a.src=t||o||e.dataset.popupableSrc;const i=getComputedStyle(e);let r,s,l;if(n.style.borderRadius=i.borderRadius,a.style.objectFit=i.objectFit,a.style.objectPosition=i.objectPosition,a.style.imageRendering=i.imageRendering,a.style.background=i.background,n.append(a),(e.dataset.popupableSrc&&o||t)&&(r=new Image,r.className="popupable-clone-layer",r.src=e.dataset.popupableSrc||o,r.style.imageRendering=i.imageRendering,n.append(r),"fill"===a.style.objectFit)){const t=e.getBoundingClientRect();e.naturalWidth&&e.naturalHeight&&Math.abs(t.width/t.height-e.naturalWidth/e.naturalHeight)<.01&&(a.style.objectFit="cover")}if(e.dataset.popupableTitle||e.dataset.popupableDescription){if(s=document.createElement("div"),s.classList="popupable-content",e.dataset.popupableTitle){const t=document.createElement("div");t.className="popupable-title",t.textContent=e.dataset.popupableTitle,s.append(t)}if(e.dataset.popupableDescription){const t=document.createElement("div");t.className="popupable-description",t.textContent=e.dataset.popupableDescription,s.append(t)}}return e.hasAttribute("data-popupable-zoomable")&&(l=!0,n.classList.add("popupable-zoomable")),{id:e.dataset.popupable,original:e,cloneContainer:n,clone:a,cloneLayer:r,maintainAspect:e.hasAttribute("data-popupable-maintain-aspect"),noUpscale:e.hasAttribute("data-popupable-no-upscale"),counter:e.hasAttribute("data-popupable-counter"),thumbnails:e.hasAttribute("data-popupable-thumbnails"),order:parsePopupableOrder(e.dataset.popupableOrder),animation:popupableAnimTypes[e.dataset.popupableAnim]?e.dataset.popupableAnim:"expand",ready:Promise.all([a,r].filter(Boolean).map(e=>e.decode().catch(()=>{}))),content:s,zoomable:l}}function handleMove(t){if("open"!==e?.state||!e.group||!r)return;if(!e.popup?.classList.contains("popupable-open"))return s=t.touches?.[0].clientX??t.clientX,void(l=t.touches?.[0].clientY??t.clientY);const n=e.group[e.group.currentIndex];n.cloneContainer.parentElement.style.transition="initial",n.cloneContainer.parentElement.style.transform=`translateX(${(t.touches?.[0].clientX??t.clientX)-s}px)`}window.popupableAnimTypes={expand(e){const t=e.getBoundingClientRect();return{top:visualViewport.offsetTop+t.top,left:visualViewport.offsetLeft+t.left,width:t.width,height:t.height,hideSource:!0,crossfade:!0}},pop:(e,{top:t,left:n,width:o,height:a})=>({top:t+.05*a,left:n+.05*o,width:.9*o,height:.9*a,fade:!0}),line:(e,{top:t,left:n,width:o,height:a})=>({top:t+a/2,left:n+.05*o,width:.9*o,height:0,fade:!0}),float:(e,{top:t,left:n,width:o,height:a})=>({top:t+40,left:n,width:o,height:a,fade:!0})},document.addEventListener("pointerdown",t=>{0===t.button&&(r||(n=t.target,s=t.clientX,l=t.clientY),r||"open"!==e?.state||t.target.closest(".popupable-header, .popupable-footer")||(r=!0))}),document.addEventListener("mousemove",handleMove),document.addEventListener("touchmove",handleMove,{passive:!0}),document.addEventListener("pointerup",async i=>{if(0!==i.button)return;if(r){r=!1;const K=e.group?e.group[e.group.currentIndex]:e;K.cloneContainer.parentElement.style.transition=null,K.cloneContainer.parentElement.style.transform=null;const Z=i.clientX-s,G=Math.abs(Z),J=i.clientY-l,Q=Math.abs(J);if("touch"===i.pointerType&&Q>56&&Q>1.1*G)return void closePopupable();if(e.group&&G>a){const ee=Math.max(0,Math.floor((G-window.innerWidth/2)/window.innerWidth));if(Z>32)for(let te=0;te<=ee;te++)e.goPrev();else if(Z<-32)for(let ne=0;ne<=ee;ne++)e.goNext();return void(e.blocked=!0)}}const p=i.target.closest(".popupable-viewport")&&!n.closest(".popupable-viewport");if(!p&&i.target!=n&&(!n.classList.contains("popupable-clone-container")||i.target!==t?.original)&&(!n.closest(".popupable-container")||i.target.closest(".popupable-container"))||Math.abs(i.clientX-s)>a||Math.abs(i.clientY-l)>a)return;const c=(p?n.closest("[data-popupable]"):null)||i.target.closest("[data-popupable]");if(!c){if(p)return void closePopupable();if(i.target.closest(".popupable-container"))return;return void(e&&("zoomed"===e.state?e.unzoom():closePopupable()))}if(i.preventDefault(),e&&"close"!==e.state&&e.original===c)return;e&&closePopupable();const u=++o;e={transition:{},listeners:[]};const d=e,b=document.createElement("div");b.className="popupable-clones";const f=cloneElement(c),{cloneContainer:m,content:h}=f;let v;if(c.dataset.popupableGroup){const oe=document.querySelectorAll(`[data-popupable-group="${c.dataset.popupableGroup}"]`);if(oe.length){v=[];for(const[ae,ie]of oe.entries())if(ie===c)v.push(f),v.currentIndex=ae,b.append(m);else{const re=cloneElement(ie,c.getAttribute("currentSrc")||c.getAttribute("src"));re.cloneContainer.style.display="none",v.push(re),b.append(re.cloneContainer)}for(const[se,le]of v.entries()){if(!le.content)continue;const pe=se-v.currentIndex;pe>0?le.content.classList.add("popupable-content-after"):pe<0&&le.content.classList.add("popupable-content-before")}}}else b.append(m);const g=document.createElement("div");g.className=`popupable-container popupable-anim-${f.animation}`,f.id&&(g.id=f.id);const y=document.createElement("div");let w,L,x,C,E,I,P,M,k,z,T;y.className="popupable-viewport",(h||v&&v.some(e=>e.content))&&(w=document.createElement("div"),w.classList="popupable-content-container");const A={};if(v){f.counter&&(C=document.createElement("div"),C.className="popupable-counter"),f.thumbnails&&(E=document.createElement("div"),E.className="popupable-thumbnails",I=v.map((e,t)=>{const n=new Image;return n.className="popupable-thumbnail",n.src=e.original.dataset.popupableSrc||e.original.getAttribute("currentSrc")||e.original.getAttribute("src"),n.dataset.thumbnailIndex=t,E.append(n),n})),y.innerHTML=`\n <div class="popupable-button-container popupable-prev-container${v.currentIndex?"":" popupable-button-disabled"}">\n <div class="popupable-button popupable-prev">\n <svg width="24px" height="24px" viewBox="0 -960 960 960" fill="currentColor">\n <path d="m313-440 224 224-57 56-320-320 320-320 57 56-224 224h487v80H313Z"/>\n </svg>\n </div>\n </div>\n <div class="popupable-button-container popupable-next-container${v.currentIndex===v.length-1?" popupable-button-disabled":""}">\n <div class="popupable-button popupable-next">\n <svg width="24px" height="24px" viewBox="0 -960 960 960" fill="currentColor">\n <path d="M647-440H160v-80h487L423-744l57-56 320 320-320 320-57-56 224-224Z"/>\n </svg>\n </div>\n </div>\n `;const ce=y.querySelector(".popupable-next-container"),ue=y.querySelector(".popupable-prev-container");let de,be,fe,me;const he=!(navigator.maxTouchPoints>0||window.matchMedia("(hover: none)").matches);function S(){ce.classList.remove("popupable-button-inactive"),ue.classList.remove("popupable-button-inactive"),e.scheduleNavHide()}if(e.scheduleNavHide=()=>{he&&(clearTimeout(de),me||(de=setTimeout(()=>{me||(ce.classList.add("popupable-button-inactive"),ue.classList.add("popupable-button-inactive"))},1500)))},e.clearNavInactive=()=>{ce.classList.remove("popupable-button-inactive"),ue.classList.remove("popupable-button-inactive")},T=async()=>{const t=v[v.currentIndex];await t.ready,v.currentIndex?ue.classList.remove("popupable-button-disabled"):ue.classList.add("popupable-button-disabled"),v.currentIndex===v.length-1?ce.classList.add("popupable-button-disabled"):ce.classList.remove("popupable-button-disabled");for(const[e,t]of v.entries()){const n=e-v.currentIndex;t.cloneContainer.style.setProperty("--popupable-offset-multiplier",n),t.cloneContainer.style.zIndex=-1*Math.abs(n),t.content&&(n?n>0?(t.content.classList.add("popupable-content-after"),t.content.classList.remove("popupable-content-before")):(t.content.classList.add("popupable-content-before"),t.content.classList.remove("popupable-content-after")):(t.content.classList.remove("popupable-content-before"),t.content.classList.remove("popupable-content-after")))}if(t.id?g.id=t.id:g.removeAttribute("id"),C&&(C.textContent=`${v.currentIndex+1} / ${v.length}`),I){for(const[e,t]of I.entries())t.classList.toggle("popupable-thumbnail-active",e===v.currentIndex);const e=I[v.currentIndex];requestAnimationFrame(()=>{if(!e||!E?.isConnected)return;const t=getComputedStyle(E),n=parseFloat(t.paddingLeft)||0,o=parseFloat(t.paddingRight)||0,a=E.scrollLeft+n,i=E.scrollLeft+E.clientWidth-o,r=e.offsetLeft,s=r+e.offsetWidth;let l=E.scrollLeft;r<a?l=Math.max(0,r-n):s>i&&(l=s-E.clientWidth+o),l!==E.scrollLeft&&(P?E.scrollTo({left:l,behavior:"smooth"}):E.scrollLeft=l),P=!0})}e.closeContainer.classList.toggle("popupable-button-inactive",!t.zoomable),updateExpandedSize()},M=()=>{v.currentIndex>=v.length-1||(v.currentIndex++,T())},k=()=>{v.currentIndex<=0||(v.currentIndex--,T())},e.listeners.push({target:ce,event:"click",func:()=>M()},{target:ue,event:"click",func:()=>k()},{target:document,event:"keydown",func:t=>{if("zoomed"!==e.state)switch(t.key){case"ArrowRight":case"ArrowDown":case"PageDown":case"d":case"s":M();break;case"ArrowLeft":case"ArrowUp":case"PageUp":case"a":case"w":k();break;case"Home":v.currentIndex=0,T();break;case"End":v.currentIndex=v.length-1,T();break;case"0":case"1":case"2":case"3":case"4":case"5":case"6":case"7":case"8":case"9":v.currentIndex=Math.min(Math.max(Number(t.key),1)-1,v.length-1),T()}}},{target:document,event:"wheel",func:t=>{if("zoomed"===e.state)return;const n=performance.now();n-(z||0)<80||(t.deltaY>50?(z=n,M()):t.deltaY<-50&&(z=n,k()))},args:{passive:!0}}),E){let ve,ge,ye,we,Le,xe,Ce,Ee,Ie;function X(){Ie&&(cancelAnimationFrame(Ie),Ie=null)}function N(){if(X(),Math.abs(Ee)<.01)return;let e=performance.now();Ie=requestAnimationFrame(function t(n){if(!E.isConnected)return void X();const o=E.scrollWidth-E.clientWidth;if(o<=0)return void X();const a=Math.min(32,n-e);e=n;let i=E.scrollLeft+Ee*a;i<0&&(i=0),i>o&&(i=o),E.scrollLeft=i,E.scrollLeft<=.1&&Ee<0||E.scrollLeft>=o-.1&&Ee>0?X():(Ee*=Math.pow(.8,a/16.67),Math.abs(Ee)<=.002?X():Ie=requestAnimationFrame(t))})}e.listeners.push({target:E,event:"pointerdown",func:e=>{if(0!==e.button)return;const t=E.scrollWidth-E.clientWidth;ye=t>0,X(),ve=!0,ge=!1,we=e.clientX,Le=E.scrollLeft,xe=E.scrollLeft,Ce=performance.now(),Ee=0,ye&&E.classList.add("popupable-thumbnails-dragging"),E.setPointerCapture(e.pointerId)}},{target:E,event:"pointermove",func:e=>{if(!ve)return;const t=e.clientX-we;Math.abs(t)>a&&(ge=!0);const n=performance.now(),o=n-Ce,i=Le-t;if(E.scrollLeft=i,o>0){const e=(E.scrollLeft-xe)/o;Ee=.65*Ee+.35*e,xe=E.scrollLeft,Ce=n}}},{target:E,event:"pointerup",func:e=>{if(!ve)return;if(ve=!1,ye&&E.classList.remove("popupable-thumbnails-dragging"),E.hasPointerCapture(e.pointerId)&&E.releasePointerCapture(e.pointerId),ge)return performance.now()-Ce>10&&(Ee=0),void N();const t=document.elementFromPoint(e.clientX,e.clientY)?.closest?.(".popupable-thumbnail");t&&(v.currentIndex=Number(t.dataset.thumbnailIndex),T())}},{target:E,event:"pointercancel",func:e=>{ve&&(ve=!1,ye&&E.classList.remove("popupable-thumbnails-dragging"),E.hasPointerCapture(e.pointerId)&&E.releasePointerCapture(e.pointerId),X())}},{target:E,event:"wheel",func:e=>{e.stopPropagation(),e.preventDefault();const t=E.scrollWidth-E.clientWidth;if(t<=0)return;const n=Math.abs(e.deltaX)>Math.abs(e.deltaY)?e.deltaX:e.deltaY,o=E.scrollLeft<=.1,a=E.scrollLeft>=t-.1;if(o&&n<0||a&&n>0)return;o&&n>0&&Ee<0&&(Ee=0),a&&n<0&&Ee>0&&(Ee=0);Ee=(Ee||0)+.015*n,Ie||N()},args:{passive:!1}})}he&&(e.listeners.push({target:ce,event:"pointerenter",func:()=>{me=!0,S()}},{target:ue,event:"pointerenter",func:()=>{me=!0,S()}},{target:ce,event:"pointerleave",func:()=>{me=!1,e.scheduleNavHide()}},{target:ue,event:"pointerleave",func:()=>{me=!1,e.scheduleNavHide()}}),e.listeners.push({target:g,event:"pointermove",func:t=>{if("zoomed"!==e.state)return null==be||null==fe?(be=t.clientX,void(fe=t.clientY)):void(Math.abs(t.clientX-be)<a&&Math.abs(t.clientY-fe)<a||(be=t.clientX,fe=t.clientY,S()))},args:{passive:!0}}));for(const Pe of v)Pe.content&&w&&w.append(Pe.content)}else h&&w.append(h);const Y={counter:!!C,content:!!w,thumbnails:!!E},R=f.order.top.filter(e=>Y[e]),B=f.order.bottom.filter(e=>Y[e]);function H(e,t){e&&("counter"===t&&C?(A[e===L?"counterTop":"counterBottom"]=!0,e.append(C)):"content"===t&&w?(A[e===L?"contentTop":"contentBottom"]=!0,e.append(w)):"thumbnails"===t&&E&&(A[e===L?"thumbnailsTop":"thumbnailsBottom"]=!0,e.append(E)))}L=document.createElement("div"),L.className="popupable-header",x=document.createElement("div"),x.className="popupable-footer";for(const Me of R)H(L,Me);for(const ke of B)H(x,ke);y.append(L),y.append(x);const D=document.createElement("div");D.className="popupable-button-container popupable-close-container",D.innerHTML='<div class="popupable-button popupable-close"><svg width="24px" height="24px" viewBox="0 -960 960 960" fill="currentColor"><path d="m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z"/></svg></div>',D.addEventListener("click",closePopupable),f.zoomable||D.classList.add("popupable-button-inactive"),L.append(D),g.append(b,y),Object.assign(d,f,{popup:g,group:v,contentContainer:w,thumbnailsContainer:E,orderPlacement:A,closeContainer:D,goNext:M,goPrev:k});const F=setTimeout(()=>{u===o&&c.classList.add("popupable-loading")},250);if(await d.ready,clearTimeout(F),c.classList.remove("popupable-loading"),u!==o||e!==d||"close"===d.state)return;m.classList.add("popupable-block-transitions"),document.body.append(g);const{hideSource:W,crossfade:V,fade:O}=setCloneToOriginalRect(m,c);W&&c.classList.add("popupable-hide"),V&&g.classList.add("popupable-crossfade"),O&&g.classList.add("popupable-fade"),disableScroll();const $=getComputedStyle(g);d.transition.duration=1e3*parseFloat($.transitionDuration)+1e3*parseFloat($.transitionDelay),g._state=d,m.classList.remove("popupable-block-transitions"),openPopupable(g._state),v&&T();let U,q=0;v&&g.addEventListener("dragstart",e=>e.preventDefault());const _=new Map;function j(e,t,n,o,i=[]){if("open"!==e.state)return;let s=0;const l=t.cloneContainer.parentElement,p=l.style.transform;if(p){const e=p.match(/translateX\((-?\d+(?:\.\d+)?)px\)/);e&&(s=Number(e[1])||0)}const c=Math.abs(s)>.5;r=!1,c?(t.cloneContainer.style.translate="0 0",t.cloneContainer.style.transition="translate var(--popupable-switch-duration), transform 0s",l.style.transition=null,l.style.transform=null,t.cloneContainer.style.translate=`${s}px 0`):(l.style.transition=null,l.style.transform=null),e.state="zoomed",g.classList.add("popupable-locked");let u,d,b=o;const f=new Map;let m,h,v,w,L,x,C,E,I,P,M=!1;const k=t.cloneContainer.getBoundingClientRect(),z=n?.clientX??k.left+k.width/2,T=n?.clientY??k.top+k.height/2;u=(z-k.left)*(1-b),d=(T-k.top)*(1-b);const A=()=>t.cloneContainer.style.transform=`translate(${u}px, ${d}px) scale(${b})`;function S(e,n,o){const a=b;var i;if(i=e,b=Math.min(6,Math.max(.5,i)),b===a)return!1;const r=t.cloneContainer.getBoundingClientRect(),s=b/a,l=n-r.left,p=o-r.top;return u+=l*(1-s),d+=p*(1-s),!0}function X(){if(1===f.size){const e=f.values().next().value;return m=e.id,h=e.x,v=e.y,w=null,L=null,void(x=null)}if(f.size>=2){m=null;const[e,t]=[...f.values()];return w=(e.x+t.x)/2,L=(e.y+t.y)/2,void(x=Math.hypot(t.x-e.x,t.y-e.y))}m=null,h=null,v=null,w=null,L=null,x=null}if(t.cloneContainer.classList.add("popupable-zoomed"),A(),i.length){c||(t.cloneContainer.style.transition="none"),P=!0;for(const e of i)f.set(e.id,{id:e.id,x:e.x,y:e.y}),g.setPointerCapture(e.id);X()}e.unzoom=()=>{e.state="open",g.classList.remove("popupable-locked");for(const e of f.keys())g.hasPointerCapture(e)&&g.releasePointerCapture(e);f.clear(),t.cloneContainer.classList.remove("popupable-zoomed"),t.cloneContainer.style.transition=null,t.cloneContainer.style.transform=null,t.cloneContainer.style.translate=null;for(const t of e.zoomListeners)t.target.removeEventListener(t.event,t.func)},e.zoomListeners=[{target:g,event:"pointerdown",func:e=>{0===e.button&&(t.cloneContainer.style.transition="none",g.setPointerCapture(e.pointerId),f.set(e.pointerId,{id:e.pointerId,x:e.clientX,y:e.clientY}),1===f.size?(C=e.target,E=e.clientX,I=e.clientY,P=!1):P=!0,X(),e.preventDefault())}},{target:g,event:"pointermove",func:e=>{const t=f.get(e.pointerId);if(t){if(t.x=e.clientX,t.y=e.clientY,!P&&(Math.abs(e.clientX-E)>a||Math.abs(e.clientY-I)>a)&&(P=!0),1===f.size&&m===e.pointerId){const t=e.clientX-h,n=e.clientY-v;if(!t&&!n)return;return u+=t,d+=n,h=e.clientX,v=e.clientY,void A()}if(f.size>=2){const[e,t]=[...f.values()],n=(e.x+t.x)/2,o=(e.y+t.y)/2,a=Math.hypot(t.x-e.x,t.y-e.y);if(!x)return w=n,L=o,void(x=a);u+=n-w,d+=o-L,S(b*(a/x),n,o),M=!0,w=n,L=o,x=a,A()}}}},{target:g,event:"pointerup",func:n=>{if(f.has(n.pointerId)){if(f.delete(n.pointerId),g.hasPointerCapture(n.pointerId)&&g.releasePointerCapture(n.pointerId),M&&b<=1.01&&f.size<2)return e.skipOpenTouchPointerUps=f.size,void e.unzoom();if(!f.size&&!P&&Math.abs(n.clientX-E)<a&&Math.abs(n.clientY-I)<a){if(C?.closest?.(".popupable-clone-container")===t.cloneContainer||(C===g||C===y))return void e.unzoom()}X()}}},{target:g,event:"pointercancel",func:e=>{f.has(e.pointerId)&&(f.delete(e.pointerId),g.hasPointerCapture(e.pointerId)&&g.releasePointerCapture(e.pointerId),X())}},{target:g,event:"wheel",func:e=>{t.cloneContainer.style.transition="none",S(b*Math.exp(.002*-e.deltaY),e.clientX,e.clientY)&&A()},args:{passive:!0}}];for(const t of e.zoomListeners)t.target.addEventListener(t.event,t.func,t.args)}e.listeners.push({target:g,event:"pointerdown",func:e=>{if("open"!==g._state.state||"touch"!==e.pointerType)return;const t=g._state,n=t.group?t.group[t.group.currentIndex]:t;e.target.closest(".popupable-clone-container")===n.cloneContainer&&(_.set(e.pointerId,{id:e.pointerId,x:e.clientX,y:e.clientY}),_.size>=2&&(j(t,n,e,1,[..._.values()].slice(0,2)),_.clear(),e.preventDefault()))}},{target:g,event:"pointermove",func:e=>{const t=_.get(e.pointerId);t&&(t.x=e.clientX,t.y=e.clientY)}},{target:g,event:"pointerup",func:e=>{_.delete(e.pointerId)}},{target:g,event:"pointercancel",func:e=>{_.delete(e.pointerId)}}),g.addEventListener("pointerup",t=>{if("zoomed"===g._state.state)return;if("touch"===t.pointerType&&(g._state.skipOpenTouchPointerUps||0)>0)return void g._state.skipOpenTouchPointerUps--;const o=performance.now(),a=null!=t.target.closest(".popupable-next-container, .popupable-prev-container");if(U&&o-q<250)return void(q=o);if(a?(U=!0,q=o):(U=!1,q=o),0!==t.button||!((t.target.classList.contains("popupable-clone")||t.target.classList.contains("popupable-clone-layer"))&&n.classList.contains("popupable-clone-container")||t.target==n&&(t.target.closest(".popupable-clone-container")||t.target.classList.contains("popupable-viewport")||t.target.classList.contains("popupable-container"))||t.target.classList.contains("popupable-container")&&n===e.original.parentElement))return;const i=g._state,r=i.group?i.group[i.group.currentIndex]:i;i.blocked&&(i.blocked=!1),"open"!==i.state?(t.stopPropagation(),e!==i&&(closePopupable(),e=i),openPopupable(e)):requestAnimationFrame(()=>{i.blocked?i.blocked=!1:r.zoomable&&(t.target.classList.contains("popupable-clone")||t.target.classList.contains("popupable-clone-layer"))?j(i,r,t,2):closePopupable()})})}),document.addEventListener("keydown",t=>{if("Escape"===t.key||"Backspace"===t.key||" "===t.key||"Delete"===t.key){if("zoomed"===e.state)return void e.unzoom();closePopupable()}}),window.addEventListener("resize",updateExpandedSize),visualViewport&&visualViewport.addEventListener("resize",updateExpandedSize)}
7
+ {let e,t,n,o=0;const a=3;let i;function triggerHaptic(){if("function"!=typeof navigator.vibrate){if(!i){i=document.createElement("label"),i.style.cssText="position:fixed;left:-9999px;top:-9999px;opacity:0;pointer-events:none";const e=document.createElement("input");e.type="checkbox",e.setAttribute("switch",""),i.append(e),document.body.append(i)}i.click()}else navigator.vibrate([10])}function disableScroll(){window.addEventListener("wheel",prevent,{passive:!1}),window.addEventListener("touchmove",prevent,{passive:!1}),window.addEventListener("keydown",blockKeys,!0)}function enableScroll(){window.removeEventListener("wheel",prevent),window.removeEventListener("touchmove",prevent),window.removeEventListener("keydown",blockKeys,!0)}function prevent(e){e.preventDefault()}function blockKeys(e){["ArrowUp","ArrowDown","PageUp","PageDown","Home","End"," "].includes(e.key)&&e.preventDefault()}function calcExpandedRect(t){const n=t.original,o=visualViewport?.width||window.innerWidth,a=visualViewport?.height||window.innerHeight,i=visualViewport?.offsetTop||0,r=visualViewport?.offsetLeft||0,l=visualViewport?.scale||1,s=1/l,p=e.popup,c=(parseFloat(getComputedStyle(p).getPropertyValue("--popupable-screen-padding"))||40)/l,u=Math.max(0,o-2*c),d=a-2*c,b=Math.max(0,d);let f;if(t.maintainAspect){const e=n.getBoundingClientRect();f=e.width/e.height}else f=t.source.naturalWidth/t.source.naturalHeight||1;let h=0,m=0;if(e.orderPlacement){const n=e.popup.querySelector(".popupable-counter"),o=n?n.getBoundingClientRect().height/s:0,a=e.thumbnailsContainer?e.thumbnailsContainer.getBoundingClientRect().height/s:0,i=e.contentContainer,r=t.content?t.content.getBoundingClientRect().height/s:i?.previousElementSibling&&i?.nextElementSibling&&parseFloat(getComputedStyle(i,"::after").height)||0,{counterTop:l,contentTop:p,thumbnailsTop:c,counterBottom:u,contentBottom:d,thumbnailsBottom:b}=e.orderPlacement;h=(l?o:0)+(p?r:0)+(c?a:0),m=(u?o:0)+(d?r:0)+(b?a:0)}const v=Math.max(0,a-h-m-2*c);let g=u,y=g/f;if(y>b&&(y=b,g=y*f),y>v&&(y=v,g=y*f),t.noUpscale){const e=t.cloneLayer||n,o=e.naturalWidth,a=e.naturalHeight;if(o&&a){const e=o/l,t=a/l,n=Math.min(1,e/g,t/y);n<1&&(g*=n,y*=n)}}let w=i+c+(b-y)/2;const x=i+a-m-c-y;return w=Math.min(w,x),w=Math.max(w,i+h+c),{top:w,left:r+c+(u-g)/2,width:g,height:y}}function setCloneToOriginalRect(t,n){const o=calcExpandedRect(e),{top:a,left:i,width:r,height:l}=e.animation.position(n,o);t.style.top=a+"px",t.style.left=i+"px",t.style.width=r+"px",t.style.height=l+"px"}function openPopupable(e){if("open"===e.state)return;e.state="open",triggerHaptic();const{cloneContainer:t,popup:n,transition:o,group:a,listeners:i}=e;n.classList.add("popupable-active"),e.clearNavInactive?.(),updateExpandedSize(),e.closingContainer?.removeEventListener("transitionend",o.listener);const r=e.closingContainer??t;o.listener=t=>{if(!t||t.target===t.currentTarget){if(r.removeEventListener("transitionend",o.listener),n.classList.add("popupable-open"),a)for(const e of a)e.cloneContainer.style.display=null;for(const e of i)e.target.addEventListener(e.event,e.func,e.args);e.scheduleNavHide?.()}},o.duration?r.addEventListener("transitionend",o.listener):o.listener()}function closePopupable(){if(!e||"close"===e.state)return;o++,e.state="close",document.body.classList.add("popupable-block-touch"),setTimeout(()=>document.body.classList.remove("popupable-block-touch"),300);const{cloneContainer:n,original:a,popup:i,transition:r,group:l,listeners:s}=e;a.classList.remove("popupable-loading"),i.classList.remove("popupable-active"),i.classList.remove("popupable-open");const p=l?l[l.currentIndex].cloneContainer:n;if(e.closingContainer=p,setCloneToOriginalRect(p,a),l)for(const e of l)e.cloneContainer!==p&&(e.cloneContainer.style.display="none");for(const e of s)e.target.removeEventListener(e.event,e.func);n.removeEventListener("transitionend",r.listener);const c=e;r.listener=n=>{n&&n.target!==n.currentTarget||(p.removeEventListener("transitionend",r.listener),a.classList.remove("popupable-hide"),i.remove(),c===e&&(enableScroll(),t=e,e=null))},r.duration?p.addEventListener("transitionend",r.listener):r.listener()}function updateExpandedSize(){if(!e||"close"===e.state)return;const t=visualViewport?.width||window.innerWidth,n=visualViewport?.height||window.innerHeight,o=visualViewport?.offsetTop||0,a=visualViewport?.offsetLeft||0,i=visualViewport?.scale||1;document.documentElement.style.setProperty("--popupable-view-width",t+"px"),e.popup.style.setProperty("--popupable-vv-width",t+"px"),e.popup.style.setProperty("--popupable-vv-height",n+"px"),e.popup.style.setProperty("--popupable-vv-top",o+"px"),e.popup.style.setProperty("--popupable-vv-left",a+"px"),e.popup.style.setProperty("--popupable-vv-scale",i),e.popup.style.setProperty("--popupable-vv-ui-scale",1/i);const r=1/i;let l;l=e.group?e.group:[e];for(const e of l){const{top:t,left:n,width:o,height:a}=calcExpandedRect(e);e.cloneContainer.style.top=t+"px",e.cloneContainer.style.left=n+"px",e.cloneContainer.style.width=o+"px",e.cloneContainer.style.height=a+"px"}if(e.contentContainer){let t;if(t=e.group?e.group[e.group.currentIndex]:e,t.content){const n=t.content.getBoundingClientRect();e.contentContainer.style.height=n.height/r+"px"}else{const t=e.contentContainer,n=t.previousElementSibling&&t.nextElementSibling&&parseFloat(getComputedStyle(t,"::after").height)||0;t.style.height=n+"px"}}}window.popupableAnimTypes={expand:{styles:!0,hideSource:!0,crossfade:!0,position(e){const t=e.getBoundingClientRect();return{top:visualViewport.offsetTop+t.top,left:visualViewport.offsetLeft+t.left,width:t.width,height:t.height}}},pop:{fade:!0,position:(e,{top:t,left:n,width:o,height:a})=>({top:t+.05*a,left:n+.05*o,width:.9*o,height:.9*a})},line:{fade:!0,position:(e,{top:t,left:n,width:o,height:a})=>({top:t+a/2,left:n+.05*o,width:.9*o,height:0})},float:{fade:!0,position:(e,{top:t,left:n,width:o,height:a})=>({top:t+40,left:n,width:o,height:a})}};const r=e=>e.getAttribute("currentSrc")||e.getAttribute("src")||e.getAttribute("data-popupable-src");function inheritAttr(e,t){const n=e.closest(`[${t}]`);if(!n)return;const o=n.getAttribute(t);return"false"!==o?o||!0:void 0}function parsePopupableOrder(e){const t=["counter","image","content","thumbnails"],n=new Set(t),o=[];if(e)for(const t of e.split(",")){const e=t.trim().toLowerCase();e&&n.has(e)&&!o.includes(e)&&o.push(e)}for(const e of t)o.includes(e)||o.push(e);const a=o.indexOf("image");return{top:o.slice(0,a),bottom:o.slice(a+1)}}function cloneElement(e,t=e){const n=popupableAnimTypes[inheritAttr(e,"data-popupable-anim")]??popupableAnimTypes.expand,o=inheritAttr(e,"data-popupable-src"),a=r(e),i=inheritAttr(e,"data-popupable-title"),l=inheritAttr(e,"data-popupable-description"),s=inheritAttr(e,"data-popupable-zoomable"),p=getComputedStyle(e),c=t===e?p:getComputedStyle(t),u=t!==e?r(t):null,d=document.createElement("div");d.className="popupable-clone-container",inheritAttr(e,"data-popupable-transparent")&&d.classList.add("popupable-transparent"),s&&d.classList.add("popupable-zoomable"),d.style.borderRadius=p.borderRadius,n.styles&&(d.style.border=p.border,d.style.outline=p.outline,d.style.boxShadow=p.boxShadow);const b=new Image;let f,h,m;if(b.className="popupable-clone",b.src=u||a||o,b.style.objectFit=c.objectFit,b.style.objectPosition=c.objectPosition,b.style.imageRendering=c.imageRendering,b.style.background=c.background,d.append(b),o&&a||u){if(f=new Image,f.className="popupable-clone-layer",f.src=o||a,f.style.imageRendering=p.imageRendering,d.append(f),"fill"===b.style.objectFit){const t=e.getBoundingClientRect();e.naturalWidth&&e.naturalHeight&&Math.abs(t.width/t.height-e.naturalWidth/e.naturalHeight)<.01&&(b.style.objectFit="cover")}h=f}else h=b;if(i||l){if(m=document.createElement("div"),m.classList="popupable-content",i){const e=document.createElement("div");e.className="popupable-title",e.textContent=i,m.append(e)}if(l){const e=document.createElement("div");e.className="popupable-description",e.textContent=l,m.append(e)}}return{id:e.dataset.popupable,original:e,cloneContainer:d,clone:b,cloneLayer:f,maintainAspect:inheritAttr(e,"data-popupable-maintain-aspect"),noUpscale:inheritAttr(e,"data-popupable-no-upscale"),counter:inheritAttr(e,"data-popupable-counter"),thumbnails:inheritAttr(e,"data-popupable-thumbnails"),order:parsePopupableOrder(inheritAttr(e,"data-popupable-order")),animation:n,ready:Promise.all([b,f].filter(Boolean).map(e=>e.decode().catch(()=>{}))),content:m,zoomable:s,source:h}}let l,s,p;function handleMove(t){if("open"!==e?.state||!e.group||!l)return;if(!e.popup?.classList.contains("popupable-open"))return s=t.touches?.[0].clientX??t.clientX,void(p=t.touches?.[0].clientY??t.clientY);const n=e.group[e.group.currentIndex];n.cloneContainer.parentElement.style.transition="initial",n.cloneContainer.parentElement.style.transform=`translateX(${(t.touches?.[0].clientX??t.clientX)-s}px)`}document.addEventListener("pointerdown",t=>{0===t.button&&(l||(n=t.target,s=t.clientX,p=t.clientY),l||"open"!==e?.state||t.target.closest(".popupable-header, .popupable-footer")||(l=!0))}),document.addEventListener("mousemove",handleMove),document.addEventListener("touchmove",handleMove,{passive:!0}),document.addEventListener("pointerup",async i=>{if(0!==i.button)return;if(l){l=!1;const Z=e.group?e.group[e.group.currentIndex]:e;Z.cloneContainer.parentElement.style.transition=null,Z.cloneContainer.parentElement.style.transform=null;const G=i.clientX-s,J=Math.abs(G),Q=i.clientY-p,ee=Math.abs(Q);if("touch"===i.pointerType&&ee>56&&ee>1.1*J)return void closePopupable();if(e.group&&J>a){const te=Math.max(0,Math.floor((J-window.innerWidth/2)/window.innerWidth));if(G>32)for(let ne=0;ne<=te;ne++)e.goPrev();else if(G<-32)for(let oe=0;oe<=te;oe++)e.goNext();return void(e.blocked=!0)}}const c=i.target.closest(".popupable-viewport")&&!n.closest(".popupable-viewport");if(!c&&i.target!=n&&(!n.classList.contains("popupable-clone-container")||i.target!==t?.original)&&(!n.closest(".popupable-container")||i.target.closest(".popupable-container"))||Math.abs(i.clientX-s)>a||Math.abs(i.clientY-p)>a)return;const u=(c?n.closest("[data-popupable]"):null)||i.target.closest("[data-popupable]");if(!u){if(c)return void closePopupable();if(i.target.closest(".popupable-container"))return;return void(e&&("zoomed"===e.state?e.unzoom():closePopupable()))}if(i.preventDefault(),e&&"close"!==e.state&&e.original===u)return;e&&closePopupable();const d=++o;e={transition:{},listeners:[]};const b=e,f=document.createElement("div");f.className="popupable-clones";const h=cloneElement(u),{cloneContainer:m,content:v}=h;let g;const y=u.closest("[data-popupable-group]"),w=y?.getAttribute("data-popupable-group"),x=w&&"false"!==w?w:null;if(x){const ae=[],ie=new Set;for(const re of document.querySelectorAll(`[data-popupable-group="${x}"]`)){re.hasAttribute("data-popupable")&&!ie.has(re)&&(ie.add(re),ae.push(re));for(const le of re.querySelectorAll("[data-popupable]")){if(ie.has(le))continue;const se=le.getAttribute("data-popupable-group");null!==se&&se!==x||le.closest("[data-popupable-group]")!==re||(ie.add(le),ae.push(le))}}if(ae.length){g=[];for(const[pe,ce]of ae.entries())if(ce===u)g.push(h),g.currentIndex=pe,f.append(m);else{const ue=cloneElement(ce,u);ue.cloneContainer.style.display="none",g.push(ue),f.append(ue.cloneContainer)}for(const[de,be]of g.entries()){if(!be.content)continue;const fe=de-g.currentIndex;fe>0?be.content.classList.add("popupable-content-after"):fe<0&&be.content.classList.add("popupable-content-before")}}}else f.append(m);const L=document.createElement("div");L.className=`popupable-container popupable-anim-${h.animation}`,h.id&&(L.id=h.id);const C=document.createElement("div");let E,I,P,M,z,A,k,T,S,X,N;C.className="popupable-viewport",(v||g&&g.some(e=>e.content))&&(E=document.createElement("div"),E.classList="popupable-content-container");const Y={};if(g){h.counter&&(M=document.createElement("div"),M.className="popupable-counter"),h.thumbnails&&(z=document.createElement("div"),z.className="popupable-thumbnails",A=g.map((e,t)=>{const n=new Image;return n.className="popupable-thumbnail",n.src=inheritAttr(e.original,"data-popupable-src")||r(e.original),n.dataset.thumbnailIndex=t,z.append(n),n})),C.innerHTML=`\n <div class="popupable-button-container popupable-prev-container${g.currentIndex?"":" popupable-button-disabled"}">\n <div class="popupable-button popupable-prev">\n <svg width="24px" height="24px" viewBox="0 -960 960 960" fill="currentColor">\n <path d="m313-440 224 224-57 56-320-320 320-320 57 56-224 224h487v80H313Z"/>\n </svg>\n </div>\n </div>\n <div class="popupable-button-container popupable-next-container${g.currentIndex===g.length-1?" popupable-button-disabled":""}">\n <div class="popupable-button popupable-next">\n <svg width="24px" height="24px" viewBox="0 -960 960 960" fill="currentColor">\n <path d="M647-440H160v-80h487L423-744l57-56 320 320-320 320-57-56 224-224Z"/>\n </svg>\n </div>\n </div>\n `;const he=C.querySelector(".popupable-next-container"),me=C.querySelector(".popupable-prev-container");let ve,ge,ye,we;const xe=!(navigator.maxTouchPoints>0||window.matchMedia("(hover: none)").matches);function R(){he.classList.remove("popupable-button-inactive"),me.classList.remove("popupable-button-inactive"),e.scheduleNavHide()}if(e.scheduleNavHide=()=>{xe&&(clearTimeout(ve),we||(ve=setTimeout(()=>{we||(he.classList.add("popupable-button-inactive"),me.classList.add("popupable-button-inactive"))},1500)))},e.clearNavInactive=()=>{he.classList.remove("popupable-button-inactive"),me.classList.remove("popupable-button-inactive")},N=async()=>{const t=g[g.currentIndex];await t.ready,g.currentIndex?me.classList.remove("popupable-button-disabled"):me.classList.add("popupable-button-disabled"),g.currentIndex===g.length-1?he.classList.add("popupable-button-disabled"):he.classList.remove("popupable-button-disabled");for(const[e,t]of g.entries()){const n=e-g.currentIndex;t.cloneContainer.style.setProperty("--popupable-offset-multiplier",n),t.cloneContainer.style.zIndex=-1*Math.abs(n),t.content&&(n?n>0?(t.content.classList.add("popupable-content-after"),t.content.classList.remove("popupable-content-before")):(t.content.classList.add("popupable-content-before"),t.content.classList.remove("popupable-content-after")):(t.content.classList.remove("popupable-content-before"),t.content.classList.remove("popupable-content-after")))}if(t.id?L.id=t.id:L.removeAttribute("id"),M&&(M.textContent=`${g.currentIndex+1} / ${g.length}`),A){for(const[e,t]of A.entries())t.classList.toggle("popupable-thumbnail-active",e===g.currentIndex);const e=A[g.currentIndex];requestAnimationFrame(()=>{if(!e||!z?.isConnected)return;const t=getComputedStyle(z),n=parseFloat(t.paddingLeft)||0,o=parseFloat(t.paddingRight)||0,a=z.scrollLeft+n,i=z.scrollLeft+z.clientWidth-o,r=e.offsetLeft,l=r+e.offsetWidth;let s=z.scrollLeft;r<a?s=Math.max(0,r-n):l>i&&(s=l-z.clientWidth+o),s!==z.scrollLeft&&(k?z.scrollTo({left:s,behavior:"smooth"}):z.scrollLeft=s),k=!0})}e.closeContainer.classList.toggle("popupable-button-inactive",!t.zoomable),updateExpandedSize()},T=()=>{g.currentIndex>=g.length-1||(g.currentIndex++,N())},S=()=>{g.currentIndex<=0||(g.currentIndex--,N())},e.listeners.push({target:he,event:"click",func:()=>T()},{target:me,event:"click",func:()=>S()},{target:document,event:"keydown",func:t=>{if("zoomed"!==e.state)switch(t.key){case"ArrowRight":case"ArrowDown":case"PageDown":case"d":case"s":T();break;case"ArrowLeft":case"ArrowUp":case"PageUp":case"a":case"w":S();break;case"Home":g.currentIndex=0,N();break;case"End":g.currentIndex=g.length-1,N();break;case"0":case"1":case"2":case"3":case"4":case"5":case"6":case"7":case"8":case"9":g.currentIndex=Math.min(Math.max(Number(t.key),1)-1,g.length-1),N()}}},{target:document,event:"wheel",func:t=>{if("zoomed"===e.state)return;const n=performance.now();n-(X||0)<80||(t.deltaY>50?(X=n,T()):t.deltaY<-50&&(X=n,S()))},args:{passive:!0}}),z){let Le,Ce,Ee,Ie,Pe,Me,ze,Ae,ke;function B(){ke&&(cancelAnimationFrame(ke),ke=null)}function H(){if(B(),Math.abs(Ae)<.01)return;let e=performance.now();ke=requestAnimationFrame(function t(n){if(!z.isConnected)return void B();const o=z.scrollWidth-z.clientWidth;if(o<=0)return void B();const a=Math.min(32,n-e);e=n;let i=z.scrollLeft+Ae*a;i<0&&(i=0),i>o&&(i=o),z.scrollLeft=i,z.scrollLeft<=.1&&Ae<0||z.scrollLeft>=o-.1&&Ae>0?B():(Ae*=Math.pow(.8,a/16.67),Math.abs(Ae)<=.002?B():ke=requestAnimationFrame(t))})}e.listeners.push({target:z,event:"pointerdown",func:e=>{if(0!==e.button)return;const t=z.scrollWidth-z.clientWidth;Ee=t>0,B(),Le=!0,Ce=!1,Ie=e.clientX,Pe=z.scrollLeft,Me=z.scrollLeft,ze=performance.now(),Ae=0,Ee&&z.classList.add("popupable-thumbnails-dragging"),z.setPointerCapture(e.pointerId)}},{target:z,event:"pointermove",func:e=>{if(!Le)return;const t=e.clientX-Ie;Math.abs(t)>a&&(Ce=!0);const n=performance.now(),o=n-ze,i=Pe-t;if(z.scrollLeft=i,o>0){const e=(z.scrollLeft-Me)/o;Ae=.65*Ae+.35*e,Me=z.scrollLeft,ze=n}}},{target:z,event:"pointerup",func:e=>{if(!Le)return;if(Le=!1,Ee&&z.classList.remove("popupable-thumbnails-dragging"),z.hasPointerCapture(e.pointerId)&&z.releasePointerCapture(e.pointerId),Ce)return performance.now()-ze>10&&(Ae=0),void H();const t=document.elementFromPoint(e.clientX,e.clientY)?.closest?.(".popupable-thumbnail");t&&(g.currentIndex=Number(t.dataset.thumbnailIndex),N())}},{target:z,event:"pointercancel",func:e=>{Le&&(Le=!1,Ee&&z.classList.remove("popupable-thumbnails-dragging"),z.hasPointerCapture(e.pointerId)&&z.releasePointerCapture(e.pointerId),B())}},{target:z,event:"wheel",func:e=>{e.stopPropagation(),e.preventDefault();const t=z.scrollWidth-z.clientWidth;if(t<=0)return;const n=Math.abs(e.deltaX)>Math.abs(e.deltaY)?e.deltaX:e.deltaY,o=z.scrollLeft<=.1,a=z.scrollLeft>=t-.1;if(o&&n<0||a&&n>0)return;o&&n>0&&Ae<0&&(Ae=0),a&&n<0&&Ae>0&&(Ae=0);Ae=(Ae||0)+.015*n,ke||H()},args:{passive:!1}})}xe&&(e.listeners.push({target:he,event:"pointerenter",func:()=>{we=!0,R()}},{target:me,event:"pointerenter",func:()=>{we=!0,R()}},{target:he,event:"pointerleave",func:()=>{we=!1,e.scheduleNavHide()}},{target:me,event:"pointerleave",func:()=>{we=!1,e.scheduleNavHide()}}),e.listeners.push({target:L,event:"pointermove",func:t=>{if("zoomed"!==e.state)return null==ge||null==ye?(ge=t.clientX,void(ye=t.clientY)):void(Math.abs(t.clientX-ge)<a&&Math.abs(t.clientY-ye)<a||(ge=t.clientX,ye=t.clientY,R()))},args:{passive:!0}}));for(const Te of g)Te.content&&E&&E.append(Te.content)}else v&&E.append(v);const F={counter:!!M,content:!!E,thumbnails:!!z},W=h.order.top.filter(e=>F[e]),V=h.order.bottom.filter(e=>F[e]);function D(e,t){e&&("counter"===t&&M?(Y[e===I?"counterTop":"counterBottom"]=!0,e.append(M)):"content"===t&&E?(Y[e===I?"contentTop":"contentBottom"]=!0,e.append(E)):"thumbnails"===t&&z&&(Y[e===I?"thumbnailsTop":"thumbnailsBottom"]=!0,e.append(z)))}I=document.createElement("div"),I.className="popupable-header",P=document.createElement("div"),P.className="popupable-footer";for(const Se of W)D(I,Se);for(const Xe of V)D(P,Xe);C.append(I),C.append(P);const $=document.createElement("div");$.className="popupable-button-container popupable-close-container",$.innerHTML='<div class="popupable-button popupable-close"><svg width="24px" height="24px" viewBox="0 -960 960 960" fill="currentColor"><path d="m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z"/></svg></div>',$.addEventListener("click",closePopupable),h.zoomable||$.classList.add("popupable-button-inactive"),I.append($),L.append(f,C),Object.assign(b,h,{popup:L,group:g,contentContainer:E,thumbnailsContainer:z,orderPlacement:Y,closeContainer:$,goNext:T,goPrev:S});const O=setTimeout(()=>{d===o&&u.classList.add("popupable-loading")},250);if(await b.ready,clearTimeout(O),u.classList.remove("popupable-loading"),d!==o||e!==b||"close"===b.state)return;h.animation.hideSource&&u.classList.add("popupable-hide"),h.animation.crossfade&&L.classList.add("popupable-crossfade"),h.animation.fade&&L.classList.add("popupable-fade"),document.body.append(L),setCloneToOriginalRect(m,u),disableScroll();const q=getComputedStyle(L);b.transition.duration=1e3*parseFloat(q.transitionDuration)+1e3*parseFloat(q.transitionDelay),L._state=b,openPopupable(L._state),g&&N();let U,_=0;g&&L.addEventListener("dragstart",e=>e.preventDefault());const j=new Map;function K(e,t,n,o,i=[]){if("open"!==e.state)return;let r=0;const s=t.cloneContainer.parentElement,p=s.style.transform;if(p){const e=p.match(/translateX\((-?\d+(?:\.\d+)?)px\)/);e&&(r=Number(e[1])||0)}const c=Math.abs(r)>.5;l=!1,c?(t.cloneContainer.style.translate="0 0",t.cloneContainer.style.transition="translate var(--popupable-switch-duration), transform 0s",s.style.transition=null,s.style.transform=null,t.cloneContainer.style.translate=`${r}px 0`):(s.style.transition=null,s.style.transform=null),e.state="zoomed",L.classList.add("popupable-locked");let u,d,b=o;const f=new Map;let h,m,v,g,y,w,x,E,I,P,M=!1;const z=t.cloneContainer.getBoundingClientRect(),A=n?.clientX??z.left+z.width/2,k=n?.clientY??z.top+z.height/2;u=(A-z.left)*(1-b),d=(k-z.top)*(1-b);const T=()=>t.cloneContainer.style.transform=`translate(${u}px, ${d}px) scale(${b})`;function S(e,n,o){const a=b;var i;if(i=e,b=Math.min(6,Math.max(.5,i)),b===a)return!1;const r=t.cloneContainer.getBoundingClientRect(),l=b/a,s=n-r.left,p=o-r.top;return u+=s*(1-l),d+=p*(1-l),!0}function X(){if(1===f.size){const e=f.values().next().value;return h=e.id,m=e.x,v=e.y,g=null,y=null,void(w=null)}if(f.size>=2){h=null;const[e,t]=[...f.values()];return g=(e.x+t.x)/2,y=(e.y+t.y)/2,void(w=Math.hypot(t.x-e.x,t.y-e.y))}h=null,m=null,v=null,g=null,y=null,w=null}if(t.cloneContainer.classList.add("popupable-zoomed"),T(),i.length){c||(t.cloneContainer.style.transition="none"),P=!0;for(const e of i)f.set(e.id,{id:e.id,x:e.x,y:e.y}),L.setPointerCapture(e.id);X()}e.unzoom=()=>{e.state="open",L.classList.remove("popupable-locked");for(const e of f.keys())L.hasPointerCapture(e)&&L.releasePointerCapture(e);f.clear(),t.cloneContainer.classList.remove("popupable-zoomed"),t.cloneContainer.style.transition=null,t.cloneContainer.style.transform=null,t.cloneContainer.style.translate=null;for(const t of e.zoomListeners)t.target.removeEventListener(t.event,t.func)},e.zoomListeners=[{target:L,event:"pointerdown",func:e=>{0===e.button&&(t.cloneContainer.style.transition="none",L.setPointerCapture(e.pointerId),f.set(e.pointerId,{id:e.pointerId,x:e.clientX,y:e.clientY}),1===f.size?(x=e.target,E=e.clientX,I=e.clientY,P=!1):P=!0,X(),e.preventDefault())}},{target:L,event:"pointermove",func:e=>{const t=f.get(e.pointerId);if(t){if(t.x=e.clientX,t.y=e.clientY,!P&&(Math.abs(e.clientX-E)>a||Math.abs(e.clientY-I)>a)&&(P=!0),1===f.size&&h===e.pointerId){const t=e.clientX-m,n=e.clientY-v;if(!t&&!n)return;return u+=t,d+=n,m=e.clientX,v=e.clientY,void T()}if(f.size>=2){const[e,t]=[...f.values()],n=(e.x+t.x)/2,o=(e.y+t.y)/2,a=Math.hypot(t.x-e.x,t.y-e.y);if(!w)return g=n,y=o,void(w=a);u+=n-g,d+=o-y,S(b*(a/w),n,o),M=!0,g=n,y=o,w=a,T()}}}},{target:L,event:"pointerup",func:n=>{if(f.has(n.pointerId)){if(f.delete(n.pointerId),L.hasPointerCapture(n.pointerId)&&L.releasePointerCapture(n.pointerId),M&&b<=1.01&&f.size<2)return e.skipOpenTouchPointerUps=f.size,void e.unzoom();if(!f.size&&!P&&Math.abs(n.clientX-E)<a&&Math.abs(n.clientY-I)<a){if(x?.closest?.(".popupable-clone-container")===t.cloneContainer||(x===L||x===C))return void e.unzoom()}X()}}},{target:L,event:"pointercancel",func:e=>{f.has(e.pointerId)&&(f.delete(e.pointerId),L.hasPointerCapture(e.pointerId)&&L.releasePointerCapture(e.pointerId),X())}},{target:L,event:"wheel",func:e=>{t.cloneContainer.style.transition="none",S(b*Math.exp(.002*-e.deltaY),e.clientX,e.clientY)&&T()},args:{passive:!0}}];for(const t of e.zoomListeners)t.target.addEventListener(t.event,t.func,t.args)}e.listeners.push({target:L,event:"pointerdown",func:e=>{if("open"!==L._state.state||"touch"!==e.pointerType)return;const t=L._state,n=t.group?t.group[t.group.currentIndex]:t;e.target.closest(".popupable-clone-container")===n.cloneContainer&&(j.set(e.pointerId,{id:e.pointerId,x:e.clientX,y:e.clientY}),j.size>=2&&(K(t,n,e,1,[...j.values()].slice(0,2)),j.clear(),e.preventDefault()))}},{target:L,event:"pointermove",func:e=>{const t=j.get(e.pointerId);t&&(t.x=e.clientX,t.y=e.clientY)}},{target:L,event:"pointerup",func:e=>{j.delete(e.pointerId)}},{target:L,event:"pointercancel",func:e=>{j.delete(e.pointerId)}}),L.addEventListener("pointerup",t=>{if("zoomed"===L._state.state)return;if("touch"===t.pointerType&&(L._state.skipOpenTouchPointerUps||0)>0)return void L._state.skipOpenTouchPointerUps--;const o=performance.now(),a=null!=t.target.closest(".popupable-next-container, .popupable-prev-container");if(U&&o-_<250)return void(_=o);if(a?(U=!0,_=o):(U=!1,_=o),0!==t.button||!((t.target.classList.contains("popupable-clone")||t.target.classList.contains("popupable-clone-layer"))&&n.classList.contains("popupable-clone-container")||t.target==n&&(t.target.closest(".popupable-clone-container")||t.target.classList.contains("popupable-viewport")||t.target.classList.contains("popupable-container"))||t.target.classList.contains("popupable-container")&&n===e.original.parentElement))return;const i=L._state,r=i.group?i.group[i.group.currentIndex]:i;i.blocked&&(i.blocked=!1),"open"!==i.state?(t.stopPropagation(),e!==i&&(closePopupable(),e=i),openPopupable(e)):requestAnimationFrame(()=>{i.blocked?i.blocked=!1:r.zoomable&&(t.target.classList.contains("popupable-clone")||t.target.classList.contains("popupable-clone-layer"))?K(i,r,t,2):closePopupable()})})}),document.addEventListener("keydown",t=>{if("Escape"===t.key||"Backspace"===t.key||" "===t.key||"Delete"===t.key){if("zoomed"===e.state)return void e.unzoom();closePopupable()}}),window.addEventListener("resize",updateExpandedSize),visualViewport&&visualViewport.addEventListener("resize",updateExpandedSize)}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "popupable",
3
- "version": "1.5.2",
3
+ "version": "1.6.1",
4
4
  "description": "A lightweight, zero-dependency lightbox library using modern JavaScript and CSS.",
5
5
  "author": "Ewan Howell <ewanhowell5195>",
6
6
  "license": "MIT",