ga-toasts 1.0.0 → 2.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.cjs ADDED
@@ -0,0 +1,1242 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ // src/styles.generated.ts
6
+ var css = '.ga-toast-container{--gat-width: 380px;--gat-radius: 14px;--gat-gap: 14px;--gat-edge: 22px;--gat-pad-y: 12px;--gat-pad-x: 16px;--gat-font-size: .875rem;--gat-accent-edge: 0px;--gat-font: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;--gat-surface: rgba(255, 255, 255, .82);--gat-surface-solid: #ffffff;--gat-border: rgba(15, 23, 42, .07);--gat-ring: rgba(15, 23, 42, .05);--gat-highlight: rgba(255, 255, 255, .75);--gat-text: #0f172a;--gat-text-soft: #475569;--gat-text-muted: #64748b;--gat-shadow: 0 1px 2px rgba(15, 23, 42, .05), 0 8px 20px -12px rgba(15, 23, 42, .16);--gat-shadow-hover: 0 2px 4px -2px rgba(15, 23, 42, .12), 0 14px 32px -12px rgba(15, 23, 42, .26);--gat-chip: rgba(15, 23, 42, .05);--gat-success: #16a34a;--gat-error: #e11d48;--gat-warning: #d97706;--gat-info: #2563eb;--gat-primary: #6366f1;--gat-secondary: #64748b;--gat-loading: #2563eb;--gat-ease: cubic-bezier(.22, 1, .36, 1)}[data-ga-theme=dark] .ga-toast-container,.ga-theme-dark .ga-toast-container,.ga-toast-container.ga-theme-dark{--gat-surface: rgba(30, 34, 44, .72);--gat-surface-solid: #22262f;--gat-border: rgba(255, 255, 255, .09);--gat-ring: rgba(255, 255, 255, .06);--gat-highlight: rgba(255, 255, 255, .07);--gat-text: #f8fafc;--gat-text-soft: #cbd5e1;--gat-text-muted: #9aa7ba;--gat-shadow: 0 1px 2px rgba(0, 0, 0, .35), 0 10px 28px -14px rgba(0, 0, 0, .5);--gat-shadow-hover: 0 2px 6px -2px rgba(0, 0, 0, .5), 0 16px 38px -12px rgba(0, 0, 0, .64);--gat-chip: rgba(255, 255, 255, .08)}@media(prefers-color-scheme:dark){:root:not([data-ga-theme]):not(.ga-theme-light):not(.ga-theme-dark) .ga-toast-container{--gat-surface: rgba(24, 27, 34, .82);--gat-surface-solid: #191c22;--gat-border: rgba(255, 255, 255, .09);--gat-ring: rgba(255, 255, 255, .06);--gat-highlight: rgba(255, 255, 255, .07);--gat-text: #f8fafc;--gat-text-soft: #cbd5e1;--gat-text-muted: #9aa7ba;--gat-shadow: 0 1px 2px rgba(0, 0, 0, .35), 0 10px 28px -14px rgba(0, 0, 0, .5);--gat-chip: rgba(255, 255, 255, .08)}}.ga-toast-backdrop{position:fixed;inset:0;z-index:2147482990;background:#0a0c146b;backdrop-filter:blur(2px);-webkit-backdrop-filter:blur(2px);opacity:0;transition:opacity .22s ease}.ga-toast-backdrop-show{opacity:1}@media(prefers-reduced-transparency:reduce){.ga-toast-backdrop{backdrop-filter:none;-webkit-backdrop-filter:none}}.ga-toast-container{position:fixed;z-index:2147483000;width:var(--gat-width);max-width:calc(100vw - 2 * var(--gat-edge));pointer-events:none;font-family:var(--gat-font)}.ga-toast-container-top-start{inset-block-start:var(--gat-edge);inset-inline-start:var(--gat-edge)}.ga-toast-container-top-end{inset-block-start:var(--gat-edge);inset-inline-end:var(--gat-edge)}.ga-toast-container-top-center{inset-block-start:var(--gat-edge);left:50%;transform:translate(-50%)}.ga-toast-container-bottom-start{inset-block-end:var(--gat-edge);inset-inline-start:var(--gat-edge)}.ga-toast-container-bottom-end{inset-block-end:var(--gat-edge);inset-inline-end:var(--gat-edge)}.ga-toast-container-bottom-center{inset-block-end:var(--gat-edge);left:50%;transform:translate(-50%)}.ga-toast-container-middle-start{top:50%;inset-inline-start:var(--gat-edge)}.ga-toast-container-middle-end{top:50%;inset-inline-end:var(--gat-edge)}.ga-toast-container-middle-center{top:50%;left:50%;transform:translate(-50%)}.ga-toast{position:absolute;inset-inline:0;width:100%;box-sizing:border-box;display:flex;flex-direction:row;align-items:flex-start;gap:11px;padding:var(--gat-pad-y, 12px) var(--gat-pad-x, 16px);color:var(--gat-text);background:var(--gat-surface-solid);border:none;border-radius:var(--gat-radius);box-shadow:var(--gat-shadow),inset 0 1px 0 var(--gat-highlight);pointer-events:auto;opacity:0;transform:translateY(-140%);transition:transform .5s var(--gat-ease),opacity .35s ease,box-shadow .2s ease;will-change:transform,opacity;overflow:hidden;font-size:var(--gat-font-size, .875rem);line-height:1.5;-webkit-font-smoothing:antialiased;text-align:start;touch-action:pan-y;overscroll-behavior:contain}@media(hover:hover){.ga-toast:hover{box-shadow:var(--gat-shadow-hover),inset 0 1px 0 var(--gat-highlight)}}[data-ga-position^=top] .ga-toast,[data-ga-position^=middle] .ga-toast{top:0;transform-origin:top center}[data-ga-position^=bottom] .ga-toast{bottom:0;transform-origin:bottom center}.ga-toast-glass{background:var(--gat-surface);backdrop-filter:blur(16px) saturate(160%);-webkit-backdrop-filter:blur(16px) saturate(160%)}.ga-toast-outline{background:transparent;border:1px solid var(--gat-border);box-shadow:var(--gat-shadow);backdrop-filter:none;-webkit-backdrop-filter:none}.ga-toast:before{content:"";position:absolute;inset-block:0;inset-inline-start:0;width:var(--gat-accent-edge, 0px);background:var(--gat-accent, var(--gat-info));pointer-events:none}.ga-toast-hide{pointer-events:none}.ga-toast-swiping,.ga-toast-swiping *{user-select:none;-webkit-user-select:none;cursor:grabbing}.ga-toast-success{--gat-accent: var(--gat-success)}.ga-toast-error{--gat-accent: var(--gat-error)}.ga-toast-warning{--gat-accent: var(--gat-warning)}.ga-toast-info{--gat-accent: var(--gat-info)}.ga-toast-primary{--gat-accent: var(--gat-primary)}.ga-toast-secondary{--gat-accent: var(--gat-secondary)}.ga-toast-loading{--gat-accent: var(--gat-loading)}.ga-toast-content{display:flex;flex-direction:column;gap:4px;flex:1;min-width:0}.ga-toast-header{display:flex;align-items:center;gap:8px;min-width:0}.ga-toast-icon{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;margin-top:1px;color:var(--gat-accent, var(--gat-info))}.ga-toast-icon svg{width:1.25rem;height:1.25rem;display:block;overflow:visible}.ga-toast-spinner{animation:ga-toast-spin .8s linear infinite}.ga-toast-icon-ring{position:relative;width:2rem;height:2rem;margin-top:0}.ga-toast-icon-ring>svg:not(.ga-toast-ring){width:1.1rem;height:1.1rem}.ga-toast-ring{position:absolute;inset:0;width:100%!important;height:100%!important;transform:rotate(-90deg);overflow:visible}.ga-toast-ring-track{fill:none;stroke:var(--gat-chip);stroke-width:2.4}.ga-toast-ring-bar{fill:none;stroke:var(--gat-accent, var(--gat-info));stroke-width:2.4;stroke-linecap:round}@media(prefers-reduced-motion:no-preference){.ga-toast-icon .ga-ic-ring{stroke-dasharray:64;stroke-dashoffset:64;animation:ga-toast-ic-draw .5s var(--gat-ease) .02s forwards}.ga-toast-icon .ga-ic-draw{stroke-dasharray:28;stroke-dashoffset:28;animation:ga-toast-ic-draw .4s var(--gat-ease) .16s forwards}}@keyframes ga-toast-ic-draw{to{stroke-dashoffset:0}}.ga-toast-avatar{flex-shrink:0;width:34px;height:34px;border-radius:50%;object-fit:cover;border:1px solid var(--gat-border)}.ga-toast-unread-dot{flex-shrink:0;width:8px;height:8px;border-radius:50%;background:var(--gat-accent, var(--gat-info))}.ga-toast-title{font-weight:600;font-size:.9rem;color:var(--gat-text);flex:1;min-width:0;letter-spacing:-.011em}.ga-toast-title-truncate{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ga-toast-status{flex-shrink:0;padding:2px 9px;border-radius:999px;font-size:.6875rem;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--gat-accent, var(--gat-info));color:color-mix(in srgb,var(--gat-accent, var(--gat-info)) 58%,var(--gat-text));background:var(--gat-chip);background:color-mix(in srgb,var(--gat-accent, var(--gat-info)) 18%,transparent)}.ga-toast-close{flex-shrink:0;align-self:flex-start;margin-top:-1px;margin-inline-end:-4px;display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;padding:0;border:none;border-radius:7px;color:var(--gat-text-muted);background:transparent;cursor:pointer;opacity:.9;transition:background .15s ease,color .15s ease,opacity .15s ease}.ga-toast:hover .ga-toast-close,.ga-toast:focus-within .ga-toast-close{opacity:1}.ga-toast-close svg{width:15px;height:15px;display:block}.ga-toast-close:hover{background:var(--gat-chip);color:var(--gat-text)}.ga-toast-close:focus-visible{outline:2px solid currentColor;outline-offset:2px}.ga-toast-body{min-width:0}.ga-toast-meta{font-size:.75rem;color:var(--gat-text-muted);margin-bottom:2px}.ga-toast-message{color:var(--gat-text-soft);font-size:.85rem;word-break:break-word;max-height:40vh;overflow-y:auto;overscroll-behavior:contain}.ga-toast-message a{color:var(--gat-accent, var(--gat-info));font-weight:500;text-decoration:underline}.ga-toast:not([class*=-filled]) .ga-toast-content>.ga-toast-body:only-child .ga-toast-message{color:var(--gat-text)}.ga-toast-actions{display:flex;gap:8px;margin-top:2px}.ga-toast-btn{flex:1;padding:8px 14px;font:inherit;font-size:.8125rem;font-weight:600;border-radius:9px;border:1px solid var(--gat-border);background:var(--gat-chip);color:var(--gat-text);cursor:pointer;transition:filter .15s ease,background .15s ease}.ga-toast-btn:hover{filter:brightness(.97)}.ga-toast-btn:focus-visible{outline:2px solid currentColor;outline-offset:2px}.ga-toast-btn-primary{background:var(--gat-accent, var(--gat-primary));border-color:transparent;color:#fff}.ga-toast-btn-primary:hover{filter:brightness(1.06)}.ga-toast-progress{position:absolute;left:0;bottom:0;height:2px;width:100%;transform:scaleX(1);transform-origin:left center;background:var(--gat-accent, var(--gat-info));opacity:.4}.ga-toast-progress-top{top:0;bottom:auto}.ga-toast-steps{display:flex;gap:4px;margin-top:4px}.ga-toast-step{flex:1;height:3px;border-radius:999px;background:var(--gat-chip)}.ga-toast-step-active{background:var(--gat-accent, var(--gat-info))}.ga-toast[class*=-filled]{background:var(--gat-accent, var(--gat-info));border-color:transparent;color:#fff}.ga-toast-success[class*=-filled]{background:#15803d}.ga-toast-primary[class*=-filled]{background:#4f46e5}.ga-toast[class*=-filled] .ga-toast-title,.ga-toast[class*=-filled] .ga-toast-message,.ga-toast[class*=-filled] .ga-toast-meta,.ga-toast[class*=-filled] .ga-toast-icon,.ga-toast[class*=-filled] .ga-toast-close{color:#fff}.ga-toast[class*=-filled] .ga-toast-close:hover{background:#fff3}.ga-toast[class*=-filled] .ga-toast-progress{background:#fff;opacity:.55}.ga-toast[class*=-filled] .ga-toast-step{background:#ffffff47}.ga-toast[class*=-filled] .ga-toast-step-active{background:#fff}.ga-toast[class*=-filled] .ga-toast-status{color:#fff;background:#ffffff38}.ga-toast[class*=-filled] .ga-toast-message a{color:#fff;text-decoration:underline}.ga-toast-warning[class*=-filled],.ga-toast-warning[class*=-filled] .ga-toast-title,.ga-toast-warning[class*=-filled] .ga-toast-message,.ga-toast-warning[class*=-filled] .ga-toast-meta,.ga-toast-warning[class*=-filled] .ga-toast-icon,.ga-toast-warning[class*=-filled] .ga-toast-close,.ga-toast-warning[class*=-filled] .ga-toast-message a{color:#2e2100}.ga-toast-warning[class*=-filled] .ga-toast-progress{background:#2e2100;opacity:.6}.ga-toast-warning[class*=-filled] .ga-toast-step{background:#0000002e}.ga-toast-warning[class*=-filled] .ga-toast-step-active{background:#2e2100}.ga-toast-warning[class*=-filled] .ga-toast-status{color:#2e2100;background:#00000029}.ga-toast-warning[class*=-filled] .ga-toast-close:hover{background:#00000024}.ga-toast[class*=-light]{background:var(--gat-surface);background-image:linear-gradient(0deg,color-mix(in srgb,var(--gat-accent, var(--gat-info)) 14%,transparent),color-mix(in srgb,var(--gat-accent, var(--gat-info)) 14%,transparent));border-color:color-mix(in srgb,var(--gat-accent, var(--gat-info)) 38%,var(--gat-border))}.ga-toast[class*=-light] .ga-toast-meta{color:var(--gat-text-soft)}.ga-toast[class*=-light] .ga-toast-message a{color:color-mix(in srgb,var(--gat-accent, var(--gat-info)) 55%,var(--gat-text));text-decoration:underline}.ga-toast-xs{padding:10px 13px;font-size:.78rem}.ga-toast-xs .ga-toast-icon svg{width:1.05rem;height:1.05rem}.ga-toast-xs .ga-toast-message{font-size:.78rem}.ga-toast-sm{padding:11px 14px;gap:9px}.ga-toast-lg{padding:18px 20px}.ga-toast-lg .ga-toast-title{font-size:1rem}.ga-toast-lg .ga-toast-message{font-size:.95rem}.ga-toast-lg .ga-toast-icon svg{width:1.4rem;height:1.4rem}.ga-toast-xl{padding:22px 24px}.ga-toast-xl .ga-toast-title{font-size:1.125rem}.ga-toast-xl .ga-toast-message{font-size:1.05rem}.ga-toast-xl .ga-toast-icon svg{width:1.6rem;height:1.6rem}.ga-toast-compact{align-items:center;gap:10px;padding:10px 14px}.ga-toast-compact .ga-toast-content{gap:2px}.ga-toast-compact .ga-toast-icon{margin-top:0}.ga-toast-compact .ga-toast-close{align-self:center;margin-top:0}.ga-toast-compact .ga-toast-steps{display:none}.ga-toast-compact .ga-toast-icon svg{width:1.05rem;height:1.05rem}.ga-toast-live-region{position:fixed;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}@media(max-width:520px){.ga-toast-container{--gat-width: auto;left:var(--gat-edge);right:var(--gat-edge);transform:none}.ga-toast-container-top-center,.ga-toast-container-bottom-center,.ga-toast-container-middle-center{transform:none}}[dir=rtl] .ga-toast-progress{transform-origin:right center}@media(prefers-reduced-motion:reduce){.ga-toast{transition:opacity .15s ease}.ga-toast-spinner{animation-duration:1.4s}}@media(prefers-reduced-transparency:reduce){.ga-toast-glass{background:var(--gat-surface-solid);backdrop-filter:none;-webkit-backdrop-filter:none}}.ga-toast-custom{min-width:0}.ga-toast-has-custom{padding:0}.ga-toast-has-custom .ga-toast-close{position:absolute;inset-block-start:8px;inset-inline-end:8px;z-index:1}@keyframes ga-toast-spin{to{transform:rotate(360deg)}}';
7
+
8
+ // src/index.ts
9
+ var canUseDOM = typeof window !== "undefined" && typeof document !== "undefined";
10
+ var PREFIX = "ga-toast";
11
+ var STYLE_ID = "ga-toasts-styles";
12
+ var REMOVE_FALLBACK = 450;
13
+ var COLLAPSE_DELAY = 140;
14
+ var DEFAULT_MAX_VISIBLE = 3;
15
+ var DEFAULT_PEEK = 14;
16
+ var DEFAULT_STACK_GAP = 14;
17
+ var DEFAULT_SCALE_STEP = 0.05;
18
+ var S = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">';
19
+ var DEFAULT_ICONS = {
20
+ success: `${S}<circle cx="12" cy="12" r="9" class="ga-ic-ring"/><path d="M7.75 12.5l2.75 2.75L16.5 9" class="ga-ic-draw"/></svg>`,
21
+ error: `${S}<circle cx="12" cy="12" r="9" class="ga-ic-ring"/><path d="M15 9l-6 6M9 9l6 6" class="ga-ic-draw"/></svg>`,
22
+ warning: `${S}<path d="M12 3.6 2.4 20.4h19.2L12 3.6z" class="ga-ic-ring"/><path d="M12 10v4" class="ga-ic-draw"/><path d="M12 17.4v.01"/></svg>`,
23
+ info: `${S}<circle cx="12" cy="12" r="9" class="ga-ic-ring"/><path d="M12 11v5" class="ga-ic-draw"/><path d="M12 7.6v.01"/></svg>`,
24
+ primary: `${S}<path d="M12 3.5l2.6 5.27 5.82.85-4.21 4.1.99 5.79L12 16.77 6.8 19.5l.99-5.79-4.21-4.1 5.82-.85L12 3.5z" class="ga-ic-draw"/></svg>`,
25
+ secondary: `${S}<path d="M6 9a6 6 0 0112 0c0 4.5 1.8 5.6 1.8 5.6H4.2S6 13.5 6 9z" class="ga-ic-draw"/><path d="M10 19a2 2 0 004 0"/></svg>`,
26
+ loading: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" aria-hidden="true" class="ga-toast-spinner"><circle cx="12" cy="12" r="9" opacity="0.25"/><path d="M12 3a9 9 0 019 9"/></svg>'
27
+ };
28
+ var DEFAULT_CLOSE_ICON = '<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2.25" stroke-linecap="round" aria-hidden="true"><path d="M6 6l12 12M18 6 6 18"/></svg>';
29
+ var ASSERTIVE_TYPES = /* @__PURE__ */ new Set(["error", "warning"]);
30
+ var POSITIONS = [
31
+ "top-start",
32
+ "top-center",
33
+ "top-end",
34
+ "middle-start",
35
+ "middle-center",
36
+ "middle-end",
37
+ "bottom-start",
38
+ "bottom-center",
39
+ "bottom-end"
40
+ ];
41
+ var DEFAULT_DURATIONS = {
42
+ success: 5e3,
43
+ error: 8e3,
44
+ warning: 6e3,
45
+ info: 4e3,
46
+ primary: 5e3,
47
+ secondary: 5e3,
48
+ loading: 0
49
+ };
50
+ var PRESETS = {
51
+ soft: {},
52
+ solid: { surface: "solid", elevation: "raised" },
53
+ minimal: { surface: "outline", elevation: "flat", radius: 10, accentEdge: 0 },
54
+ sharp: { radius: 4 },
55
+ material: { surface: "solid", elevation: "floating", radius: 12 }
56
+ };
57
+ var DENSITY = {
58
+ // padY, padX, font-size
59
+ compact: ["8px", "13px", "0.82rem"],
60
+ comfortable: ["12px", "16px", "0.875rem"],
61
+ spacious: ["16px", "20px", "0.94rem"]
62
+ };
63
+ var ELEVATION = {
64
+ flat: "0 1px 2px rgba(15,23,42,0.06)",
65
+ raised: "0 1px 2px rgba(15,23,42,0.05), 0 8px 20px -12px rgba(15,23,42,0.16)",
66
+ floating: "0 4px 10px -3px rgba(15,23,42,0.12), 0 20px 44px -14px rgba(15,23,42,0.34)"
67
+ };
68
+ function px(v) {
69
+ return typeof v === "number" ? `${v}px` : String(v);
70
+ }
71
+ function resolveTheme(theme) {
72
+ if (!theme) return {};
73
+ if (typeof theme === "string") return { ...PRESETS[theme] || {} };
74
+ const { preset, ...rest } = theme;
75
+ const base = preset ? PRESETS[preset] || {} : {};
76
+ return { ...base, ...rest };
77
+ }
78
+ function themeVars(t) {
79
+ const v = {};
80
+ if (t.width != null) v["--gat-width"] = px(t.width);
81
+ if (t.radius != null) v["--gat-radius"] = px(t.radius);
82
+ if (t.gap != null) v["--gat-gap"] = px(t.gap);
83
+ if (t.edge != null) v["--gat-edge"] = px(t.edge);
84
+ if (t.font) v["--gat-font"] = t.font;
85
+ if (t.density && DENSITY[t.density]) {
86
+ const [py, pxv, fs] = DENSITY[t.density];
87
+ v["--gat-pad-y"] = py;
88
+ v["--gat-pad-x"] = pxv;
89
+ v["--gat-font-size"] = fs;
90
+ }
91
+ if (t.elevation && ELEVATION[t.elevation]) v["--gat-shadow"] = ELEVATION[t.elevation];
92
+ if (t.accentEdge != null) v["--gat-accent-edge"] = px(t.accentEdge);
93
+ if (t.accent) v["--gat-primary"] = t.accent;
94
+ if (t.colors) {
95
+ for (const k of Object.keys(t.colors))
96
+ if (t.colors[k]) v[`--gat-${k}`] = t.colors[k];
97
+ }
98
+ if (t.text) v["--gat-text"] = t.text;
99
+ if (t.textSoft) v["--gat-text-soft"] = t.textSoft;
100
+ if (t.textMuted) v["--gat-text-muted"] = t.textMuted;
101
+ if (t.surfaceColor) {
102
+ v["--gat-surface"] = t.surfaceColor;
103
+ v["--gat-surface-solid"] = t.surfaceColor;
104
+ }
105
+ if (t.border) v["--gat-border"] = t.border;
106
+ if (t.shadow) v["--gat-shadow"] = t.shadow;
107
+ if (t.chip) v["--gat-chip"] = t.chip;
108
+ if (t.ease) v["--gat-ease"] = t.ease;
109
+ return v;
110
+ }
111
+ function verticalDir(position) {
112
+ return position.startsWith("bottom") ? -1 : 1;
113
+ }
114
+ function isRTL() {
115
+ if (!canUseDOM) return false;
116
+ const dir = document.documentElement.getAttribute("dir") || document.body && document.body.getAttribute("dir");
117
+ return dir === "rtl";
118
+ }
119
+ function offscreenTransform(position) {
120
+ const inFromLeft = "translateX(-115%)";
121
+ const inFromRight = "translateX(115%)";
122
+ const rtl = isRTL();
123
+ if (position.endsWith("start")) return rtl ? inFromRight : inFromLeft;
124
+ if (position.endsWith("end")) return rtl ? inFromLeft : inFromRight;
125
+ return verticalDir(position) === 1 ? "translateY(-140%)" : "translateY(140%)";
126
+ }
127
+ function swipeOpacity(dx) {
128
+ return Math.max(0.15, 1 - Math.abs(dx) / 220);
129
+ }
130
+ function normalizeId(idOrSelector) {
131
+ return idOrSelector.startsWith("#") ? idOrSelector.slice(1) : idOrSelector;
132
+ }
133
+ function now() {
134
+ return typeof performance !== "undefined" ? performance.now() : Date.now();
135
+ }
136
+ function prefersReducedMotion() {
137
+ return canUseDOM && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
138
+ }
139
+ var toasterSeq = 0;
140
+ function makeToaster(config = {}) {
141
+ const seq = toasterSeq += 1;
142
+ const instanceClass = `ga-toaster-${seq}`;
143
+ const registry = /* @__PURE__ */ new Map();
144
+ const containers = /* @__PURE__ */ new Map();
145
+ let globalDefaults = { ...config.defaults || {} };
146
+ let logger = config.logger || null;
147
+ let liveRegions = null;
148
+ let escapeBound = false;
149
+ let visibilityBound = false;
150
+ let idCounter = 0;
151
+ let activeModalId = null;
152
+ let backdropEl = null;
153
+ const icons = { ...DEFAULT_ICONS };
154
+ let closeIcon = DEFAULT_CLOSE_ICON;
155
+ const durations = { ...DEFAULT_DURATIONS };
156
+ let maxVisible = DEFAULT_MAX_VISIBLE;
157
+ let peek = DEFAULT_PEEK;
158
+ let stackGap = DEFAULT_STACK_GAP;
159
+ let scaleStep = DEFAULT_SCALE_STEP;
160
+ let expandMode = "hover";
161
+ let newestOnTop = false;
162
+ let injectEnabled = config.injectStyles !== false;
163
+ const styleNonce = config.styleNonce;
164
+ const renderFn = config.render || null;
165
+ let themeTokens = {};
166
+ let surfaceMode;
167
+ let progressMode = "bar";
168
+ let themeStyleEl = null;
169
+ const mount = config.root || (canUseDOM ? document.body : null);
170
+ const isShadow = !!config.root && typeof config.root.host !== "undefined";
171
+ const styleTarget = isShadow ? config.root : canUseDOM ? document.head : null;
172
+ function applyConfigDurations(d) {
173
+ if (!d) return;
174
+ for (const k of Object.keys(d))
175
+ if (typeof d[k] === "number") durations[k] = d[k];
176
+ }
177
+ function applyConfigIcons(ic) {
178
+ if (!ic) return;
179
+ for (const k of Object.keys(ic)) {
180
+ if (k === "close") {
181
+ if (ic.close) closeIcon = ic.close;
182
+ continue;
183
+ }
184
+ icons[k] = ic[k];
185
+ }
186
+ }
187
+ function applyConfigStack(s) {
188
+ if (!s) return;
189
+ if (typeof s.peek === "number") peek = s.peek;
190
+ if (typeof s.gap === "number") stackGap = s.gap;
191
+ if (typeof s.scaleStep === "number") scaleStep = s.scaleStep;
192
+ if (s.expand) expandMode = s.expand;
193
+ if (typeof s.newestOnTop === "boolean") newestOnTop = s.newestOnTop;
194
+ }
195
+ applyConfigDurations(config.durations);
196
+ applyConfigIcons(config.icons);
197
+ applyConfigStack(config.stack);
198
+ if (typeof config.maxVisible === "number")
199
+ maxVisible = Math.max(1, Math.floor(config.maxVisible) || 1);
200
+ function nextId() {
201
+ idCounter += 1;
202
+ const rand = Math.random().toString(36).slice(2, 8);
203
+ return `${PREFIX}-${idCounter}-${rand}`;
204
+ }
205
+ function log(event, payload) {
206
+ if (logger) {
207
+ try {
208
+ logger(event, payload);
209
+ } catch {
210
+ }
211
+ }
212
+ }
213
+ function injectStyles() {
214
+ if (!canUseDOM || !injectEnabled || !styleTarget) return;
215
+ const doc = document;
216
+ if (styleTarget.querySelector?.(`#${STYLE_ID}`)) return;
217
+ if (styleTarget === doc.head && doc.getElementById(STYLE_ID)) return;
218
+ const el = doc.createElement("style");
219
+ el.id = STYLE_ID;
220
+ if (styleNonce) el.setAttribute("nonce", styleNonce);
221
+ el.textContent = css;
222
+ styleTarget.appendChild(el);
223
+ }
224
+ function applyTheme() {
225
+ if (!canUseDOM || !styleTarget) return;
226
+ const t = themeTokens;
227
+ surfaceMode = t.surface;
228
+ progressMode = t.progress || "bar";
229
+ const sel = `.${instanceClass}.ga-toast-container`;
230
+ const light = themeVars(t);
231
+ const lightBody = Object.entries(light).map(([k, v]) => `${k}:${v}`).join(";");
232
+ const darkVars = t.dark ? themeVars(t.dark) : {};
233
+ const darkBody = Object.entries(darkVars).map(([k, v]) => `${k}:${v}`).join(";");
234
+ let cssText = "";
235
+ if (lightBody) cssText += `${sel}{${lightBody}}`;
236
+ if (darkBody)
237
+ cssText += `[data-ga-theme="dark"] ${sel},.ga-theme-dark ${sel}{${darkBody}}`;
238
+ if (!cssText) {
239
+ themeStyleEl?.remove();
240
+ themeStyleEl = null;
241
+ return;
242
+ }
243
+ if (!themeStyleEl) {
244
+ themeStyleEl = document.createElement("style");
245
+ themeStyleEl.id = `ga-toasts-theme-${seq}`;
246
+ if (styleNonce) themeStyleEl.setAttribute("nonce", styleNonce);
247
+ styleTarget.appendChild(themeStyleEl);
248
+ }
249
+ themeStyleEl.textContent = cssText;
250
+ }
251
+ function ensureLiveRegions() {
252
+ if (!canUseDOM || !mount) return;
253
+ if (liveRegions && liveRegions.polite.isConnected && liveRegions.assertive.isConnected)
254
+ return;
255
+ const make = (politeness) => {
256
+ const region = document.createElement("div");
257
+ region.className = "ga-toast-live-region";
258
+ region.setAttribute("aria-live", politeness);
259
+ region.setAttribute("aria-atomic", "true");
260
+ region.setAttribute("role", politeness === "assertive" ? "alert" : "status");
261
+ mount.appendChild(region);
262
+ return region;
263
+ };
264
+ liveRegions = { polite: make("polite"), assertive: make("assertive") };
265
+ }
266
+ function announce(text, assertive) {
267
+ if (!text) return;
268
+ ensureLiveRegions();
269
+ if (!liveRegions) return;
270
+ const region = assertive ? liveRegions.assertive : liveRegions.polite;
271
+ region.textContent = "";
272
+ window.setTimeout(() => {
273
+ region.textContent = text;
274
+ }, 50);
275
+ }
276
+ function getContainer(position) {
277
+ const existing = containers.get(position);
278
+ if (existing && existing.isConnected) return existing;
279
+ const container = document.createElement("div");
280
+ container.className = `ga-toast-container ga-toast-container-${position} ${instanceClass}`;
281
+ container.setAttribute("data-ga-position", position);
282
+ container.setAttribute("role", "region");
283
+ container.setAttribute("aria-label", "Notifications");
284
+ if (expandMode === "always") container.dataset.gaExpanded = "true";
285
+ if (expandMode === "hover") {
286
+ let collapseTimer = null;
287
+ const cancelCollapse = () => {
288
+ if (collapseTimer != null) {
289
+ clearTimeout(collapseTimer);
290
+ collapseTimer = null;
291
+ }
292
+ };
293
+ const expand = () => {
294
+ cancelCollapse();
295
+ setExpanded(container, true);
296
+ };
297
+ const collapse = () => {
298
+ cancelCollapse();
299
+ collapseTimer = setTimeout(() => {
300
+ collapseTimer = null;
301
+ setExpanded(container, false);
302
+ }, COLLAPSE_DELAY);
303
+ };
304
+ container.addEventListener("pointerenter", expand);
305
+ container.addEventListener("pointerleave", collapse);
306
+ container.addEventListener("focusin", expand);
307
+ container.addEventListener("focusout", (e) => {
308
+ if (!container.contains(e.relatedTarget)) collapse();
309
+ });
310
+ }
311
+ mount?.appendChild(container);
312
+ containers.set(position, container);
313
+ return container;
314
+ }
315
+ function setExpanded(container, expanded) {
316
+ if (expandMode !== "hover") return;
317
+ if (container.dataset.gaExpanded === "true" === expanded) return;
318
+ container.dataset.gaExpanded = expanded ? "true" : "false";
319
+ layout(container);
320
+ }
321
+ function instancesIn(container) {
322
+ return Array.from(container.children).map((child) => registry.get(child.id)).filter((inst) => !!inst);
323
+ }
324
+ function layout(container) {
325
+ const all = instancesIn(container);
326
+ const position = container.dataset.gaPosition || "top-end";
327
+ const dir = verticalDir(position);
328
+ const expanded = expandMode === "always" || expandMode === "hover" && container.dataset.gaExpanded === "true";
329
+ const reduce = prefersReducedMotion();
330
+ const front = newestOnTop ? all.slice() : all.slice().reverse();
331
+ container.style.setProperty("--ga-toasts-count", String(front.length));
332
+ let cumulative = 0;
333
+ front.forEach((inst, frontIndex) => {
334
+ if (inst.closing) return;
335
+ if (!inst.el.dataset.gaMounted) {
336
+ cumulative += inst.height + stackGap;
337
+ return;
338
+ }
339
+ let translateY;
340
+ let scale;
341
+ let opacity;
342
+ const overflow = frontIndex >= maxVisible;
343
+ if (overflow) pauseTimer(inst, "overflow");
344
+ else resumeTimer(inst, "overflow");
345
+ if (expanded || reduce) {
346
+ translateY = dir * cumulative;
347
+ scale = 1;
348
+ opacity = 1;
349
+ } else {
350
+ translateY = dir * frontIndex * peek;
351
+ scale = Math.max(1 - frontIndex * scaleStep, 0.85);
352
+ opacity = overflow ? 0 : 1;
353
+ }
354
+ const swipe = inst.swipeX ? ` translateX(${inst.swipeX}px)` : "";
355
+ const ty = position.startsWith("middle") ? `calc(-50% + ${translateY}px)` : `${translateY}px`;
356
+ inst.el.style.transform = `translateY(${ty})${swipe} scale(${scale})`;
357
+ inst.el.style.opacity = inst.swipeX ? String(swipeOpacity(inst.swipeX)) : String(opacity);
358
+ inst.el.style.zIndex = String(1e3 + front.length - frontIndex);
359
+ inst.el.style.pointerEvents = opacity === 0 ? "none" : "auto";
360
+ inst.el.setAttribute("aria-hidden", opacity === 0 ? "true" : "false");
361
+ cumulative += inst.height + stackGap;
362
+ });
363
+ }
364
+ function measure(inst) {
365
+ const h = inst.el.offsetHeight;
366
+ if (h > 0) inst.height = h;
367
+ }
368
+ function resolveOptions(options) {
369
+ const merged = {
370
+ type: "info",
371
+ duration: 5e3,
372
+ closable: true,
373
+ position: "top-end",
374
+ animation: "slide",
375
+ swipeToClose: true,
376
+ closeOnEscape: true,
377
+ progress: true,
378
+ progressPosition: "bottom",
379
+ pauseOnHover: true,
380
+ pauseOnPageHidden: true,
381
+ autoIcon: true,
382
+ glassmorphism: true,
383
+ html: false,
384
+ ...globalDefaults,
385
+ ...options
386
+ };
387
+ merged.position = merged.position || "top-end";
388
+ if (!POSITIONS.includes(merged.position)) merged.position = "top-end";
389
+ return merged;
390
+ }
391
+ function iconFor(type) {
392
+ return icons[type] ?? icons.info ?? null;
393
+ }
394
+ function createCloseButton() {
395
+ const btn = document.createElement("button");
396
+ btn.type = "button";
397
+ btn.className = "ga-toast-close";
398
+ btn.setAttribute("aria-label", "Close notification");
399
+ btn.setAttribute("data-ga-close", "");
400
+ btn.innerHTML = closeIcon;
401
+ return btn;
402
+ }
403
+ function appendProgress(el, opts, type) {
404
+ if (progressMode === "ring" || !opts.progress || (opts.duration || 0) <= 0 || opts.progressPosition === "none")
405
+ return null;
406
+ const p = document.createElement("div");
407
+ p.className = `ga-toast-progress ga-toast-progress-${type}` + (opts.progressPosition === "top" ? " ga-toast-progress-top" : "");
408
+ el.appendChild(p);
409
+ return p;
410
+ }
411
+ function buildIcon(iconHtml, opts) {
412
+ const icon = document.createElement("div");
413
+ icon.className = "ga-toast-icon";
414
+ icon.innerHTML = iconHtml;
415
+ const wantRing = progressMode === "ring" && opts.progress !== false && (opts.duration || 0) > 0;
416
+ if (!wantRing) return { el: icon, ring: null };
417
+ icon.classList.add("ga-toast-icon-ring");
418
+ const ns = "http://www.w3.org/2000/svg";
419
+ const svg = document.createElementNS(ns, "svg");
420
+ svg.setAttribute("class", "ga-toast-ring");
421
+ svg.setAttribute("viewBox", "0 0 36 36");
422
+ svg.setAttribute("aria-hidden", "true");
423
+ const track = document.createElementNS(ns, "circle");
424
+ track.setAttribute("class", "ga-toast-ring-track");
425
+ track.setAttribute("cx", "18");
426
+ track.setAttribute("cy", "18");
427
+ track.setAttribute("r", "16");
428
+ const bar = document.createElementNS(ns, "circle");
429
+ bar.setAttribute("class", "ga-toast-ring-bar");
430
+ bar.setAttribute("cx", "18");
431
+ bar.setAttribute("cy", "18");
432
+ bar.setAttribute("r", "16");
433
+ svg.appendChild(track);
434
+ svg.appendChild(bar);
435
+ icon.appendChild(svg);
436
+ return { el: icon, ring: bar };
437
+ }
438
+ function buildToastElement(opts, id) {
439
+ const type = opts.type || "info";
440
+ const el = document.createElement("div");
441
+ el.id = id;
442
+ const glass = surfaceMode === "glass" || surfaceMode === void 0 && opts.glassmorphism;
443
+ el.className = [
444
+ "ga-toast",
445
+ `ga-toast-${type}`,
446
+ opts.size ? `ga-toast-${opts.size}` : "",
447
+ opts.variant ? `ga-toast-${type}-${opts.variant}` : "",
448
+ opts.animation || "",
449
+ opts.compact ? "ga-toast-compact" : "",
450
+ glass ? "ga-toast-glass" : "",
451
+ surfaceMode === "outline" ? "ga-toast-outline" : "",
452
+ type === "loading" ? "ga-toast-loading" : "",
453
+ opts.className || ""
454
+ ].filter(Boolean).join(" ");
455
+ const role = opts.role || (ASSERTIVE_TYPES.has(type) ? "alert" : "status");
456
+ el.setAttribute("role", role);
457
+ el.setAttribute("aria-live", "off");
458
+ el.setAttribute("data-ga-type", type);
459
+ if (role === "alertdialog" || role === "dialog") {
460
+ const label = [opts.title, opts.message].filter(Boolean).join(". ");
461
+ if (label) el.setAttribute("aria-label", label);
462
+ }
463
+ let customEl = null;
464
+ if (opts.content != null) {
465
+ customEl = typeof opts.content === "function" ? opts.content() : opts.content;
466
+ } else if (renderFn) {
467
+ const produced = renderFn(opts, { id, close: () => close(id) });
468
+ if (produced) customEl = produced;
469
+ }
470
+ if (customEl != null) {
471
+ el.classList.add("ga-toast-has-custom");
472
+ const wrap = document.createElement("div");
473
+ wrap.className = "ga-toast-custom";
474
+ if (typeof customEl === "string") wrap.innerHTML = customEl;
475
+ else wrap.appendChild(customEl);
476
+ el.appendChild(wrap);
477
+ if (opts.closable) el.appendChild(createCloseButton());
478
+ return { el, progressEl: appendProgress(el, opts, type), ring: null };
479
+ }
480
+ if (opts.avatar) {
481
+ const avatar = document.createElement("img");
482
+ avatar.className = "ga-toast-avatar";
483
+ avatar.src = opts.avatar;
484
+ avatar.alt = opts.avatarAlt || opts.title || "Notification";
485
+ el.appendChild(avatar);
486
+ }
487
+ let iconHtml = null;
488
+ if (opts.icon) iconHtml = String(opts.icon);
489
+ else if (opts.icon !== false && opts.autoIcon !== false && !opts.avatar)
490
+ iconHtml = iconFor(type);
491
+ let ring = null;
492
+ if (iconHtml) {
493
+ const built = buildIcon(iconHtml, opts);
494
+ ring = built.ring;
495
+ el.appendChild(built.el);
496
+ }
497
+ const content = document.createElement("div");
498
+ content.className = "ga-toast-content";
499
+ if (opts.title || opts.showStatus || opts.unread) {
500
+ const header = document.createElement("div");
501
+ header.className = "ga-toast-header";
502
+ if (opts.unread) {
503
+ const dot = document.createElement("span");
504
+ dot.className = "ga-toast-unread-dot";
505
+ header.appendChild(dot);
506
+ }
507
+ if (opts.title) {
508
+ const title = document.createElement("div");
509
+ title.className = "ga-toast-title" + (opts.truncateTitle ? " ga-toast-title-truncate" : "");
510
+ title.textContent = opts.title;
511
+ header.appendChild(title);
512
+ }
513
+ if (opts.showStatus) {
514
+ const status = document.createElement("span");
515
+ status.className = "ga-toast-status";
516
+ const label = String(opts.statusText || type);
517
+ status.textContent = label.charAt(0).toUpperCase() + label.slice(1);
518
+ header.appendChild(status);
519
+ }
520
+ content.appendChild(header);
521
+ }
522
+ if (opts.message || opts.meta) {
523
+ const body = document.createElement("div");
524
+ body.className = "ga-toast-body";
525
+ if (opts.meta) {
526
+ const meta = document.createElement("div");
527
+ meta.className = "ga-toast-meta";
528
+ meta.textContent = opts.meta;
529
+ body.appendChild(meta);
530
+ }
531
+ if (opts.message) {
532
+ const message = document.createElement("div");
533
+ message.className = "ga-toast-message";
534
+ if (opts.html) message.innerHTML = opts.message;
535
+ else message.textContent = opts.message;
536
+ body.appendChild(message);
537
+ }
538
+ content.appendChild(body);
539
+ }
540
+ if (opts.actions && opts.actions.length && !opts.compact) {
541
+ const actions = document.createElement("div");
542
+ actions.className = "ga-toast-actions";
543
+ for (const action of opts.actions) {
544
+ const btn = document.createElement("button");
545
+ btn.type = "button";
546
+ btn.className = [
547
+ "ga-toast-btn",
548
+ action.className || action.class || "ga-toast-btn-secondary"
549
+ ].join(" ");
550
+ btn.textContent = action.text;
551
+ btn.setAttribute("data-ga-action", "");
552
+ actions.appendChild(btn);
553
+ }
554
+ content.appendChild(actions);
555
+ }
556
+ if (opts.steps && opts.steps > 1) {
557
+ const steps = document.createElement("div");
558
+ steps.className = "ga-toast-steps";
559
+ const active = Math.min(opts.steps, Math.max(1, opts.currentStep || 1));
560
+ for (let i = 1; i <= opts.steps; i += 1) {
561
+ const step = document.createElement("div");
562
+ step.className = "ga-toast-step" + (i <= active ? " ga-toast-step-active" : "");
563
+ steps.appendChild(step);
564
+ }
565
+ content.appendChild(steps);
566
+ }
567
+ el.appendChild(content);
568
+ if (opts.closable) el.appendChild(createCloseButton());
569
+ return { el, progressEl: appendProgress(el, opts, type), ring };
570
+ }
571
+ const RING_CIRC = 2 * Math.PI * 16;
572
+ function startTimer(inst) {
573
+ const { timer } = inst;
574
+ if (timer.duration <= 0 || timer.remaining <= 0) return;
575
+ if (timer.holds.size > 0) return;
576
+ timer.startedAt = now();
577
+ timer.handle = setTimeout(() => close(inst.id), timer.remaining);
578
+ animateProgress(inst, timer.remaining);
579
+ }
580
+ function pauseTimer(inst, reason) {
581
+ const { timer } = inst;
582
+ const wasHeld = timer.holds.size > 0;
583
+ timer.holds.add(reason);
584
+ if (wasHeld || timer.handle == null) return;
585
+ clearTimeout(timer.handle);
586
+ timer.handle = null;
587
+ const elapsed = now() - timer.startedAt;
588
+ timer.remaining = Math.max(0, timer.remaining - elapsed);
589
+ freezeProgress(inst);
590
+ }
591
+ function resumeTimer(inst, reason) {
592
+ const { timer } = inst;
593
+ if (!timer.holds.delete(reason)) return;
594
+ if (timer.holds.size > 0) return;
595
+ startTimer(inst);
596
+ }
597
+ function animateProgress(inst, ms) {
598
+ const fraction = inst.timer.duration > 0 ? ms / inst.timer.duration : 0;
599
+ if (inst.ring) {
600
+ const ring = inst.ring;
601
+ ring.style.transition = "none";
602
+ ring.style.strokeDasharray = String(RING_CIRC);
603
+ ring.style.strokeDashoffset = String(RING_CIRC * (1 - fraction));
604
+ void ring.getBoundingClientRect?.();
605
+ ring.style.transition = `stroke-dashoffset ${ms}ms linear`;
606
+ ring.style.strokeDashoffset = String(RING_CIRC);
607
+ return;
608
+ }
609
+ const bar = inst.progressEl;
610
+ if (!bar) return;
611
+ bar.style.transition = "none";
612
+ bar.style.transform = `scaleX(${fraction})`;
613
+ void bar.offsetWidth;
614
+ bar.style.transition = `transform ${ms}ms linear`;
615
+ bar.style.transform = "scaleX(0)";
616
+ }
617
+ function freezeProgress(inst) {
618
+ const fraction = inst.timer.duration > 0 ? inst.timer.remaining / inst.timer.duration : 0;
619
+ if (inst.ring) {
620
+ inst.ring.style.transition = "none";
621
+ inst.ring.style.strokeDashoffset = String(RING_CIRC * (1 - fraction));
622
+ return;
623
+ }
624
+ const bar = inst.progressEl;
625
+ if (!bar) return;
626
+ bar.style.transition = "none";
627
+ bar.style.transform = `scaleX(${fraction})`;
628
+ }
629
+ function attachHandlers(inst, opts) {
630
+ const { el, controller } = inst;
631
+ const signal = controller.signal;
632
+ el.addEventListener(
633
+ "click",
634
+ (e) => {
635
+ const target = e.target;
636
+ if (target.closest("[data-ga-close]")) {
637
+ e.preventDefault();
638
+ close(inst.id);
639
+ return;
640
+ }
641
+ const actionBtn = target.closest("[data-ga-action]");
642
+ if (actionBtn && opts.actions) {
643
+ const idx = Array.from(
644
+ actionBtn.parentElement?.children || []
645
+ ).indexOf(actionBtn);
646
+ const action = opts.actions[idx];
647
+ if (action) {
648
+ const handler = action.onClick || action.click;
649
+ if (handler) handler(e, inst.handle);
650
+ if (action.closeOnClick !== false) close(inst.id);
651
+ }
652
+ return;
653
+ }
654
+ if (opts.clickToClose && !target.closest(".ga-toast-actions")) {
655
+ close(inst.id);
656
+ }
657
+ },
658
+ { signal }
659
+ );
660
+ if (opts.pauseOnHover) {
661
+ el.addEventListener("pointerenter", () => pauseTimer(inst, "hover"), { signal });
662
+ el.addEventListener("pointerleave", () => resumeTimer(inst, "hover"), { signal });
663
+ el.addEventListener("focusin", () => pauseTimer(inst, "focus"), { signal });
664
+ el.addEventListener("focusout", () => resumeTimer(inst, "focus"), { signal });
665
+ }
666
+ if (opts.swipeToClose) attachSwipe(inst);
667
+ if (opts.modal) {
668
+ el.addEventListener(
669
+ "keydown",
670
+ (e) => {
671
+ if (e.key !== "Tab") return;
672
+ const focusables = el.querySelectorAll(
673
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
674
+ );
675
+ if (!focusables.length) return;
676
+ const first = focusables[0];
677
+ const last = focusables[focusables.length - 1];
678
+ const active = document.activeElement;
679
+ if (e.shiftKey && active === first) {
680
+ e.preventDefault();
681
+ last.focus();
682
+ } else if (!e.shiftKey && active === last) {
683
+ e.preventDefault();
684
+ first.focus();
685
+ }
686
+ },
687
+ { signal }
688
+ );
689
+ }
690
+ }
691
+ function attachSwipe(inst) {
692
+ const { el, controller } = inst;
693
+ const signal = controller.signal;
694
+ const THRESHOLD = 70;
695
+ let dragging = false;
696
+ let swiping = false;
697
+ let startX = 0;
698
+ let startY = 0;
699
+ el.addEventListener(
700
+ "pointerdown",
701
+ (e) => {
702
+ if (e.pointerType === "mouse" && e.button !== 0) return;
703
+ if (e.target.closest("button, a, input, textarea, select"))
704
+ return;
705
+ dragging = true;
706
+ swiping = false;
707
+ startX = e.clientX;
708
+ startY = e.clientY;
709
+ pauseTimer(inst, "swipe");
710
+ try {
711
+ el.setPointerCapture(e.pointerId);
712
+ } catch {
713
+ }
714
+ },
715
+ { signal }
716
+ );
717
+ el.addEventListener(
718
+ "pointermove",
719
+ (e) => {
720
+ if (!dragging) return;
721
+ const dx = e.clientX - startX;
722
+ const dy = e.clientY - startY;
723
+ if (Math.abs(dx) < 6 || Math.abs(dx) < Math.abs(dy)) return;
724
+ e.preventDefault();
725
+ if (!swiping) {
726
+ swiping = true;
727
+ el.classList.add("ga-toast-swiping");
728
+ const sel = typeof window !== "undefined" && typeof window.getSelection === "function" ? window.getSelection() : null;
729
+ sel?.removeAllRanges();
730
+ }
731
+ inst.swipeX = dx;
732
+ const container = el.parentElement;
733
+ if (container) layout(container);
734
+ },
735
+ { signal }
736
+ );
737
+ const end = (e) => {
738
+ if (!dragging) return;
739
+ dragging = false;
740
+ swiping = false;
741
+ el.classList.remove("ga-toast-swiping");
742
+ const dx = e.clientX - startX;
743
+ if (Math.abs(dx) > THRESHOLD) {
744
+ const dir = dx > 0 ? 1 : -1;
745
+ inst.el.style.transition = "transform 0.2s ease, opacity 0.2s ease";
746
+ inst.el.style.transform = `translateX(${dir * 400}px)`;
747
+ inst.el.style.opacity = "0";
748
+ close(inst.id);
749
+ } else {
750
+ inst.swipeX = 0;
751
+ const container = el.parentElement;
752
+ if (container) layout(container);
753
+ resumeTimer(inst, "swipe");
754
+ }
755
+ };
756
+ el.addEventListener("pointerup", end, { signal });
757
+ el.addEventListener("pointercancel", end, { signal });
758
+ }
759
+ function bindEscape() {
760
+ if (escapeBound || !canUseDOM) return;
761
+ escapeBound = true;
762
+ document.addEventListener("keydown", (e) => {
763
+ if (e.key !== "Escape") return;
764
+ let newest = null;
765
+ for (const inst of registry.values()) {
766
+ if (inst.closing) continue;
767
+ if (inst.opts.closeOnEscape === false || !inst.opts.closable) continue;
768
+ newest = inst;
769
+ }
770
+ if (newest) close(newest.id);
771
+ });
772
+ }
773
+ function bindVisibility() {
774
+ if (visibilityBound || !canUseDOM || typeof document.addEventListener !== "function")
775
+ return;
776
+ visibilityBound = true;
777
+ document.addEventListener("visibilitychange", () => {
778
+ const hidden = document.hidden;
779
+ for (const inst of registry.values()) {
780
+ if (inst.closing || inst.opts.pauseOnPageHidden === false) continue;
781
+ if (hidden) pauseTimer(inst, "tab");
782
+ else resumeTimer(inst, "tab");
783
+ }
784
+ });
785
+ }
786
+ function showBackdrop() {
787
+ if (!canUseDOM || !mount) return;
788
+ if (!backdropEl) {
789
+ backdropEl = document.createElement("div");
790
+ backdropEl.className = "ga-toast-backdrop";
791
+ }
792
+ if (!backdropEl.isConnected) mount.appendChild(backdropEl);
793
+ const bd = backdropEl;
794
+ requestAnimationFrame(() => bd.classList.add("ga-toast-backdrop-show"));
795
+ }
796
+ function hideBackdrop() {
797
+ if (!backdropEl) return;
798
+ const bd = backdropEl;
799
+ bd.classList.remove("ga-toast-backdrop-show");
800
+ setTimeout(() => {
801
+ if (activeModalId == null && bd.parentNode) bd.remove();
802
+ }, 220);
803
+ }
804
+ function noopHandle(id) {
805
+ const handle = {
806
+ id,
807
+ el: canUseDOM ? document.createElement("div") : {},
808
+ update: () => handle,
809
+ close: () => {
810
+ }
811
+ };
812
+ return handle;
813
+ }
814
+ function show(options = {}) {
815
+ if (!canUseDOM) return noopHandle(options.id || nextId());
816
+ const opts = resolveOptions(options);
817
+ if (opts.id && registry.has(opts.id)) {
818
+ return update(opts.id, options) || noopHandle(opts.id);
819
+ }
820
+ if (opts.modal) {
821
+ if (activeModalId && registry.has(activeModalId)) {
822
+ return registry.get(activeModalId).handle;
823
+ }
824
+ opts.swipeToClose = false;
825
+ opts.closeOnEscape = false;
826
+ opts.moveFocus = true;
827
+ }
828
+ injectStyles();
829
+ bindEscape();
830
+ bindVisibility();
831
+ const id = opts.id || nextId();
832
+ const { el, progressEl, ring } = buildToastElement(opts, id);
833
+ const controller = new AbortController();
834
+ const timer = {
835
+ duration: opts.duration || 0,
836
+ remaining: opts.duration || 0,
837
+ startedAt: 0,
838
+ handle: null,
839
+ holds: /* @__PURE__ */ new Set()
840
+ };
841
+ const handle = {
842
+ id,
843
+ el,
844
+ update: (o) => update(id, o) ? handle : handle,
845
+ close: () => close(id)
846
+ };
847
+ const inst = {
848
+ id,
849
+ el,
850
+ handle,
851
+ opts,
852
+ controller,
853
+ progressEl,
854
+ ring,
855
+ timer,
856
+ height: 0,
857
+ closing: false,
858
+ swipeX: 0,
859
+ resizeObserver: null,
860
+ prevFocus: opts.moveFocus ? document.activeElement : null
861
+ };
862
+ registry.set(id, inst);
863
+ if (opts.modal) {
864
+ activeModalId = id;
865
+ el.setAttribute("aria-modal", "true");
866
+ showBackdrop();
867
+ }
868
+ const container = getContainer(opts.position);
869
+ el.style.transform = offscreenTransform(opts.position);
870
+ el.style.opacity = "0";
871
+ container.appendChild(el);
872
+ attachHandlers(inst, opts);
873
+ if (typeof ResizeObserver !== "undefined") {
874
+ inst.resizeObserver = new ResizeObserver(() => {
875
+ measure(inst);
876
+ const parent = el.parentElement;
877
+ if (parent) layout(parent);
878
+ });
879
+ inst.resizeObserver.observe(el);
880
+ }
881
+ requestAnimationFrame(() => {
882
+ requestAnimationFrame(() => {
883
+ if (!registry.has(id)) return;
884
+ measure(inst);
885
+ el.dataset.gaMounted = "true";
886
+ el.style.opacity = "1";
887
+ layout(container);
888
+ startTimer(inst);
889
+ if (opts.moveFocus) {
890
+ const target = el.querySelector("[data-ga-action]") || el;
891
+ if (target === el) el.tabIndex = -1;
892
+ try {
893
+ target.focus({ preventScroll: true });
894
+ } catch {
895
+ }
896
+ }
897
+ });
898
+ });
899
+ if (!opts.modal && opts.role !== "alertdialog") {
900
+ announce(
901
+ [opts.title, opts.message].filter(Boolean).join(". "),
902
+ ASSERTIVE_TYPES.has(opts.type || "info")
903
+ );
904
+ }
905
+ if (opts.onShow) {
906
+ try {
907
+ opts.onShow(handle);
908
+ } catch (err) {
909
+ log("toast:onShow:error", err);
910
+ }
911
+ }
912
+ log("toast:show", { id, type: opts.type });
913
+ return handle;
914
+ }
915
+ function close(target) {
916
+ if (!target) return;
917
+ const id = typeof target === "string" ? normalizeId(target) : target.id;
918
+ const inst = registry.get(id);
919
+ if (!inst || inst.closing) return;
920
+ inst.closing = true;
921
+ if (inst.timer.handle != null) {
922
+ clearTimeout(inst.timer.handle);
923
+ inst.timer.handle = null;
924
+ }
925
+ inst.controller.abort();
926
+ inst.resizeObserver?.disconnect();
927
+ const { el } = inst;
928
+ const container = el.parentElement;
929
+ if (!inst.swipeX) {
930
+ el.style.transition = "";
931
+ el.classList.add("ga-toast-hide");
932
+ el.style.transform = offscreenTransform(inst.opts.position);
933
+ el.style.opacity = "0";
934
+ }
935
+ let removed = false;
936
+ const finalize = () => {
937
+ if (removed) return;
938
+ removed = true;
939
+ el.removeEventListener("transitionend", onEnd);
940
+ registry.delete(id);
941
+ el.remove();
942
+ if (activeModalId === id) {
943
+ activeModalId = null;
944
+ hideBackdrop();
945
+ }
946
+ if (container) layout(container);
947
+ const prev = inst.prevFocus;
948
+ if (prev && typeof prev.focus === "function" && document.contains(prev)) {
949
+ try {
950
+ prev.focus({ preventScroll: true });
951
+ } catch {
952
+ }
953
+ }
954
+ if (inst.opts.onClose) {
955
+ try {
956
+ inst.opts.onClose(inst.handle);
957
+ } catch (err) {
958
+ log("toast:onClose:error", err);
959
+ }
960
+ }
961
+ log("toast:close", { id });
962
+ };
963
+ const onEnd = (e) => {
964
+ if (e.target === el && (e.propertyName === "transform" || e.propertyName === "opacity"))
965
+ finalize();
966
+ };
967
+ el.addEventListener("transitionend", onEnd);
968
+ setTimeout(finalize, REMOVE_FALLBACK);
969
+ }
970
+ function update(target, options) {
971
+ const id = typeof target === "string" ? normalizeId(target) : target.id;
972
+ const inst = registry.get(id);
973
+ if (!inst) {
974
+ log("toast:update:not-found", id);
975
+ return null;
976
+ }
977
+ const opts = inst.opts;
978
+ if (options.title !== void 0) {
979
+ let title = inst.el.querySelector(".ga-toast-title");
980
+ if (!title && options.title) {
981
+ const content = inst.el.querySelector(".ga-toast-content");
982
+ if (content) {
983
+ let header = content.querySelector(".ga-toast-header");
984
+ if (!header) {
985
+ header = document.createElement("div");
986
+ header.className = "ga-toast-header";
987
+ content.insertBefore(header, content.firstChild);
988
+ }
989
+ title = document.createElement("div");
990
+ title.className = "ga-toast-title";
991
+ header.appendChild(title);
992
+ }
993
+ }
994
+ if (title) title.textContent = options.title;
995
+ opts.title = options.title;
996
+ }
997
+ if (options.message !== void 0) {
998
+ const useHtml = options.html ?? opts.html;
999
+ let message = inst.el.querySelector(".ga-toast-message");
1000
+ if (!message && options.message) {
1001
+ let body = inst.el.querySelector(".ga-toast-body");
1002
+ if (!body) {
1003
+ body = document.createElement("div");
1004
+ body.className = "ga-toast-body";
1005
+ const content = inst.el.querySelector(".ga-toast-content");
1006
+ const anchor = content?.querySelector(".ga-toast-actions, .ga-toast-steps");
1007
+ if (anchor) content.insertBefore(body, anchor);
1008
+ else content?.appendChild(body);
1009
+ }
1010
+ message = document.createElement("div");
1011
+ message.className = "ga-toast-message";
1012
+ body.appendChild(message);
1013
+ }
1014
+ if (message) {
1015
+ if (useHtml) message.innerHTML = options.message;
1016
+ else message.textContent = options.message;
1017
+ }
1018
+ opts.message = options.message;
1019
+ }
1020
+ if (options.type && options.type !== opts.type) {
1021
+ const prev = opts.type;
1022
+ inst.el.classList.remove(`ga-toast-${prev}`, "ga-toast-loading");
1023
+ inst.el.classList.add(`ga-toast-${options.type}`);
1024
+ inst.el.setAttribute("data-ga-type", options.type);
1025
+ if (options.type === "loading") inst.el.classList.add("ga-toast-loading");
1026
+ if (inst.progressEl) {
1027
+ inst.progressEl.className = inst.progressEl.className.replace(
1028
+ /ga-toast-progress-\w+/,
1029
+ `ga-toast-progress-${options.type}`
1030
+ );
1031
+ }
1032
+ const iconEl = inst.el.querySelector(".ga-toast-icon");
1033
+ if (iconEl && opts.autoIcon !== false && !options.icon) {
1034
+ const next = iconFor(options.type);
1035
+ if (next != null) iconEl.innerHTML = next;
1036
+ }
1037
+ opts.type = options.type;
1038
+ }
1039
+ if (options.icon !== void 0) {
1040
+ let iconEl = inst.el.querySelector(".ga-toast-icon");
1041
+ if (options.icon) {
1042
+ if (!iconEl) {
1043
+ iconEl = document.createElement("div");
1044
+ iconEl.className = "ga-toast-icon";
1045
+ const content = inst.el.querySelector(".ga-toast-content");
1046
+ inst.el.insertBefore(iconEl, content);
1047
+ }
1048
+ iconEl.innerHTML = String(options.icon);
1049
+ } else if (iconEl) {
1050
+ iconEl.remove();
1051
+ }
1052
+ }
1053
+ if (options.closable !== void 0) {
1054
+ const existing = inst.el.querySelector(":scope > .ga-toast-close");
1055
+ if (options.closable && !existing) {
1056
+ inst.el.insertBefore(createCloseButton(), inst.progressEl || null);
1057
+ } else if (!options.closable && existing) {
1058
+ existing.remove();
1059
+ }
1060
+ opts.closable = options.closable;
1061
+ }
1062
+ if (options.duration !== void 0) {
1063
+ if (inst.timer.handle != null) clearTimeout(inst.timer.handle);
1064
+ inst.timer.duration = options.duration;
1065
+ inst.timer.remaining = options.duration;
1066
+ opts.duration = options.duration;
1067
+ if (options.duration > 0 && inst.el.dataset.gaMounted) startTimer(inst);
1068
+ }
1069
+ const parent = inst.el.parentElement;
1070
+ if (parent) layout(parent);
1071
+ log("toast:update", { id });
1072
+ return inst.handle;
1073
+ }
1074
+ function closeAll() {
1075
+ for (const id of Array.from(registry.keys())) close(id);
1076
+ }
1077
+ function clear(type) {
1078
+ if (!type) return closeAll();
1079
+ for (const inst of Array.from(registry.values()))
1080
+ if (inst.opts.type === type) close(inst.id);
1081
+ }
1082
+ function getCount(type) {
1083
+ if (!type) return registry.size;
1084
+ let n = 0;
1085
+ for (const inst of registry.values()) if (inst.opts.type === type) n += 1;
1086
+ return n;
1087
+ }
1088
+ function exists(id) {
1089
+ return registry.has(normalizeId(id));
1090
+ }
1091
+ function get(id) {
1092
+ return registry.get(normalizeId(id))?.el || null;
1093
+ }
1094
+ function typed(type) {
1095
+ return (message, options = {}) => show({ ...options, type, message, duration: options.duration ?? durations[type] });
1096
+ }
1097
+ const success = typed("success");
1098
+ const error = typed("error");
1099
+ const warning = typed("warning");
1100
+ const info = typed("info");
1101
+ function loading(message = "Loading\u2026", options = {}) {
1102
+ return show({
1103
+ closable: false,
1104
+ ...options,
1105
+ type: "loading",
1106
+ message,
1107
+ duration: 0,
1108
+ progress: false
1109
+ });
1110
+ }
1111
+ function confirm(message, options = {}) {
1112
+ const { confirmText, cancelText, onConfirm, onCancel, ...rest } = options;
1113
+ return show({
1114
+ type: "warning",
1115
+ duration: 0,
1116
+ closable: false,
1117
+ swipeToClose: false,
1118
+ role: "alertdialog",
1119
+ moveFocus: true,
1120
+ modal: true,
1121
+ position: "middle-center",
1122
+ ...rest,
1123
+ message,
1124
+ actions: [
1125
+ {
1126
+ text: cancelText || "Cancel",
1127
+ className: "ga-toast-btn-secondary",
1128
+ onClick: () => onCancel?.()
1129
+ },
1130
+ {
1131
+ text: confirmText || "Confirm",
1132
+ className: "ga-toast-btn-primary",
1133
+ onClick: () => onConfirm?.()
1134
+ }
1135
+ ]
1136
+ });
1137
+ }
1138
+ function promise(input, messages, options = {}) {
1139
+ const p = typeof input === "function" ? input() : input;
1140
+ const handle = loading(messages.loading, options);
1141
+ return p.then(
1142
+ (value) => {
1143
+ handle.update({
1144
+ type: "success",
1145
+ message: typeof messages.success === "function" ? messages.success(value) : messages.success,
1146
+ duration: options.duration ?? durations.success,
1147
+ closable: true
1148
+ });
1149
+ return value;
1150
+ },
1151
+ (err) => {
1152
+ handle.update({
1153
+ type: "error",
1154
+ message: typeof messages.error === "function" ? messages.error(err) : messages.error,
1155
+ duration: options.duration ?? durations.error,
1156
+ closable: true
1157
+ });
1158
+ throw err;
1159
+ }
1160
+ );
1161
+ }
1162
+ function custom(content, options = {}) {
1163
+ return show({ ...options, content });
1164
+ }
1165
+ function setDefaults(defaults) {
1166
+ globalDefaults = { ...globalDefaults, ...defaults };
1167
+ }
1168
+ function setLogger(fn) {
1169
+ logger = typeof fn === "function" ? fn : null;
1170
+ }
1171
+ function setMaxVisible(count) {
1172
+ maxVisible = Math.max(1, Math.floor(count) || 1);
1173
+ containers.forEach((container) => layout(container));
1174
+ }
1175
+ function theme(tokens) {
1176
+ themeTokens = resolveTheme(tokens);
1177
+ applyTheme();
1178
+ containers.forEach((container) => layout(container));
1179
+ return api;
1180
+ }
1181
+ function configure(cfg) {
1182
+ if (cfg.defaults) setDefaults(cfg.defaults);
1183
+ if (cfg.logger !== void 0) logger = cfg.logger;
1184
+ applyConfigDurations(cfg.durations);
1185
+ applyConfigIcons(cfg.icons);
1186
+ applyConfigStack(cfg.stack);
1187
+ if (typeof cfg.maxVisible === "number")
1188
+ maxVisible = Math.max(1, Math.floor(cfg.maxVisible) || 1);
1189
+ if (cfg.injectStyles !== void 0) injectEnabled = cfg.injectStyles !== false;
1190
+ if (cfg.theme !== void 0) {
1191
+ themeTokens = resolveTheme(cfg.theme);
1192
+ applyTheme();
1193
+ }
1194
+ containers.forEach((container) => layout(container));
1195
+ return api;
1196
+ }
1197
+ const api = Object.assign(
1198
+ (message, options) => info(message, options),
1199
+ {
1200
+ show,
1201
+ success,
1202
+ error,
1203
+ warning,
1204
+ info,
1205
+ loading,
1206
+ confirm,
1207
+ promise,
1208
+ custom,
1209
+ close,
1210
+ closeAll,
1211
+ dismiss: close,
1212
+ dismissAll: closeAll,
1213
+ clear,
1214
+ update,
1215
+ get,
1216
+ exists,
1217
+ getCount,
1218
+ setDefaults,
1219
+ setMaxVisible,
1220
+ setLogger,
1221
+ configure,
1222
+ theme,
1223
+ injectStyles
1224
+ }
1225
+ );
1226
+ if (config.theme !== void 0) {
1227
+ themeTokens = resolveTheme(config.theme);
1228
+ applyTheme();
1229
+ }
1230
+ return api;
1231
+ }
1232
+ function createToaster(config = {}) {
1233
+ return makeToaster(config);
1234
+ }
1235
+ var toast = makeToaster();
1236
+ var GaToasts = toast;
1237
+ var src_default = toast;
1238
+
1239
+ exports.GaToasts = GaToasts;
1240
+ exports.createToaster = createToaster;
1241
+ exports.default = src_default;
1242
+ exports.toast = toast;