@juspay/svelte-ui-components 2.133.0 → 2.134.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 +30 -0
- package/dist/Checkbox/Checkbox.svelte +3 -1
- package/dist/Checkbox/properties.d.ts +5 -0
- package/dist/Draggable/Draggable.svelte +175 -0
- package/dist/Draggable/Draggable.svelte.d.ts +4 -0
- package/dist/Draggable/properties.d.ts +26 -0
- package/dist/Draggable/properties.js +1 -0
- package/dist/Gallery/Gallery.svelte +593 -0
- package/dist/Gallery/Gallery.svelte.d.ts +4 -0
- package/dist/Gallery/properties.d.ts +39 -0
- package/dist/Gallery/properties.js +1 -0
- package/dist/Input/Input.svelte +17 -4
- package/dist/Input/properties.d.ts +6 -0
- package/dist/MediaPlayer/MediaPlayer.svelte +277 -0
- package/dist/MediaPlayer/MediaPlayer.svelte.d.ts +4 -0
- package/dist/MediaPlayer/properties.d.ts +34 -0
- package/dist/MediaPlayer/properties.js +1 -0
- package/dist/MediaUpload/MediaUpload.svelte +528 -0
- package/dist/MediaUpload/MediaUpload.svelte.d.ts +4 -0
- package/dist/MediaUpload/properties.d.ts +46 -0
- package/dist/MediaUpload/properties.js +1 -0
- package/dist/Menu/Menu.svelte +2 -0
- package/dist/Menu/properties.d.ts +5 -0
- package/dist/Modal/Modal.svelte +17 -3
- package/dist/Modal/properties.d.ts +4 -0
- package/dist/assets/add.svg +4 -0
- package/dist/assets/delete.svg +7 -0
- package/dist/assets/edit.svg +4 -0
- package/dist/assets/file.svg +4 -0
- package/dist/assets/mute.svg +5 -0
- package/dist/assets/pause.svg +4 -0
- package/dist/assets/play.svg +3 -0
- package/dist/assets/volume.svg +5 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +4 -0
- package/dist-wc/index.d.ts +1 -0
- package/dist-wc/index.js +33018 -0
- package/package.json +16 -9
|
@@ -0,0 +1,593 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { tick } from 'svelte';
|
|
3
|
+
import type { Snippet } from 'svelte';
|
|
4
|
+
import { fade } from 'svelte/transition';
|
|
5
|
+
import type { GalleryImage, GalleryProperties } from './properties';
|
|
6
|
+
import Img from '../Img/Img.svelte';
|
|
7
|
+
import Button from '../Button/Button.svelte';
|
|
8
|
+
import Icon from '../Icon/Icon.svelte';
|
|
9
|
+
import closeSvg from '../assets/close.svg?raw';
|
|
10
|
+
import chevronLeftSvg from '../assets/chevron-left-lg.svg?raw';
|
|
11
|
+
import chevronRightSvg from '../assets/chevron-right-lg.svg?raw';
|
|
12
|
+
import editSvg from '../assets/edit.svg?raw';
|
|
13
|
+
import deleteSvg from '../assets/delete.svg?raw';
|
|
14
|
+
|
|
15
|
+
let {
|
|
16
|
+
images,
|
|
17
|
+
view = 'grid',
|
|
18
|
+
open = $bindable(false),
|
|
19
|
+
activeIndex = $bindable(0),
|
|
20
|
+
enableLightbox = true,
|
|
21
|
+
loop = false,
|
|
22
|
+
showCounter = true,
|
|
23
|
+
showCaption = true,
|
|
24
|
+
previousIcon,
|
|
25
|
+
nextIcon,
|
|
26
|
+
closeIcon,
|
|
27
|
+
editIcon,
|
|
28
|
+
deleteIcon,
|
|
29
|
+
itemFooter,
|
|
30
|
+
testId,
|
|
31
|
+
onImageClick,
|
|
32
|
+
onEditClick,
|
|
33
|
+
onDeleteClick,
|
|
34
|
+
onOpen,
|
|
35
|
+
onDismiss,
|
|
36
|
+
onIndexChange,
|
|
37
|
+
onkeydown,
|
|
38
|
+
classes
|
|
39
|
+
}: GalleryProperties = $props();
|
|
40
|
+
|
|
41
|
+
let lightboxDiv: HTMLDivElement | null = $state(null);
|
|
42
|
+
let closeButtonWrap: HTMLDivElement | null = $state(null);
|
|
43
|
+
let previousButtonWrap: HTMLDivElement | null = $state(null);
|
|
44
|
+
let nextButtonWrap: HTMLDivElement | null = $state(null);
|
|
45
|
+
let openerElement: HTMLElement | null = null;
|
|
46
|
+
|
|
47
|
+
let activeImage = $derived(images.at(activeIndex));
|
|
48
|
+
let hasPrevious = $derived(loop ? images.length > 1 : activeIndex > 0);
|
|
49
|
+
let hasNext = $derived(loop ? images.length > 1 : activeIndex < images.length - 1);
|
|
50
|
+
let itemsAreInteractive = $derived(enableLightbox || typeof onImageClick === 'function');
|
|
51
|
+
let hasItemFooter = $derived(view === 'grid' && typeof itemFooter === 'function');
|
|
52
|
+
let showEditButton = $derived(typeof onEditClick === 'function');
|
|
53
|
+
let showDeleteButton = $derived(typeof onDeleteClick === 'function');
|
|
54
|
+
let showItemActions = $derived(showEditButton || showDeleteButton);
|
|
55
|
+
|
|
56
|
+
function lightboxAction(node: HTMLElement) {
|
|
57
|
+
if (openerElement === null && document.activeElement instanceof HTMLElement) {
|
|
58
|
+
openerElement = document.activeElement;
|
|
59
|
+
}
|
|
60
|
+
document.body.style.overflow = 'hidden';
|
|
61
|
+
tick().then(() => {
|
|
62
|
+
node.focus();
|
|
63
|
+
});
|
|
64
|
+
return {
|
|
65
|
+
destroy() {
|
|
66
|
+
document.body.style.overflow = '';
|
|
67
|
+
if (openerElement !== null) {
|
|
68
|
+
openerElement.focus();
|
|
69
|
+
openerElement = null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function openLightbox(index: number, opener: EventTarget | null): void {
|
|
76
|
+
if (opener instanceof HTMLElement) {
|
|
77
|
+
openerElement = opener;
|
|
78
|
+
}
|
|
79
|
+
activeIndex = index;
|
|
80
|
+
open = true;
|
|
81
|
+
onOpen?.(index);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function closeLightbox(): void {
|
|
85
|
+
open = false;
|
|
86
|
+
onDismiss?.();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function goToIndex(index: number): Promise<void> {
|
|
90
|
+
if (index < 0 || index >= images.length) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
activeIndex = index;
|
|
94
|
+
onIndexChange?.(activeIndex);
|
|
95
|
+
await tick();
|
|
96
|
+
if (lightboxDiv !== null && !lightboxDiv.contains(document.activeElement)) {
|
|
97
|
+
lightboxDiv.focus();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function showPrevious(): void {
|
|
102
|
+
if (activeIndex > 0) {
|
|
103
|
+
goToIndex(activeIndex - 1);
|
|
104
|
+
} else if (loop && images.length > 1) {
|
|
105
|
+
goToIndex(images.length - 1);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function showNext(): void {
|
|
110
|
+
if (activeIndex < images.length - 1) {
|
|
111
|
+
goToIndex(activeIndex + 1);
|
|
112
|
+
} else if (loop && images.length > 1) {
|
|
113
|
+
goToIndex(0);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function handleImageClick(index: number, event: MouseEvent): void {
|
|
118
|
+
onImageClick?.(index, event);
|
|
119
|
+
if (enableLightbox) {
|
|
120
|
+
openLightbox(index, event.currentTarget);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function handleEditClick(index: number, event: MouseEvent): void {
|
|
125
|
+
onEditClick?.(index, event);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function handleDeleteClick(index: number, event: MouseEvent): void {
|
|
129
|
+
onDeleteClick?.(index, event);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function trapFocus(event: KeyboardEvent): void {
|
|
133
|
+
const first = closeButtonWrap?.querySelector('button') ?? null;
|
|
134
|
+
const last =
|
|
135
|
+
(nextButtonWrap ?? previousButtonWrap ?? closeButtonWrap)?.querySelector('button') ?? null;
|
|
136
|
+
if (first === null || last === null) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (
|
|
140
|
+
event.shiftKey &&
|
|
141
|
+
(document.activeElement === first || document.activeElement === lightboxDiv)
|
|
142
|
+
) {
|
|
143
|
+
event.preventDefault();
|
|
144
|
+
last.focus();
|
|
145
|
+
} else if (!event.shiftKey && document.activeElement === last) {
|
|
146
|
+
event.preventDefault();
|
|
147
|
+
first.focus();
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function handleLightboxKeydown(event: KeyboardEvent): void {
|
|
152
|
+
onkeydown?.(event);
|
|
153
|
+
if (event.key === 'Escape') {
|
|
154
|
+
closeLightbox();
|
|
155
|
+
} else if (event.key === 'ArrowLeft') {
|
|
156
|
+
event.preventDefault();
|
|
157
|
+
showPrevious();
|
|
158
|
+
} else if (event.key === 'ArrowRight') {
|
|
159
|
+
event.preventDefault();
|
|
160
|
+
showNext();
|
|
161
|
+
} else if (event.key === 'Home') {
|
|
162
|
+
event.preventDefault();
|
|
163
|
+
goToIndex(0);
|
|
164
|
+
} else if (event.key === 'End') {
|
|
165
|
+
event.preventDefault();
|
|
166
|
+
goToIndex(images.length - 1);
|
|
167
|
+
} else if (event.key === 'Tab') {
|
|
168
|
+
trapFocus(event);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function handleBackdropClick(event: MouseEvent): void {
|
|
173
|
+
if (event.target === lightboxDiv) {
|
|
174
|
+
closeLightbox();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
</script>
|
|
178
|
+
|
|
179
|
+
{#snippet controlIcon(fallbackMarkup: string, custom?: Snippet)}
|
|
180
|
+
{#if typeof custom === 'function'}
|
|
181
|
+
{@render custom()}
|
|
182
|
+
{:else}
|
|
183
|
+
<Icon svg={fallbackMarkup} />
|
|
184
|
+
{/if}
|
|
185
|
+
{/snippet}
|
|
186
|
+
|
|
187
|
+
{#snippet itemContent(image: GalleryImage, index: number)}
|
|
188
|
+
{#if hasItemFooter}
|
|
189
|
+
<span class="grid-image-wrap">
|
|
190
|
+
<Img src={image.thumbnail ?? image.src} alt={image.alt} fallback={image.fallback} />
|
|
191
|
+
</span>
|
|
192
|
+
{#if typeof itemFooter === 'function'}
|
|
193
|
+
{@render itemFooter(image, index)}
|
|
194
|
+
{/if}
|
|
195
|
+
{:else}
|
|
196
|
+
<Img src={image.thumbnail ?? image.src} alt={image.alt} fallback={image.fallback} />
|
|
197
|
+
{#if view === 'list'}
|
|
198
|
+
<span class="list-text">
|
|
199
|
+
<span class="list-title">{image.alt}</span>
|
|
200
|
+
{#if typeof image.caption === 'string' && image.caption.length > 0}
|
|
201
|
+
<span class="list-caption">{image.caption}</span>
|
|
202
|
+
{/if}
|
|
203
|
+
</span>
|
|
204
|
+
{/if}
|
|
205
|
+
{/if}
|
|
206
|
+
{/snippet}
|
|
207
|
+
|
|
208
|
+
<div
|
|
209
|
+
class="gallery {view} {hasItemFooter ? 'has-item-footer' : ''} {classes ?? ''}"
|
|
210
|
+
data-pw={typeof testId === 'string' ? testId : null}
|
|
211
|
+
role="list"
|
|
212
|
+
>
|
|
213
|
+
{#each images as image, index (index)}
|
|
214
|
+
<div class="gallery-item" role="listitem">
|
|
215
|
+
{#if itemsAreInteractive}
|
|
216
|
+
<button
|
|
217
|
+
class="gallery-item-content"
|
|
218
|
+
onclick={(event) => handleImageClick(index, event)}
|
|
219
|
+
aria-label={enableLightbox
|
|
220
|
+
? `View image ${index + 1} of ${images.length}: ${image.alt}`
|
|
221
|
+
: image.alt}
|
|
222
|
+
>
|
|
223
|
+
{@render itemContent(image, index)}
|
|
224
|
+
</button>
|
|
225
|
+
{:else}
|
|
226
|
+
<div class="gallery-item-content">
|
|
227
|
+
{@render itemContent(image, index)}
|
|
228
|
+
</div>
|
|
229
|
+
{/if}
|
|
230
|
+
{#if showItemActions}
|
|
231
|
+
<div class="gallery-item-actions">
|
|
232
|
+
{#if showEditButton}
|
|
233
|
+
<div class="gallery-item-action">
|
|
234
|
+
<Button
|
|
235
|
+
ariaLabel={`Edit image ${index + 1}: ${image.alt}`}
|
|
236
|
+
onclick={(event) => handleEditClick(index, event)}
|
|
237
|
+
>
|
|
238
|
+
{@render controlIcon(editSvg, editIcon)}
|
|
239
|
+
</Button>
|
|
240
|
+
</div>
|
|
241
|
+
{/if}
|
|
242
|
+
{#if showDeleteButton}
|
|
243
|
+
<div class="gallery-item-action">
|
|
244
|
+
<Button
|
|
245
|
+
ariaLabel={`Delete image ${index + 1}: ${image.alt}`}
|
|
246
|
+
onclick={(event) => handleDeleteClick(index, event)}
|
|
247
|
+
>
|
|
248
|
+
{@render controlIcon(deleteSvg, deleteIcon)}
|
|
249
|
+
</Button>
|
|
250
|
+
</div>
|
|
251
|
+
{/if}
|
|
252
|
+
</div>
|
|
253
|
+
{/if}
|
|
254
|
+
</div>
|
|
255
|
+
{/each}
|
|
256
|
+
</div>
|
|
257
|
+
|
|
258
|
+
{#if open}
|
|
259
|
+
<div
|
|
260
|
+
class="lightbox {classes ?? ''}"
|
|
261
|
+
role="dialog"
|
|
262
|
+
aria-modal="true"
|
|
263
|
+
aria-label={typeof activeImage === 'object' ? activeImage.alt : 'Image gallery'}
|
|
264
|
+
tabindex="-1"
|
|
265
|
+
bind:this={lightboxDiv}
|
|
266
|
+
use:lightboxAction
|
|
267
|
+
onclick={handleBackdropClick}
|
|
268
|
+
onkeydown={handleLightboxKeydown}
|
|
269
|
+
transition:fade={{ duration: 200 }}
|
|
270
|
+
>
|
|
271
|
+
<div class="lightbox-close" bind:this={closeButtonWrap}>
|
|
272
|
+
<Button ariaLabel="Close gallery" onclick={closeLightbox}>
|
|
273
|
+
{@render controlIcon(closeSvg, closeIcon)}
|
|
274
|
+
</Button>
|
|
275
|
+
</div>
|
|
276
|
+
{#if hasPrevious}
|
|
277
|
+
<div class="lightbox-previous" bind:this={previousButtonWrap}>
|
|
278
|
+
<Button ariaLabel="Previous image" onclick={showPrevious}>
|
|
279
|
+
{@render controlIcon(chevronLeftSvg, previousIcon)}
|
|
280
|
+
</Button>
|
|
281
|
+
</div>
|
|
282
|
+
{/if}
|
|
283
|
+
{#if typeof activeImage === 'object'}
|
|
284
|
+
<figure class="lightbox-figure">
|
|
285
|
+
<Img src={activeImage.src} alt={activeImage.alt} fallback={activeImage.fallback} />
|
|
286
|
+
{#if showCaption && typeof activeImage.caption === 'string' && activeImage.caption.length > 0}
|
|
287
|
+
<figcaption class="lightbox-caption">{activeImage.caption}</figcaption>
|
|
288
|
+
{/if}
|
|
289
|
+
</figure>
|
|
290
|
+
{/if}
|
|
291
|
+
{#if hasNext}
|
|
292
|
+
<div class="lightbox-next" bind:this={nextButtonWrap}>
|
|
293
|
+
<Button ariaLabel="Next image" onclick={showNext}>
|
|
294
|
+
{@render controlIcon(chevronRightSvg, nextIcon)}
|
|
295
|
+
</Button>
|
|
296
|
+
</div>
|
|
297
|
+
{/if}
|
|
298
|
+
{#if showCounter && typeof activeImage === 'object'}
|
|
299
|
+
<div class="lightbox-counter" aria-live="polite">
|
|
300
|
+
{activeIndex + 1} / {images.length}
|
|
301
|
+
</div>
|
|
302
|
+
{/if}
|
|
303
|
+
</div>
|
|
304
|
+
{/if}
|
|
305
|
+
|
|
306
|
+
<style>
|
|
307
|
+
.gallery {
|
|
308
|
+
width: var(--gallery-width, 100%);
|
|
309
|
+
padding: var(--gallery-padding, 0px);
|
|
310
|
+
margin: var(--gallery-margin, 0px);
|
|
311
|
+
background: var(--gallery-background, transparent);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
.gallery.grid {
|
|
315
|
+
display: grid;
|
|
316
|
+
grid-template-columns: var(
|
|
317
|
+
--gallery-grid-template-columns,
|
|
318
|
+
repeat(var(--gallery-columns, 3), 1fr)
|
|
319
|
+
);
|
|
320
|
+
gap: var(--gallery-gap, 8px);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
.gallery.list {
|
|
324
|
+
display: flex;
|
|
325
|
+
flex-direction: column;
|
|
326
|
+
gap: var(--gallery-gap, 8px);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.gallery-item {
|
|
330
|
+
position: relative;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
.grid .gallery-item {
|
|
334
|
+
aspect-ratio: var(--gallery-item-aspect-ratio, 1);
|
|
335
|
+
border-radius: var(--gallery-item-border-radius, 0px);
|
|
336
|
+
overflow: hidden;
|
|
337
|
+
--image-width: 100%;
|
|
338
|
+
--image-height: 100%;
|
|
339
|
+
--image-object-fit: var(--gallery-item-image-fit, cover);
|
|
340
|
+
--image-border-radius: var(--gallery-item-border-radius, 0px);
|
|
341
|
+
--image-transition: var(--gallery-item-image-transition);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.grid.has-item-footer .gallery-item {
|
|
345
|
+
aspect-ratio: auto;
|
|
346
|
+
overflow: visible;
|
|
347
|
+
border-radius: 0px;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
.list .gallery-item {
|
|
351
|
+
display: flex;
|
|
352
|
+
align-items: center;
|
|
353
|
+
border-radius: var(--gallery-list-item-border-radius, 0px);
|
|
354
|
+
background: var(--gallery-list-item-background, transparent);
|
|
355
|
+
--image-width: var(--gallery-list-thumbnail-width, 56px);
|
|
356
|
+
--image-height: var(--gallery-list-thumbnail-height, 56px);
|
|
357
|
+
--image-object-fit: var(--gallery-list-thumbnail-fit, cover);
|
|
358
|
+
--image-border-radius: var(--gallery-list-thumbnail-border-radius, 0px);
|
|
359
|
+
--image-transition: var(--gallery-item-image-transition);
|
|
360
|
+
--image-flex-shrink: 0;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
.gallery-item-content {
|
|
364
|
+
font: inherit;
|
|
365
|
+
color: inherit;
|
|
366
|
+
border: var(--gallery-item-border, none);
|
|
367
|
+
background: transparent;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
button.gallery-item-content {
|
|
371
|
+
cursor: var(--gallery-item-cursor, pointer);
|
|
372
|
+
transition: var(--gallery-item-transition);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
.grid .gallery-item-content {
|
|
376
|
+
display: block;
|
|
377
|
+
width: 100%;
|
|
378
|
+
height: 100%;
|
|
379
|
+
padding: 0;
|
|
380
|
+
border-radius: var(--gallery-item-border-radius, 0px);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.grid.has-item-footer .gallery-item-content {
|
|
384
|
+
display: flex;
|
|
385
|
+
flex-direction: column;
|
|
386
|
+
height: auto;
|
|
387
|
+
border-radius: var(--gallery-item-border-radius, 0px);
|
|
388
|
+
overflow: hidden;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
.grid-image-wrap {
|
|
392
|
+
display: block;
|
|
393
|
+
aspect-ratio: var(--gallery-item-aspect-ratio, 1);
|
|
394
|
+
overflow: hidden;
|
|
395
|
+
--image-width: 100%;
|
|
396
|
+
--image-height: 100%;
|
|
397
|
+
--image-object-fit: var(--gallery-item-image-fit, cover);
|
|
398
|
+
--image-border-radius: 0px;
|
|
399
|
+
--image-transition: var(--gallery-item-image-transition);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.list .gallery-item-content {
|
|
403
|
+
display: flex;
|
|
404
|
+
align-items: center;
|
|
405
|
+
text-align: left;
|
|
406
|
+
flex: 1;
|
|
407
|
+
min-width: 0;
|
|
408
|
+
gap: var(--gallery-list-item-gap, 12px);
|
|
409
|
+
padding: var(--gallery-list-item-padding, 8px);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
.grid button.gallery-item-content:hover {
|
|
413
|
+
opacity: var(--gallery-item-hover-opacity, 1);
|
|
414
|
+
transform: var(--gallery-item-hover-transform);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
.grid button.gallery-item-content:focus-visible {
|
|
418
|
+
outline: var(--gallery-item-focus-outline, 2px solid currentColor);
|
|
419
|
+
outline-offset: var(--gallery-item-focus-outline-offset, 2px);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
.list button.gallery-item-content:focus-visible {
|
|
423
|
+
outline: none;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
.list .gallery-item:has(button.gallery-item-content):hover {
|
|
427
|
+
background: var(--gallery-list-item-hover-background, transparent);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
.list .gallery-item:has(button.gallery-item-content:focus-visible) {
|
|
431
|
+
outline: var(--gallery-item-focus-outline, 2px solid currentColor);
|
|
432
|
+
outline-offset: var(--gallery-item-focus-outline-offset, 2px);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
.list-text {
|
|
436
|
+
display: flex;
|
|
437
|
+
flex-direction: column;
|
|
438
|
+
gap: var(--gallery-list-text-gap, 2px);
|
|
439
|
+
min-width: 0;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
.list-title,
|
|
443
|
+
.list-caption {
|
|
444
|
+
overflow: hidden;
|
|
445
|
+
text-overflow: ellipsis;
|
|
446
|
+
white-space: nowrap;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
.list-title {
|
|
450
|
+
color: var(--gallery-list-title-color, inherit);
|
|
451
|
+
font-size: var(--gallery-list-title-font-size, 14px);
|
|
452
|
+
font-weight: var(--gallery-list-title-font-weight, 500);
|
|
453
|
+
font-family: var(--gallery-list-title-font-family);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
.list-caption {
|
|
457
|
+
color: var(--gallery-list-caption-color, inherit);
|
|
458
|
+
font-size: var(--gallery-list-caption-font-size, 12px);
|
|
459
|
+
font-family: var(--gallery-list-caption-font-family);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
.gallery-item-actions {
|
|
463
|
+
display: flex;
|
|
464
|
+
align-items: center;
|
|
465
|
+
gap: var(--gallery-item-actions-gap, 4px);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
.grid .gallery-item-actions {
|
|
469
|
+
position: absolute;
|
|
470
|
+
top: var(--gallery-item-actions-top, 8px);
|
|
471
|
+
right: var(--gallery-item-actions-right, 8px);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
.list .gallery-item-actions {
|
|
475
|
+
flex-shrink: 0;
|
|
476
|
+
padding-right: var(--gallery-item-actions-right, 8px);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
.gallery-item-action {
|
|
480
|
+
border-radius: var(--gallery-item-action-border-radius, 8px);
|
|
481
|
+
--button-color: transparent;
|
|
482
|
+
--button-hover-color: transparent;
|
|
483
|
+
--button-padding: var(--gallery-item-action-padding, 6px);
|
|
484
|
+
--button-border-radius: var(--gallery-item-action-border-radius, 8px);
|
|
485
|
+
--icon-container-padding: 0px;
|
|
486
|
+
--icon-padding: 0px;
|
|
487
|
+
--icon-width: var(--gallery-item-action-icon-size, 16px);
|
|
488
|
+
--icon-height: var(--gallery-item-action-icon-size, 16px);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
.grid .gallery-item-action {
|
|
492
|
+
background: var(--gallery-item-action-background, #00000066);
|
|
493
|
+
-webkit-backdrop-filter: var(--gallery-item-action-backdrop-filter, blur(8px));
|
|
494
|
+
backdrop-filter: var(--gallery-item-action-backdrop-filter, blur(8px));
|
|
495
|
+
--button-text-color: var(--gallery-item-action-color, #ffffff);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
.grid .gallery-item-action:hover {
|
|
499
|
+
background: var(--gallery-item-action-hover-background, #00000099);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
.list .gallery-item-action {
|
|
503
|
+
background: var(--gallery-item-action-background, transparent);
|
|
504
|
+
--button-text-color: var(--gallery-item-action-color, currentColor);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
.list .gallery-item-action:hover {
|
|
508
|
+
background: var(--gallery-item-action-hover-background, #80808026);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
.lightbox {
|
|
512
|
+
position: fixed;
|
|
513
|
+
top: 0;
|
|
514
|
+
bottom: 0;
|
|
515
|
+
left: 0;
|
|
516
|
+
right: 0;
|
|
517
|
+
z-index: var(--gallery-lightbox-z-index, 15);
|
|
518
|
+
display: flex;
|
|
519
|
+
justify-content: center;
|
|
520
|
+
align-items: center;
|
|
521
|
+
background: var(--gallery-lightbox-background, #000000e6);
|
|
522
|
+
-webkit-tap-highlight-color: transparent;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
.lightbox:focus {
|
|
526
|
+
outline: none;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
.lightbox-figure {
|
|
530
|
+
display: flex;
|
|
531
|
+
flex-direction: column;
|
|
532
|
+
align-items: center;
|
|
533
|
+
gap: var(--gallery-lightbox-caption-gap, 12px);
|
|
534
|
+
margin: 0;
|
|
535
|
+
pointer-events: none;
|
|
536
|
+
--image-width: var(--gallery-lightbox-image-width, 85vw);
|
|
537
|
+
--image-height: var(--gallery-lightbox-image-height, 75vh);
|
|
538
|
+
--image-object-fit: var(--gallery-lightbox-image-fit, contain);
|
|
539
|
+
--image-border-radius: var(--gallery-lightbox-image-border-radius, 0px);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
.lightbox-caption {
|
|
543
|
+
color: var(--gallery-lightbox-caption-color, #ffffff);
|
|
544
|
+
font-size: var(--gallery-lightbox-caption-font-size, 14px);
|
|
545
|
+
font-family: var(--gallery-lightbox-caption-font-family);
|
|
546
|
+
text-align: center;
|
|
547
|
+
max-width: var(--gallery-lightbox-image-width, 85vw);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
.lightbox-counter {
|
|
551
|
+
position: absolute;
|
|
552
|
+
bottom: var(--gallery-lightbox-counter-bottom, 16px);
|
|
553
|
+
left: 50%;
|
|
554
|
+
transform: translateX(-50%);
|
|
555
|
+
color: var(--gallery-lightbox-counter-color, #ffffff);
|
|
556
|
+
font-size: var(--gallery-lightbox-counter-font-size, 13px);
|
|
557
|
+
font-family: var(--gallery-lightbox-counter-font-family);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
.lightbox-close,
|
|
561
|
+
.lightbox-previous,
|
|
562
|
+
.lightbox-next {
|
|
563
|
+
position: absolute;
|
|
564
|
+
--button-color: var(--gallery-lightbox-control-background, transparent);
|
|
565
|
+
--button-text-color: var(--gallery-lightbox-control-color, #ffffff);
|
|
566
|
+
--button-hover-color: var(--gallery-lightbox-control-hover-background, #ffffff1f);
|
|
567
|
+
--button-padding: var(--gallery-lightbox-control-padding, 8px);
|
|
568
|
+
--button-border-radius: var(--gallery-lightbox-control-border-radius, 50%);
|
|
569
|
+
--icon-container-padding: 0px;
|
|
570
|
+
--icon-padding: 0px;
|
|
571
|
+
--icon-width: var(--gallery-lightbox-control-icon-size, 24px);
|
|
572
|
+
--icon-height: var(--gallery-lightbox-control-icon-size, 24px);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
.lightbox-close {
|
|
576
|
+
top: var(--gallery-lightbox-close-top, 16px);
|
|
577
|
+
right: var(--gallery-lightbox-close-right, 16px);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
.lightbox-previous,
|
|
581
|
+
.lightbox-next {
|
|
582
|
+
top: 50%;
|
|
583
|
+
transform: translateY(-50%);
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
.lightbox-previous {
|
|
587
|
+
left: var(--gallery-lightbox-nav-inset, 16px);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
.lightbox-next {
|
|
591
|
+
right: var(--gallery-lightbox-nav-inset, 16px);
|
|
592
|
+
}
|
|
593
|
+
</style>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
export type GalleryView = 'grid' | 'list';
|
|
3
|
+
export type GalleryImage = {
|
|
4
|
+
src: string;
|
|
5
|
+
alt: string;
|
|
6
|
+
thumbnail?: string;
|
|
7
|
+
fallback?: string;
|
|
8
|
+
caption?: string;
|
|
9
|
+
};
|
|
10
|
+
export type GalleryProperties = MandatoryGalleryProperties & OptionalGalleryProperties & GalleryEventProperties;
|
|
11
|
+
export type MandatoryGalleryProperties = {
|
|
12
|
+
images: GalleryImage[];
|
|
13
|
+
};
|
|
14
|
+
export type OptionalGalleryProperties = {
|
|
15
|
+
view?: GalleryView;
|
|
16
|
+
open?: boolean;
|
|
17
|
+
activeIndex?: number;
|
|
18
|
+
enableLightbox?: boolean;
|
|
19
|
+
loop?: boolean;
|
|
20
|
+
showCounter?: boolean;
|
|
21
|
+
showCaption?: boolean;
|
|
22
|
+
previousIcon?: Snippet;
|
|
23
|
+
nextIcon?: Snippet;
|
|
24
|
+
closeIcon?: Snippet;
|
|
25
|
+
editIcon?: Snippet;
|
|
26
|
+
deleteIcon?: Snippet;
|
|
27
|
+
itemFooter?: Snippet<[GalleryImage, number]>;
|
|
28
|
+
testId?: string;
|
|
29
|
+
classes?: string;
|
|
30
|
+
};
|
|
31
|
+
export type GalleryEventProperties = {
|
|
32
|
+
onImageClick?: (index: number, event: MouseEvent) => void;
|
|
33
|
+
onEditClick?: (index: number, event: MouseEvent) => void;
|
|
34
|
+
onDeleteClick?: (index: number, event: MouseEvent) => void;
|
|
35
|
+
onOpen?: (index: number) => void;
|
|
36
|
+
onDismiss?: () => void;
|
|
37
|
+
onIndexChange?: (index: number) => void;
|
|
38
|
+
onkeydown?: (event: KeyboardEvent) => void;
|
|
39
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/Input/Input.svelte
CHANGED
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
autoComplete = 'on',
|
|
27
27
|
inputMode,
|
|
28
28
|
name = '',
|
|
29
|
+
id,
|
|
29
30
|
testId = '',
|
|
30
31
|
textTransformers = [],
|
|
31
32
|
textViewPresentation = [],
|
|
@@ -60,6 +61,16 @@
|
|
|
60
61
|
showCount = false
|
|
61
62
|
}: InputProperties = $props();
|
|
62
63
|
|
|
64
|
+
/* `for` on a <label> resolves against an element's id, never its name. The label
|
|
65
|
+
was emitted with for={name} while the field itself carried only name={name},
|
|
66
|
+
so the association never completed for any caller. Derive the id from name so
|
|
67
|
+
every existing caller that already passes label + name becomes correctly
|
|
68
|
+
labelled with no change on their part; an explicit id wins, for callers who
|
|
69
|
+
need uniqueness across repeated rows or who set no name. */
|
|
70
|
+
const uid = $props.id();
|
|
71
|
+
const effectiveId = $derived(id || (name !== '' ? `${name}-${uid}` : uid));
|
|
72
|
+
const hasVisibleLabel = $derived(typeof label === 'string' && label !== '' && !actionInput);
|
|
73
|
+
|
|
63
74
|
export function focus() {
|
|
64
75
|
try {
|
|
65
76
|
inputElement?.focus();
|
|
@@ -268,8 +279,8 @@
|
|
|
268
279
|
</script>
|
|
269
280
|
|
|
270
281
|
<div class="input-container {classes ?? ''}" class:input-error={showError && !actionInput}>
|
|
271
|
-
{#if
|
|
272
|
-
<label class="label" for={
|
|
282
|
+
{#if hasVisibleLabel}
|
|
283
|
+
<label class="label" for={effectiveId}>
|
|
273
284
|
{label}{#if mandatory}<span class="input-mandatory-asterisk" aria-hidden="true">*</span>{/if}
|
|
274
285
|
</label>
|
|
275
286
|
{/if}
|
|
@@ -277,9 +288,10 @@
|
|
|
277
288
|
{#snippet fieldElement()}
|
|
278
289
|
{#if useTextArea}
|
|
279
290
|
<textarea
|
|
291
|
+
id={effectiveId}
|
|
280
292
|
{value}
|
|
281
293
|
{placeholder}
|
|
282
|
-
aria-label={ariaLabel}
|
|
294
|
+
aria-label={hasVisibleLabel ? null : (ariaLabel ?? null)}
|
|
283
295
|
autocomplete={autoComplete}
|
|
284
296
|
inputmode={inputMode}
|
|
285
297
|
{name}
|
|
@@ -311,10 +323,11 @@
|
|
|
311
323
|
></textarea>
|
|
312
324
|
{:else}
|
|
313
325
|
<input
|
|
326
|
+
id={effectiveId}
|
|
314
327
|
type={dataType}
|
|
315
328
|
{value}
|
|
316
329
|
{placeholder}
|
|
317
|
-
aria-label={ariaLabel}
|
|
330
|
+
aria-label={hasVisibleLabel ? null : (ariaLabel ?? null)}
|
|
318
331
|
autocomplete={autoComplete}
|
|
319
332
|
inputmode={inputMode}
|
|
320
333
|
{name}
|