@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,22 @@
|
|
|
1
|
+
type $$ComponentProps = {
|
|
2
|
+
before: string;
|
|
3
|
+
after: string;
|
|
4
|
+
before_alt?: string;
|
|
5
|
+
after_alt?: string;
|
|
6
|
+
position?: number;
|
|
7
|
+
vertical?: boolean;
|
|
8
|
+
show_labels?: boolean;
|
|
9
|
+
label_before?: string;
|
|
10
|
+
label_after?: string;
|
|
11
|
+
skeleton?: boolean;
|
|
12
|
+
snaps?: number[];
|
|
13
|
+
id?: string;
|
|
14
|
+
class?: string;
|
|
15
|
+
onchange?: (detail: {
|
|
16
|
+
position: number;
|
|
17
|
+
}) => void;
|
|
18
|
+
};
|
|
19
|
+
declare const Comparison: import("svelte").Component<$$ComponentProps, {}, "position">;
|
|
20
|
+
type Comparison = ReturnType<typeof Comparison>;
|
|
21
|
+
export default Comparison;
|
|
22
|
+
//# sourceMappingURL=Comparison.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Comparison.svelte.d.ts","sourceRoot":"","sources":["../../src/display/Comparison.svelte.ts"],"names":[],"mappings":"AAGC,KAAK,gBAAgB,GAAI;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAClD,CAAC;AA0VH,QAAA,MAAM,UAAU,8DAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { intersectionObserver } from '@delightstack/utilities';
|
|
3
|
+
import { untrack } from 'svelte';
|
|
4
|
+
|
|
5
|
+
type EasingFunction = (t: number) => number;
|
|
6
|
+
|
|
7
|
+
const propId = $props.id();
|
|
8
|
+
let {
|
|
9
|
+
/** The target numeric value to display. May be assigned later (e.g.
|
|
10
|
+
* while stats load) — pair with `skeleton` to show a placeholder
|
|
11
|
+
* until it arrives. */
|
|
12
|
+
value = undefined as number | undefined,
|
|
13
|
+
|
|
14
|
+
/** Animation duration in milliseconds */
|
|
15
|
+
duration = 5000,
|
|
16
|
+
|
|
17
|
+
/** Delay before animation starts in milliseconds */
|
|
18
|
+
delay = 0,
|
|
19
|
+
|
|
20
|
+
/** Number of decimal places to display */
|
|
21
|
+
decimals = 0,
|
|
22
|
+
|
|
23
|
+
/** Text to display before the number (rendered smaller, top-aligned) */
|
|
24
|
+
prefix = undefined as string | undefined,
|
|
25
|
+
|
|
26
|
+
/** Text to display after the number (rendered smaller, top-aligned) */
|
|
27
|
+
suffix = undefined as string | undefined,
|
|
28
|
+
|
|
29
|
+
/** Custom format function — takes precedence over Intl.NumberFormat */
|
|
30
|
+
format = undefined as ((value: number) => string) | undefined,
|
|
31
|
+
|
|
32
|
+
/** Whether to show thousands separators */
|
|
33
|
+
separator = true,
|
|
34
|
+
|
|
35
|
+
/** BCP 47 locale for number formatting */
|
|
36
|
+
locale = undefined as string | undefined,
|
|
37
|
+
|
|
38
|
+
/** Custom easing function (receives t in 0..1, returns 0..1) */
|
|
39
|
+
easing = undefined as EasingFunction | undefined,
|
|
40
|
+
|
|
41
|
+
/** Show a number-shaped shimmer placeholder while `value` is not yet
|
|
42
|
+
* available. It dismisses itself (and the count-up starts) as soon as
|
|
43
|
+
* `value` arrives. Width can be tuned via `--counter-skeleton-width`. */
|
|
44
|
+
skeleton = false,
|
|
45
|
+
|
|
46
|
+
/** Callback fired when the animation completes */
|
|
47
|
+
oncomplete = undefined as (() => void) | undefined,
|
|
48
|
+
|
|
49
|
+
/** Element ID */
|
|
50
|
+
id = propId,
|
|
51
|
+
|
|
52
|
+
/** Additional CSS classes */
|
|
53
|
+
class: class_name = '',
|
|
54
|
+
} = $props();
|
|
55
|
+
|
|
56
|
+
const easeFn = $derived(
|
|
57
|
+
easing ??
|
|
58
|
+
/** Heavy ease-out: fast start, slow finish. */
|
|
59
|
+
((t: number) => 1 - Math.pow(1 - t, Math.max(5, Math.min(50, duration * 0.006)))),
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
let has_animated = $state(false);
|
|
63
|
+
let is_animating = $state(false);
|
|
64
|
+
let has_intersected = $state(false);
|
|
65
|
+
|
|
66
|
+
/** Placeholder shows until the value exists; the count-up is deferred so
|
|
67
|
+
* it doesn't run invisibly behind the shimmer. */
|
|
68
|
+
const show_skeleton = $derived(skeleton && value == null);
|
|
69
|
+
/** Start at 0 so SSR and the initial client render match (no hydration jump):
|
|
70
|
+
* the count then animates up from 0 once the element scrolls into view. The
|
|
71
|
+
* true value is always exposed via `aria-label` for assistive tech. */
|
|
72
|
+
let display_value = $state(0);
|
|
73
|
+
let raf_id = $state(0);
|
|
74
|
+
|
|
75
|
+
let prefers_reduced_motion = $state(false);
|
|
76
|
+
$effect(() => {
|
|
77
|
+
if (typeof window !== 'undefined') {
|
|
78
|
+
const mql = window.matchMedia('(prefers-reduced-motion: reduce)');
|
|
79
|
+
prefers_reduced_motion = mql.matches;
|
|
80
|
+
const handler = (e: MediaQueryListEvent) => {
|
|
81
|
+
prefers_reduced_motion = e.matches;
|
|
82
|
+
};
|
|
83
|
+
mql.addEventListener('change', handler);
|
|
84
|
+
return () => mql.removeEventListener('change', handler);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
function formatNumber(n: number): string {
|
|
89
|
+
if (format) return format(n);
|
|
90
|
+
const options: Intl.NumberFormatOptions = {
|
|
91
|
+
minimumFractionDigits: decimals,
|
|
92
|
+
maximumFractionDigits: decimals,
|
|
93
|
+
useGrouping: separator,
|
|
94
|
+
};
|
|
95
|
+
return new Intl.NumberFormat(locale, options).format(n);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const formatted_value = $derived(formatNumber(display_value));
|
|
99
|
+
|
|
100
|
+
const aria_label_text = $derived(
|
|
101
|
+
value == null
|
|
102
|
+
? 'Loading'
|
|
103
|
+
: (prefix ?? '') + formatNumber(value) + (suffix ? ` ${suffix}` : ''),
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
function animateCount(from: number, to: number) {
|
|
107
|
+
if (raf_id) cancelAnimationFrame(raf_id);
|
|
108
|
+
is_animating = true;
|
|
109
|
+
const start_time = performance.now() + delay;
|
|
110
|
+
|
|
111
|
+
function step(now: number) {
|
|
112
|
+
const elapsed = now - start_time;
|
|
113
|
+
if (elapsed < 0) {
|
|
114
|
+
raf_id = requestAnimationFrame(step);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const progress = Math.min(elapsed / duration, 1);
|
|
118
|
+
const eased = easeFn(progress);
|
|
119
|
+
display_value = from + (to - from) * eased;
|
|
120
|
+
|
|
121
|
+
if (progress < 1) {
|
|
122
|
+
raf_id = requestAnimationFrame(step);
|
|
123
|
+
} else {
|
|
124
|
+
display_value = to;
|
|
125
|
+
is_animating = false;
|
|
126
|
+
raf_id = 0;
|
|
127
|
+
oncomplete?.();
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
raf_id = requestAnimationFrame(step);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function startAnimation() {
|
|
135
|
+
if (has_animated || value == null) return;
|
|
136
|
+
has_animated = true;
|
|
137
|
+
|
|
138
|
+
if (prefers_reduced_motion) {
|
|
139
|
+
display_value = value;
|
|
140
|
+
oncomplete?.();
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
animateCount(0, value);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Kick off the count-up once the counter is both on screen and has a real
|
|
148
|
+
// value (the skeleton may still be covering it when it first intersects).
|
|
149
|
+
// The animation itself is untracked: animateCount reads AND writes raf_id
|
|
150
|
+
// (and display_value), which would otherwise re-trigger this effect
|
|
151
|
+
// forever (effect_update_depth_exceeded).
|
|
152
|
+
$effect(() => {
|
|
153
|
+
if (!has_intersected || show_skeleton) return;
|
|
154
|
+
untrack(() => startAnimation());
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
$effect(() => {
|
|
158
|
+
const current_target = value;
|
|
159
|
+
const animated = untrack(() => has_animated);
|
|
160
|
+
const prev = untrack(() => display_value);
|
|
161
|
+
|
|
162
|
+
if (!animated || current_target == null) return;
|
|
163
|
+
|
|
164
|
+
if (prefers_reduced_motion) {
|
|
165
|
+
display_value = current_target;
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
untrack(() => animateCount(prev, current_target));
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
$effect(() => {
|
|
173
|
+
return () => {
|
|
174
|
+
if (raf_id) cancelAnimationFrame(raf_id);
|
|
175
|
+
};
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
export function restart() {
|
|
179
|
+
if (raf_id) cancelAnimationFrame(raf_id);
|
|
180
|
+
display_value = 0;
|
|
181
|
+
has_animated = false;
|
|
182
|
+
is_animating = false;
|
|
183
|
+
startAnimation();
|
|
184
|
+
}
|
|
185
|
+
</script>
|
|
186
|
+
|
|
187
|
+
<span
|
|
188
|
+
class={['counter', class_name].filter(Boolean).join(' ')}
|
|
189
|
+
class:skeleton={show_skeleton}
|
|
190
|
+
{id}
|
|
191
|
+
role="img"
|
|
192
|
+
aria-live="polite"
|
|
193
|
+
aria-busy={show_skeleton || undefined}
|
|
194
|
+
aria-label={aria_label_text}
|
|
195
|
+
{@attach intersectionObserver({ onintersectonce: () => (has_intersected = true) })}>
|
|
196
|
+
{#if prefix}<span class="affix prefix">{prefix}</span>{/if}
|
|
197
|
+
|
|
198
|
+
{#if show_skeleton}
|
|
199
|
+
<!-- Known affixes render for real; only the unknown number is a pill. -->
|
|
200
|
+
<span class="skeleton-pill"></span>
|
|
201
|
+
{:else}
|
|
202
|
+
<span class="value">{formatted_value}</span>
|
|
203
|
+
{/if}
|
|
204
|
+
|
|
205
|
+
{#if suffix}<span class="affix suffix">{suffix}</span>{/if}
|
|
206
|
+
</span>
|
|
207
|
+
|
|
208
|
+
<style>
|
|
209
|
+
.counter {
|
|
210
|
+
display: inline-flex;
|
|
211
|
+
align-items: flex-start;
|
|
212
|
+
font-variant-numeric: tabular-nums;
|
|
213
|
+
white-space: nowrap;
|
|
214
|
+
line-height: 1;
|
|
215
|
+
|
|
216
|
+
&.skeleton {
|
|
217
|
+
user-select: none;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.affix {
|
|
221
|
+
display: inline-block;
|
|
222
|
+
font-size: 0.5em;
|
|
223
|
+
line-height: 1;
|
|
224
|
+
font-weight: 500;
|
|
225
|
+
opacity: 0.85;
|
|
226
|
+
align-self: flex-start;
|
|
227
|
+
padding-top: 0.15em;
|
|
228
|
+
}
|
|
229
|
+
.prefix {
|
|
230
|
+
margin-right: 0.1em;
|
|
231
|
+
}
|
|
232
|
+
.suffix {
|
|
233
|
+
margin-left: 0.1em;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.value {
|
|
237
|
+
display: inline;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/* A digits-sized pill standing in for the unknown number. `ch` units track
|
|
242
|
+
the counter's own font, so a 3rem stat gets a proportionally large pill. */
|
|
243
|
+
.skeleton-pill {
|
|
244
|
+
display: inline-block;
|
|
245
|
+
width: var(--counter-skeleton-width, 4ch);
|
|
246
|
+
height: 0.8em;
|
|
247
|
+
margin-top: 0.1em;
|
|
248
|
+
border-radius: var(--radius-full, 1e5px);
|
|
249
|
+
position: relative;
|
|
250
|
+
overflow: hidden;
|
|
251
|
+
background: var(--skeleton-bg, rgb(from var(--color-text, #888) r g b / 0.1));
|
|
252
|
+
|
|
253
|
+
&::after {
|
|
254
|
+
content: '';
|
|
255
|
+
position: absolute;
|
|
256
|
+
inset: 0;
|
|
257
|
+
transform: translateX(-100%);
|
|
258
|
+
background-image: linear-gradient(
|
|
259
|
+
105deg,
|
|
260
|
+
transparent 25%,
|
|
261
|
+
var(--skeleton-sheen, rgb(from var(--color-text, #888) r g b / 0.12)) 50%,
|
|
262
|
+
transparent 75%
|
|
263
|
+
);
|
|
264
|
+
animation: delight-skeleton-shimmer var(--skeleton-duration, 2.4s) ease-in-out
|
|
265
|
+
infinite;
|
|
266
|
+
animation-delay: var(--shimmer-delay, 0s);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
@keyframes -global-delight-skeleton-shimmer {
|
|
271
|
+
0% {
|
|
272
|
+
transform: translateX(-100%);
|
|
273
|
+
}
|
|
274
|
+
55%,
|
|
275
|
+
100% {
|
|
276
|
+
transform: translateX(100%);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
@media (prefers-reduced-motion: reduce) {
|
|
281
|
+
.skeleton-pill::after {
|
|
282
|
+
animation: none;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
</style>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
declare const Counter: import("svelte").Component<{
|
|
2
|
+
value?: number | undefined;
|
|
3
|
+
duration?: number;
|
|
4
|
+
delay?: number;
|
|
5
|
+
decimals?: number;
|
|
6
|
+
prefix?: string | undefined;
|
|
7
|
+
suffix?: string | undefined;
|
|
8
|
+
format?: ((value: number) => string) | undefined;
|
|
9
|
+
separator?: boolean;
|
|
10
|
+
locale?: string | undefined;
|
|
11
|
+
easing?: ((t: number) => number) | undefined;
|
|
12
|
+
skeleton?: boolean;
|
|
13
|
+
oncomplete?: (() => void) | undefined;
|
|
14
|
+
id?: string;
|
|
15
|
+
class?: string;
|
|
16
|
+
}, {
|
|
17
|
+
restart: () => void;
|
|
18
|
+
}, "">;
|
|
19
|
+
type Counter = ReturnType<typeof Counter>;
|
|
20
|
+
export default Counter;
|
|
21
|
+
//# sourceMappingURL=Counter.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Counter.svelte.d.ts","sourceRoot":"","sources":["../../src/display/Counter.svelte.ts"],"names":[],"mappings":"AAiNA,QAAA,MAAM,OAAO;YArMoE,MAAM,GAAG,SAAS;eAAa,MAAM;YAAU,MAAM;eAAa,MAAM;aAAW,MAAM,GAAG,SAAS;aAAW,MAAM,GAAG,SAAS;aAAW,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,GAAG,SAAS;gBAAc,OAAO;aAAW,MAAM,GAAG,SAAS;aAAW,KAFvS,MAAM,KAAK,MAAM,IAEuS,SAAS;eAAa,OAAO;iBAAe,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS;;YAA8B,MAAM;;;MAqMtY,CAAC;AACtD,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC;AAC1C,eAAe,OAAO,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { type Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
/** Whether the content is expanded (visible). Bindable */
|
|
6
|
+
show = $bindable(false),
|
|
7
|
+
|
|
8
|
+
/** Additional inline styles applied to the expanding container */
|
|
9
|
+
style = '',
|
|
10
|
+
|
|
11
|
+
/** The content to expand/collapse */
|
|
12
|
+
children = undefined as undefined | Snippet,
|
|
13
|
+
} = $props();
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
{#if children}
|
|
17
|
+
<div class="expand" class:show inert={!show} {style}>
|
|
18
|
+
<div>{@render children()}</div>
|
|
19
|
+
</div>
|
|
20
|
+
{/if}
|
|
21
|
+
|
|
22
|
+
<style>
|
|
23
|
+
.expand {
|
|
24
|
+
display: grid;
|
|
25
|
+
grid-template-rows: min-content 0fr;
|
|
26
|
+
transition:
|
|
27
|
+
grid-template-rows 300ms cubic-bezier(0.23, 1, 0.32, 1),
|
|
28
|
+
opacity 200ms;
|
|
29
|
+
opacity: 0;
|
|
30
|
+
&::before {
|
|
31
|
+
content: '';
|
|
32
|
+
}
|
|
33
|
+
:global(> *) {
|
|
34
|
+
visibility: hidden;
|
|
35
|
+
overflow: hidden;
|
|
36
|
+
transition-behavior: allow-discrete;
|
|
37
|
+
transition: visibility 0ms 200ms;
|
|
38
|
+
}
|
|
39
|
+
&.show {
|
|
40
|
+
opacity: 1;
|
|
41
|
+
grid-template-rows: min-content 1fr;
|
|
42
|
+
:global(> *) {
|
|
43
|
+
visibility: visible;
|
|
44
|
+
transition: visibility 0ms;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
</style>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Snippet } from 'svelte';
|
|
2
|
+
declare const Expand: import("svelte").Component<{
|
|
3
|
+
show?: boolean;
|
|
4
|
+
style?: string;
|
|
5
|
+
children?: undefined | Snippet;
|
|
6
|
+
}, {}, "show">;
|
|
7
|
+
type Expand = ReturnType<typeof Expand>;
|
|
8
|
+
export default Expand;
|
|
9
|
+
//# sourceMappingURL=Expand.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Expand.svelte.d.ts","sourceRoot":"","sources":["../../src/display/Expand.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,QAAQ,CAAC;AA2BtC,QAAA,MAAM,MAAM;WAxByC,OAAO;YAAU,MAAM;eAAa,SAAS,GAAG,OAAO;cAwBxD,CAAC;AACrD,KAAK,MAAM,GAAG,UAAU,CAAC,OAAO,MAAM,CAAC,CAAC;AACxC,eAAe,MAAM,CAAC"}
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
export interface ListContext {
|
|
3
|
+
/** How items in the list behave (plain text, buttons, or selection controls) */
|
|
4
|
+
type: 'button' | 'text' | 'radio' | 'checkbox' | 'toggle';
|
|
5
|
+
/** Indices of the currently selected items */
|
|
6
|
+
value: number[];
|
|
7
|
+
/** Whether the list uses dense (compact) spacing */
|
|
8
|
+
dense: boolean;
|
|
9
|
+
/** Whether the list uses comfortable (roomy) spacing */
|
|
10
|
+
comfortable: boolean;
|
|
11
|
+
/** Whether the whole list is disabled */
|
|
12
|
+
disabled: boolean;
|
|
13
|
+
/** The nesting depth of this list (drives indentation of nested lists) */
|
|
14
|
+
level: number;
|
|
15
|
+
/** Unique ID of the list element */
|
|
16
|
+
id: string;
|
|
17
|
+
}
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<script lang="ts">
|
|
21
|
+
import { onFocusWithin } from '@delightstack/utilities';
|
|
22
|
+
import { getContext, setContext, type Snippet } from 'svelte';
|
|
23
|
+
|
|
24
|
+
const propId = $props.id();
|
|
25
|
+
let {
|
|
26
|
+
/** Whether the list should display in a condensed view (less padding) */
|
|
27
|
+
dense = false,
|
|
28
|
+
|
|
29
|
+
/** Whether the form should display in an expanded view (more padding) */
|
|
30
|
+
comfortable = false,
|
|
31
|
+
|
|
32
|
+
/** Whether the list sits on a subtle filled surface (the iOS "inset
|
|
33
|
+
* grouped" look). Off by default — the list is transparent so it
|
|
34
|
+
* composes onto any surface. Combine with `outline` for a defined card. */
|
|
35
|
+
filled = false,
|
|
36
|
+
|
|
37
|
+
/** Whether the list has a 1px outline + rounded corners (transparent
|
|
38
|
+
* fill). Gives visible rounded corners without imposing a surface fill. */
|
|
39
|
+
outline = false,
|
|
40
|
+
|
|
41
|
+
/** Whether the buttons/checkboxes/radios should be disabled */
|
|
42
|
+
disabled = false,
|
|
43
|
+
|
|
44
|
+
/** Whether the field has been touched (and blurred) */
|
|
45
|
+
touched = $bindable(false) as boolean,
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The type of items in the list.
|
|
49
|
+
* @default 'button'
|
|
50
|
+
* `button` - A list of buttons that can be clicked
|
|
51
|
+
* `text` - A list of non-interactive text
|
|
52
|
+
* `radio` - A list of radio buttons (only one can be selected)
|
|
53
|
+
* `checkbox` - A list of checkboxes (multiple can be selected)
|
|
54
|
+
* `toggle` - A list of toggle switches (multiple can be selected)
|
|
55
|
+
*/
|
|
56
|
+
type = 'button' as 'button' | 'text' | 'radio' | 'checkbox' | 'toggle',
|
|
57
|
+
|
|
58
|
+
/** The list of indexes that have been selected (can be multiple for checkbox and only one for radio)*/
|
|
59
|
+
value = $bindable([]) as number[],
|
|
60
|
+
|
|
61
|
+
/** The css amount (@example '16px') to pad the list items in the X direction */
|
|
62
|
+
padding_x = undefined as string | undefined,
|
|
63
|
+
|
|
64
|
+
/** The css amount (@example '16px') to pad the list items in the Y direction */
|
|
65
|
+
padding_y = undefined as string | undefined,
|
|
66
|
+
|
|
67
|
+
/** Whether to show a skeleton loading placeholder instead of the items */
|
|
68
|
+
skeleton = false,
|
|
69
|
+
|
|
70
|
+
/** Number of skeleton rows to render when `skeleton` is true */
|
|
71
|
+
skeleton_count = 5,
|
|
72
|
+
|
|
73
|
+
/** The css style string added to the component from the parent */
|
|
74
|
+
style = '',
|
|
75
|
+
|
|
76
|
+
/** The ID of the select element. @defaults to a random ID */
|
|
77
|
+
id = propId,
|
|
78
|
+
|
|
79
|
+
/** Specifies a custom class name for the container element */
|
|
80
|
+
class: class_name = '',
|
|
81
|
+
|
|
82
|
+
/** The child elements to display inside the element */
|
|
83
|
+
children = undefined as undefined | Snippet,
|
|
84
|
+
|
|
85
|
+
/** Called when the field is touched */
|
|
86
|
+
ontouch = undefined as (() => void) | undefined,
|
|
87
|
+
|
|
88
|
+
/** Called when the value changes */
|
|
89
|
+
onchange = undefined as ((val: number[]) => void) | undefined,
|
|
90
|
+
} = $props();
|
|
91
|
+
|
|
92
|
+
let element = $state<HTMLElement | undefined>(undefined);
|
|
93
|
+
const parentContext = getContext<ListContext | undefined>('list');
|
|
94
|
+
|
|
95
|
+
// Emit the necessary events when the field is touched or dirty or value changes
|
|
96
|
+
$effect(() => {
|
|
97
|
+
if (touched) ontouch?.();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const context = $state({
|
|
101
|
+
type,
|
|
102
|
+
value,
|
|
103
|
+
dense,
|
|
104
|
+
comfortable,
|
|
105
|
+
disabled,
|
|
106
|
+
id,
|
|
107
|
+
...parentContext,
|
|
108
|
+
level: (parentContext?.level || 0) + 1,
|
|
109
|
+
});
|
|
110
|
+
setContext<ListContext>('list', context);
|
|
111
|
+
$effect(() => {
|
|
112
|
+
context.type = parentContext?.type ?? type;
|
|
113
|
+
context.value = parentContext?.value ?? value;
|
|
114
|
+
context.dense = parentContext?.dense ?? dense;
|
|
115
|
+
context.comfortable = parentContext?.comfortable ?? comfortable;
|
|
116
|
+
context.disabled = parentContext?.disabled ?? disabled;
|
|
117
|
+
context.id = parentContext?.id ?? id;
|
|
118
|
+
context.level = (parentContext?.level || 0) + 1;
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
function handleChangeEvent(e: Event) {
|
|
122
|
+
const target = e.target as HTMLInputElement;
|
|
123
|
+
if (!target || (target.type !== 'checkbox' && target.type !== 'radio')) return;
|
|
124
|
+
let hostChild: HTMLElement | null = target;
|
|
125
|
+
while (hostChild && hostChild.parentElement !== element) {
|
|
126
|
+
hostChild = hostChild.parentElement;
|
|
127
|
+
}
|
|
128
|
+
if (!hostChild || !element) return;
|
|
129
|
+
const index = Array.from(element.children).indexOf(hostChild);
|
|
130
|
+
let tempSelected = type === 'radio' ? [index] : [...value].filter((i) => i !== index);
|
|
131
|
+
if (type !== 'radio' && target.checked) {
|
|
132
|
+
tempSelected = [...tempSelected, index];
|
|
133
|
+
}
|
|
134
|
+
if (JSON.stringify(tempSelected) === JSON.stringify(value)) return;
|
|
135
|
+
value = tempSelected;
|
|
136
|
+
if (onchange) onchange(value);
|
|
137
|
+
}
|
|
138
|
+
</script>
|
|
139
|
+
|
|
140
|
+
{#if skeleton && !parentContext?.level}
|
|
141
|
+
<ul
|
|
142
|
+
class={['list', 'skeleton', class_name].filter(Boolean).join(' ')}
|
|
143
|
+
class:dense
|
|
144
|
+
class:comfortable
|
|
145
|
+
class:filled
|
|
146
|
+
class:outline
|
|
147
|
+
style:--list-pad-x={padding_x}
|
|
148
|
+
style:--list-pad-y={padding_y}
|
|
149
|
+
{style}
|
|
150
|
+
aria-hidden="true">
|
|
151
|
+
{#each { length: skeleton_count } as _, i}
|
|
152
|
+
<li class="skeleton-item" style:--shimmer-delay="{i * 120}ms">
|
|
153
|
+
<span class="skeleton-bar" style:width={`${55 + ((i * 37) % 35)}%`}></span>
|
|
154
|
+
</li>
|
|
155
|
+
{/each}
|
|
156
|
+
</ul>
|
|
157
|
+
{:else if !parentContext?.level}
|
|
158
|
+
<ul
|
|
159
|
+
class={['list', class_name].filter(Boolean).join(' ')}
|
|
160
|
+
class:dense
|
|
161
|
+
class:comfortable
|
|
162
|
+
class:filled
|
|
163
|
+
class:outline
|
|
164
|
+
class:disabled
|
|
165
|
+
style:--list-pad-x={padding_x}
|
|
166
|
+
style:--list-pad-y={padding_y}
|
|
167
|
+
{@attach onFocusWithin({
|
|
168
|
+
onfocuswithin: () => touched || (touched = true),
|
|
169
|
+
})}
|
|
170
|
+
{style}
|
|
171
|
+
bind:this={element}
|
|
172
|
+
onchange={handleChangeEvent}>
|
|
173
|
+
{#if children}{@render children()}{/if}
|
|
174
|
+
</ul>
|
|
175
|
+
{:else if children}{@render children()}{/if}
|
|
176
|
+
|
|
177
|
+
<style>
|
|
178
|
+
ul {
|
|
179
|
+
--_radius: calc(var(--radius-lg) * 1.5);
|
|
180
|
+
--border-inset: 6px;
|
|
181
|
+
border-radius: var(--_radius);
|
|
182
|
+
@supports (corner-shape: superellipse(var(--squircle-ratio, 2))) {
|
|
183
|
+
corner-shape: superellipse(var(--squircle-ratio, 2));
|
|
184
|
+
border-radius: calc(var(--_radius) * var(--squircle-ratio, 2));
|
|
185
|
+
}
|
|
186
|
+
padding: 0;
|
|
187
|
+
margin: 0;
|
|
188
|
+
/* Transparent by default so the list composes onto any surface; the
|
|
189
|
+
rounded corners only become visible with `filled` or `outline`. The
|
|
190
|
+
item hover/active highlights stay rounded regardless. */
|
|
191
|
+
background-color: transparent;
|
|
192
|
+
perspective: 100px;
|
|
193
|
+
&.filled {
|
|
194
|
+
background-color: var(--color-bg-active);
|
|
195
|
+
}
|
|
196
|
+
&.outline {
|
|
197
|
+
border: 1px solid var(--color-border);
|
|
198
|
+
}
|
|
199
|
+
&.disabled {
|
|
200
|
+
color: var(--color-text-disabled);
|
|
201
|
+
cursor: not-allowed;
|
|
202
|
+
}
|
|
203
|
+
&.dense {
|
|
204
|
+
--border-inset: 4px;
|
|
205
|
+
--_radius: var(--radius-lg);
|
|
206
|
+
|
|
207
|
+
.skeleton-item {
|
|
208
|
+
min-height: 2.5rem;
|
|
209
|
+
padding: 0 1rem;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
&.comfortable {
|
|
213
|
+
--border-inset: 8px;
|
|
214
|
+
--_radius: var(--radius-xl);
|
|
215
|
+
|
|
216
|
+
.skeleton-item {
|
|
217
|
+
min-height: 3.5rem;
|
|
218
|
+
padding: 0 calc(2rem + var(--list-pad-x, 0px));
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
&.skeleton {
|
|
222
|
+
/* Lists fill their container. In a definite-width parent this spans the
|
|
223
|
+
* full width (so skeleton %-width bars and rows lay out correctly); in a
|
|
224
|
+
* shrink-to-fit parent (e.g. a popover) it collapses to content width. */
|
|
225
|
+
width: 100%;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
:global(> li:first-child) {
|
|
229
|
+
border-top-left-radius: var(--_radius);
|
|
230
|
+
border-top-right-radius: var(--_radius);
|
|
231
|
+
@supports (corner-shape: superellipse(var(--squircle-ratio, 2))) {
|
|
232
|
+
corner-shape: superellipse(var(--squircle-ratio, 2));
|
|
233
|
+
border-top-left-radius: calc(var(--_radius) * var(--squircle-ratio, 2));
|
|
234
|
+
border-top-right-radius: calc(var(--_radius) * var(--squircle-ratio, 2));
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
:global(> li:last-child) {
|
|
238
|
+
border-bottom-left-radius: var(--_radius);
|
|
239
|
+
border-bottom-right-radius: var(--_radius);
|
|
240
|
+
@supports (corner-shape: superellipse(var(--squircle-ratio, 2))) {
|
|
241
|
+
corner-shape: superellipse(var(--squircle-ratio, 2));
|
|
242
|
+
border-bottom-left-radius: calc(var(--_radius) * var(--squircle-ratio, 2));
|
|
243
|
+
border-bottom-right-radius: calc(var(--_radius) * var(--squircle-ratio, 2));
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/* Mirrors the real ListItem row metrics (min-height + inline padding,
|
|
249
|
+
incl. dense/comfortable) so the swap to loaded items doesn't shift. */
|
|
250
|
+
.skeleton-item {
|
|
251
|
+
list-style: none;
|
|
252
|
+
display: flex;
|
|
253
|
+
align-items: center;
|
|
254
|
+
min-height: 3rem;
|
|
255
|
+
padding: 0 calc(1.5rem + var(--list-pad-x, 0px));
|
|
256
|
+
}
|
|
257
|
+
.skeleton-bar {
|
|
258
|
+
height: 0.7em;
|
|
259
|
+
border-radius: var(--radius-full, 1e5px);
|
|
260
|
+
position: relative;
|
|
261
|
+
overflow: hidden;
|
|
262
|
+
background: var(--skeleton-bg, rgb(from var(--color-text, #888) r g b / 0.1));
|
|
263
|
+
|
|
264
|
+
&::after {
|
|
265
|
+
content: '';
|
|
266
|
+
position: absolute;
|
|
267
|
+
inset: 0;
|
|
268
|
+
transform: translateX(-100%);
|
|
269
|
+
background-image: linear-gradient(
|
|
270
|
+
105deg,
|
|
271
|
+
transparent 25%,
|
|
272
|
+
var(--skeleton-sheen, rgb(from var(--color-text, #888) r g b / 0.12)) 50%,
|
|
273
|
+
transparent 75%
|
|
274
|
+
);
|
|
275
|
+
animation: delight-skeleton-shimmer var(--skeleton-duration, 2.4s) ease-in-out
|
|
276
|
+
infinite;
|
|
277
|
+
animation-delay: var(--shimmer-delay, 0s);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
@keyframes -global-delight-skeleton-shimmer {
|
|
281
|
+
0% {
|
|
282
|
+
transform: translateX(-100%);
|
|
283
|
+
}
|
|
284
|
+
55%,
|
|
285
|
+
100% {
|
|
286
|
+
transform: translateX(100%);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
@media (prefers-reduced-motion: reduce) {
|
|
290
|
+
.skeleton-bar::after {
|
|
291
|
+
animation: none;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
</style>
|