@delightstack/components 0.1.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/LICENSE +21 -0
- package/README.md +136 -0
- package/SKILL.md +149 -0
- package/bin/agents.js +63 -0
- package/dist/actions/Alert.svelte +202 -0
- package/dist/actions/Alert.svelte.d.ts +36 -0
- package/dist/actions/Alert.svelte.d.ts.map +1 -0
- package/dist/actions/Button.svelte +1450 -0
- package/dist/actions/Button.svelte.d.ts +56 -0
- package/dist/actions/Button.svelte.d.ts.map +1 -0
- package/dist/actions/ButtonGroup.svelte +111 -0
- package/dist/actions/ButtonGroup.svelte.d.ts +41 -0
- package/dist/actions/ButtonGroup.svelte.d.ts.map +1 -0
- package/dist/actions/CommandPalette.svelte +939 -0
- package/dist/actions/CommandPalette.svelte.d.ts +37 -0
- package/dist/actions/CommandPalette.svelte.d.ts.map +1 -0
- package/dist/actions/ContextMenu.svelte +138 -0
- package/dist/actions/ContextMenu.svelte.d.ts +54 -0
- package/dist/actions/ContextMenu.svelte.d.ts.map +1 -0
- package/dist/actions/Modal.svelte +474 -0
- package/dist/actions/Modal.svelte.d.ts +28 -0
- package/dist/actions/Modal.svelte.d.ts.map +1 -0
- package/dist/actions/Popover.svelte +1214 -0
- package/dist/actions/Popover.svelte.d.ts +31 -0
- package/dist/actions/Popover.svelte.d.ts.map +1 -0
- package/dist/actions/Portal.svelte +80 -0
- package/dist/actions/Portal.svelte.d.ts +17 -0
- package/dist/actions/Portal.svelte.d.ts.map +1 -0
- package/dist/actions/ThemeToggle.svelte +345 -0
- package/dist/actions/ThemeToggle.svelte.d.ts +15 -0
- package/dist/actions/ThemeToggle.svelte.d.ts.map +1 -0
- package/dist/actions/index.d.ts +13 -0
- package/dist/actions/index.d.ts.map +1 -0
- package/dist/actions/index.js +10 -0
- package/dist/actions/scrollbar.d.ts +48 -0
- package/dist/actions/scrollbar.d.ts.map +1 -0
- package/dist/actions/scrollbar.js +404 -0
- package/dist/display/Accordion.svelte +586 -0
- package/dist/display/Accordion.svelte.d.ts +41 -0
- package/dist/display/Accordion.svelte.d.ts.map +1 -0
- package/dist/display/Avatar.svelte +527 -0
- package/dist/display/Avatar.svelte.d.ts +22 -0
- package/dist/display/Avatar.svelte.d.ts.map +1 -0
- package/dist/display/AvatarGroup.svelte +298 -0
- package/dist/display/AvatarGroup.svelte.d.ts +31 -0
- package/dist/display/AvatarGroup.svelte.d.ts.map +1 -0
- package/dist/display/Calendar.svelte +1366 -0
- package/dist/display/Calendar.svelte.d.ts +58 -0
- package/dist/display/Calendar.svelte.d.ts.map +1 -0
- package/dist/display/Chart.svelte +1426 -0
- package/dist/display/Chart.svelte.d.ts +35 -0
- package/dist/display/Chart.svelte.d.ts.map +1 -0
- package/dist/display/Code.svelte +780 -0
- package/dist/display/Code.svelte.d.ts +19 -0
- package/dist/display/Code.svelte.d.ts.map +1 -0
- package/dist/display/Comparison.svelte +686 -0
- package/dist/display/Comparison.svelte.d.ts +22 -0
- package/dist/display/Comparison.svelte.d.ts.map +1 -0
- package/dist/display/Counter.svelte +285 -0
- package/dist/display/Counter.svelte.d.ts +21 -0
- package/dist/display/Counter.svelte.d.ts.map +1 -0
- package/dist/display/Expand.svelte +48 -0
- package/dist/display/Expand.svelte.d.ts +9 -0
- package/dist/display/Expand.svelte.d.ts.map +1 -0
- package/dist/display/List.svelte +294 -0
- package/dist/display/List.svelte.d.ts +40 -0
- package/dist/display/List.svelte.d.ts.map +1 -0
- package/dist/display/ListContextReset.svelte +19 -0
- package/dist/display/ListContextReset.svelte.d.ts +7 -0
- package/dist/display/ListContextReset.svelte.d.ts.map +1 -0
- package/dist/display/ListItem.svelte +834 -0
- package/dist/display/ListItem.svelte.d.ts +22 -0
- package/dist/display/ListItem.svelte.d.ts.map +1 -0
- package/dist/display/QR.svelte +1193 -0
- package/dist/display/QR.svelte.d.ts +23 -0
- package/dist/display/QR.svelte.d.ts.map +1 -0
- package/dist/display/SplitPane.svelte +744 -0
- package/dist/display/SplitPane.svelte.d.ts +25 -0
- package/dist/display/SplitPane.svelte.d.ts.map +1 -0
- package/dist/display/Stat.svelte +439 -0
- package/dist/display/Stat.svelte.d.ts +24 -0
- package/dist/display/Stat.svelte.d.ts.map +1 -0
- package/dist/display/Table.svelte +4654 -0
- package/dist/display/Table.svelte.d.ts +249 -0
- package/dist/display/Table.svelte.d.ts.map +1 -0
- package/dist/display/TableCellEditor.svelte +935 -0
- package/dist/display/TableCellEditor.svelte.d.ts +58 -0
- package/dist/display/TableCellEditor.svelte.d.ts.map +1 -0
- package/dist/display/Timeline.svelte +1258 -0
- package/dist/display/Timeline.svelte.d.ts +43 -0
- package/dist/display/Timeline.svelte.d.ts.map +1 -0
- package/dist/display/Tree.svelte +1740 -0
- package/dist/display/Tree.svelte.d.ts +74 -0
- package/dist/display/Tree.svelte.d.ts.map +1 -0
- package/dist/display/Typewriter.svelte +338 -0
- package/dist/display/Typewriter.svelte.d.ts +22 -0
- package/dist/display/Typewriter.svelte.d.ts.map +1 -0
- package/dist/display/index.d.ts +24 -0
- package/dist/display/index.d.ts.map +1 -0
- package/dist/display/index.js +18 -0
- package/dist/feedback/Callout.svelte +529 -0
- package/dist/feedback/Callout.svelte.d.ts +24 -0
- package/dist/feedback/Callout.svelte.d.ts.map +1 -0
- package/dist/feedback/Confetti.svelte +631 -0
- package/dist/feedback/Confetti.svelte.d.ts +90 -0
- package/dist/feedback/Confetti.svelte.d.ts.map +1 -0
- package/dist/feedback/Progress.svelte +382 -0
- package/dist/feedback/Progress.svelte.d.ts +25 -0
- package/dist/feedback/Progress.svelte.d.ts.map +1 -0
- package/dist/feedback/Toast.svelte +967 -0
- package/dist/feedback/Toast.svelte.d.ts +54 -0
- package/dist/feedback/Toast.svelte.d.ts.map +1 -0
- package/dist/feedback/index.d.ts +7 -0
- package/dist/feedback/index.d.ts.map +1 -0
- package/dist/feedback/index.js +4 -0
- package/dist/form/Checkbox.svelte +449 -0
- package/dist/form/Checkbox.svelte.d.ts +27 -0
- package/dist/form/Checkbox.svelte.d.ts.map +1 -0
- package/dist/form/Fieldset.svelte +410 -0
- package/dist/form/Fieldset.svelte.d.ts +22 -0
- package/dist/form/Fieldset.svelte.d.ts.map +1 -0
- package/dist/form/FileUpload.svelte +934 -0
- package/dist/form/FileUpload.svelte.d.ts +41 -0
- package/dist/form/FileUpload.svelte.d.ts.map +1 -0
- package/dist/form/Form.svelte +530 -0
- package/dist/form/Form.svelte.d.ts +120 -0
- package/dist/form/Form.svelte.d.ts.map +1 -0
- package/dist/form/Input.svelte +2858 -0
- package/dist/form/Input.svelte.d.ts +66 -0
- package/dist/form/Input.svelte.d.ts.map +1 -0
- package/dist/form/Radio.svelte +507 -0
- package/dist/form/Radio.svelte.d.ts +39 -0
- package/dist/form/Radio.svelte.d.ts.map +1 -0
- package/dist/form/Range.svelte +912 -0
- package/dist/form/Range.svelte.d.ts +33 -0
- package/dist/form/Range.svelte.d.ts.map +1 -0
- package/dist/form/Rating.svelte +429 -0
- package/dist/form/Rating.svelte.d.ts +28 -0
- package/dist/form/Rating.svelte.d.ts.map +1 -0
- package/dist/form/Select.svelte +1933 -0
- package/dist/form/Select.svelte.d.ts +54 -0
- package/dist/form/Select.svelte.d.ts.map +1 -0
- package/dist/form/Toggle.svelte +645 -0
- package/dist/form/Toggle.svelte.d.ts +50 -0
- package/dist/form/Toggle.svelte.d.ts.map +1 -0
- package/dist/form/index.d.ts +15 -0
- package/dist/form/index.d.ts.map +1 -0
- package/dist/form/index.js +10 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/layout/README.md +172 -0
- package/dist/media/Carousel.svelte +2424 -0
- package/dist/media/Carousel.svelte.d.ts +47 -0
- package/dist/media/Carousel.svelte.d.ts.map +1 -0
- package/dist/media/Gallery.svelte +2881 -0
- package/dist/media/Gallery.svelte.d.ts +82 -0
- package/dist/media/Gallery.svelte.d.ts.map +1 -0
- package/dist/media/Image.svelte +389 -0
- package/dist/media/Image.svelte.d.ts +33 -0
- package/dist/media/Image.svelte.d.ts.map +1 -0
- package/dist/media/PDF.svelte +1793 -0
- package/dist/media/PDF.svelte.d.ts +44 -0
- package/dist/media/PDF.svelte.d.ts.map +1 -0
- package/dist/media/Panorama.svelte +1391 -0
- package/dist/media/Panorama.svelte.d.ts +47 -0
- package/dist/media/Panorama.svelte.d.ts.map +1 -0
- package/dist/media/Video.svelte +2501 -0
- package/dist/media/Video.svelte.d.ts +58 -0
- package/dist/media/Video.svelte.d.ts.map +1 -0
- package/dist/media/carousel.d.ts +211 -0
- package/dist/media/carousel.d.ts.map +1 -0
- package/dist/media/carousel.js +408 -0
- package/dist/media/index.d.ts +11 -0
- package/dist/media/index.d.ts.map +1 -0
- package/dist/media/index.js +5 -0
- package/dist/navigation/BottomSheet.svelte +636 -0
- package/dist/navigation/BottomSheet.svelte.d.ts +27 -0
- package/dist/navigation/BottomSheet.svelte.d.ts.map +1 -0
- package/dist/navigation/Breadcrumbs.svelte +611 -0
- package/dist/navigation/Breadcrumbs.svelte.d.ts +28 -0
- package/dist/navigation/Breadcrumbs.svelte.d.ts.map +1 -0
- package/dist/navigation/Pagination.svelte +641 -0
- package/dist/navigation/Pagination.svelte.d.ts +27 -0
- package/dist/navigation/Pagination.svelte.d.ts.map +1 -0
- package/dist/navigation/Steps.svelte +965 -0
- package/dist/navigation/Steps.svelte.d.ts +43 -0
- package/dist/navigation/Steps.svelte.d.ts.map +1 -0
- package/dist/navigation/Tabs.svelte +698 -0
- package/dist/navigation/Tabs.svelte.d.ts +41 -0
- package/dist/navigation/Tabs.svelte.d.ts.map +1 -0
- package/dist/navigation/index.d.ts +8 -0
- package/dist/navigation/index.d.ts.map +1 -0
- package/dist/navigation/index.js +5 -0
- package/package.json +139 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { Component } from 'svelte';
|
|
2
|
+
import type { CarouselItem } from './carousel';
|
|
3
|
+
/** A single action button that can be shown on a gallery item or in the carousel header. */
|
|
4
|
+
export interface GalleryItemAction {
|
|
5
|
+
/** The icon component to show for the action */
|
|
6
|
+
icon?: Component<Record<string, unknown>>;
|
|
7
|
+
/** The main action text - e.g. 'Download' or 'Pay now' */
|
|
8
|
+
name?: string;
|
|
9
|
+
/** A short descriptor of the action - e.g. file size or filename */
|
|
10
|
+
tooltip?: string;
|
|
11
|
+
/** The link that the button should go to */
|
|
12
|
+
href?: string;
|
|
13
|
+
/** Called when the button is clicked */
|
|
14
|
+
click?: (event: Event) => unknown;
|
|
15
|
+
/** Anchor target (only used if href is provided) */
|
|
16
|
+
target?: '_blank' | '_self';
|
|
17
|
+
/** The list of subactions (shown in a context menu) */
|
|
18
|
+
actions?: GalleryItemAction[];
|
|
19
|
+
}
|
|
20
|
+
export type GalleryDisplay = 'grid' | 'masonry' | 'masonry-row' | 'list' | 'slider' | 'slideshow' | 'lightbox';
|
|
21
|
+
/**
|
|
22
|
+
* Thumbnail size on the delightstack numeric scale: `'1'` is the standard,
|
|
23
|
+
* lower (`'0'`, `'00'`) is smaller, higher (`'2'`, `'3'`) is larger.
|
|
24
|
+
*/
|
|
25
|
+
export type GallerySize = '00' | '0' | '1' | '2' | '3';
|
|
26
|
+
/**
|
|
27
|
+
* Gap between gallery items on the delightstack numeric scale: `'0'` removes
|
|
28
|
+
* the gap, `'1'` is the standard, `'2'`/`'3'` are progressively larger.
|
|
29
|
+
*/
|
|
30
|
+
export type GallerySpacing = '0' | '1' | '2' | '3';
|
|
31
|
+
/**
|
|
32
|
+
* Corner radius of gallery items on the delightstack numeric scale: `'0'` is
|
|
33
|
+
* square, `'1'` is the standard, `'2'`/`'3'` are progressively rounder.
|
|
34
|
+
*/
|
|
35
|
+
export type GalleryRadius = '0' | '1' | '2' | '3';
|
|
36
|
+
export type GalleryItem = string | (Partial<CarouselItem> & {
|
|
37
|
+
favorite?: boolean;
|
|
38
|
+
});
|
|
39
|
+
import { type Snippet } from 'svelte';
|
|
40
|
+
declare const Gallery: Component<{
|
|
41
|
+
display?: GalleryDisplay;
|
|
42
|
+
size?: GallerySize;
|
|
43
|
+
spacing?: GallerySpacing;
|
|
44
|
+
radius?: GalleryRadius;
|
|
45
|
+
slide?: number;
|
|
46
|
+
fit?: "cover" | "contain";
|
|
47
|
+
items?: GalleryItem[];
|
|
48
|
+
duration?: number;
|
|
49
|
+
autoplay?: boolean;
|
|
50
|
+
autoplay_video?: boolean;
|
|
51
|
+
aspect_ratio?: string | undefined;
|
|
52
|
+
disable_fullscreen?: boolean;
|
|
53
|
+
inline?: boolean | undefined;
|
|
54
|
+
controls?: "default" | "inline" | "overlay" | "disable";
|
|
55
|
+
page?: number;
|
|
56
|
+
num_pages?: number;
|
|
57
|
+
meta_display?: "none" | "always" | "hover";
|
|
58
|
+
meta_display_fullscreen?: "none" | "always";
|
|
59
|
+
action_display?: "none" | "always" | "hover";
|
|
60
|
+
actions?: GalleryItemAction[][];
|
|
61
|
+
custom?: Snippet<[{
|
|
62
|
+
item: CarouselItem;
|
|
63
|
+
onload: () => void;
|
|
64
|
+
onerror: (err: unknown) => void;
|
|
65
|
+
active: boolean;
|
|
66
|
+
gesture_disabled: boolean;
|
|
67
|
+
}]> | undefined;
|
|
68
|
+
onclick?: undefined | ((event: MouseEvent | KeyboardEvent, index: number) => void | false);
|
|
69
|
+
style?: string;
|
|
70
|
+
}, {
|
|
71
|
+
open: (index: number, from?: HTMLElement) => void;
|
|
72
|
+
close: () => void;
|
|
73
|
+
goto: (i: number) => void;
|
|
74
|
+
next: (amount?: number) => void;
|
|
75
|
+
prev: (amount?: number) => void;
|
|
76
|
+
play: () => void;
|
|
77
|
+
pause: () => void;
|
|
78
|
+
toggleFullscreen: () => void;
|
|
79
|
+
}, "page" | "slide" | "num_pages">;
|
|
80
|
+
type Gallery = ReturnType<typeof Gallery>;
|
|
81
|
+
export default Gallery;
|
|
82
|
+
//# sourceMappingURL=Gallery.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Gallery.svelte.d.ts","sourceRoot":"","sources":["../../src/media/Gallery.svelte.ts"],"names":[],"mappings":"AAGC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,4FAA4F;AAC5F,MAAM,WAAW,iBAAiB;IACjC,gDAAgD;IAChD,IAAI,CAAC,EAAE,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAE1C,0DAA0D;IAC1D,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,oEAAoE;IACpE,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,4CAA4C;IAC5C,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,wCAAwC;IACxC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC;IAElC,oDAAoD;IACpD,MAAM,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC;IAE5B,uDAAuD;IACvD,OAAO,CAAC,EAAE,iBAAiB,EAAE,CAAC;CAC9B;AAED,MAAM,MAAM,cAAc,GACvB,MAAM,GACN,SAAS,GACT,aAAa,GACb,MAAM,GACN,QAAQ,GACR,WAAW,GACX,UAAU,CAAC;AAEd;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,IAAI,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AAEvD;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AAEnD;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AAElD,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,GAAG;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AAKrF,OAAO,EAA+B,KAAK,OAAO,EAAE,MAAM,QAAQ,CAAC;AA40BnE,QAAA,MAAM,OAAO;cA1hB2C,cAAc;WAAS,WAAW;cAAY,cAAc;aAAW,aAAa;YAAU,MAAM;UAAQ,OAAO,GAAG,SAAS;YAAU,WAAW,EAAE;eAAa,MAAM;eAAa,OAAO;qBAAmB,OAAO;mBAAiB,MAAM,GAAG,SAAS;yBAAuB,OAAO;aAAW,OAAO,GAAG,SAAS;eAAa,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS;WAAS,MAAM;gBAAc,MAAM;mBAAiB,MAAM,GAAG,QAAQ,GAAG,OAAO;8BAA4B,MAAM,GAAG,QAAQ;qBAAmB,MAAM,GAAG,QAAQ,GAAG,OAAO;cAAY,iBAAiB,EAAE,EAAE;aAAa,OAAO,CACjoB,CACC;QACC,IAAI,EAAE,YAAY,CAAC;QACnB,MAAM,EAAE,MAAM,IAAI,CAAC;QACnB,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;QAChC,MAAM,EAAE,OAAO,CAAC;QAChB,gBAAgB,EAAE,OAAO,CAAC;KAC1B,CACD,CACA,GACD,SAAS;cAAc,SAAS,GAChC,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,aAAa,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,KAAK,CAAC;YAAU,MAAM;;kBAwNlE,MAAM,SAAS,WAAW;;cAqB9B,MAAM;;;;;;kCAiS4B,CAAC;AACtD,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC;AAC1C,eAAe,OAAO,CAAC"}
|
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { decodeThumbHash } from './carousel';
|
|
3
|
+
|
|
4
|
+
type ImageState = 'loading' | 'loaded' | 'error';
|
|
5
|
+
|
|
6
|
+
let {
|
|
7
|
+
/** The image source URL (required) */
|
|
8
|
+
src,
|
|
9
|
+
|
|
10
|
+
/** The alt text for the image (required) */
|
|
11
|
+
alt,
|
|
12
|
+
|
|
13
|
+
/** The intrinsic width of the image in pixels */
|
|
14
|
+
width = undefined as number | undefined,
|
|
15
|
+
|
|
16
|
+
/** The intrinsic height of the image in pixels */
|
|
17
|
+
height = undefined as number | undefined,
|
|
18
|
+
|
|
19
|
+
/** The aspect ratio of the container (e.g. '16/9') to prevent CLS */
|
|
20
|
+
aspect_ratio = undefined as string | undefined,
|
|
21
|
+
|
|
22
|
+
/** How the image should be scaled within its container */
|
|
23
|
+
fit = 'cover' as 'cover' | 'contain' | 'fill' | 'none',
|
|
24
|
+
|
|
25
|
+
/** The position of the image within its container */
|
|
26
|
+
position = 'center',
|
|
27
|
+
|
|
28
|
+
/** Whether the image should be lazily loaded */
|
|
29
|
+
lazy = true,
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* A base64-encoded [ThumbHash](https://evanw.github.io/thumbhash/) used as
|
|
33
|
+
* a tiny blurred placeholder while the full image loads. The component
|
|
34
|
+
* decodes this internally. Takes precedence over {@link placeholder}.
|
|
35
|
+
*/
|
|
36
|
+
thumbhash = undefined as string | undefined,
|
|
37
|
+
|
|
38
|
+
/** A small placeholder image URL for the blur-up effect (used when no thumbhash) */
|
|
39
|
+
placeholder = undefined as string | undefined,
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* A solid background colour painted on the container — paints before any
|
|
43
|
+
* image data arrives (no JS needed). Useful for SSR where a dominant
|
|
44
|
+
* colour avoids any flash of empty box even before the thumbhash decodes.
|
|
45
|
+
* Accepts any CSS colour value.
|
|
46
|
+
*/
|
|
47
|
+
bg_color = undefined as string | undefined,
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Mark this image as above-the-fold for faster initial paint. When true,
|
|
51
|
+
* the image uses `loading="eager"` + `fetchpriority="high"`.
|
|
52
|
+
*/
|
|
53
|
+
priority = false,
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Retry-on-error. `true` retries 3 times with exponential backoff
|
|
57
|
+
* (1s, 4s, 9s); a number sets the max retry count; `false` (default)
|
|
58
|
+
* disables retries and transitions to the error state immediately.
|
|
59
|
+
*/
|
|
60
|
+
retry = false as boolean | number,
|
|
61
|
+
|
|
62
|
+
/** Error fallback: true = built-in broken-image SVG, string = fallback image URL, false = disabled */
|
|
63
|
+
fallback = false as string | boolean,
|
|
64
|
+
|
|
65
|
+
/** Responsive srcset attribute passed through to the img element */
|
|
66
|
+
srcset = undefined as string | undefined,
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Responsive sizes attribute. If omitted and the image is lazy with a
|
|
70
|
+
* srcset, defaults to `auto, 100vw` — modern browsers use the actual
|
|
71
|
+
* rendered width to pick a variant, older browsers fall back to 100vw.
|
|
72
|
+
*/
|
|
73
|
+
sizes = undefined as string | undefined,
|
|
74
|
+
|
|
75
|
+
/** Whether to show a skeleton shimmer animation while loading */
|
|
76
|
+
skeleton = false,
|
|
77
|
+
|
|
78
|
+
/** The ID of the element */
|
|
79
|
+
id = undefined as string | undefined,
|
|
80
|
+
|
|
81
|
+
/** Specifies a custom class name for the container element */
|
|
82
|
+
class: class_name = '',
|
|
83
|
+
|
|
84
|
+
/** A bindable reference to the root HTML element */
|
|
85
|
+
element = $bindable(undefined as HTMLElement | undefined),
|
|
86
|
+
|
|
87
|
+
/** Callback fired when the full image loads */
|
|
88
|
+
onload = undefined as
|
|
89
|
+
| undefined
|
|
90
|
+
| ((detail: { natural_width: number; natural_height: number }) => void),
|
|
91
|
+
|
|
92
|
+
/** Callback fired when the image fails to load */
|
|
93
|
+
onerror = undefined as undefined | ((detail: { error: Event }) => void),
|
|
94
|
+
}: {
|
|
95
|
+
src: string;
|
|
96
|
+
alt: string;
|
|
97
|
+
width?: number;
|
|
98
|
+
height?: number;
|
|
99
|
+
aspect_ratio?: string;
|
|
100
|
+
fit?: 'cover' | 'contain' | 'fill' | 'none';
|
|
101
|
+
position?: string;
|
|
102
|
+
lazy?: boolean;
|
|
103
|
+
thumbhash?: string;
|
|
104
|
+
placeholder?: string;
|
|
105
|
+
bg_color?: string;
|
|
106
|
+
priority?: boolean;
|
|
107
|
+
retry?: boolean | number;
|
|
108
|
+
fallback?: string | boolean;
|
|
109
|
+
srcset?: string;
|
|
110
|
+
sizes?: string;
|
|
111
|
+
skeleton?: boolean;
|
|
112
|
+
id?: string;
|
|
113
|
+
class?: string;
|
|
114
|
+
element?: HTMLElement | undefined;
|
|
115
|
+
onload?: (detail: { natural_width: number; natural_height: number }) => void;
|
|
116
|
+
onerror?: (detail: { error: Event }) => void;
|
|
117
|
+
} = $props();
|
|
118
|
+
|
|
119
|
+
let load_state = $state<ImageState>('loading');
|
|
120
|
+
let fading = $state(false);
|
|
121
|
+
let img_el = $state<HTMLImageElement | undefined>(undefined);
|
|
122
|
+
let retry_count = $state(0);
|
|
123
|
+
let retry_timer: ReturnType<typeof setTimeout> | undefined;
|
|
124
|
+
|
|
125
|
+
const max_retries = $derived(
|
|
126
|
+
retry === true ? 3 : typeof retry === 'number' ? Math.max(0, retry) : 0,
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
const placeholder_src = $derived(thumbhash ? decodeThumbHash(thumbhash) : placeholder);
|
|
130
|
+
const has_placeholder = $derived(!!placeholder_src);
|
|
131
|
+
const show_skeleton = $derived(
|
|
132
|
+
skeleton && load_state === 'loading' && !has_placeholder,
|
|
133
|
+
);
|
|
134
|
+
const show_placeholder = $derived(has_placeholder && load_state !== 'error');
|
|
135
|
+
const show_fallback = $derived(load_state === 'error' && fallback !== false);
|
|
136
|
+
const fallback_is_url = $derived(typeof fallback === 'string');
|
|
137
|
+
|
|
138
|
+
const computed_sizes = $derived(sizes ?? (lazy && srcset ? 'auto, 100vw' : undefined));
|
|
139
|
+
const computed_loading = $derived(priority ? 'eager' : lazy ? 'lazy' : 'eager');
|
|
140
|
+
|
|
141
|
+
const container_style = $derived.by(() => {
|
|
142
|
+
const parts: string[] = [];
|
|
143
|
+
if (aspect_ratio) parts.push(`aspect-ratio: ${aspect_ratio}`);
|
|
144
|
+
if (width) parts.push(`width: ${width}px`);
|
|
145
|
+
if (height && !aspect_ratio) parts.push(`height: ${height}px`);
|
|
146
|
+
if (bg_color) parts.push(`background-color: ${bg_color}`);
|
|
147
|
+
return parts.join('; ') || undefined;
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
function handleLoad(e: Event) {
|
|
151
|
+
load_state = 'loaded';
|
|
152
|
+
fading = false;
|
|
153
|
+
retry_count = 0;
|
|
154
|
+
clearTimeout(retry_timer);
|
|
155
|
+
if (onload) {
|
|
156
|
+
const img = e.target as HTMLImageElement;
|
|
157
|
+
onload({
|
|
158
|
+
natural_width: img.naturalWidth,
|
|
159
|
+
natural_height: img.naturalHeight,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function handleError(e: Event) {
|
|
165
|
+
// Opt-in retry — schedule another attempt with exponential backoff
|
|
166
|
+
// before transitioning to the error state.
|
|
167
|
+
if (retry_count < max_retries) {
|
|
168
|
+
retry_count++;
|
|
169
|
+
clearTimeout(retry_timer);
|
|
170
|
+
const delay = retry_count ** 2 * 1000; // 1s, 4s, 9s, …
|
|
171
|
+
retry_timer = setTimeout(() => {
|
|
172
|
+
if (!img_el || load_state === 'loaded') return;
|
|
173
|
+
const current = img_el.src;
|
|
174
|
+
img_el.src = '';
|
|
175
|
+
img_el.src = current;
|
|
176
|
+
}, delay);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
load_state = 'error';
|
|
180
|
+
fading = false;
|
|
181
|
+
if (onerror) {
|
|
182
|
+
onerror({ error: e });
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Reset retry counter when src changes so a new src gets its own retry budget.
|
|
187
|
+
$effect(() => {
|
|
188
|
+
void src;
|
|
189
|
+
retry_count = 0;
|
|
190
|
+
clearTimeout(retry_timer);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// Cancel any pending retry on unmount.
|
|
194
|
+
$effect(() => () => clearTimeout(retry_timer));
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Sync load_state with the actual `<img>` load load_state. Runs once on mount and again
|
|
198
|
+
* whenever `src` changes. For cached images, `el.complete` is `true`
|
|
199
|
+
* synchronously — without this, the SSR/hydration race causes cached images
|
|
200
|
+
* to stay invisible because the load event fires before Svelte attaches its
|
|
201
|
+
* onload listener.
|
|
202
|
+
*/
|
|
203
|
+
$effect(() => {
|
|
204
|
+
// No src yet (e.g. progressively assigned) — stay in the loading state so
|
|
205
|
+
// the placeholder/skeleton shows rather than falling through to `error`.
|
|
206
|
+
if (!src) {
|
|
207
|
+
load_state = 'loading';
|
|
208
|
+
fading = true;
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
if (!img_el) return;
|
|
212
|
+
if (img_el.complete && img_el.naturalWidth > 0) {
|
|
213
|
+
load_state = 'loaded';
|
|
214
|
+
fading = false;
|
|
215
|
+
} else if (img_el.complete && img_el.naturalWidth === 0) {
|
|
216
|
+
load_state = 'error';
|
|
217
|
+
fading = false;
|
|
218
|
+
} else {
|
|
219
|
+
load_state = 'loading';
|
|
220
|
+
fading = true;
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
</script>
|
|
224
|
+
|
|
225
|
+
<div
|
|
226
|
+
{id}
|
|
227
|
+
class={['image', class_name].filter(Boolean).join(' ')}
|
|
228
|
+
style={container_style}
|
|
229
|
+
bind:this={element}>
|
|
230
|
+
{#if show_skeleton}
|
|
231
|
+
<div class="skeleton"></div>
|
|
232
|
+
{/if}
|
|
233
|
+
|
|
234
|
+
{#if show_placeholder}
|
|
235
|
+
<img
|
|
236
|
+
class="placeholder"
|
|
237
|
+
src={placeholder_src}
|
|
238
|
+
alt=""
|
|
239
|
+
aria-hidden="true"
|
|
240
|
+
style:object-fit={fit}
|
|
241
|
+
style:object-position={position} />
|
|
242
|
+
{/if}
|
|
243
|
+
|
|
244
|
+
{#if src && load_state !== 'error'}
|
|
245
|
+
<img
|
|
246
|
+
class="main"
|
|
247
|
+
class:fading
|
|
248
|
+
bind:this={img_el}
|
|
249
|
+
{src}
|
|
250
|
+
{alt}
|
|
251
|
+
{width}
|
|
252
|
+
{height}
|
|
253
|
+
{srcset}
|
|
254
|
+
sizes={computed_sizes}
|
|
255
|
+
loading={computed_loading}
|
|
256
|
+
fetchpriority={priority ? 'high' : undefined}
|
|
257
|
+
style:object-fit={fit}
|
|
258
|
+
style:object-position={position}
|
|
259
|
+
onload={handleLoad}
|
|
260
|
+
onerror={handleError} />
|
|
261
|
+
{/if}
|
|
262
|
+
|
|
263
|
+
{#if show_fallback}
|
|
264
|
+
{#if fallback_is_url}
|
|
265
|
+
<img
|
|
266
|
+
class="fallback-img"
|
|
267
|
+
src={fallback as string}
|
|
268
|
+
{alt}
|
|
269
|
+
style:object-fit={fit}
|
|
270
|
+
style:object-position={position} />
|
|
271
|
+
{:else}
|
|
272
|
+
<div class="fallback">
|
|
273
|
+
<svg
|
|
274
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
275
|
+
viewBox="0 0 24 24"
|
|
276
|
+
fill="none"
|
|
277
|
+
stroke="currentColor"
|
|
278
|
+
stroke-width="1.5"
|
|
279
|
+
stroke-linecap="round"
|
|
280
|
+
stroke-linejoin="round"
|
|
281
|
+
class="fallback-icon"
|
|
282
|
+
aria-hidden="true">
|
|
283
|
+
<rect x="3" y="3" width="18" height="18" rx="2" ry="2" />
|
|
284
|
+
<circle cx="8.5" cy="8.5" r="1.5" />
|
|
285
|
+
<polyline points="21 15 16 10 5 21" />
|
|
286
|
+
<line x1="2" y1="2" x2="22" y2="22" />
|
|
287
|
+
</svg>
|
|
288
|
+
</div>
|
|
289
|
+
{/if}
|
|
290
|
+
{/if}
|
|
291
|
+
</div>
|
|
292
|
+
|
|
293
|
+
<style>
|
|
294
|
+
.image {
|
|
295
|
+
position: relative;
|
|
296
|
+
overflow: hidden;
|
|
297
|
+
display: grid;
|
|
298
|
+
width: 100%;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
.image > * {
|
|
302
|
+
grid-row: 1 / 1;
|
|
303
|
+
grid-column: 1 / 1;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/* Skeleton shimmer */
|
|
307
|
+
.skeleton {
|
|
308
|
+
position: relative;
|
|
309
|
+
overflow: hidden;
|
|
310
|
+
background: var(--skeleton-bg, rgb(from var(--color-text, #888) r g b / 0.1));
|
|
311
|
+
z-index: 0;
|
|
312
|
+
|
|
313
|
+
&::after {
|
|
314
|
+
content: '';
|
|
315
|
+
position: absolute;
|
|
316
|
+
inset: 0;
|
|
317
|
+
transform: translateX(-100%);
|
|
318
|
+
background-image: linear-gradient(
|
|
319
|
+
105deg,
|
|
320
|
+
transparent 25%,
|
|
321
|
+
var(--skeleton-sheen, rgb(from var(--color-text, #888) r g b / 0.12)) 50%,
|
|
322
|
+
transparent 75%
|
|
323
|
+
);
|
|
324
|
+
animation: delight-skeleton-shimmer var(--skeleton-duration, 2.4s) ease-in-out
|
|
325
|
+
infinite;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
@keyframes -global-delight-skeleton-shimmer {
|
|
330
|
+
0% {
|
|
331
|
+
transform: translateX(-100%);
|
|
332
|
+
}
|
|
333
|
+
55%,
|
|
334
|
+
100% {
|
|
335
|
+
transform: translateX(100%);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/* Blur-up placeholder — always renders underneath the main image so it shows
|
|
340
|
+
through while the main image is loading, and is covered once it paints.
|
|
341
|
+
`transform` makes it form a stacking context, so an explicit z-index is
|
|
342
|
+
required to keep it below the (later-painted but un-transformed) main image. */
|
|
343
|
+
.placeholder {
|
|
344
|
+
display: block;
|
|
345
|
+
width: 100%;
|
|
346
|
+
height: 100%;
|
|
347
|
+
filter: blur(20px);
|
|
348
|
+
transform: scale(1.1);
|
|
349
|
+
pointer-events: none;
|
|
350
|
+
z-index: 0;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/* Main image — defaults to opacity 1 so cached images paint instantly during
|
|
354
|
+
SSR. The `fading` class is added by JS only when the image isn't already
|
|
355
|
+
loaded; `onload` removes it, triggering the fade-in over the placeholder. */
|
|
356
|
+
.main {
|
|
357
|
+
display: block;
|
|
358
|
+
width: 100%;
|
|
359
|
+
height: 100%;
|
|
360
|
+
transition: opacity 300ms ease;
|
|
361
|
+
z-index: 1;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.main.fading {
|
|
365
|
+
opacity: 0;
|
|
366
|
+
transition: none;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
.fallback-img {
|
|
370
|
+
display: block;
|
|
371
|
+
width: 100%;
|
|
372
|
+
height: 100%;
|
|
373
|
+
z-index: 2;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
.fallback {
|
|
377
|
+
z-index: 2;
|
|
378
|
+
display: flex;
|
|
379
|
+
align-items: center;
|
|
380
|
+
justify-content: center;
|
|
381
|
+
background-color: var(--color-bg-muted, rgba(128, 128, 128, 0.1));
|
|
382
|
+
color: var(--color-text-muted, rgba(128, 128, 128, 0.6));
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.fallback-icon {
|
|
386
|
+
width: 2.5em;
|
|
387
|
+
height: 2.5em;
|
|
388
|
+
}
|
|
389
|
+
</style>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
type $$ComponentProps = {
|
|
2
|
+
src: string;
|
|
3
|
+
alt: string;
|
|
4
|
+
width?: number;
|
|
5
|
+
height?: number;
|
|
6
|
+
aspect_ratio?: string;
|
|
7
|
+
fit?: 'cover' | 'contain' | 'fill' | 'none';
|
|
8
|
+
position?: string;
|
|
9
|
+
lazy?: boolean;
|
|
10
|
+
thumbhash?: string;
|
|
11
|
+
placeholder?: string;
|
|
12
|
+
bg_color?: string;
|
|
13
|
+
priority?: boolean;
|
|
14
|
+
retry?: boolean | number;
|
|
15
|
+
fallback?: string | boolean;
|
|
16
|
+
srcset?: string;
|
|
17
|
+
sizes?: string;
|
|
18
|
+
skeleton?: boolean;
|
|
19
|
+
id?: string;
|
|
20
|
+
class?: string;
|
|
21
|
+
element?: HTMLElement | undefined;
|
|
22
|
+
onload?: (detail: {
|
|
23
|
+
natural_width: number;
|
|
24
|
+
natural_height: number;
|
|
25
|
+
}) => void;
|
|
26
|
+
onerror?: (detail: {
|
|
27
|
+
error: Event;
|
|
28
|
+
}) => void;
|
|
29
|
+
};
|
|
30
|
+
declare const Image: import("svelte").Component<$$ComponentProps, {}, "element">;
|
|
31
|
+
type Image = ReturnType<typeof Image>;
|
|
32
|
+
export default Image;
|
|
33
|
+
//# sourceMappingURL=Image.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Image.svelte.d.ts","sourceRoot":"","sources":["../../src/media/Image.svelte.ts"],"names":[],"mappings":"AAOC,KAAK,gBAAgB,GAAI;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,GAAG,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IAClC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC7E,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,KAAK,CAAA;KAAE,KAAK,IAAI,CAAC;CAC7C,CAAC;AA0OH,QAAA,MAAM,KAAK,6DAAwC,CAAC;AACpD,KAAK,KAAK,GAAG,UAAU,CAAC,OAAO,KAAK,CAAC,CAAC;AACtC,eAAe,KAAK,CAAC"}
|