@classic-homes/theme-svelte 1.2.3 → 1.4.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 (52) 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 +20 -19
  11. package/dist/lib/components/FileUpload.svelte +221 -4
  12. package/dist/lib/components/FileUpload.svelte.d.ts +13 -1
  13. package/dist/lib/components/media/AspectRatio.svelte +47 -0
  14. package/dist/lib/components/media/AspectRatio.svelte.d.ts +20 -0
  15. package/dist/lib/components/media/Audio.svelte +247 -0
  16. package/dist/lib/components/media/Audio.svelte.d.ts +34 -0
  17. package/dist/lib/components/media/Image.svelte +209 -0
  18. package/dist/lib/components/media/Image.svelte.d.ts +52 -0
  19. package/dist/lib/components/media/Lightbox.svelte +449 -0
  20. package/dist/lib/components/media/Lightbox.svelte.d.ts +33 -0
  21. package/dist/lib/components/media/MediaCarousel.svelte +359 -0
  22. package/dist/lib/components/media/MediaCarousel.svelte.d.ts +30 -0
  23. package/dist/lib/components/media/MediaEmbed.svelte +314 -0
  24. package/dist/lib/components/media/MediaEmbed.svelte.d.ts +99 -0
  25. package/dist/lib/components/media/MediaGrid.svelte +327 -0
  26. package/dist/lib/components/media/MediaGrid.svelte.d.ts +57 -0
  27. package/dist/lib/components/media/Video.svelte +455 -0
  28. package/dist/lib/components/media/Video.svelte.d.ts +81 -0
  29. package/dist/lib/components/media/index.d.ts +14 -0
  30. package/dist/lib/components/media/index.js +14 -0
  31. package/dist/lib/components/media/internal/MediaControls.svelte +283 -0
  32. package/dist/lib/components/media/internal/MediaControls.svelte.d.ts +32 -0
  33. package/dist/lib/components/media/internal/MediaScrubber.svelte +211 -0
  34. package/dist/lib/components/media/internal/MediaScrubber.svelte.d.ts +27 -0
  35. package/dist/lib/components/media/internal/MediaTile.svelte +129 -0
  36. package/dist/lib/components/media/internal/MediaTile.svelte.d.ts +22 -0
  37. package/dist/lib/composables/index.d.ts +3 -0
  38. package/dist/lib/composables/index.js +4 -0
  39. package/dist/lib/composables/useHlsSource.svelte.d.ts +70 -0
  40. package/dist/lib/composables/useHlsSource.svelte.js +119 -0
  41. package/dist/lib/composables/useMediaPlayer.svelte.d.ts +114 -0
  42. package/dist/lib/composables/useMediaPlayer.svelte.js +422 -0
  43. package/dist/lib/composables/useMediaZoom.svelte.d.ts +59 -0
  44. package/dist/lib/composables/useMediaZoom.svelte.js +199 -0
  45. package/dist/lib/index.d.ts +8 -2
  46. package/dist/lib/index.js +14 -1
  47. package/dist/lib/types/components.d.ts +172 -0
  48. package/dist/lib/utils/hls.d.ts +76 -0
  49. package/dist/lib/utils/hls.js +83 -0
  50. package/dist/lib/utils/media.d.ts +379 -0
  51. package/dist/lib/utils/media.js +715 -0
  52. package/package.json +6 -2
@@ -0,0 +1,209 @@
1
+ <script lang="ts">
2
+ /**
3
+ * Image - Responsive image with loading, placeholder, and error handling
4
+ *
5
+ * Features:
6
+ * - Lazy loading and async decoding by default
7
+ * - Skeleton or blurred low-resolution placeholder while loading
8
+ * - Graceful fallback when the source fails to load
9
+ * - Optional aspect-ratio box that reserves layout space (no layout shift)
10
+ * - `alt` is required at the type level; pass an empty string for decorative images
11
+ */
12
+ import type { Snippet } from 'svelte';
13
+ import type { HTMLImgAttributes } from 'svelte/elements';
14
+ import { tv, type VariantProps } from 'tailwind-variants';
15
+ import type { MediaAspectRatio } from '../../types/components.js';
16
+ import { cn } from '../../utils.js';
17
+ import Icon from '../Icon.svelte';
18
+ import Skeleton from '../Skeleton.svelte';
19
+ import AspectRatio from './AspectRatio.svelte';
20
+
21
+ const imageVariants = tv({
22
+ slots: {
23
+ wrapper: 'relative isolate overflow-hidden bg-muted',
24
+ img: 'block h-full w-full',
25
+ },
26
+ variants: {
27
+ fit: {
28
+ cover: { img: 'object-cover' },
29
+ contain: { img: 'object-contain' },
30
+ fill: { img: 'object-fill' },
31
+ none: { img: 'object-none' },
32
+ 'scale-down': { img: 'object-scale-down' },
33
+ },
34
+ rounded: {
35
+ none: { wrapper: 'rounded-none' },
36
+ sm: { wrapper: 'rounded-sm' },
37
+ md: { wrapper: 'rounded-md' },
38
+ lg: { wrapper: 'rounded-lg' },
39
+ xl: { wrapper: 'rounded-xl' },
40
+ full: { wrapper: 'rounded-full' },
41
+ },
42
+ },
43
+ defaultVariants: {
44
+ fit: 'cover',
45
+ rounded: 'none',
46
+ },
47
+ });
48
+
49
+ type ImageVariants = VariantProps<typeof imageVariants>;
50
+
51
+ interface Props extends Omit<HTMLImgAttributes, 'alt' | 'class' | 'onload' | 'onerror'> {
52
+ /** Image URL */
53
+ src: string;
54
+ /**
55
+ * Alternative text. Required — pass an empty string only when the image is
56
+ * purely decorative and the surrounding content already conveys its meaning.
57
+ */
58
+ alt: string;
59
+ /**
60
+ * Constrain the image to a ratio box. Omit to let the image size itself
61
+ * from its intrinsic dimensions.
62
+ */
63
+ ratio?: MediaAspectRatio | number;
64
+ /** How the image fills its box */
65
+ fit?: ImageVariants['fit'];
66
+ /** Corner rounding */
67
+ rounded?: ImageVariants['rounded'];
68
+ /**
69
+ * Low-resolution placeholder (typically a tiny data URL) blurred in behind
70
+ * the image and cross-faded out once the real image decodes.
71
+ */
72
+ placeholder?: string;
73
+ /** Show a pulsing skeleton while loading (ignored when `placeholder` is set) */
74
+ showSkeleton?: boolean;
75
+ /** Called once the image has loaded */
76
+ onload?: (event: Event) => void;
77
+ /** Called when the image fails to load */
78
+ onerror?: (event: Event) => void;
79
+ /** Custom content rendered in place of a failed image */
80
+ fallback?: Snippet;
81
+ /** Content overlaid on the image, such as a caption or badge */
82
+ overlay?: Snippet;
83
+ /** Additional classes applied to the wrapper */
84
+ class?: string;
85
+ /** Additional classes applied to the `<img>` element itself */
86
+ imgClass?: string;
87
+ }
88
+
89
+ let {
90
+ src,
91
+ alt,
92
+ ratio,
93
+ fit = 'cover',
94
+ rounded = 'none',
95
+ placeholder,
96
+ showSkeleton = true,
97
+ onload,
98
+ onerror,
99
+ fallback,
100
+ overlay,
101
+ class: className,
102
+ imgClass,
103
+ loading = 'lazy',
104
+ decoding = 'async',
105
+ ...restProps
106
+ }: Props = $props();
107
+
108
+ // Track load state per-URL rather than as a mutable flag, so swapping `src`
109
+ // automatically returns to the loading state without a reset effect.
110
+ let loadedSrc = $state<string | null>(null);
111
+ let erroredSrc = $state<string | null>(null);
112
+
113
+ const status = $derived<'loading' | 'loaded' | 'error'>(
114
+ erroredSrc === src ? 'error' : loadedSrc === src ? 'loaded' : 'loading'
115
+ );
116
+
117
+ const styles = $derived(imageVariants({ fit, rounded }));
118
+
119
+ function handleLoad(event: Event) {
120
+ loadedSrc = src;
121
+ onload?.(event);
122
+ }
123
+
124
+ function handleError(event: Event) {
125
+ erroredSrc = src;
126
+ onerror?.(event);
127
+ }
128
+
129
+ /**
130
+ * An image already in the memory cache can finish decoding before the `load`
131
+ * listener is attached, and then never fires it. `complete` covers that case.
132
+ */
133
+ function detectCached(node: HTMLImageElement) {
134
+ if (node.complete && node.currentSrc) {
135
+ if (node.naturalWidth > 0) loadedSrc = src;
136
+ else erroredSrc = src;
137
+ }
138
+ }
139
+
140
+ if (typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production') {
141
+ $effect(() => {
142
+ if (alt === undefined) {
143
+ console.warn(
144
+ 'Image: Missing "alt" prop. Provide descriptive alternative text, or an empty string ("") if the image is decorative.'
145
+ );
146
+ }
147
+ });
148
+ }
149
+ </script>
150
+
151
+ {#snippet layers()}
152
+ {#if status === 'error'}
153
+ <div
154
+ class="flex h-full w-full min-h-[3rem] items-center justify-center bg-muted text-muted-foreground"
155
+ >
156
+ {#if fallback}
157
+ {@render fallback()}
158
+ {:else}
159
+ <Icon name="image" size={32} label={alt ? `Failed to load: ${alt}` : 'Image unavailable'} />
160
+ {/if}
161
+ </div>
162
+ {:else}
163
+ {#if placeholder}
164
+ <img
165
+ src={placeholder}
166
+ alt=""
167
+ aria-hidden="true"
168
+ class={cn(
169
+ styles.img(),
170
+ 'absolute inset-0 scale-105 blur-lg transition-opacity duration-300',
171
+ status === 'loaded' ? 'opacity-0' : 'opacity-100'
172
+ )}
173
+ />
174
+ {:else if showSkeleton && status === 'loading'}
175
+ <Skeleton class="absolute inset-0 h-full w-full rounded-none" />
176
+ {/if}
177
+
178
+ <img
179
+ {src}
180
+ {alt}
181
+ {loading}
182
+ {decoding}
183
+ class={cn(
184
+ styles.img(),
185
+ 'relative transition-opacity duration-300',
186
+ status === 'loaded' ? 'opacity-100' : 'opacity-0',
187
+ imgClass
188
+ )}
189
+ onload={handleLoad}
190
+ onerror={handleError}
191
+ use:detectCached
192
+ {...restProps}
193
+ />
194
+ {/if}
195
+
196
+ {#if overlay}
197
+ {@render overlay()}
198
+ {/if}
199
+ {/snippet}
200
+
201
+ {#if ratio}
202
+ <AspectRatio {ratio} class={cn(styles.wrapper(), className)}>
203
+ {@render layers()}
204
+ </AspectRatio>
205
+ {:else}
206
+ <div class={cn(styles.wrapper(), 'inline-block', className)}>
207
+ {@render layers()}
208
+ </div>
209
+ {/if}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Image - Responsive image with loading, placeholder, and error handling
3
+ *
4
+ * Features:
5
+ * - Lazy loading and async decoding by default
6
+ * - Skeleton or blurred low-resolution placeholder while loading
7
+ * - Graceful fallback when the source fails to load
8
+ * - Optional aspect-ratio box that reserves layout space (no layout shift)
9
+ * - `alt` is required at the type level; pass an empty string for decorative images
10
+ */
11
+ import type { Snippet } from 'svelte';
12
+ import type { HTMLImgAttributes } from 'svelte/elements';
13
+ import type { MediaAspectRatio } from '../../types/components.js';
14
+ declare const Image: import("svelte").Component<Omit<HTMLImgAttributes, "class" | "onload" | "onerror" | "alt"> & {
15
+ /** Image URL */
16
+ src: string;
17
+ /**
18
+ * Alternative text. Required — pass an empty string only when the image is
19
+ * purely decorative and the surrounding content already conveys its meaning.
20
+ */
21
+ alt: string;
22
+ /**
23
+ * Constrain the image to a ratio box. Omit to let the image size itself
24
+ * from its intrinsic dimensions.
25
+ */
26
+ ratio?: MediaAspectRatio | number;
27
+ /** How the image fills its box */
28
+ fit?: "none" | "cover" | "contain" | "fill" | "scale-down" | undefined;
29
+ /** Corner rounding */
30
+ rounded?: "sm" | "md" | "lg" | "xl" | "none" | "full" | undefined;
31
+ /**
32
+ * Low-resolution placeholder (typically a tiny data URL) blurred in behind
33
+ * the image and cross-faded out once the real image decodes.
34
+ */
35
+ placeholder?: string;
36
+ /** Show a pulsing skeleton while loading (ignored when `placeholder` is set) */
37
+ showSkeleton?: boolean;
38
+ /** Called once the image has loaded */
39
+ onload?: (event: Event) => void;
40
+ /** Called when the image fails to load */
41
+ onerror?: (event: Event) => void;
42
+ /** Custom content rendered in place of a failed image */
43
+ fallback?: Snippet;
44
+ /** Content overlaid on the image, such as a caption or badge */
45
+ overlay?: Snippet;
46
+ /** Additional classes applied to the wrapper */
47
+ class?: string;
48
+ /** Additional classes applied to the `<img>` element itself */
49
+ imgClass?: string;
50
+ }, {}, "">;
51
+ type Image = ReturnType<typeof Image>;
52
+ export default Image;