@classic-homes/theme-svelte 1.2.2 → 1.3.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.
Files changed (55) hide show
  1. package/README.md +43 -0
  2. package/dist/__tests__/a11y/Media.a11y.test.js +145 -0
  3. package/dist/__tests__/media-components.test.js +206 -0
  4. package/dist/__tests__/media-hls.test.js +273 -0
  5. package/dist/__tests__/media-matterport.test.js +296 -0
  6. package/dist/__tests__/media-players.test.js +244 -0
  7. package/dist/__tests__/media-scrubber.test.js +44 -0
  8. package/dist/__tests__/media-utils.test.js +427 -0
  9. package/dist/__tests__/setup.js +50 -0
  10. package/dist/lib/components/DataPanel.svelte +8 -0
  11. package/dist/lib/components/DataPanel.svelte.d.ts +6 -0
  12. package/dist/lib/components/DataPanelTab.svelte +38 -8
  13. package/dist/lib/components/DataPanelTab.svelte.d.ts +7 -0
  14. package/dist/lib/components/FileUpload.svelte +221 -4
  15. package/dist/lib/components/FileUpload.svelte.d.ts +13 -1
  16. package/dist/lib/components/media/AspectRatio.svelte +47 -0
  17. package/dist/lib/components/media/AspectRatio.svelte.d.ts +20 -0
  18. package/dist/lib/components/media/Audio.svelte +247 -0
  19. package/dist/lib/components/media/Audio.svelte.d.ts +34 -0
  20. package/dist/lib/components/media/Image.svelte +209 -0
  21. package/dist/lib/components/media/Image.svelte.d.ts +52 -0
  22. package/dist/lib/components/media/Lightbox.svelte +449 -0
  23. package/dist/lib/components/media/Lightbox.svelte.d.ts +33 -0
  24. package/dist/lib/components/media/MediaCarousel.svelte +359 -0
  25. package/dist/lib/components/media/MediaCarousel.svelte.d.ts +30 -0
  26. package/dist/lib/components/media/MediaEmbed.svelte +314 -0
  27. package/dist/lib/components/media/MediaEmbed.svelte.d.ts +99 -0
  28. package/dist/lib/components/media/MediaGrid.svelte +327 -0
  29. package/dist/lib/components/media/MediaGrid.svelte.d.ts +57 -0
  30. package/dist/lib/components/media/Video.svelte +455 -0
  31. package/dist/lib/components/media/Video.svelte.d.ts +81 -0
  32. package/dist/lib/components/media/index.d.ts +14 -0
  33. package/dist/lib/components/media/index.js +14 -0
  34. package/dist/lib/components/media/internal/MediaControls.svelte +283 -0
  35. package/dist/lib/components/media/internal/MediaControls.svelte.d.ts +32 -0
  36. package/dist/lib/components/media/internal/MediaScrubber.svelte +211 -0
  37. package/dist/lib/components/media/internal/MediaScrubber.svelte.d.ts +27 -0
  38. package/dist/lib/components/media/internal/MediaTile.svelte +129 -0
  39. package/dist/lib/components/media/internal/MediaTile.svelte.d.ts +22 -0
  40. package/dist/lib/composables/index.d.ts +3 -0
  41. package/dist/lib/composables/index.js +4 -0
  42. package/dist/lib/composables/useHlsSource.svelte.d.ts +70 -0
  43. package/dist/lib/composables/useHlsSource.svelte.js +119 -0
  44. package/dist/lib/composables/useMediaPlayer.svelte.d.ts +114 -0
  45. package/dist/lib/composables/useMediaPlayer.svelte.js +422 -0
  46. package/dist/lib/composables/useMediaZoom.svelte.d.ts +59 -0
  47. package/dist/lib/composables/useMediaZoom.svelte.js +199 -0
  48. package/dist/lib/index.d.ts +8 -2
  49. package/dist/lib/index.js +14 -1
  50. package/dist/lib/types/components.d.ts +172 -0
  51. package/dist/lib/utils/hls.d.ts +76 -0
  52. package/dist/lib/utils/hls.js +83 -0
  53. package/dist/lib/utils/media.d.ts +379 -0
  54. package/dist/lib/utils/media.js +715 -0
  55. package/package.json +6 -2
@@ -0,0 +1,455 @@
1
+ <script lang="ts">
2
+ /**
3
+ * Video - Video player with themed controls
4
+ *
5
+ * Features:
6
+ * - Custom control bar: play/pause, seek, volume, speed, captions, PiP, fullscreen
7
+ * - Full keyboard control (Space/K, arrows, J/L, M, F, C, Home/End, 0-9)
8
+ * - Controls auto-hide during playback and stay visible while paused or focused
9
+ * - Buffering spinner and an inline error state with retry
10
+ * - Honours `prefers-reduced-motion` by never fading the controls away
11
+ *
12
+ * @example
13
+ * ```svelte
14
+ * <Video
15
+ * src="/clip.mp4"
16
+ * poster="/clip.jpg"
17
+ * label="Product walkthrough"
18
+ * tracks={[{ src: '/clip.en.vtt', srclang: 'en', label: 'English', default: true }]}
19
+ * />
20
+ * ```
21
+ */
22
+ import type { Snippet } from 'svelte';
23
+ import { useHlsSource } from '../../composables/useHlsSource.svelte.js';
24
+ import { useMediaPlayer } from '../../composables/useMediaPlayer.svelte.js';
25
+ import { useReducedMotion } from '../../composables/useReducedMotion.svelte.js';
26
+ import type {
27
+ MediaAspectRatio,
28
+ MediaFit,
29
+ MediaSource,
30
+ MediaTextTrack,
31
+ VideoMediaItem,
32
+ } from '../../types/components.js';
33
+ import { cn } from '../../utils.js';
34
+ import type { HlsLoader } from '../../utils/hls.js';
35
+ import { isHlsSource, resolveAspectRatio, toMediaSources } from '../../utils/media.js';
36
+ import Icon from '../Icon.svelte';
37
+ import Spinner from '../Spinner.svelte';
38
+ import MediaControls from './internal/MediaControls.svelte';
39
+
40
+ /** How long the controls stay up after the last pointer movement */
41
+ const CONTROLS_HIDE_DELAY = 3000;
42
+
43
+ interface Props {
44
+ /**
45
+ * A video media item supplying `src`, `poster`, `tracks`, and the label.
46
+ * Individual props still win when both are given.
47
+ *
48
+ * Pairs with `cloudflareStream()` to play a Cloudflare Stream video.
49
+ */
50
+ item?: VideoMediaItem;
51
+ /** A single URL, or several sources for format fallback */
52
+ src?: string | MediaSource[];
53
+ /** Poster image shown before playback starts */
54
+ poster?: string;
55
+ /** Caption and subtitle tracks — required for WCAG 1.2.2 on recorded content */
56
+ tracks?: MediaTextTrack[];
57
+ /** Accessible name for the player region */
58
+ label?: string;
59
+ /** Constrain the player to an aspect ratio (default `video`) */
60
+ ratio?: MediaAspectRatio | number | null;
61
+ /** How the video fills its box */
62
+ fit?: MediaFit;
63
+ /** Start playing automatically — requires `muted`, and browsers may still refuse */
64
+ autoplay?: boolean;
65
+ /** Start muted */
66
+ muted?: boolean;
67
+ /** Loop playback */
68
+ loop?: boolean;
69
+ /** Initial volume, 0–1 */
70
+ volume?: number;
71
+ /** How much to preload (default `metadata`, enough for duration without fetching the file) */
72
+ preload?: 'none' | 'metadata' | 'auto';
73
+ /** Hide the control bar entirely — the player is then keyboard-only */
74
+ hideControls?: boolean;
75
+ /** Show the picture-in-picture button */
76
+ showPictureInPicture?: boolean;
77
+ /** Show the fullscreen button */
78
+ showFullscreen?: boolean;
79
+ /** Called when playback ends */
80
+ onEnded?: () => void;
81
+ /** Called on each time update, with seconds elapsed */
82
+ onTimeUpdate?: (currentTime: number) => void;
83
+ /**
84
+ * How to load `hls.js` for this player, overriding any loader registered
85
+ * with `setHlsLoader`. Only consulted for HLS sources in browsers without
86
+ * native HLS support.
87
+ *
88
+ * @example hlsLoader={() => import('hls.js').then((m) => m.default)}
89
+ */
90
+ hlsLoader?: HlsLoader;
91
+ /** Extra hls.js configuration */
92
+ hlsConfig?: Record<string, unknown>;
93
+ /** Content overlaid on the video, above the controls */
94
+ overlay?: Snippet;
95
+ /** Additional classes for the player container */
96
+ class?: string;
97
+ }
98
+
99
+ let {
100
+ item,
101
+ src,
102
+ poster,
103
+ tracks,
104
+ label,
105
+ ratio = 'video',
106
+ fit = 'contain',
107
+ autoplay = false,
108
+ muted = false,
109
+ loop = false,
110
+ volume = 1,
111
+ preload = 'metadata',
112
+ hideControls = false,
113
+ showPictureInPicture = true,
114
+ showFullscreen = true,
115
+ onEnded,
116
+ onTimeUpdate,
117
+ hlsLoader,
118
+ hlsConfig,
119
+ overlay,
120
+ class: className,
121
+ }: Props = $props();
122
+
123
+ let videoElement = $state<HTMLVideoElement | null>(null);
124
+ let container = $state<HTMLDivElement | null>(null);
125
+ let pointerActive = $state(false);
126
+ let hideTimer: ReturnType<typeof setTimeout> | undefined;
127
+
128
+ // Explicit props win over the item, so a single field can be overridden
129
+ // without unpacking the whole thing.
130
+ const resolvedSrc = $derived(src ?? item?.src ?? []);
131
+ const resolvedPoster = $derived(poster ?? item?.poster);
132
+ const resolvedTracks = $derived(tracks ?? item?.tracks ?? []);
133
+ const resolvedLabel = $derived(label ?? item?.title ?? 'Video player');
134
+
135
+ const reducedMotion = useReducedMotion();
136
+ const player = useMediaPlayer(() => videoElement, {
137
+ volume: () => volume,
138
+ // Autoplay is only permitted while muted, so requesting it implies muting.
139
+ muted: () => muted || autoplay,
140
+ onEnded: () => onEnded?.(),
141
+ onTimeUpdate: (currentTime) => onTimeUpdate?.(currentTime),
142
+ });
143
+
144
+ const sources = $derived(toMediaSources(resolvedSrc));
145
+ const hlsSource = $derived(sources.find((source) => isHlsSource(source)) ?? null);
146
+
147
+ const hls = useHlsSource(() => videoElement, {
148
+ src: () => hlsSource?.src ?? null,
149
+ loader: () => hlsLoader,
150
+ config: () => hlsConfig,
151
+ // hls.js downloads through its own loader and ignores the element's
152
+ // `preload`, so mirror it here or a `preload="none"` stream would still
153
+ // pull segments the moment it mounts.
154
+ autoStartLoad: () => preload !== 'none',
155
+ });
156
+
157
+ /** Play, first releasing the stream loader when `preload="none"` held it back. */
158
+ function requestPlay() {
159
+ hls.startLoad();
160
+ return player.play();
161
+ }
162
+
163
+ function togglePlayback() {
164
+ if (player.paused) return requestPlay();
165
+ return player.toggle();
166
+ }
167
+
168
+ /**
169
+ * An HLS playlist is only rendered as a `<source>` when the browser can
170
+ * play it natively. Everywhere else hls.js sets the element's source, and
171
+ * leaving a `<source>` the browser cannot decode would make it report an
172
+ * error before hls.js ever attaches.
173
+ */
174
+ const elementSources = $derived(
175
+ hlsSource && !hls.native ? sources.filter((source) => !isHlsSource(source)) : sources
176
+ );
177
+
178
+ /** hls.js is required but unavailable, and there is no other playable source. */
179
+ const hlsUnavailable = $derived(
180
+ hlsSource !== null &&
181
+ (hls.status === 'unsupported' || hls.status === 'error') &&
182
+ elementSources.length === 0
183
+ );
184
+
185
+ // Controls stay up unless the video is actually playing and the pointer has
186
+ // gone idle. Keyboard users never lose them, because `focus-within` on the
187
+ // container keeps `pointerActive` irrelevant while focus is inside.
188
+ const controlsVisible = $derived(!player.playing || pointerActive || player.buffering);
189
+
190
+ const fitClass = $derived(
191
+ (
192
+ {
193
+ cover: 'object-cover',
194
+ contain: 'object-contain',
195
+ fill: 'object-fill',
196
+ none: 'object-none',
197
+ 'scale-down': 'object-scale-down',
198
+ } as const
199
+ )[fit]
200
+ );
201
+
202
+ function scheduleHide() {
203
+ clearTimeout(hideTimer);
204
+ pointerActive = true;
205
+ hideTimer = setTimeout(() => {
206
+ pointerActive = false;
207
+ }, CONTROLS_HIDE_DELAY);
208
+ }
209
+
210
+ function handlePointerLeave() {
211
+ clearTimeout(hideTimer);
212
+ pointerActive = false;
213
+ }
214
+
215
+ $effect(() => () => clearTimeout(hideTimer));
216
+
217
+ /** Typing in a field inside the overlay must not be swallowed as a shortcut. */
218
+ function isTextEntry(target: EventTarget | null): boolean {
219
+ if (!(target instanceof HTMLElement)) return false;
220
+ return (
221
+ target.isContentEditable ||
222
+ ['INPUT', 'TEXTAREA', 'SELECT'].includes(target.tagName) ||
223
+ // The Bits UI slider thumb has its own arrow-key handling.
224
+ target.getAttribute('role') === 'slider'
225
+ );
226
+ }
227
+
228
+ function handleKeyDown(event: KeyboardEvent) {
229
+ if (isTextEntry(event.target) || event.metaKey || event.ctrlKey || event.altKey) return;
230
+
231
+ scheduleHide();
232
+
233
+ switch (event.key) {
234
+ case ' ':
235
+ case 'k':
236
+ case 'K':
237
+ event.preventDefault();
238
+ togglePlayback();
239
+ break;
240
+ case 'ArrowLeft':
241
+ event.preventDefault();
242
+ player.seekBy(-5);
243
+ break;
244
+ case 'ArrowRight':
245
+ event.preventDefault();
246
+ player.seekBy(5);
247
+ break;
248
+ case 'j':
249
+ case 'J':
250
+ event.preventDefault();
251
+ player.seekBy(-10);
252
+ break;
253
+ case 'l':
254
+ case 'L':
255
+ event.preventDefault();
256
+ player.seekBy(10);
257
+ break;
258
+ case 'ArrowUp':
259
+ event.preventDefault();
260
+ player.adjustVolume(0.1);
261
+ break;
262
+ case 'ArrowDown':
263
+ event.preventDefault();
264
+ player.adjustVolume(-0.1);
265
+ break;
266
+ case 'm':
267
+ case 'M':
268
+ event.preventDefault();
269
+ player.toggleMute();
270
+ break;
271
+ case 'f':
272
+ case 'F':
273
+ if (!showFullscreen) return;
274
+ event.preventDefault();
275
+ player.toggleFullscreen(container);
276
+ break;
277
+ case 'c':
278
+ case 'C':
279
+ event.preventDefault();
280
+ player.toggleCaptions();
281
+ break;
282
+ case 'Home':
283
+ event.preventDefault();
284
+ player.seek(0);
285
+ break;
286
+ case 'End':
287
+ event.preventDefault();
288
+ player.seek(player.duration);
289
+ break;
290
+ default:
291
+ if (/^[0-9]$/.test(event.key)) {
292
+ event.preventDefault();
293
+ player.seekToFraction(Number(event.key) / 10);
294
+ }
295
+ }
296
+ }
297
+
298
+ function retry() {
299
+ videoElement?.load();
300
+ }
301
+
302
+ if (typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production') {
303
+ $effect(() => {
304
+ // HLS carries its own subtitle tracks inside the playlist, so an empty
305
+ // `tracks` prop is expected and not a captions failure there.
306
+ if (resolvedTracks.length === 0 && !hlsSource) {
307
+ console.warn(
308
+ 'Video: No caption tracks provided. Pre-recorded video needs captions to meet WCAG 1.2.2 — pass `tracks` with at least one WebVTT file.'
309
+ );
310
+ }
311
+ if (hls.status === 'unsupported') {
312
+ console.warn(
313
+ 'Video: This browser cannot play HLS natively and no hls.js loader is available. Install hls.js and call setHlsLoader(() => import("hls.js").then((m) => m.default)) once at app startup, or pass an `hlsLoader` prop.'
314
+ );
315
+ }
316
+ if (autoplay && !muted) {
317
+ console.warn(
318
+ 'Video: `autoplay` without `muted` is blocked by every major browser. Set `muted` as well, or drop `autoplay`.'
319
+ );
320
+ }
321
+ });
322
+ }
323
+ </script>
324
+
325
+ <!--
326
+ The container is the keyboard target and the fullscreen element. Player
327
+ shortcuts are bound here rather than on <video>, so they work while focus is
328
+ on any control inside.
329
+ -->
330
+ <!--
331
+ `tabindex="0"` is deliberate: the player region has to be reachable so the
332
+ playback shortcuts below work, which is the pattern the WAI media player
333
+ guidance calls for.
334
+ -->
335
+ <!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
336
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
337
+ <div
338
+ bind:this={container}
339
+ role="region"
340
+ aria-label={resolvedLabel}
341
+ tabindex="0"
342
+ class={cn(
343
+ 'group/player relative isolate flex w-full flex-col overflow-hidden rounded-lg bg-black outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background',
344
+ className
345
+ )}
346
+ style={ratio ? `aspect-ratio: ${resolveAspectRatio(ratio)}` : undefined}
347
+ onkeydown={handleKeyDown}
348
+ onpointermove={scheduleHide}
349
+ onpointerdown={scheduleHide}
350
+ onpointerleave={handlePointerLeave}
351
+ >
352
+ <!-- svelte-ignore a11y_media_has_caption -- tracks are supplied via the `tracks` prop -->
353
+ <video
354
+ bind:this={videoElement}
355
+ class={cn('h-full w-full', fitClass)}
356
+ poster={resolvedPoster}
357
+ {autoplay}
358
+ {loop}
359
+ muted={muted || autoplay}
360
+ {preload}
361
+ playsinline
362
+ controls={false}
363
+ onclick={() => togglePlayback()}
364
+ >
365
+ {#each elementSources as source (source.src)}
366
+ <source src={source.src} type={source.type} />
367
+ {/each}
368
+ {#each resolvedTracks as track (track.src)}
369
+ <track
370
+ src={track.src}
371
+ kind={track.kind ?? 'subtitles'}
372
+ srclang={track.srclang}
373
+ label={track.label}
374
+ default={track.default}
375
+ />
376
+ {/each}
377
+ Your browser does not support embedded video.
378
+ </video>
379
+
380
+ {#if hlsUnavailable}
381
+ <div
382
+ class="absolute inset-0 z-20 flex flex-col items-center justify-center gap-2 bg-black/80 p-6 text-center text-white"
383
+ role="alert"
384
+ >
385
+ <Icon name="video" size={32} />
386
+ <p class="text-sm">This browser cannot play adaptive streams.</p>
387
+ <p class="max-w-sm text-xs text-white/70">
388
+ Playing HLS outside Safari needs hls.js. Install it and register a loader once at app
389
+ startup.
390
+ </p>
391
+ </div>
392
+ {:else if player.error}
393
+ <div
394
+ class="absolute inset-0 z-20 flex flex-col items-center justify-center gap-3 bg-black/80 p-6 text-center text-white"
395
+ role="alert"
396
+ >
397
+ <Icon name="video" size={32} />
398
+ <p class="text-sm">This video could not be played.</p>
399
+ <button
400
+ type="button"
401
+ class="rounded-md bg-white/15 px-3 py-1.5 text-sm font-medium hover:bg-white/25 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white"
402
+ onclick={retry}
403
+ >
404
+ Try again
405
+ </button>
406
+ </div>
407
+ {:else}
408
+ {#if player.buffering}
409
+ <div class="pointer-events-none absolute inset-0 z-10 flex items-center justify-center">
410
+ <Spinner size="lg" class="text-white" />
411
+ </div>
412
+ {:else if !player.playing && (player.currentTime === 0 || player.ended)}
413
+ <button
414
+ type="button"
415
+ class="absolute inset-0 z-10 flex items-center justify-center bg-black/20 outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-white"
416
+ onclick={() => requestPlay()}
417
+ aria-label={player.ended ? 'Replay video' : 'Play video'}
418
+ >
419
+ <span
420
+ class="flex h-16 w-16 items-center justify-center rounded-full bg-black/60 text-white shadow-lg"
421
+ >
422
+ <Icon name="play" size={32} />
423
+ </span>
424
+ </button>
425
+ {/if}
426
+
427
+ {#if overlay}
428
+ {@render overlay()}
429
+ {/if}
430
+
431
+ {#if !hideControls}
432
+ <div
433
+ class={cn(
434
+ 'absolute inset-x-0 bottom-0 z-20',
435
+ // Reduced motion keeps the bar permanently visible rather than fading it.
436
+ reducedMotion.value
437
+ ? ''
438
+ : cn(
439
+ 'transition-opacity duration-200 focus-within:opacity-100',
440
+ controlsVisible ? 'opacity-100' : 'opacity-0'
441
+ )
442
+ )}
443
+ >
444
+ <MediaControls
445
+ {player}
446
+ variant="video"
447
+ {showPictureInPicture}
448
+ {showFullscreen}
449
+ fullscreenTarget={container}
450
+ onTogglePlay={togglePlayback}
451
+ />
452
+ </div>
453
+ {/if}
454
+ {/if}
455
+ </div>
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Video - Video player with themed controls
3
+ *
4
+ * Features:
5
+ * - Custom control bar: play/pause, seek, volume, speed, captions, PiP, fullscreen
6
+ * - Full keyboard control (Space/K, arrows, J/L, M, F, C, Home/End, 0-9)
7
+ * - Controls auto-hide during playback and stay visible while paused or focused
8
+ * - Buffering spinner and an inline error state with retry
9
+ * - Honours `prefers-reduced-motion` by never fading the controls away
10
+ *
11
+ * @example
12
+ * ```svelte
13
+ * <Video
14
+ * src="/clip.mp4"
15
+ * poster="/clip.jpg"
16
+ * label="Product walkthrough"
17
+ * tracks={[{ src: '/clip.en.vtt', srclang: 'en', label: 'English', default: true }]}
18
+ * />
19
+ * ```
20
+ */
21
+ import type { Snippet } from 'svelte';
22
+ import type { MediaAspectRatio, MediaFit, MediaSource, MediaTextTrack, VideoMediaItem } from '../../types/components.js';
23
+ import type { HlsLoader } from '../../utils/hls.js';
24
+ interface Props {
25
+ /**
26
+ * A video media item supplying `src`, `poster`, `tracks`, and the label.
27
+ * Individual props still win when both are given.
28
+ *
29
+ * Pairs with `cloudflareStream()` to play a Cloudflare Stream video.
30
+ */
31
+ item?: VideoMediaItem;
32
+ /** A single URL, or several sources for format fallback */
33
+ src?: string | MediaSource[];
34
+ /** Poster image shown before playback starts */
35
+ poster?: string;
36
+ /** Caption and subtitle tracks — required for WCAG 1.2.2 on recorded content */
37
+ tracks?: MediaTextTrack[];
38
+ /** Accessible name for the player region */
39
+ label?: string;
40
+ /** Constrain the player to an aspect ratio (default `video`) */
41
+ ratio?: MediaAspectRatio | number | null;
42
+ /** How the video fills its box */
43
+ fit?: MediaFit;
44
+ /** Start playing automatically — requires `muted`, and browsers may still refuse */
45
+ autoplay?: boolean;
46
+ /** Start muted */
47
+ muted?: boolean;
48
+ /** Loop playback */
49
+ loop?: boolean;
50
+ /** Initial volume, 0–1 */
51
+ volume?: number;
52
+ /** How much to preload (default `metadata`, enough for duration without fetching the file) */
53
+ preload?: 'none' | 'metadata' | 'auto';
54
+ /** Hide the control bar entirely — the player is then keyboard-only */
55
+ hideControls?: boolean;
56
+ /** Show the picture-in-picture button */
57
+ showPictureInPicture?: boolean;
58
+ /** Show the fullscreen button */
59
+ showFullscreen?: boolean;
60
+ /** Called when playback ends */
61
+ onEnded?: () => void;
62
+ /** Called on each time update, with seconds elapsed */
63
+ onTimeUpdate?: (currentTime: number) => void;
64
+ /**
65
+ * How to load `hls.js` for this player, overriding any loader registered
66
+ * with `setHlsLoader`. Only consulted for HLS sources in browsers without
67
+ * native HLS support.
68
+ *
69
+ * @example hlsLoader={() => import('hls.js').then((m) => m.default)}
70
+ */
71
+ hlsLoader?: HlsLoader;
72
+ /** Extra hls.js configuration */
73
+ hlsConfig?: Record<string, unknown>;
74
+ /** Content overlaid on the video, above the controls */
75
+ overlay?: Snippet;
76
+ /** Additional classes for the player container */
77
+ class?: string;
78
+ }
79
+ declare const Video: import("svelte").Component<Props, {}, "">;
80
+ type Video = ReturnType<typeof Video>;
81
+ export default Video;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Media Components
3
+ *
4
+ * Presentation components for images, video, audio, and third-party embeds,
5
+ * plus collection views and a full-screen lightbox.
6
+ */
7
+ export { default as AspectRatio } from './AspectRatio.svelte';
8
+ export { default as Image } from './Image.svelte';
9
+ export { default as Video } from './Video.svelte';
10
+ export { default as Audio } from './Audio.svelte';
11
+ export { default as MediaEmbed } from './MediaEmbed.svelte';
12
+ export { default as MediaGrid } from './MediaGrid.svelte';
13
+ export { default as MediaCarousel } from './MediaCarousel.svelte';
14
+ export { default as Lightbox } from './Lightbox.svelte';
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Media Components
3
+ *
4
+ * Presentation components for images, video, audio, and third-party embeds,
5
+ * plus collection views and a full-screen lightbox.
6
+ */
7
+ export { default as AspectRatio } from './AspectRatio.svelte';
8
+ export { default as Image } from './Image.svelte';
9
+ export { default as Video } from './Video.svelte';
10
+ export { default as Audio } from './Audio.svelte';
11
+ export { default as MediaEmbed } from './MediaEmbed.svelte';
12
+ export { default as MediaGrid } from './MediaGrid.svelte';
13
+ export { default as MediaCarousel } from './MediaCarousel.svelte';
14
+ export { default as Lightbox } from './Lightbox.svelte';