@f-ewald/components 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -0
- package/custom-elements.json +3808 -930
- package/dist/chat-message.d.ts +39 -0
- package/dist/chat-message.d.ts.map +1 -0
- package/dist/chat-message.js +155 -0
- package/dist/chat-message.js.map +1 -0
- package/dist/data-table.d.ts +39 -0
- package/dist/data-table.d.ts.map +1 -0
- package/dist/data-table.js +119 -0
- package/dist/data-table.js.map +1 -0
- package/dist/dropdown-button.d.ts +37 -0
- package/dist/dropdown-button.d.ts.map +1 -0
- package/dist/dropdown-button.js +219 -0
- package/dist/dropdown-button.js.map +1 -0
- package/dist/editable-text.d.ts +52 -0
- package/dist/editable-text.d.ts.map +1 -0
- package/dist/editable-text.js +224 -0
- package/dist/editable-text.js.map +1 -0
- package/dist/form-select.d.ts +46 -0
- package/dist/form-select.d.ts.map +1 -0
- package/dist/form-select.js +245 -0
- package/dist/form-select.js.map +1 -0
- package/dist/frame-box.d.ts +22 -0
- package/dist/frame-box.d.ts.map +1 -0
- package/dist/frame-box.js +68 -0
- package/dist/frame-box.js.map +1 -0
- package/dist/gallery-item-variant.d.ts +21 -0
- package/dist/gallery-item-variant.d.ts.map +1 -0
- package/dist/gallery-item-variant.js +44 -0
- package/dist/gallery-item-variant.js.map +1 -0
- package/dist/gallery-item.d.ts +28 -0
- package/dist/gallery-item.d.ts.map +1 -0
- package/dist/gallery-item.js +54 -0
- package/dist/gallery-item.js.map +1 -0
- package/dist/icon-button.d.ts +27 -0
- package/dist/icon-button.d.ts.map +1 -0
- package/dist/icon-button.js +83 -0
- package/dist/icon-button.js.map +1 -0
- package/dist/icons.d.ts +8 -0
- package/dist/icons.d.ts.map +1 -1
- package/dist/icons.js +8 -0
- package/dist/icons.js.map +1 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -1
- package/dist/index.js.map +1 -1
- package/dist/live-timer.d.ts +31 -0
- package/dist/live-timer.d.ts.map +1 -0
- package/dist/live-timer.js +78 -0
- package/dist/live-timer.js.map +1 -0
- package/dist/mcp-server.d.ts +3 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +82 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/photo-gallery.d.ts +117 -0
- package/dist/photo-gallery.d.ts.map +1 -0
- package/dist/photo-gallery.js +620 -0
- package/dist/photo-gallery.js.map +1 -0
- package/dist/popover-panel.d.ts +37 -0
- package/dist/popover-panel.d.ts.map +1 -0
- package/dist/popover-panel.js +184 -0
- package/dist/popover-panel.js.map +1 -0
- package/dist/status-pill.d.ts +24 -0
- package/dist/status-pill.d.ts.map +1 -0
- package/dist/status-pill.js +101 -0
- package/dist/status-pill.js.map +1 -0
- package/dist/tile-grid.d.ts +32 -0
- package/dist/tile-grid.d.ts.map +1 -0
- package/dist/tile-grid.js +102 -0
- package/dist/tile-grid.js.map +1 -0
- package/dist/tokens.css +39 -0
- package/dist/tokens.d.ts +28 -3
- package/dist/tokens.d.ts.map +1 -1
- package/dist/tokens.js +42 -43
- package/dist/tokens.js.map +1 -1
- package/dist/user-avatar.d.ts +5 -2
- package/dist/user-avatar.d.ts.map +1 -1
- package/dist/user-avatar.js +22 -6
- package/dist/user-avatar.js.map +1 -1
- package/dist/utils/duration.d.ts +12 -0
- package/dist/utils/duration.d.ts.map +1 -0
- package/dist/utils/duration.js +27 -0
- package/dist/utils/duration.js.map +1 -0
- package/docs/chat-message.md +59 -0
- package/docs/data-table.md +63 -0
- package/docs/dropdown-button.md +61 -0
- package/docs/editable-text.md +51 -0
- package/docs/form-select.md +70 -0
- package/docs/frame-box.md +43 -0
- package/docs/gallery-item-variant.md +37 -0
- package/docs/gallery-item.md +47 -0
- package/docs/icon-button.md +52 -0
- package/docs/live-timer.md +39 -0
- package/docs/mcp-evaluation.md +18 -11
- package/docs/photo-gallery.md +77 -0
- package/docs/popover-panel.md +70 -0
- package/docs/status-pill.md +43 -0
- package/docs/tile-grid.md +58 -0
- package/docs/user-avatar.md +1 -1
- package/llms.txt +357 -1
- package/package.json +10 -3
|
@@ -0,0 +1,620 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { LitElement, css, html, nothing } from "lit";
|
|
8
|
+
import { customElement, property, queryAssignedElements, state } from "lit/decorators.js";
|
|
9
|
+
import { ifDefined } from "lit/directives/if-defined.js";
|
|
10
|
+
import { styleMap } from "lit/directives/style-map.js";
|
|
11
|
+
import { iconChevronLeft, iconChevronRight } from "./icons.js";
|
|
12
|
+
import { tokens } from "./tokens.js";
|
|
13
|
+
const transitionDurationMs = 250;
|
|
14
|
+
const swipeThresholdPx = 40;
|
|
15
|
+
const booleanAttribute = {
|
|
16
|
+
fromAttribute(value) {
|
|
17
|
+
return value !== null && value !== "false";
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Responsive, accessible image carousel composed from declarative
|
|
22
|
+
* `gallery-item` children.
|
|
23
|
+
*
|
|
24
|
+
* @element photo-gallery
|
|
25
|
+
* @slot - `gallery-item` elements rendered as slides.
|
|
26
|
+
* @fires slide-change - The active image changed.
|
|
27
|
+
*/
|
|
28
|
+
let PhotoGallery = class PhotoGallery extends LitElement {
|
|
29
|
+
constructor() {
|
|
30
|
+
super(...arguments);
|
|
31
|
+
/** Zero-based active image index. */
|
|
32
|
+
this.currentIndex = 0;
|
|
33
|
+
/** Autoplay interval in milliseconds. Set to zero to disable autoplay. */
|
|
34
|
+
this.delay = 0;
|
|
35
|
+
/** Whether previous and next buttons are shown. */
|
|
36
|
+
this.showControls = true;
|
|
37
|
+
/** Whether a current/total counter is shown. */
|
|
38
|
+
this.showCounter = false;
|
|
39
|
+
/** Whether clickable slide indicators are shown. */
|
|
40
|
+
this.showIndicators = false;
|
|
41
|
+
/** Whether autoplay includes a built-in pause/play control. */
|
|
42
|
+
this.showAutoplayControl = true;
|
|
43
|
+
/** Whether autoplay is explicitly paused. */
|
|
44
|
+
this.paused = false;
|
|
45
|
+
/** CSS aspect ratio used by the image viewport. */
|
|
46
|
+
this.aspectRatio = "16 / 9";
|
|
47
|
+
/** How images fit within the stable viewport. */
|
|
48
|
+
this.objectFit = "cover";
|
|
49
|
+
this._previousIndex = null;
|
|
50
|
+
this._itemsVersion = 0;
|
|
51
|
+
this._hovered = false;
|
|
52
|
+
this._focusWithin = false;
|
|
53
|
+
this._nextChangeReason = null;
|
|
54
|
+
this._itemsChanged = false;
|
|
55
|
+
this._renderedIndex = 0;
|
|
56
|
+
/** Pauses or resumes scheduling when page visibility changes. */
|
|
57
|
+
this._handleVisibilityChange = () => {
|
|
58
|
+
this._restartAutoplay();
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
static { this.styles = [
|
|
62
|
+
tokens,
|
|
63
|
+
css `
|
|
64
|
+
:host {
|
|
65
|
+
display: block;
|
|
66
|
+
color: var(--ui-text, #0f172a);
|
|
67
|
+
font-family: var(--ui-font, ui-sans-serif, system-ui, sans-serif);
|
|
68
|
+
font-size: var(--ui-font-size, 0.875rem);
|
|
69
|
+
}
|
|
70
|
+
.gallery {
|
|
71
|
+
outline: none;
|
|
72
|
+
}
|
|
73
|
+
figure {
|
|
74
|
+
margin: 0;
|
|
75
|
+
}
|
|
76
|
+
.viewport {
|
|
77
|
+
position: relative;
|
|
78
|
+
overflow: hidden;
|
|
79
|
+
aspect-ratio: var(--photo-gallery-aspect-ratio, 16 / 9);
|
|
80
|
+
border-radius: var(--ui-radius, 0.5rem);
|
|
81
|
+
background: var(--ui-surface-muted, #f8fafc);
|
|
82
|
+
touch-action: pan-y;
|
|
83
|
+
user-select: none;
|
|
84
|
+
}
|
|
85
|
+
.gallery:focus-visible .viewport {
|
|
86
|
+
box-shadow: var(--ui-focus-ring, 0 0 0 3px rgb(79 70 229 / 0.35));
|
|
87
|
+
}
|
|
88
|
+
.slides {
|
|
89
|
+
display: grid;
|
|
90
|
+
width: 100%;
|
|
91
|
+
height: 100%;
|
|
92
|
+
}
|
|
93
|
+
picture {
|
|
94
|
+
grid-area: 1 / 1;
|
|
95
|
+
display: block;
|
|
96
|
+
width: 100%;
|
|
97
|
+
height: 100%;
|
|
98
|
+
}
|
|
99
|
+
picture img {
|
|
100
|
+
display: block;
|
|
101
|
+
width: 100%;
|
|
102
|
+
height: 100%;
|
|
103
|
+
object-fit: var(--photo-gallery-object-fit, cover);
|
|
104
|
+
}
|
|
105
|
+
.entering {
|
|
106
|
+
z-index: 2;
|
|
107
|
+
animation: gallery-fade-in 250ms ease both;
|
|
108
|
+
}
|
|
109
|
+
.leaving {
|
|
110
|
+
z-index: 1;
|
|
111
|
+
pointer-events: none;
|
|
112
|
+
animation: gallery-fade-out 250ms ease both;
|
|
113
|
+
}
|
|
114
|
+
.arrow-controls {
|
|
115
|
+
position: absolute;
|
|
116
|
+
z-index: 3;
|
|
117
|
+
inset: 0;
|
|
118
|
+
display: flex;
|
|
119
|
+
align-items: center;
|
|
120
|
+
justify-content: space-between;
|
|
121
|
+
padding: 0.5rem;
|
|
122
|
+
pointer-events: none;
|
|
123
|
+
}
|
|
124
|
+
button {
|
|
125
|
+
border: 0;
|
|
126
|
+
font: inherit;
|
|
127
|
+
cursor: pointer;
|
|
128
|
+
}
|
|
129
|
+
button:focus-visible {
|
|
130
|
+
outline: none;
|
|
131
|
+
box-shadow: var(--ui-focus-ring, 0 0 0 3px rgb(79 70 229 / 0.35));
|
|
132
|
+
}
|
|
133
|
+
.arrow-button {
|
|
134
|
+
display: inline-flex;
|
|
135
|
+
align-items: center;
|
|
136
|
+
justify-content: center;
|
|
137
|
+
width: 2.5rem;
|
|
138
|
+
height: 2.5rem;
|
|
139
|
+
padding: 0;
|
|
140
|
+
color: var(--ui-surface, #fff);
|
|
141
|
+
background: var(--ui-overlay, rgb(15 23 42 / 0.45));
|
|
142
|
+
border-radius: 9999px;
|
|
143
|
+
pointer-events: auto;
|
|
144
|
+
}
|
|
145
|
+
.arrow-button:hover {
|
|
146
|
+
background: color-mix(in srgb, var(--ui-text, #0f172a) 70%, transparent);
|
|
147
|
+
}
|
|
148
|
+
figcaption {
|
|
149
|
+
padding: 0.5rem 0.25rem 0;
|
|
150
|
+
color: var(--ui-text-muted, #64748b);
|
|
151
|
+
}
|
|
152
|
+
.footer {
|
|
153
|
+
display: flex;
|
|
154
|
+
align-items: center;
|
|
155
|
+
justify-content: space-between;
|
|
156
|
+
gap: 0.75rem;
|
|
157
|
+
min-height: 2rem;
|
|
158
|
+
padding-top: 0.5rem;
|
|
159
|
+
}
|
|
160
|
+
.counter {
|
|
161
|
+
color: var(--ui-text-muted, #64748b);
|
|
162
|
+
font-size: var(--ui-font-size-sm, 0.75rem);
|
|
163
|
+
white-space: nowrap;
|
|
164
|
+
}
|
|
165
|
+
.indicators {
|
|
166
|
+
display: flex;
|
|
167
|
+
flex: 1 1 auto;
|
|
168
|
+
align-items: center;
|
|
169
|
+
justify-content: center;
|
|
170
|
+
gap: 0.375rem;
|
|
171
|
+
}
|
|
172
|
+
.indicator {
|
|
173
|
+
width: 0.625rem;
|
|
174
|
+
height: 0.625rem;
|
|
175
|
+
padding: 0;
|
|
176
|
+
background: var(--ui-border, #e2e8f0);
|
|
177
|
+
border-radius: 9999px;
|
|
178
|
+
}
|
|
179
|
+
.indicator[aria-current="true"] {
|
|
180
|
+
background: var(--ui-primary, #4f46e5);
|
|
181
|
+
}
|
|
182
|
+
.autoplay-button {
|
|
183
|
+
padding: 0.25rem 0.625rem;
|
|
184
|
+
color: var(--ui-text, #0f172a);
|
|
185
|
+
background: var(--ui-surface-muted, #f8fafc);
|
|
186
|
+
border: 1px solid var(--ui-border, #e2e8f0);
|
|
187
|
+
border-radius: var(--ui-radius-sm, 0.25rem);
|
|
188
|
+
font-size: var(--ui-font-size-sm, 0.75rem);
|
|
189
|
+
white-space: nowrap;
|
|
190
|
+
}
|
|
191
|
+
.autoplay-button:hover {
|
|
192
|
+
background: var(--ui-surface, #fff);
|
|
193
|
+
}
|
|
194
|
+
slot {
|
|
195
|
+
display: none;
|
|
196
|
+
}
|
|
197
|
+
@keyframes gallery-fade-in {
|
|
198
|
+
from {
|
|
199
|
+
opacity: 0;
|
|
200
|
+
}
|
|
201
|
+
to {
|
|
202
|
+
opacity: 1;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
@keyframes gallery-fade-out {
|
|
206
|
+
from {
|
|
207
|
+
opacity: 1;
|
|
208
|
+
}
|
|
209
|
+
to {
|
|
210
|
+
opacity: 0;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
@media (prefers-reduced-motion: reduce) {
|
|
214
|
+
.entering,
|
|
215
|
+
.leaving {
|
|
216
|
+
animation: none;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
`,
|
|
220
|
+
]; }
|
|
221
|
+
/** Starts observers and autoplay integration when the element connects. */
|
|
222
|
+
connectedCallback() {
|
|
223
|
+
super.connectedCallback();
|
|
224
|
+
this._metadataObserver ??= new MutationObserver(() => this._handleMetadataChange());
|
|
225
|
+
this._metadataObserver.observe(this, {
|
|
226
|
+
attributes: true,
|
|
227
|
+
attributeFilter: ["alt", "caption", "media", "src", "srcset"],
|
|
228
|
+
childList: true,
|
|
229
|
+
subtree: true,
|
|
230
|
+
});
|
|
231
|
+
document.addEventListener("visibilitychange", this._handleVisibilityChange);
|
|
232
|
+
}
|
|
233
|
+
/** Releases timers and observers when the element disconnects. */
|
|
234
|
+
disconnectedCallback() {
|
|
235
|
+
this._clearAutoplay();
|
|
236
|
+
this._clearTransition();
|
|
237
|
+
this._metadataObserver?.disconnect();
|
|
238
|
+
document.removeEventListener("visibilitychange", this._handleVisibilityChange);
|
|
239
|
+
super.disconnectedCallback();
|
|
240
|
+
}
|
|
241
|
+
/** Normalizes external index changes before rendering. */
|
|
242
|
+
willUpdate(changed) {
|
|
243
|
+
if (!changed.has("currentIndex"))
|
|
244
|
+
return;
|
|
245
|
+
const normalized = this._normalizeIndex(this.currentIndex);
|
|
246
|
+
if (normalized !== this.currentIndex) {
|
|
247
|
+
this.currentIndex = normalized;
|
|
248
|
+
}
|
|
249
|
+
const previousValue = changed.get("currentIndex");
|
|
250
|
+
if (previousValue === undefined || this._galleryItems.length === 0) {
|
|
251
|
+
this._nextChangeReason = null;
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
const previousIndex = this._normalizeIndex(Number(previousValue));
|
|
255
|
+
if (previousIndex === normalized) {
|
|
256
|
+
this._nextChangeReason = null;
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
const reason = this._nextChangeReason ?? "programmatic";
|
|
260
|
+
this._previousIndex = reason === "items" ? null : previousIndex;
|
|
261
|
+
this._pendingChange = {
|
|
262
|
+
previousIndex,
|
|
263
|
+
currentIndex: normalized,
|
|
264
|
+
item: this._galleryItems[normalized],
|
|
265
|
+
reason,
|
|
266
|
+
};
|
|
267
|
+
this._nextChangeReason = null;
|
|
268
|
+
}
|
|
269
|
+
/** Emits completed changes and keeps autoplay synchronized with properties. */
|
|
270
|
+
updated(changed) {
|
|
271
|
+
if (this._pendingChange) {
|
|
272
|
+
this.dispatchEvent(new CustomEvent("slide-change", {
|
|
273
|
+
bubbles: true,
|
|
274
|
+
composed: true,
|
|
275
|
+
detail: this._pendingChange,
|
|
276
|
+
}));
|
|
277
|
+
this._pendingChange = undefined;
|
|
278
|
+
this._scheduleTransitionCleanup();
|
|
279
|
+
}
|
|
280
|
+
if (changed.has("currentIndex") || changed.has("delay") || changed.has("paused") || this._itemsChanged) {
|
|
281
|
+
this._itemsChanged = false;
|
|
282
|
+
this._restartAutoplay();
|
|
283
|
+
}
|
|
284
|
+
this._activeItem = this._galleryItems[this.currentIndex];
|
|
285
|
+
this._renderedIndex = this.currentIndex;
|
|
286
|
+
}
|
|
287
|
+
/** Wraps any requested index into the current item range. */
|
|
288
|
+
_normalizeIndex(index) {
|
|
289
|
+
const count = this._galleryItems.length;
|
|
290
|
+
if (count === 0 || !Number.isFinite(index))
|
|
291
|
+
return 0;
|
|
292
|
+
return ((Math.trunc(index) % count) + count) % count;
|
|
293
|
+
}
|
|
294
|
+
/** Returns responsive variants declared directly under an item. */
|
|
295
|
+
_variantsFor(item) {
|
|
296
|
+
return Array.from(item.children).filter((child) => child.tagName.toLowerCase() === "gallery-item-variant");
|
|
297
|
+
}
|
|
298
|
+
/** Changes slides through the shared state and event path. */
|
|
299
|
+
_goTo(index, reason) {
|
|
300
|
+
const normalized = this._normalizeIndex(index);
|
|
301
|
+
if (this._galleryItems.length < 2 || normalized === this.currentIndex) {
|
|
302
|
+
this._restartAutoplay();
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
this._nextChangeReason = reason;
|
|
306
|
+
this.currentIndex = normalized;
|
|
307
|
+
}
|
|
308
|
+
/** Moves to the previous image. */
|
|
309
|
+
_showPrevious(reason = "previous") {
|
|
310
|
+
this._goTo(this.currentIndex - 1, reason);
|
|
311
|
+
}
|
|
312
|
+
/** Moves to the next image. */
|
|
313
|
+
_showNext(reason = "next") {
|
|
314
|
+
this._goTo(this.currentIndex + 1, reason);
|
|
315
|
+
}
|
|
316
|
+
/** Toggles explicit autoplay pause state. */
|
|
317
|
+
_togglePaused() {
|
|
318
|
+
this.paused = !this.paused;
|
|
319
|
+
}
|
|
320
|
+
/** Clears and conditionally schedules the next autoplay advance. */
|
|
321
|
+
_restartAutoplay() {
|
|
322
|
+
this._clearAutoplay();
|
|
323
|
+
if (!this._canAutoplay())
|
|
324
|
+
return;
|
|
325
|
+
this._autoplayTimer = window.setTimeout(() => {
|
|
326
|
+
this._showNext("autoplay");
|
|
327
|
+
}, this.delay);
|
|
328
|
+
}
|
|
329
|
+
/** Reports whether autoplay may currently schedule a transition. */
|
|
330
|
+
_canAutoplay() {
|
|
331
|
+
return (this.delay > 0 &&
|
|
332
|
+
this._galleryItems.length > 1 &&
|
|
333
|
+
!this.paused &&
|
|
334
|
+
!this._hovered &&
|
|
335
|
+
!this._focusWithin &&
|
|
336
|
+
!document.hidden);
|
|
337
|
+
}
|
|
338
|
+
/** Clears the pending autoplay timeout. */
|
|
339
|
+
_clearAutoplay() {
|
|
340
|
+
if (this._autoplayTimer === undefined)
|
|
341
|
+
return;
|
|
342
|
+
window.clearTimeout(this._autoplayTimer);
|
|
343
|
+
this._autoplayTimer = undefined;
|
|
344
|
+
}
|
|
345
|
+
/** Removes the outgoing crossfade layer after the animation completes. */
|
|
346
|
+
_scheduleTransitionCleanup() {
|
|
347
|
+
this._clearTransition();
|
|
348
|
+
this._transitionTimer = window.setTimeout(() => {
|
|
349
|
+
this._previousIndex = null;
|
|
350
|
+
this._transitionTimer = undefined;
|
|
351
|
+
}, transitionDurationMs);
|
|
352
|
+
}
|
|
353
|
+
/** Clears pending transition cleanup. */
|
|
354
|
+
_clearTransition() {
|
|
355
|
+
if (this._transitionTimer === undefined)
|
|
356
|
+
return;
|
|
357
|
+
window.clearTimeout(this._transitionTimer);
|
|
358
|
+
this._transitionTimer = undefined;
|
|
359
|
+
}
|
|
360
|
+
/** Refreshes item metadata and clamps the active index after light-DOM changes. */
|
|
361
|
+
_handleMetadataChange() {
|
|
362
|
+
this._itemsChanged = true;
|
|
363
|
+
this._itemsVersion += 1;
|
|
364
|
+
const preserveActiveItem = this.currentIndex === this._renderedIndex;
|
|
365
|
+
const preservedIndex = preserveActiveItem && this._activeItem ? this._galleryItems.indexOf(this._activeItem) : -1;
|
|
366
|
+
const normalized = preservedIndex >= 0 ? preservedIndex : this._normalizeIndex(this.currentIndex);
|
|
367
|
+
if (normalized === this.currentIndex) {
|
|
368
|
+
const item = this._galleryItems[normalized];
|
|
369
|
+
const activeItemRemoved = Boolean(this._activeItem && !this._galleryItems.includes(this._activeItem));
|
|
370
|
+
if (item && activeItemRemoved) {
|
|
371
|
+
this._previousIndex = null;
|
|
372
|
+
this._pendingChange = {
|
|
373
|
+
previousIndex: this._renderedIndex,
|
|
374
|
+
currentIndex: normalized,
|
|
375
|
+
item,
|
|
376
|
+
reason: "items",
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
this._nextChangeReason = "items";
|
|
382
|
+
this.currentIndex = normalized;
|
|
383
|
+
}
|
|
384
|
+
/** Refreshes item state after the default slot changes. */
|
|
385
|
+
_handleSlotChange() {
|
|
386
|
+
this._handleMetadataChange();
|
|
387
|
+
}
|
|
388
|
+
/** Temporarily pauses autoplay while the pointer is over the gallery. */
|
|
389
|
+
_handleMouseEnter() {
|
|
390
|
+
this._hovered = true;
|
|
391
|
+
this._restartAutoplay();
|
|
392
|
+
}
|
|
393
|
+
/** Resumes autoplay with a fresh delay after hover ends. */
|
|
394
|
+
_handleMouseLeave() {
|
|
395
|
+
this._hovered = false;
|
|
396
|
+
this._restartAutoplay();
|
|
397
|
+
}
|
|
398
|
+
/** Temporarily pauses autoplay while focus is inside the gallery. */
|
|
399
|
+
_handleFocusIn() {
|
|
400
|
+
this._focusWithin = true;
|
|
401
|
+
this._restartAutoplay();
|
|
402
|
+
}
|
|
403
|
+
/** Resumes autoplay with a fresh delay after focus leaves the gallery. */
|
|
404
|
+
_handleFocusOut() {
|
|
405
|
+
queueMicrotask(() => {
|
|
406
|
+
this._focusWithin = this.matches(":focus-within");
|
|
407
|
+
this._restartAutoplay();
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
/** Handles Left and Right arrow keyboard navigation. */
|
|
411
|
+
_handleKeyDown(event) {
|
|
412
|
+
if (event.key === "ArrowLeft") {
|
|
413
|
+
event.preventDefault();
|
|
414
|
+
this._showPrevious("keyboard");
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
if (event.key === "ArrowRight") {
|
|
418
|
+
event.preventDefault();
|
|
419
|
+
this._showNext("keyboard");
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
/** Records the starting point of a primary pointer gesture. */
|
|
423
|
+
_handlePointerDown(event) {
|
|
424
|
+
if (!event.isPrimary || event.button !== 0)
|
|
425
|
+
return;
|
|
426
|
+
if (event.composedPath().some((target) => target instanceof HTMLButtonElement))
|
|
427
|
+
return;
|
|
428
|
+
this._pointerStart = { id: event.pointerId, x: event.clientX, y: event.clientY };
|
|
429
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
430
|
+
}
|
|
431
|
+
/** Converts a sufficiently horizontal pointer gesture into navigation. */
|
|
432
|
+
_handlePointerUp(event) {
|
|
433
|
+
const start = this._pointerStart;
|
|
434
|
+
this._pointerStart = undefined;
|
|
435
|
+
if (!start || start.id !== event.pointerId)
|
|
436
|
+
return;
|
|
437
|
+
const deltaX = event.clientX - start.x;
|
|
438
|
+
const deltaY = event.clientY - start.y;
|
|
439
|
+
if (Math.abs(deltaX) < swipeThresholdPx || Math.abs(deltaX) <= Math.abs(deltaY))
|
|
440
|
+
return;
|
|
441
|
+
event.preventDefault();
|
|
442
|
+
if (deltaX < 0) {
|
|
443
|
+
this._showNext("swipe");
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
this._showPrevious("swipe");
|
|
447
|
+
}
|
|
448
|
+
/** Cancels an incomplete pointer gesture. */
|
|
449
|
+
_handlePointerCancel() {
|
|
450
|
+
this._pointerStart = undefined;
|
|
451
|
+
}
|
|
452
|
+
/** Renders one responsive image layer for the crossfade stack. */
|
|
453
|
+
_renderPicture(index, state) {
|
|
454
|
+
const item = this._galleryItems[index];
|
|
455
|
+
if (!item)
|
|
456
|
+
return nothing;
|
|
457
|
+
const isPrevious = state === "previous";
|
|
458
|
+
const transitionClass = this._previousIndex === null ? "" : isPrevious ? "leaving" : "entering";
|
|
459
|
+
return html `
|
|
460
|
+
<picture class=${transitionClass} aria-hidden=${isPrevious ? "true" : nothing}>
|
|
461
|
+
${this._variantsFor(item)
|
|
462
|
+
.filter((variant) => variant.srcset)
|
|
463
|
+
.map((variant) => html `<source media=${ifDefined(variant.media || undefined)} srcset=${variant.srcset} />`)}
|
|
464
|
+
<img src=${item.src} alt=${isPrevious ? "" : item.alt} draggable="false" />
|
|
465
|
+
</picture>
|
|
466
|
+
`;
|
|
467
|
+
}
|
|
468
|
+
/** Renders enabled counter, indicator, and autoplay controls. */
|
|
469
|
+
_renderFooter(itemCount) {
|
|
470
|
+
const showIndicators = this.showIndicators && itemCount > 1;
|
|
471
|
+
const showAutoplay = this.showAutoplayControl && this.delay > 0 && itemCount > 1;
|
|
472
|
+
if (!this.showCounter && !showIndicators && !showAutoplay)
|
|
473
|
+
return nothing;
|
|
474
|
+
return html `
|
|
475
|
+
<div class="footer">
|
|
476
|
+
${this.showCounter
|
|
477
|
+
? html `<span class="counter">${this.currentIndex + 1} / ${itemCount}</span>`
|
|
478
|
+
: nothing}
|
|
479
|
+
${showIndicators
|
|
480
|
+
? html `
|
|
481
|
+
<div class="indicators" role="group" aria-label="Choose image">
|
|
482
|
+
${this._galleryItems.map((_item, index) => html `
|
|
483
|
+
<button
|
|
484
|
+
class="indicator"
|
|
485
|
+
type="button"
|
|
486
|
+
aria-label="Show image ${index + 1} of ${itemCount}"
|
|
487
|
+
aria-current=${String(index === this.currentIndex)}
|
|
488
|
+
@click=${() => this._goTo(index, "indicator")}
|
|
489
|
+
></button>
|
|
490
|
+
`)}
|
|
491
|
+
</div>
|
|
492
|
+
`
|
|
493
|
+
: nothing}
|
|
494
|
+
${showAutoplay
|
|
495
|
+
? html `
|
|
496
|
+
<button
|
|
497
|
+
class="autoplay-button"
|
|
498
|
+
type="button"
|
|
499
|
+
aria-label=${this.paused ? "Resume slideshow" : "Pause slideshow"}
|
|
500
|
+
@click=${this._togglePaused}
|
|
501
|
+
>
|
|
502
|
+
${this.paused ? "Play" : "Pause"}
|
|
503
|
+
</button>
|
|
504
|
+
`
|
|
505
|
+
: nothing}
|
|
506
|
+
</div>
|
|
507
|
+
`;
|
|
508
|
+
}
|
|
509
|
+
/** Renders the active image, navigation, caption, and hidden metadata slot. */
|
|
510
|
+
render() {
|
|
511
|
+
const itemCount = this._galleryItems.length;
|
|
512
|
+
const currentItem = this._galleryItems[this.currentIndex];
|
|
513
|
+
const objectFit = this.objectFit === "contain" ? "contain" : "cover";
|
|
514
|
+
const viewportStyles = {
|
|
515
|
+
"--photo-gallery-aspect-ratio": this.aspectRatio.trim() || "16 / 9",
|
|
516
|
+
"--photo-gallery-object-fit": objectFit,
|
|
517
|
+
};
|
|
518
|
+
return html `
|
|
519
|
+
<div
|
|
520
|
+
class="gallery"
|
|
521
|
+
role="region"
|
|
522
|
+
aria-roledescription="carousel"
|
|
523
|
+
aria-label="Photo gallery"
|
|
524
|
+
tabindex="0"
|
|
525
|
+
@keydown=${this._handleKeyDown}
|
|
526
|
+
@mouseenter=${this._handleMouseEnter}
|
|
527
|
+
@mouseleave=${this._handleMouseLeave}
|
|
528
|
+
@focusin=${this._handleFocusIn}
|
|
529
|
+
@focusout=${this._handleFocusOut}
|
|
530
|
+
>
|
|
531
|
+
${currentItem
|
|
532
|
+
? html `
|
|
533
|
+
<figure>
|
|
534
|
+
<div
|
|
535
|
+
class="viewport"
|
|
536
|
+
style=${styleMap(viewportStyles)}
|
|
537
|
+
@pointerdown=${this._handlePointerDown}
|
|
538
|
+
@pointerup=${this._handlePointerUp}
|
|
539
|
+
@pointercancel=${this._handlePointerCancel}
|
|
540
|
+
>
|
|
541
|
+
<div class="slides">
|
|
542
|
+
${this._previousIndex === null
|
|
543
|
+
? nothing
|
|
544
|
+
: this._renderPicture(this._previousIndex, "previous")}
|
|
545
|
+
${this._renderPicture(this.currentIndex, "current")}
|
|
546
|
+
</div>
|
|
547
|
+
${this.showControls && itemCount > 1
|
|
548
|
+
? html `
|
|
549
|
+
<div class="arrow-controls">
|
|
550
|
+
<button
|
|
551
|
+
class="arrow-button previous"
|
|
552
|
+
type="button"
|
|
553
|
+
aria-label="Previous image"
|
|
554
|
+
@click=${() => this._showPrevious()}
|
|
555
|
+
>
|
|
556
|
+
${iconChevronLeft(24)}
|
|
557
|
+
</button>
|
|
558
|
+
<button
|
|
559
|
+
class="arrow-button next"
|
|
560
|
+
type="button"
|
|
561
|
+
aria-label="Next image"
|
|
562
|
+
@click=${() => this._showNext()}
|
|
563
|
+
>
|
|
564
|
+
${iconChevronRight(24)}
|
|
565
|
+
</button>
|
|
566
|
+
</div>
|
|
567
|
+
`
|
|
568
|
+
: nothing}
|
|
569
|
+
</div>
|
|
570
|
+
${currentItem.caption ? html `<figcaption>${currentItem.caption}</figcaption>` : nothing}
|
|
571
|
+
</figure>
|
|
572
|
+
${this._renderFooter(itemCount)}
|
|
573
|
+
`
|
|
574
|
+
: nothing}
|
|
575
|
+
<slot @slotchange=${this._handleSlotChange}></slot>
|
|
576
|
+
</div>
|
|
577
|
+
`;
|
|
578
|
+
}
|
|
579
|
+
};
|
|
580
|
+
__decorate([
|
|
581
|
+
property({ type: Number, attribute: "current-index", reflect: true })
|
|
582
|
+
], PhotoGallery.prototype, "currentIndex", void 0);
|
|
583
|
+
__decorate([
|
|
584
|
+
property({ type: Number })
|
|
585
|
+
], PhotoGallery.prototype, "delay", void 0);
|
|
586
|
+
__decorate([
|
|
587
|
+
property({ attribute: "show-controls", converter: booleanAttribute })
|
|
588
|
+
], PhotoGallery.prototype, "showControls", void 0);
|
|
589
|
+
__decorate([
|
|
590
|
+
property({ type: Boolean, attribute: "show-counter" })
|
|
591
|
+
], PhotoGallery.prototype, "showCounter", void 0);
|
|
592
|
+
__decorate([
|
|
593
|
+
property({ type: Boolean, attribute: "show-indicators" })
|
|
594
|
+
], PhotoGallery.prototype, "showIndicators", void 0);
|
|
595
|
+
__decorate([
|
|
596
|
+
property({ attribute: "show-autoplay-control", converter: booleanAttribute })
|
|
597
|
+
], PhotoGallery.prototype, "showAutoplayControl", void 0);
|
|
598
|
+
__decorate([
|
|
599
|
+
property({ type: Boolean, reflect: true })
|
|
600
|
+
], PhotoGallery.prototype, "paused", void 0);
|
|
601
|
+
__decorate([
|
|
602
|
+
property({ attribute: "aspect-ratio" })
|
|
603
|
+
], PhotoGallery.prototype, "aspectRatio", void 0);
|
|
604
|
+
__decorate([
|
|
605
|
+
property({ attribute: "object-fit" })
|
|
606
|
+
], PhotoGallery.prototype, "objectFit", void 0);
|
|
607
|
+
__decorate([
|
|
608
|
+
queryAssignedElements({ selector: "gallery-item" })
|
|
609
|
+
], PhotoGallery.prototype, "_galleryItems", void 0);
|
|
610
|
+
__decorate([
|
|
611
|
+
state()
|
|
612
|
+
], PhotoGallery.prototype, "_previousIndex", void 0);
|
|
613
|
+
__decorate([
|
|
614
|
+
state()
|
|
615
|
+
], PhotoGallery.prototype, "_itemsVersion", void 0);
|
|
616
|
+
PhotoGallery = __decorate([
|
|
617
|
+
customElement("photo-gallery")
|
|
618
|
+
], PhotoGallery);
|
|
619
|
+
export { PhotoGallery };
|
|
620
|
+
//# sourceMappingURL=photo-gallery.js.map
|