sileo-solid 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/dist/index.mjs ADDED
@@ -0,0 +1,824 @@
1
+ function __insertCSS(code) {
2
+ if (!code || typeof document == 'undefined') return
3
+ let head = document.head || document.getElementsByTagName('head')[0]
4
+ let style = document.createElement('style')
5
+ style.type = 'text/css'
6
+ head.appendChild(style)
7
+ ;style.styleSheet ? (style.styleSheet.cssText = code) : style.appendChild(document.createTextNode(code))
8
+ }
9
+
10
+ import { createMemo, createSignal, createEffect, onCleanup, onMount, Show, For } from 'solid-js';
11
+ import 'motion';
12
+
13
+ __insertCSS(":root{--sileo-spring-easing:linear(\r\n\t\t0,\r\n\t\t0.002 0.6%,\r\n\t\t0.007 1.2%,\r\n\t\t0.015 1.8%,\r\n\t\t0.026 2.4%,\r\n\t\t0.041 3.1%,\r\n\t\t0.06 3.8%,\r\n\t\t0.108 5.3%,\r\n\t\t0.157 6.6%,\r\n\t\t0.214 8%,\r\n\t\t0.467 13.7%,\r\n\t\t0.577 16.3%,\r\n\t\t0.631 17.7%,\r\n\t\t0.682 19.1%,\r\n\t\t0.73 20.5%,\r\n\t\t0.771 21.8%,\r\n\t\t0.808 23.1%,\r\n\t\t0.844 24.5%,\r\n\t\t0.874 25.8%,\r\n\t\t0.903 27.2%,\r\n\t\t0.928 28.6%,\r\n\t\t0.952 30.1%,\r\n\t\t0.972 31.6%,\r\n\t\t0.988 33.1%,\r\n\t\t1.01 35.7%,\r\n\t\t1.025 38.5%,\r\n\t\t1.034 41.6%,\r\n\t\t1.038 45%,\r\n\t\t1.035 50.1%,\r\n\t\t1.012 64.2%,\r\n\t\t1.003 73%,\r\n\t\t0.999 83.7%,\r\n\t\t1\r\n\t);--sileo-duration:600ms;--sileo-height:40px;--sileo-width:350px;--sileo-state-success:oklch(0.723 0.219 142.136);--sileo-state-loading:oklch(0.556 0 0);--sileo-state-error:oklch(0.637 0.237 25.331);--sileo-state-warning:oklch(0.795 0.184 86.047);--sileo-state-info:oklch(0.685 0.169 237.323);--sileo-state-action:oklch(0.623 0.214 259.815)}[data-sileo-toast]{position:relative;cursor:pointer;pointer-events:auto;touch-action:none;border:0;background:0 0;padding:0;width:var(--sileo-width);height:var(--_h,var(--sileo-height));opacity:0;transform:translateZ(0) scale(.95);transform-origin:center;contain:layout style;overflow:visible}[data-sileo-toast][data-state=loading]{cursor:default}[data-sileo-toast][data-ready=true]{opacity:1;transform:translateZ(0) scale(1);transition:transform calc(var(--sileo-duration) * .66) var(--sileo-spring-easing),opacity calc(var(--sileo-duration) * .66) var(--sileo-spring-easing),margin-bottom calc(var(--sileo-duration) * .66) var(--sileo-spring-easing),margin-top calc(var(--sileo-duration) * .66) var(--sileo-spring-easing),height var(--sileo-duration) var(--sileo-spring-easing)}[data-sileo-viewport][data-position^=top] [data-sileo-toast]:not([data-ready=true]){transform:translateY(-6px) scale(.95)}[data-sileo-viewport][data-position^=bottom] [data-sileo-toast]:not([data-ready=true]){transform:translateY(6px) scale(.95)}[data-sileo-toast][data-ready=true][data-exiting=true]{opacity:0;pointer-events:none}[data-sileo-viewport][data-position^=top] [data-sileo-toast][data-ready=true][data-exiting=true]{transform:translateY(-6px) scale(.95)}[data-sileo-viewport][data-position^=bottom] [data-sileo-toast][data-ready=true][data-exiting=true]{transform:translateY(6px) scale(.95)}[data-sileo-canvas]{position:absolute;left:0;right:0;pointer-events:none;transform:translateZ(0);contain:layout style;overflow:visible}[data-sileo-canvas][data-edge=top]{bottom:0;transform:scaleY(-1) translateZ(0)}[data-sileo-canvas][data-edge=bottom]{top:0}[data-sileo-svg]{overflow:visible}[data-sileo-header]{position:absolute;z-index:20;display:flex;align-items:center;padding:.5rem;height:var(--sileo-height);overflow:hidden;left:var(--_px,0);transform:var(--_ht);max-width:var(--_pw)}[data-sileo-toast][data-ready=true] [data-sileo-header]{transition:transform var(--sileo-duration) var(--sileo-spring-easing),left var(--sileo-duration) var(--sileo-spring-easing),max-width var(--sileo-duration) var(--sileo-spring-easing)}[data-sileo-header][data-edge=top]{bottom:0}[data-sileo-header][data-edge=bottom]{top:0}[data-sileo-header-stack]{position:relative;display:inline-flex;align-items:center;height:100%}[data-sileo-header-inner]{display:flex;align-items:center;gap:.5rem;white-space:nowrap;opacity:1;filter:blur(0px);transform:translateZ(0)}[data-sileo-header-inner][data-layer=current]{position:relative;z-index:1;animation:sileo-header-enter var(--sileo-duration) var(--sileo-spring-easing) both}[data-sileo-header-inner][data-exiting=true],[data-sileo-header-inner][data-layer=current]:not(:only-child){will-change:opacity,filter}[data-sileo-header-inner][data-layer=prev]{position:absolute;left:0;top:0;z-index:0;pointer-events:none}[data-sileo-header-inner][data-exiting=true]{animation:sileo-header-exit calc(var(--sileo-duration) * .7) ease forwards}[data-sileo-badge]{display:flex;height:24px;width:24px;flex-shrink:0;align-items:center;justify-content:center;padding:2px;box-sizing:border-box;border-radius:9999px;color:var(--sileo-tone,currentColor);background-color:var(--sileo-tone-bg,transparent)}[data-sileo-title]{font-size:.825rem;line-height:1rem;font-weight:500;text-transform:capitalize;color:var(--sileo-tone,currentColor)}:is([data-sileo-badge],[data-sileo-title],[data-sileo-button])[data-state]{--_c:var(--sileo-state-success)}:is(\r[data-sileo-badge],[data-sileo-title],[data-sileo-button]\r)[data-state=loading]{--_c:var(--sileo-state-loading)}:is(\r[data-sileo-badge],[data-sileo-title],[data-sileo-button]\r)[data-state=error]{--_c:var(--sileo-state-error)}:is(\r[data-sileo-badge],[data-sileo-title],[data-sileo-button]\r)[data-state=warning]{--_c:var(--sileo-state-warning)}:is(\r[data-sileo-badge],[data-sileo-title],[data-sileo-button]\r)[data-state=info]{--_c:var(--sileo-state-info)}:is(\r[data-sileo-badge],[data-sileo-title],[data-sileo-button]\r)[data-state=action]{--_c:var(--sileo-state-action)}:is([data-sileo-badge],[data-sileo-title])[data-state]{--sileo-tone:var(--_c);--sileo-tone-bg:color-mix(in oklch, var(--_c) 20%, transparent)}[data-sileo-content]{position:absolute;left:0;z-index:10;width:100%;pointer-events:none;opacity:var(--_co, 0)}[data-sileo-content]:not([data-visible=true]){content-visibility:hidden}[data-sileo-toast][data-ready=true] [data-sileo-content]{transition:opacity calc(var(--sileo-duration) * .08) ease calc(var(--sileo-duration) * .04)}[data-sileo-content][data-edge=top]{top:0}[data-sileo-content][data-edge=bottom]{top:var(--sileo-height)}[data-sileo-content][data-visible=true]{pointer-events:auto}[data-sileo-toast][data-ready=true] [data-sileo-content][data-visible=true]{transition:opacity calc(var(--sileo-duration) * .6) ease calc(var(--sileo-duration) * .3)}[data-sileo-description]{width:100%;text-align:left;padding:1rem;font-size:.875rem;line-height:1.25rem;contain:layout style paint;content-visibility:auto;color:rgba(0,0,0,.7)}[data-sileo-button]{display:flex;align-items:center;justify-content:center;height:1.75rem;padding:0 .625rem;margin-top:.75rem;border-radius:9999px;border:0;font-size:.75rem;font-weight:500;cursor:pointer;color:var(--sileo-btn-color,currentColor);background-color:var(--sileo-btn-bg,transparent);transition:background-color 150ms ease}[data-sileo-button]:hover{background-color:var(--sileo-btn-bg-hover,transparent)}[data-sileo-button][data-state]{--sileo-btn-color:var(--_c);--sileo-btn-bg:color-mix(in oklch, var(--_c) 15%, transparent);--sileo-btn-bg-hover:color-mix(in oklch, var(--_c) 25%, transparent)}[data-sileo-icon=spin]{animation:sileo-spin 1s linear infinite}@keyframes sileo-spin{to{transform:rotate(360deg)}}@keyframes sileo-header-enter{from{opacity:0;filter:blur(6px)}to{opacity:1;filter:blur(0px)}}@keyframes sileo-header-exit{from{opacity:1;filter:blur(0px)}to{opacity:0;filter:blur(6px)}}[data-sileo-viewport]{position:fixed;z-index:50;display:flex;gap:.75rem;padding:.75rem;pointer-events:none;max-width:calc(100vw - 1.5rem);contain:layout style}[data-sileo-viewport][data-position^=top] [data-sileo-toast]:not([data-ready=true]){margin-bottom:calc(-1 * (var(--sileo-height) + .75rem))}[data-sileo-viewport][data-position^=bottom] [data-sileo-toast]:not([data-ready=true]){margin-top:calc(-1 * (var(--sileo-height) + .75rem))}[data-sileo-viewport][data-position^=top]{top:0;flex-direction:column-reverse}[data-sileo-viewport][data-position^=bottom]{bottom:0;flex-direction:column}[data-sileo-viewport][data-position$=left]{left:0;align-items:flex-start}[data-sileo-viewport][data-position$=right]{right:0;align-items:flex-end}[data-sileo-viewport][data-position$=center]{left:50%;transform:translateX(-50%);align-items:center}@media (prefers-reduced-motion:no-preference){[data-sileo-toast][data-ready=true]:hover,[data-sileo-toast][data-ready=true][data-exiting=true]{will-change:transform,opacity,height}}@media (prefers-reduced-motion:reduce){[data-sileo-viewport],[data-sileo-viewport] *,[data-sileo-viewport] ::after,[data-sileo-viewport] ::before{animation-duration:0s;animation-iteration-count:1;transition-duration:0s}}[data-sileo-viewport][data-theme=dark] [data-sileo-description]{color:rgba(0,0,0,.7)}[data-sileo-viewport][data-theme=light] [data-sileo-description]{color:rgba(255,255,255,.5)}");
14
+
15
+ /* --------------------------------- Layout --------------------------------- */ const HEIGHT = 40;
16
+ const WIDTH = 350;
17
+ const DEFAULT_ROUNDNESS = 16;
18
+ /* --------------------------------- Timing --------------------------------- */ const DURATION_MS = 600;
19
+ const DEFAULT_TOAST_DURATION = 6000;
20
+ const EXIT_DURATION = DEFAULT_TOAST_DURATION * 0.1;
21
+ const AUTO_EXPAND_DELAY = DEFAULT_TOAST_DURATION * 0.025;
22
+ const AUTO_COLLAPSE_DELAY = DEFAULT_TOAST_DURATION - 2000;
23
+ /* --------------------------------- Render --------------------------------- */ const BLUR_RATIO = 0.5;
24
+ const MIN_EXPAND_RATIO = 2.25;
25
+ const SWAP_COLLAPSE_MS = 200;
26
+ const HEADER_EXIT_MS = DURATION_MS * 0.7;
27
+
28
+ const Icon = (props)=>{
29
+ const { title, children, ...rest } = props;
30
+ return /*#__PURE__*/ React.createElement("svg", {
31
+ ...rest,
32
+ xmlns: "http://www.w3.org/2000/svg",
33
+ width: "16",
34
+ height: "16",
35
+ viewBox: "0 0 24 24",
36
+ fill: "none",
37
+ stroke: "currentColor",
38
+ "stroke-width": "2",
39
+ "stroke-linecap": "round",
40
+ "stroke-linejoin": "round"
41
+ }, /*#__PURE__*/ React.createElement("title", null, title), children);
42
+ };
43
+ const ArrowRight = ()=>/*#__PURE__*/ React.createElement(Icon, {
44
+ title: "Arrow Right"
45
+ }, /*#__PURE__*/ React.createElement("path", {
46
+ d: "M5 12h14"
47
+ }), /*#__PURE__*/ React.createElement("path", {
48
+ d: "m12 5 7 7-7 7"
49
+ }));
50
+ const LifeBuoy = ()=>/*#__PURE__*/ React.createElement(Icon, {
51
+ title: "Life Buoy"
52
+ }, /*#__PURE__*/ React.createElement("circle", {
53
+ cx: "12",
54
+ cy: "12",
55
+ r: "10"
56
+ }), /*#__PURE__*/ React.createElement("path", {
57
+ d: "m4.93 4.93 4.24 4.24"
58
+ }), /*#__PURE__*/ React.createElement("path", {
59
+ d: "m14.83 9.17 4.24-4.24"
60
+ }), /*#__PURE__*/ React.createElement("path", {
61
+ d: "m14.83 14.83 4.24 4.24"
62
+ }), /*#__PURE__*/ React.createElement("path", {
63
+ d: "m9.17 14.83-4.24 4.24"
64
+ }), /*#__PURE__*/ React.createElement("circle", {
65
+ cx: "12",
66
+ cy: "12",
67
+ r: "4"
68
+ }));
69
+ const LoaderCircle = (props)=>/*#__PURE__*/ React.createElement(Icon, {
70
+ title: "Loader Circle",
71
+ ...props
72
+ }, /*#__PURE__*/ React.createElement("path", {
73
+ d: "M21 12a9 9 0 1 1-6.219-8.56"
74
+ }));
75
+ const X = ()=>/*#__PURE__*/ React.createElement(Icon, {
76
+ title: "X"
77
+ }, /*#__PURE__*/ React.createElement("path", {
78
+ d: "M18 6 6 18"
79
+ }), /*#__PURE__*/ React.createElement("path", {
80
+ d: "m6 6 12 12"
81
+ }));
82
+ const CircleAlert = ()=>/*#__PURE__*/ React.createElement(Icon, {
83
+ title: "Circle Alert"
84
+ }, /*#__PURE__*/ React.createElement("circle", {
85
+ cx: "12",
86
+ cy: "12",
87
+ r: "10"
88
+ }), /*#__PURE__*/ React.createElement("line", {
89
+ x1: "12",
90
+ x2: "12",
91
+ y1: "8",
92
+ y2: "12"
93
+ }), /*#__PURE__*/ React.createElement("line", {
94
+ x1: "12",
95
+ x2: "12.01",
96
+ y1: "16",
97
+ y2: "16"
98
+ }));
99
+ const Check = ()=>/*#__PURE__*/ React.createElement(Icon, {
100
+ title: "Check"
101
+ }, /*#__PURE__*/ React.createElement("path", {
102
+ d: "M20 6 9 17l-5-5"
103
+ }));
104
+
105
+ const STATE_ICON = {
106
+ success: /*#__PURE__*/ React.createElement(Check, null),
107
+ loading: /*#__PURE__*/ React.createElement(LoaderCircle, {
108
+ "data-sileo-icon": "spin",
109
+ "aria-hidden": "true"
110
+ }),
111
+ error: /*#__PURE__*/ React.createElement(X, null),
112
+ warning: /*#__PURE__*/ React.createElement(CircleAlert, null),
113
+ info: /*#__PURE__*/ React.createElement(LifeBuoy, null),
114
+ action: /*#__PURE__*/ React.createElement(ArrowRight, null)
115
+ };
116
+ function GooeyDefs(props) {
117
+ return /*#__PURE__*/ React.createElement("defs", null, /*#__PURE__*/ React.createElement("filter", {
118
+ id: props.filterId,
119
+ x: "-20%",
120
+ y: "-20%",
121
+ width: "140%",
122
+ height: "140%",
123
+ "color-interpolation-filters": "sRGB"
124
+ }, /*#__PURE__*/ React.createElement("feGaussianBlur", {
125
+ in: "SourceGraphic",
126
+ stdDeviation: props.blur,
127
+ result: "blur"
128
+ }), /*#__PURE__*/ React.createElement("feColorMatrix", {
129
+ in: "blur",
130
+ type: "matrix",
131
+ values: "1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 20 -10",
132
+ result: "goo"
133
+ }), /*#__PURE__*/ React.createElement("feComposite", {
134
+ in: "SourceGraphic",
135
+ in2: "goo",
136
+ operator: "atop"
137
+ })));
138
+ }
139
+ function Sileo(props) {
140
+ const state = ()=>props.state ?? "success";
141
+ const fill = ()=>props.fill ?? "#FFFFFF";
142
+ const next = createMemo(()=>({
143
+ title: props.title,
144
+ description: props.description,
145
+ state: state(),
146
+ icon: props.icon,
147
+ styles: props.styles,
148
+ button: props.button,
149
+ fill: fill()
150
+ }));
151
+ const [view, setView] = createSignal(next());
152
+ const [applied, setApplied] = createSignal(props.refreshKey);
153
+ const [isExpanded, setIsExpanded] = createSignal(false);
154
+ const [ready, setReady] = createSignal(false);
155
+ const [pillWidth, setPillWidth] = createSignal(0);
156
+ const [contentHeight, setContentHeight] = createSignal(0);
157
+ const hasDesc = createMemo(()=>Boolean(view().description) || Boolean(view().button));
158
+ const isLoading = createMemo(()=>view().state === "loading");
159
+ const open = createMemo(()=>hasDesc() && isExpanded() && !isLoading());
160
+ const allowExpand = createMemo(()=>isLoading() ? false : props.canExpand ?? (!props.interruptKey || props.interruptKey === props.id));
161
+ const headerKey = createMemo(()=>`${view().state}-${view().title}`);
162
+ const filterId = `sileo-gooey-${props.id}`;
163
+ const resolvedRoundness = createMemo(()=>Math.max(0, props.roundness ?? DEFAULT_ROUNDNESS));
164
+ const blur = createMemo(()=>resolvedRoundness() * BLUR_RATIO);
165
+ let contentRef;
166
+ const headerExitRef = {
167
+ current: null
168
+ };
169
+ const autoExpandRef = {
170
+ current: null
171
+ };
172
+ const autoCollapseRef = {
173
+ current: null
174
+ };
175
+ const swapTimerRef = {
176
+ current: null
177
+ };
178
+ const lastRefreshKeyRef = {
179
+ current: props.refreshKey
180
+ };
181
+ const pendingRef = {
182
+ current: null
183
+ };
184
+ const [headerCurrentKey, setHeaderCurrentKey] = createSignal(headerKey());
185
+ const [headerCurrentView, setHeaderCurrentView] = createSignal(view());
186
+ const [headerPrevKey, setHeaderPrevKey] = createSignal(null);
187
+ const [headerPrevView, setHeaderPrevView] = createSignal(null);
188
+ let innerRef;
189
+ let headerEl;
190
+ let pillRaf = 0;
191
+ createEffect(()=>{
192
+ headerKey();
193
+ return;
194
+ });
195
+ onCleanup(()=>{
196
+ cancelAnimationFrame(pillRaf);
197
+ });
198
+ createEffect(()=>{
199
+ if (!hasDesc()) {
200
+ setContentHeight(0);
201
+ return;
202
+ }
203
+ return;
204
+ });
205
+ onMount(()=>{
206
+ const raf = requestAnimationFrame(()=>setReady(true));
207
+ return ()=>cancelAnimationFrame(raf);
208
+ });
209
+ createEffect(()=>{
210
+ const hk = headerKey();
211
+ const v = view();
212
+ if (headerCurrentKey() === hk) {
213
+ if (headerCurrentView() === v) return;
214
+ setHeaderCurrentView(v);
215
+ } else {
216
+ setHeaderPrevKey(headerCurrentKey());
217
+ setHeaderPrevView(headerCurrentView());
218
+ setHeaderCurrentKey(hk);
219
+ setHeaderCurrentView(v);
220
+ }
221
+ });
222
+ createEffect(()=>{
223
+ if (!headerPrevKey()) return;
224
+ if (headerExitRef.current) clearTimeout(headerExitRef.current);
225
+ headerExitRef.current = window.setTimeout(()=>{
226
+ headerExitRef.current = null;
227
+ setHeaderPrevKey(null);
228
+ setHeaderPrevView(null);
229
+ }, HEADER_EXIT_MS);
230
+ return ()=>{
231
+ if (headerExitRef.current) {
232
+ clearTimeout(headerExitRef.current);
233
+ headerExitRef.current = null;
234
+ }
235
+ };
236
+ });
237
+ createEffect(()=>{
238
+ const f = props.fill;
239
+ if (f !== undefined && view().fill !== f) {
240
+ setView((prev)=>({
241
+ ...prev,
242
+ fill: f
243
+ }));
244
+ }
245
+ });
246
+ createEffect(()=>{
247
+ const rk = props.refreshKey;
248
+ const n = next();
249
+ if (rk === undefined) {
250
+ setView(n);
251
+ setApplied(undefined);
252
+ pendingRef.current = null;
253
+ lastRefreshKeyRef.current = rk;
254
+ return;
255
+ }
256
+ if (lastRefreshKeyRef.current === rk) return;
257
+ lastRefreshKeyRef.current = rk;
258
+ if (swapTimerRef.current) {
259
+ clearTimeout(swapTimerRef.current);
260
+ swapTimerRef.current = null;
261
+ }
262
+ if (open()) {
263
+ pendingRef.current = {
264
+ key: rk,
265
+ payload: n
266
+ };
267
+ setIsExpanded(false);
268
+ swapTimerRef.current = window.setTimeout(()=>{
269
+ swapTimerRef.current = null;
270
+ const pending = pendingRef.current;
271
+ if (!pending) return;
272
+ setView(pending.payload);
273
+ setApplied(pending.key);
274
+ pendingRef.current = null;
275
+ }, SWAP_COLLAPSE_MS);
276
+ } else {
277
+ pendingRef.current = null;
278
+ setView(n);
279
+ setApplied(rk);
280
+ }
281
+ });
282
+ createEffect(()=>{
283
+ const hd = hasDesc();
284
+ if (!hd) return;
285
+ if (autoExpandRef.current) clearTimeout(autoExpandRef.current);
286
+ if (autoCollapseRef.current) clearTimeout(autoCollapseRef.current);
287
+ if (props.exiting || !allowExpand()) {
288
+ setIsExpanded(false);
289
+ return;
290
+ }
291
+ if (props.autoExpandDelayMs == null && props.autoCollapseDelayMs == null) return;
292
+ const expandDelay = props.autoExpandDelayMs ?? 0;
293
+ const collapseDelay = props.autoCollapseDelayMs ?? 0;
294
+ if (expandDelay > 0) {
295
+ autoExpandRef.current = window.setTimeout(()=>setIsExpanded(true), expandDelay);
296
+ } else {
297
+ setIsExpanded(true);
298
+ }
299
+ if (collapseDelay > 0) {
300
+ autoCollapseRef.current = window.setTimeout(()=>setIsExpanded(false), collapseDelay);
301
+ }
302
+ return ()=>{
303
+ if (autoExpandRef.current) clearTimeout(autoExpandRef.current);
304
+ if (autoCollapseRef.current) clearTimeout(autoCollapseRef.current);
305
+ };
306
+ });
307
+ const minExpanded = HEIGHT * MIN_EXPAND_RATIO;
308
+ const rawExpanded = createMemo(()=>hasDesc() ? Math.max(minExpanded, HEIGHT + contentHeight()) : minExpanded);
309
+ const frozenExpandedRef = {
310
+ current: minExpanded
311
+ };
312
+ createEffect(()=>{
313
+ if (open()) {
314
+ frozenExpandedRef.current = rawExpanded();
315
+ }
316
+ });
317
+ const expanded = open() ? rawExpanded() : frozenExpandedRef.current;
318
+ const svgHeight = hasDesc() ? Math.max(expanded, minExpanded) : HEIGHT;
319
+ const resolvedPillWidth = createMemo(()=>Math.max(pillWidth() || HEIGHT, HEIGHT));
320
+ HEIGHT + blur() * 3;
321
+ const pillX = createMemo(()=>props.position === "right" ? WIDTH - resolvedPillWidth() : props.position === "center" ? (WIDTH - resolvedPillWidth()) / 2 : 0);
322
+ const viewBox = `0 0 ${WIDTH} ${svgHeight}`;
323
+ const canvasStyle = createMemo(()=>({
324
+ filter: `url(#${filterId})`
325
+ }));
326
+ const rootStyle = createMemo(()=>({
327
+ "--_h": `${open() ? expanded : HEIGHT}px`,
328
+ "--_pw": `${resolvedPillWidth()}px`,
329
+ "--_px": `${pillX()}px`,
330
+ "--_ht": `translateY(${open() ? props.expand === "bottom" ? 3 : -3 : 0}px) scale(${open() ? 0.9 : 1})`,
331
+ "--_co": `${open() ? 1 : 0}`
332
+ }));
333
+ const handleEnter = (e)=>{
334
+ props.onMouseEnter?.(e);
335
+ if (hasDesc()) setIsExpanded(true);
336
+ };
337
+ const handleLeave = (e)=>{
338
+ props.onMouseLeave?.(e);
339
+ setIsExpanded(false);
340
+ };
341
+ const handleTransitionEnd = (e)=>{
342
+ if (e.propertyName !== "height" && e.propertyName !== "transform") return;
343
+ if (open()) return;
344
+ const pending = pendingRef.current;
345
+ if (!pending) return;
346
+ if (swapTimerRef.current) {
347
+ clearTimeout(swapTimerRef.current);
348
+ swapTimerRef.current = null;
349
+ }
350
+ setView(pending.payload);
351
+ setApplied(pending.key);
352
+ pendingRef.current = null;
353
+ };
354
+ let buttonRef;
355
+ ({
356
+ current: props.onDismiss
357
+ });
358
+ props.onDismiss;
359
+ const handleButtonClick = (e)=>{
360
+ e.preventDefault();
361
+ e.stopPropagation();
362
+ view().button?.onClick();
363
+ };
364
+ const handlePointerDown = (e)=>{
365
+ if (props.exiting || !props.onDismiss) return;
366
+ const target = e.target;
367
+ if (target.closest("[data-sileo-button]")) return;
368
+ e.clientY;
369
+ e.currentTarget.setPointerCapture(e.pointerId);
370
+ };
371
+ let pillRef;
372
+ let bodyRef;
373
+ createEffect(()=>{
374
+ return;
375
+ });
376
+ createEffect(()=>{
377
+ return;
378
+ });
379
+ const curView = headerCurrentView();
380
+ const prevView = headerPrevView();
381
+ return /*#__PURE__*/ React.createElement("button", {
382
+ ref: buttonRef,
383
+ type: "button",
384
+ "data-sileo-toast": true,
385
+ "data-ready": ready(),
386
+ "data-expanded": open(),
387
+ "data-exiting": props.exiting,
388
+ "data-edge": props.expand,
389
+ "data-position": props.position,
390
+ "data-state": view().state,
391
+ class: props.class,
392
+ style: rootStyle(),
393
+ onMouseEnter: handleEnter,
394
+ onMouseLeave: handleLeave,
395
+ onTransitionEnd: handleTransitionEnd,
396
+ onPointerDown: handlePointerDown
397
+ }, /*#__PURE__*/ React.createElement("div", {
398
+ "data-sileo-canvas": true,
399
+ "data-edge": props.expand,
400
+ style: canvasStyle()
401
+ }, /*#__PURE__*/ React.createElement("svg", {
402
+ "data-sileo-svg": true,
403
+ width: WIDTH,
404
+ height: svgHeight,
405
+ viewBox: viewBox
406
+ }, /*#__PURE__*/ React.createElement("title", null, "Sileo Notification"), /*#__PURE__*/ React.createElement(GooeyDefs, {
407
+ filterId: filterId,
408
+ blur: blur()
409
+ }), /*#__PURE__*/ React.createElement("rect", {
410
+ ref: pillRef,
411
+ "data-sileo-pill": true,
412
+ rx: resolvedRoundness(),
413
+ ry: resolvedRoundness(),
414
+ fill: view().fill
415
+ }), /*#__PURE__*/ React.createElement("rect", {
416
+ ref: bodyRef,
417
+ "data-sileo-body": true,
418
+ y: HEIGHT,
419
+ width: WIDTH,
420
+ rx: resolvedRoundness(),
421
+ ry: resolvedRoundness(),
422
+ fill: view().fill
423
+ }))), /*#__PURE__*/ React.createElement("div", {
424
+ ref: headerEl,
425
+ "data-sileo-header": true,
426
+ "data-edge": props.expand
427
+ }, /*#__PURE__*/ React.createElement("div", {
428
+ "data-sileo-header-stack": true
429
+ }, /*#__PURE__*/ React.createElement("div", {
430
+ ref: innerRef,
431
+ "data-sileo-header-inner": true,
432
+ "data-layer": "current"
433
+ }, /*#__PURE__*/ React.createElement("div", {
434
+ "data-sileo-badge": true,
435
+ "data-state": curView.state,
436
+ class: curView.styles?.badge
437
+ }, curView.icon ?? STATE_ICON[curView.state]), /*#__PURE__*/ React.createElement("span", {
438
+ "data-sileo-title": true,
439
+ "data-state": curView.state,
440
+ class: curView.styles?.title
441
+ }, curView.title)), /*#__PURE__*/ React.createElement(Show, {
442
+ when: prevView
443
+ }, (pv)=>/*#__PURE__*/ React.createElement("div", {
444
+ "data-sileo-header-inner": true,
445
+ "data-layer": "prev",
446
+ "data-exiting": "true"
447
+ }, /*#__PURE__*/ React.createElement("div", {
448
+ "data-sileo-badge": true,
449
+ "data-state": pv().state,
450
+ class: pv().styles?.badge
451
+ }, pv().icon ?? STATE_ICON[pv().state]), /*#__PURE__*/ React.createElement("span", {
452
+ "data-sileo-title": true,
453
+ "data-state": pv().state,
454
+ class: pv().styles?.title
455
+ }, pv().title))))), hasDesc() && /*#__PURE__*/ React.createElement("div", {
456
+ "data-sileo-content": true,
457
+ "data-edge": props.expand,
458
+ "data-visible": open()
459
+ }, /*#__PURE__*/ React.createElement("div", {
460
+ ref: contentRef,
461
+ "data-sileo-description": true,
462
+ class: view().styles?.description
463
+ }, view().description, view().button && /*#__PURE__*/ React.createElement("a", {
464
+ href: "#",
465
+ type: "button",
466
+ "data-sileo-button": true,
467
+ "data-state": view().state,
468
+ class: view().styles?.button,
469
+ onClick: handleButtonClick
470
+ }, view().button?.title))));
471
+ }
472
+
473
+ const pillAlign = (pos)=>pos.includes("right") ? "right" : pos.includes("center") ? "center" : "left";
474
+ const expandDir = (pos)=>pos.startsWith("top") ? "bottom" : "top";
475
+ const store = {
476
+ toasts: [],
477
+ listeners: new Set(),
478
+ position: "top-right",
479
+ options: undefined,
480
+ emit () {
481
+ for (const fn of this.listeners)fn(this.toasts);
482
+ },
483
+ update (fn) {
484
+ this.toasts = fn(this.toasts);
485
+ this.emit();
486
+ }
487
+ };
488
+ let idCounter = 0;
489
+ const generateId = ()=>`${++idCounter}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
490
+ const timeoutKey = (t)=>`${t.id}:${t.instanceId}`;
491
+ /* ------------------------------- Toast API -------------------------------- */ const dismissToast = (id)=>{
492
+ const item = store.toasts.find((t)=>t.id === id);
493
+ if (!item || item.exiting) return;
494
+ store.update((prev)=>prev.map((t)=>t.id === id ? {
495
+ ...t,
496
+ exiting: true
497
+ } : t));
498
+ setTimeout(()=>store.update((prev)=>prev.filter((t)=>t.id !== id)), EXIT_DURATION);
499
+ };
500
+ const resolveAutopilot = (opts, duration)=>{
501
+ if (opts.autopilot === false || !duration || duration <= 0) return {};
502
+ const cfg = typeof opts.autopilot === "object" ? opts.autopilot : undefined;
503
+ const clamp = (v)=>Math.min(duration, Math.max(0, v));
504
+ return {
505
+ expandDelayMs: clamp(cfg?.expand ?? AUTO_EXPAND_DELAY),
506
+ collapseDelayMs: clamp(cfg?.collapse ?? AUTO_COLLAPSE_DELAY)
507
+ };
508
+ };
509
+ const mergeOptions = (options)=>({
510
+ ...store.options,
511
+ ...options,
512
+ styles: {
513
+ ...store.options?.styles,
514
+ ...options.styles
515
+ }
516
+ });
517
+ const buildSileoItem = (merged, id, fallbackPosition)=>{
518
+ const duration = merged.duration ?? DEFAULT_TOAST_DURATION;
519
+ const auto = resolveAutopilot(merged, duration);
520
+ return {
521
+ ...merged,
522
+ id,
523
+ instanceId: generateId(),
524
+ position: merged.position ?? fallbackPosition ?? store.position,
525
+ autoExpandDelayMs: auto.expandDelayMs,
526
+ autoCollapseDelayMs: auto.collapseDelayMs
527
+ };
528
+ };
529
+ const createToast = (options)=>{
530
+ const live = store.toasts.filter((t)=>!t.exiting);
531
+ const merged = mergeOptions(options);
532
+ const id = merged.id ?? "sileo-default";
533
+ const prev = live.find((t)=>t.id === id);
534
+ const item = buildSileoItem(merged, id, prev?.position);
535
+ if (prev) {
536
+ store.update((p)=>p.map((t)=>t.id === id ? item : t));
537
+ } else {
538
+ store.update((p)=>[
539
+ ...p.filter((t)=>t.id !== id),
540
+ item
541
+ ]);
542
+ }
543
+ return {
544
+ id,
545
+ duration: merged.duration ?? DEFAULT_TOAST_DURATION
546
+ };
547
+ };
548
+ const updateToast = (id, options)=>{
549
+ const existing = store.toasts.find((t)=>t.id === id);
550
+ if (!existing) return;
551
+ const item = buildSileoItem(mergeOptions(options), id, existing.position);
552
+ store.update((prev)=>prev.map((t)=>t.id === id ? item : t));
553
+ };
554
+ const sileo = {
555
+ show: (opts)=>createToast({
556
+ ...opts,
557
+ state: opts.type
558
+ }).id,
559
+ success: (opts)=>createToast({
560
+ ...opts,
561
+ state: "success"
562
+ }).id,
563
+ error: (opts)=>createToast({
564
+ ...opts,
565
+ state: "error"
566
+ }).id,
567
+ warning: (opts)=>createToast({
568
+ ...opts,
569
+ state: "warning"
570
+ }).id,
571
+ info: (opts)=>createToast({
572
+ ...opts,
573
+ state: "info"
574
+ }).id,
575
+ action: (opts)=>createToast({
576
+ ...opts,
577
+ state: "action"
578
+ }).id,
579
+ promise: (promise, opts)=>{
580
+ const { id } = createToast({
581
+ ...opts.loading,
582
+ state: "loading",
583
+ duration: null,
584
+ position: opts.position
585
+ });
586
+ const p = typeof promise === "function" ? promise() : promise;
587
+ p.then((data)=>{
588
+ if (opts.action) {
589
+ const actionOpts = typeof opts.action === "function" ? opts.action(data) : opts.action;
590
+ updateToast(id, {
591
+ ...actionOpts,
592
+ state: "action",
593
+ id
594
+ });
595
+ } else {
596
+ const successOpts = typeof opts.success === "function" ? opts.success(data) : opts.success;
597
+ updateToast(id, {
598
+ ...successOpts,
599
+ state: "success",
600
+ id
601
+ });
602
+ }
603
+ }).catch((err)=>{
604
+ const errorOpts = typeof opts.error === "function" ? opts.error(err) : opts.error;
605
+ updateToast(id, {
606
+ ...errorOpts,
607
+ state: "error",
608
+ id
609
+ });
610
+ });
611
+ return p;
612
+ },
613
+ dismiss: dismissToast,
614
+ clear: (position)=>store.update((prev)=>position ? prev.filter((t)=>t.position !== position) : [])
615
+ };
616
+ /* ------------------------------ Toaster Component ------------------------- */ const THEME_FILLS = {
617
+ light: "#1a1a1a",
618
+ dark: "#f2f2f2"
619
+ };
620
+ function useResolvedTheme(theme) {
621
+ const getInitialTheme = ()=>{
622
+ if (theme === "light" || theme === "dark") return theme;
623
+ if (typeof window === "undefined") return "light";
624
+ return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
625
+ };
626
+ const [resolved, setResolved] = createSignal(getInitialTheme());
627
+ createEffect(()=>{
628
+ const t = theme;
629
+ if (t === "light" || t === "dark") {
630
+ setResolved(t);
631
+ return;
632
+ }
633
+ const mq = window.matchMedia("(prefers-color-scheme: dark)");
634
+ const handler = (e)=>setResolved(e.matches ? "dark" : "light");
635
+ setResolved(mq.matches ? "dark" : "light");
636
+ mq.addEventListener("change", handler);
637
+ return ()=>mq.removeEventListener("change", handler);
638
+ });
639
+ return resolved;
640
+ }
641
+ function Toaster(props) {
642
+ const resolvedTheme = useResolvedTheme(props.theme);
643
+ const [toasts, setToasts] = createSignal(store.toasts);
644
+ const [activeId, setActiveId] = createSignal();
645
+ const hoverRef = {
646
+ current: false
647
+ };
648
+ const timersRef = {
649
+ current: new Map()
650
+ };
651
+ const listRef = {
652
+ current: store.toasts
653
+ };
654
+ const latestRef = {
655
+ current: undefined
656
+ };
657
+ const handlersCache = {
658
+ current: new Map()
659
+ };
660
+ createEffect(()=>{
661
+ store.position = props.position ?? "top-right";
662
+ store.options = props.options;
663
+ });
664
+ const clearAllTimers = ()=>{
665
+ for (const t of timersRef.current.values())clearTimeout(t);
666
+ timersRef.current.clear();
667
+ };
668
+ const schedule = (items)=>{
669
+ if (hoverRef.current) return;
670
+ for (const item of items){
671
+ if (item.exiting) continue;
672
+ const key = timeoutKey(item);
673
+ if (timersRef.current.has(key)) continue;
674
+ if (item.duration === null) continue;
675
+ const dur = item.duration ?? DEFAULT_TOAST_DURATION;
676
+ if (dur <= 0) continue;
677
+ timersRef.current.set(key, window.setTimeout(()=>dismissToast(item.id), dur));
678
+ }
679
+ };
680
+ createEffect(()=>{
681
+ const listener = (next)=>setToasts(next);
682
+ store.listeners.add(listener);
683
+ return ()=>{
684
+ store.listeners.delete(listener);
685
+ clearAllTimers();
686
+ };
687
+ });
688
+ createEffect(()=>{
689
+ const items = toasts();
690
+ listRef.current = items;
691
+ const toastKeys = new Set(items.map(timeoutKey));
692
+ const toastIds = new Set(items.map((t)=>t.id));
693
+ for (const [key, timer] of timersRef.current){
694
+ if (!toastKeys.has(key)) {
695
+ clearTimeout(timer);
696
+ timersRef.current.delete(key);
697
+ }
698
+ }
699
+ for (const id of handlersCache.current.keys()){
700
+ if (!toastIds.has(id)) handlersCache.current.delete(id);
701
+ }
702
+ schedule(items);
703
+ });
704
+ const handleMouseEnterRef = {
705
+ current: null
706
+ };
707
+ const handleMouseLeaveRef = {
708
+ current: null
709
+ };
710
+ handleMouseEnterRef.current = ()=>{
711
+ if (hoverRef.current) return;
712
+ hoverRef.current = true;
713
+ clearAllTimers();
714
+ };
715
+ handleMouseLeaveRef.current = ()=>{
716
+ if (!hoverRef.current) return;
717
+ hoverRef.current = false;
718
+ schedule(listRef.current);
719
+ };
720
+ const latest = createMemo(()=>{
721
+ const items = toasts();
722
+ for(let i = items.length - 1; i >= 0; i--){
723
+ if (!items[i].exiting) return items[i].id;
724
+ }
725
+ return undefined;
726
+ });
727
+ createEffect(()=>{
728
+ latestRef.current = latest();
729
+ setActiveId(latest());
730
+ });
731
+ const getHandlers = (toastId)=>{
732
+ let cached = handlersCache.current.get(toastId);
733
+ if (cached) return cached;
734
+ cached = {
735
+ enter: (e)=>{
736
+ setActiveId((prev)=>prev === toastId ? prev : toastId);
737
+ handleMouseEnterRef.current?.(e);
738
+ },
739
+ leave: (e)=>{
740
+ setActiveId((prev)=>prev === latestRef.current ? prev : latestRef.current);
741
+ handleMouseLeaveRef.current?.(e);
742
+ },
743
+ dismiss: ()=>dismissToast(toastId)
744
+ };
745
+ handlersCache.current.set(toastId, cached);
746
+ return cached;
747
+ };
748
+ const getViewportStyle = (pos)=>{
749
+ if (props.offset === undefined) return undefined;
750
+ const o = typeof props.offset === "object" ? props.offset : {
751
+ top: props.offset,
752
+ right: props.offset,
753
+ bottom: props.offset,
754
+ left: props.offset
755
+ };
756
+ const s = {};
757
+ const px = (v)=>typeof v === "number" ? `${v}px` : v;
758
+ if (pos.startsWith("top") && o.top) s.top = px(o.top);
759
+ if (pos.startsWith("bottom") && o.bottom) s.bottom = px(o.bottom);
760
+ if (pos.endsWith("left") && o.left) s.left = px(o.left);
761
+ if (pos.endsWith("right") && o.right) s.right = px(o.right);
762
+ return s;
763
+ };
764
+ const activePositionsList = createMemo(()=>{
765
+ const map = new Map();
766
+ const items = toasts();
767
+ const pos = props.position ?? "top-right";
768
+ for (const t of items){
769
+ const tPos = t.position ?? pos;
770
+ const arr = map.get(tPos);
771
+ if (arr) {
772
+ arr.push(t);
773
+ } else {
774
+ map.set(tPos, [
775
+ t
776
+ ]);
777
+ }
778
+ }
779
+ return Array.from(map, ([p, its])=>({
780
+ pos: p,
781
+ items: its
782
+ }));
783
+ });
784
+ return /*#__PURE__*/ React.createElement(React.Fragment, null, props.children, /*#__PURE__*/ React.createElement(For, {
785
+ each: activePositionsList()
786
+ }, ({ pos, items })=>{
787
+ const pill = pillAlign(pos);
788
+ const expand = expandDir(pos);
789
+ return /*#__PURE__*/ React.createElement("section", {
790
+ "data-sileo-viewport": true,
791
+ "data-position": pos,
792
+ "data-theme": props.theme ? resolvedTheme() : undefined,
793
+ "aria-live": "polite",
794
+ style: getViewportStyle(pos)
795
+ }, /*#__PURE__*/ React.createElement(For, {
796
+ each: items
797
+ }, (item)=>{
798
+ const h = getHandlers(item.id);
799
+ return /*#__PURE__*/ React.createElement(Sileo, {
800
+ id: item.id,
801
+ state: item.state,
802
+ title: item.title,
803
+ description: item.description,
804
+ position: pill,
805
+ expand: expand,
806
+ icon: item.icon,
807
+ fill: item.fill ?? (props.theme ? THEME_FILLS[resolvedTheme()] : undefined),
808
+ styles: item.styles,
809
+ button: item.button,
810
+ roundness: item.roundness,
811
+ exiting: item.exiting,
812
+ autoExpandDelayMs: item.autoExpandDelayMs,
813
+ autoCollapseDelayMs: item.autoCollapseDelayMs,
814
+ refreshKey: item.instanceId,
815
+ canExpand: activeId() === undefined || activeId() === item.id,
816
+ onMouseEnter: h.enter,
817
+ onMouseLeave: h.leave,
818
+ onDismiss: h.dismiss
819
+ });
820
+ }));
821
+ }));
822
+ }
823
+
824
+ export { Toaster, sileo };