@devalok/shilp-sutra 0.42.0 → 0.43.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/AGENTS.md +10 -0
- package/MIGRATION.md +32 -0
- package/dist/_chunks/chat.js +1 -1
- package/dist/_chunks/chat.js.map +1 -1
- package/dist/_chunks/mention-suggestion.js +1 -1
- package/dist/_chunks/mention-suggestion.js.map +1 -1
- package/dist/_chunks/popover.js +1 -1
- package/dist/_chunks/popover.js.map +1 -1
- package/dist/_chunks/shared.js +1 -1
- package/dist/_chunks/shared.js.map +1 -1
- package/dist/ai/command-bar.js +1 -1
- package/dist/ai/command-bar.js.map +1 -1
- package/dist/composed/bulk-action-bar.js +1 -1
- package/dist/composed/bulk-action-bar.js.map +1 -1
- package/dist/composed/command-palette.js +1 -1
- package/dist/composed/command-palette.js.map +1 -1
- package/dist/composed/content-card.js +1 -1
- package/dist/composed/content-card.js.map +1 -1
- package/dist/composed/error-boundary.js +1 -1
- package/dist/composed/error-boundary.js.map +1 -1
- package/dist/composed/extensions/emoji-suggestion.js +1 -1
- package/dist/composed/extensions/emoji-suggestion.js.map +1 -1
- package/dist/composed/file-preview.js +1 -1
- package/dist/composed/file-preview.js.map +1 -1
- package/dist/composed/rich-chat-input.js +5 -5
- package/dist/composed/rich-chat-input.js.map +1 -1
- package/dist/composed/rich-text-editor.js +2 -2
- package/dist/composed/rich-text-editor.js.map +1 -1
- package/dist/shell/notification-center.js +1 -1
- package/dist/shell/notification-center.js.map +1 -1
- package/dist/shell/sidebar.d.ts +6 -0
- package/dist/shell/sidebar.d.ts.map +1 -1
- package/dist/shell/sidebar.js +27 -18
- package/dist/shell/sidebar.js.map +1 -1
- package/dist/shell/top-bar.js +1 -1
- package/dist/shell/top-bar.js.map +1 -1
- package/dist/tokens/semantic.css +8 -2
- package/dist/ui/alert-dialog.js +1 -1
- package/dist/ui/alert-dialog.js.map +1 -1
- package/dist/ui/autocomplete.js +1 -1
- package/dist/ui/autocomplete.js.map +1 -1
- package/dist/ui/card.d.ts.map +1 -1
- package/dist/ui/card.js +3 -3
- package/dist/ui/card.js.map +1 -1
- package/dist/ui/color-input.js +1 -1
- package/dist/ui/color-input.js.map +1 -1
- package/dist/ui/combobox.js +1 -1
- package/dist/ui/combobox.js.map +1 -1
- package/dist/ui/context-menu.js +2 -2
- package/dist/ui/context-menu.js.map +1 -1
- package/dist/ui/data-table-bulk-actions.js +1 -1
- package/dist/ui/data-table-bulk-actions.js.map +1 -1
- package/dist/ui/dialog.js +1 -1
- package/dist/ui/dialog.js.map +1 -1
- package/dist/ui/dropdown-menu.js +2 -2
- package/dist/ui/dropdown-menu.js.map +1 -1
- package/dist/ui/hover-card.js +1 -1
- package/dist/ui/hover-card.js.map +1 -1
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +20 -19
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/input-otp.js +1 -1
- package/dist/ui/input-otp.js.map +1 -1
- package/dist/ui/menubar.js +3 -3
- package/dist/ui/menubar.js.map +1 -1
- package/dist/ui/navigation-menu.js +1 -1
- package/dist/ui/navigation-menu.js.map +1 -1
- package/dist/ui/select.js +1 -1
- package/dist/ui/select.js.map +1 -1
- package/dist/ui/sidebar.js +1 -1
- package/dist/ui/sidebar.js.map +1 -1
- package/dist/ui/split-button.js +1 -1
- package/dist/ui/split-button.js.map +1 -1
- package/dist/ui/stat-card.d.ts +22 -2
- package/dist/ui/stat-card.d.ts.map +1 -1
- package/dist/ui/stat-card.js +101 -86
- package/dist/ui/stat-card.js.map +1 -1
- package/dist/ui/stat-flash.d.ts +46 -0
- package/dist/ui/stat-flash.d.ts.map +1 -0
- package/dist/ui/stat-flash.js +114 -0
- package/dist/ui/stat-flash.js.map +1 -0
- package/dist/ui/toast.js +2 -2
- package/dist/ui/toast.js.map +1 -1
- package/docs/components/ui/stat-card.md +13 -3
- package/docs/components/ui/stat-flash.md +44 -0
- package/llms-full.txt +58 -4
- package/llms.txt +4 -0
- package/package.json +1 -1
- package/skill/SKILL.md +1 -1
- package/skill/references/components-full.md +58 -4
- package/skill/references/components.md +4 -0
package/dist/ui/stat-card.js
CHANGED
|
@@ -5,18 +5,12 @@ import { cn as r } from "./lib/utils.js";
|
|
|
5
5
|
import { Icon as i } from "./icon.js";
|
|
6
6
|
import { t as a } from "../_chunks/normalize-icon.js";
|
|
7
7
|
import { n as o } from "../_chunks/link-context.js";
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
8
|
+
import { StatFlash as s } from "./stat-flash.js";
|
|
9
|
+
import * as c from "react";
|
|
10
|
+
import { Fragment as l, jsx as u, jsxs as d } from "react/jsx-runtime";
|
|
11
|
+
import { IconMinus as f, IconTrendingDown as p, IconTrendingUp as m } from "@tabler/icons-react";
|
|
12
|
+
import { motion as h } from "framer-motion";
|
|
12
13
|
//#region src/ui/stat-card.tsx
|
|
13
|
-
var h = {
|
|
14
|
-
default: "border-l-accent-9",
|
|
15
|
-
success: "border-l-success-9",
|
|
16
|
-
warning: "border-l-warning-9",
|
|
17
|
-
error: "border-l-error-9",
|
|
18
|
-
info: "border-l-info-9"
|
|
19
|
-
};
|
|
20
14
|
function g(e, t, n) {
|
|
21
15
|
let r = e.filter((e) => Number.isFinite(e));
|
|
22
16
|
if (r.length < 2) return "";
|
|
@@ -27,42 +21,42 @@ function g(e, t, n) {
|
|
|
27
21
|
}).join(" ");
|
|
28
22
|
}
|
|
29
23
|
function _({ data: e, colorClass: t }) {
|
|
30
|
-
let n =
|
|
31
|
-
if (
|
|
24
|
+
let n = c.useId(), i = c.useRef(null), [a, o] = c.useState(0), s = g(e, 80, 32);
|
|
25
|
+
if (c.useEffect(() => {
|
|
32
26
|
i.current && o(i.current.getTotalLength());
|
|
33
|
-
}, [
|
|
34
|
-
let
|
|
35
|
-
return /* @__PURE__ */
|
|
27
|
+
}, [s]), !s) return null;
|
|
28
|
+
let l = `${s} L${80 .toFixed(1)},${32 .toFixed(1)} L0,${32 .toFixed(1)} Z`;
|
|
29
|
+
return /* @__PURE__ */ d("div", {
|
|
36
30
|
className: r("w-20 h-8", t),
|
|
37
31
|
"aria-hidden": "true",
|
|
38
|
-
children: [/* @__PURE__ */
|
|
32
|
+
children: [/* @__PURE__ */ d("svg", {
|
|
39
33
|
viewBox: "0 0 80 32",
|
|
40
34
|
className: "w-full h-full overflow-visible",
|
|
41
35
|
preserveAspectRatio: "none",
|
|
42
36
|
children: [
|
|
43
|
-
/* @__PURE__ */
|
|
37
|
+
/* @__PURE__ */ u("defs", { children: /* @__PURE__ */ d("linearGradient", {
|
|
44
38
|
id: `sparkline-fill-${n}`,
|
|
45
39
|
x1: "0",
|
|
46
40
|
y1: "0",
|
|
47
41
|
x2: "0",
|
|
48
42
|
y2: "1",
|
|
49
|
-
children: [/* @__PURE__ */
|
|
43
|
+
children: [/* @__PURE__ */ u("stop", {
|
|
50
44
|
offset: "0%",
|
|
51
45
|
stopColor: "currentColor",
|
|
52
46
|
stopOpacity: "0.1"
|
|
53
|
-
}), /* @__PURE__ */
|
|
47
|
+
}), /* @__PURE__ */ u("stop", {
|
|
54
48
|
offset: "100%",
|
|
55
49
|
stopColor: "currentColor",
|
|
56
50
|
stopOpacity: "0"
|
|
57
51
|
})]
|
|
58
52
|
}) }),
|
|
59
|
-
/* @__PURE__ */
|
|
60
|
-
d,
|
|
53
|
+
/* @__PURE__ */ u("path", {
|
|
54
|
+
d: l,
|
|
61
55
|
fill: `url(#sparkline-fill-${n})`
|
|
62
56
|
}),
|
|
63
|
-
/* @__PURE__ */
|
|
57
|
+
/* @__PURE__ */ u("path", {
|
|
64
58
|
ref: i,
|
|
65
|
-
d:
|
|
59
|
+
d: s,
|
|
66
60
|
fill: "none",
|
|
67
61
|
stroke: "currentColor",
|
|
68
62
|
strokeWidth: "1.5",
|
|
@@ -76,51 +70,72 @@ function _({ data: e, colorClass: t }) {
|
|
|
76
70
|
}
|
|
77
71
|
})
|
|
78
72
|
]
|
|
79
|
-
}), a > 0 && /* @__PURE__ */
|
|
73
|
+
}), a > 0 && /* @__PURE__ */ u("style", { children: `@keyframes sparkline-draw-${n.replace(/:/g, "")} { to { stroke-dashoffset: 0; } }` })]
|
|
80
74
|
});
|
|
81
75
|
}
|
|
82
76
|
function v({ progress: e, label: t }) {
|
|
83
77
|
let n = Math.max(0, Math.min(100, e)), i = n >= 90 ? "bg-success-9" : n >= 70 ? "bg-warning-9" : "bg-accent-9";
|
|
84
|
-
return /* @__PURE__ */
|
|
78
|
+
return /* @__PURE__ */ u("div", {
|
|
85
79
|
className: "h-1 w-full rounded-pill bg-surface-raised mt-ds-04",
|
|
86
80
|
role: "progressbar",
|
|
87
81
|
"aria-label": `${t} progress`,
|
|
88
82
|
"aria-valuenow": n,
|
|
89
83
|
"aria-valuemin": 0,
|
|
90
84
|
"aria-valuemax": 100,
|
|
91
|
-
children: /* @__PURE__ */
|
|
85
|
+
children: /* @__PURE__ */ u("div", {
|
|
92
86
|
className: r("h-full rounded-pill transition-[width] duration-moderate-02 ease-productive-standard", i),
|
|
93
87
|
style: { width: `${n}%` }
|
|
94
88
|
})
|
|
95
89
|
});
|
|
96
90
|
}
|
|
97
|
-
var y =
|
|
98
|
-
let
|
|
99
|
-
if (T) return /* @__PURE__ */
|
|
100
|
-
ref:
|
|
101
|
-
className: r("rounded-surface
|
|
102
|
-
...
|
|
91
|
+
var y = c.forwardRef(({ className: c, label: g, title: y, value: b, prefix: x, suffix: S, delta: C, icon: w, loading: T, comparisonLabel: E, secondaryLabel: D, progress: O, surface: k = "raised", accentStyle: A = "none", iconFill: j = "soft", flash: M, flashSpeed: N, sparkline: P, onClick: F, href: I, footer: L, ...R }, z) => {
|
|
92
|
+
let B = o(), V = y ?? g ?? "", H = !!(F || I), U = H && !R["aria-label"] ? `View ${V}` : R["aria-label"];
|
|
93
|
+
if (T) return /* @__PURE__ */ d("div", {
|
|
94
|
+
ref: z,
|
|
95
|
+
className: r("rounded-surface bg-surface-raised p-ds-05b", k === "flat" ? "border border-surface-border" : "shadow-raised", c),
|
|
96
|
+
...R,
|
|
103
97
|
children: [
|
|
104
|
-
/* @__PURE__ */
|
|
105
|
-
/* @__PURE__ */
|
|
106
|
-
/* @__PURE__ */
|
|
98
|
+
/* @__PURE__ */ u("div", { className: "h-ds-04 w-24 rounded-control-inner bg-skeleton-base animate-pulse mb-ds-05" }),
|
|
99
|
+
/* @__PURE__ */ u("div", { className: "h-ds-sm w-32 rounded-control bg-skeleton-base animate-pulse mb-ds-03" }),
|
|
100
|
+
/* @__PURE__ */ u("div", { className: "h-3 w-16 rounded-control-inner bg-skeleton-base animate-pulse" })
|
|
107
101
|
]
|
|
108
102
|
});
|
|
109
|
-
let
|
|
110
|
-
/* @__PURE__ */
|
|
103
|
+
let W = C?.direction === "up" ? m : C?.direction === "down" ? p : f, G = C?.direction === "up" ? "text-success-11" : C?.direction === "down" ? "text-error-11" : "text-surface-fg-muted", K = C?.direction === "up" ? "text-success-11" : C?.direction === "down" ? "text-error-11" : "text-accent-11", q = /* @__PURE__ */ d(l, { children: [
|
|
104
|
+
/* @__PURE__ */ d("div", {
|
|
111
105
|
className: "flex items-center justify-between mb-ds-04",
|
|
112
|
-
children: [/* @__PURE__ */
|
|
106
|
+
children: [/* @__PURE__ */ u(h.p, {
|
|
113
107
|
className: "text-ds-md font-medium text-surface-fg-muted",
|
|
114
108
|
initial: { opacity: 0 },
|
|
115
109
|
animate: { opacity: 1 },
|
|
116
110
|
transition: n.fade,
|
|
117
|
-
children:
|
|
118
|
-
}), /* @__PURE__ */
|
|
111
|
+
children: V
|
|
112
|
+
}), /* @__PURE__ */ d("div", {
|
|
119
113
|
className: "flex items-center gap-ds-03",
|
|
120
|
-
children: [
|
|
121
|
-
data:
|
|
122
|
-
colorClass:
|
|
123
|
-
}), w && /* @__PURE__ */
|
|
114
|
+
children: [P && P.length >= 2 && /* @__PURE__ */ u(_, {
|
|
115
|
+
data: P,
|
|
116
|
+
colorClass: K
|
|
117
|
+
}), w && M ? /* @__PURE__ */ u(s, {
|
|
118
|
+
icon: w,
|
|
119
|
+
flash: M,
|
|
120
|
+
fill: j,
|
|
121
|
+
speed: N
|
|
122
|
+
}) : w ? A === "icon" ? /* @__PURE__ */ u(h.span, {
|
|
123
|
+
className: r("inline-flex items-center justify-center rounded-control p-ds-02", j === "solid" ? "bg-accent-9 text-accent-fg" : "bg-accent-3 text-accent-11"),
|
|
124
|
+
initial: {
|
|
125
|
+
opacity: 0,
|
|
126
|
+
scale: .96
|
|
127
|
+
},
|
|
128
|
+
animate: {
|
|
129
|
+
opacity: 1,
|
|
130
|
+
scale: 1
|
|
131
|
+
},
|
|
132
|
+
transition: t.snappy,
|
|
133
|
+
"aria-hidden": "true",
|
|
134
|
+
children: /* @__PURE__ */ u(e, {
|
|
135
|
+
size: "md",
|
|
136
|
+
children: a(w, "md")
|
|
137
|
+
})
|
|
138
|
+
}) : /* @__PURE__ */ u(h.span, {
|
|
124
139
|
className: "text-surface-fg-muted",
|
|
125
140
|
initial: {
|
|
126
141
|
opacity: 0,
|
|
@@ -132,17 +147,17 @@ var y = s.forwardRef(({ className: s, label: g, title: y, value: b, prefix: x, s
|
|
|
132
147
|
},
|
|
133
148
|
transition: t.snappy,
|
|
134
149
|
"aria-hidden": "true",
|
|
135
|
-
children: /* @__PURE__ */
|
|
150
|
+
children: /* @__PURE__ */ u(e, {
|
|
136
151
|
size: "lg",
|
|
137
152
|
children: a(w, "lg")
|
|
138
153
|
})
|
|
139
|
-
})]
|
|
154
|
+
}) : null]
|
|
140
155
|
})]
|
|
141
156
|
}),
|
|
142
|
-
/* @__PURE__ */
|
|
157
|
+
/* @__PURE__ */ u("div", {
|
|
143
158
|
className: "overflow-hidden",
|
|
144
|
-
children: /* @__PURE__ */
|
|
145
|
-
className: "inline-block text-ds-3xl font-semibold text-surface-fg",
|
|
159
|
+
children: /* @__PURE__ */ d(h.p, {
|
|
160
|
+
className: r("inline-block text-ds-3xl font-semibold", A === "tint" ? "text-accent-11" : "text-surface-fg"),
|
|
146
161
|
initial: {
|
|
147
162
|
opacity: 0,
|
|
148
163
|
y: "100%"
|
|
@@ -153,22 +168,22 @@ var y = s.forwardRef(({ className: s, label: g, title: y, value: b, prefix: x, s
|
|
|
153
168
|
},
|
|
154
169
|
transition: t.smooth,
|
|
155
170
|
children: [
|
|
156
|
-
x && /* @__PURE__ */
|
|
171
|
+
x && /* @__PURE__ */ u("span", {
|
|
157
172
|
className: "text-surface-fg-muted text-ds-lg",
|
|
158
173
|
children: x
|
|
159
174
|
}),
|
|
160
|
-
/* @__PURE__ */
|
|
175
|
+
/* @__PURE__ */ u("span", {
|
|
161
176
|
className: "tabular-nums",
|
|
162
177
|
children: b
|
|
163
178
|
}),
|
|
164
|
-
S && /* @__PURE__ */
|
|
179
|
+
S && /* @__PURE__ */ u("span", {
|
|
165
180
|
className: "text-surface-fg-muted text-ds-lg",
|
|
166
181
|
children: S
|
|
167
182
|
})
|
|
168
183
|
]
|
|
169
184
|
})
|
|
170
185
|
}),
|
|
171
|
-
D && /* @__PURE__ */
|
|
186
|
+
D && /* @__PURE__ */ u(h.p, {
|
|
172
187
|
className: "text-ds-sm text-surface-fg-subtle mt-ds-01",
|
|
173
188
|
initial: { opacity: 0 },
|
|
174
189
|
animate: { opacity: 1 },
|
|
@@ -178,20 +193,20 @@ var y = s.forwardRef(({ className: s, label: g, title: y, value: b, prefix: x, s
|
|
|
178
193
|
},
|
|
179
194
|
children: D
|
|
180
195
|
}),
|
|
181
|
-
O != null && /* @__PURE__ */
|
|
196
|
+
O != null && /* @__PURE__ */ u(h.div, {
|
|
182
197
|
initial: { opacity: 0 },
|
|
183
198
|
animate: { opacity: 1 },
|
|
184
199
|
transition: {
|
|
185
200
|
...n.fade,
|
|
186
201
|
delay: .15
|
|
187
202
|
},
|
|
188
|
-
children: /* @__PURE__ */
|
|
203
|
+
children: /* @__PURE__ */ u(v, {
|
|
189
204
|
progress: O,
|
|
190
|
-
label:
|
|
205
|
+
label: V
|
|
191
206
|
})
|
|
192
207
|
}),
|
|
193
|
-
C && /* @__PURE__ */
|
|
194
|
-
className: r("mt-ds-03 flex items-center gap-ds-02 text-ds-sm font-medium",
|
|
208
|
+
C && /* @__PURE__ */ d(h.div, {
|
|
209
|
+
className: r("mt-ds-03 flex items-center gap-ds-02 text-ds-sm font-medium", G),
|
|
195
210
|
initial: {
|
|
196
211
|
opacity: 0,
|
|
197
212
|
y: 8
|
|
@@ -205,7 +220,7 @@ var y = s.forwardRef(({ className: s, label: g, title: y, value: b, prefix: x, s
|
|
|
205
220
|
delay: .2
|
|
206
221
|
},
|
|
207
222
|
children: [
|
|
208
|
-
/* @__PURE__ */
|
|
223
|
+
/* @__PURE__ */ u(h.span, {
|
|
209
224
|
className: "inline-flex",
|
|
210
225
|
initial: {
|
|
211
226
|
opacity: .5,
|
|
@@ -216,19 +231,19 @@ var y = s.forwardRef(({ className: s, label: g, title: y, value: b, prefix: x, s
|
|
|
216
231
|
scale: 1
|
|
217
232
|
},
|
|
218
233
|
transition: t.bouncy,
|
|
219
|
-
children: /* @__PURE__ */
|
|
220
|
-
icon:
|
|
234
|
+
children: /* @__PURE__ */ u(i, {
|
|
235
|
+
icon: W,
|
|
221
236
|
size: "sm"
|
|
222
237
|
})
|
|
223
238
|
}),
|
|
224
|
-
/* @__PURE__ */
|
|
225
|
-
E && /* @__PURE__ */
|
|
239
|
+
/* @__PURE__ */ u("span", { children: C.value }),
|
|
240
|
+
E && /* @__PURE__ */ u("span", {
|
|
226
241
|
className: "text-surface-fg-subtle font-normal",
|
|
227
242
|
children: E
|
|
228
243
|
})
|
|
229
244
|
]
|
|
230
245
|
}),
|
|
231
|
-
|
|
246
|
+
L && /* @__PURE__ */ u(h.div, {
|
|
232
247
|
className: "mt-ds-04 pt-ds-04 border-t border-surface-border text-ds-sm",
|
|
233
248
|
initial: { opacity: 0 },
|
|
234
249
|
animate: { opacity: 1 },
|
|
@@ -236,29 +251,29 @@ var y = s.forwardRef(({ className: s, label: g, title: y, value: b, prefix: x, s
|
|
|
236
251
|
...n.fade,
|
|
237
252
|
delay: .25
|
|
238
253
|
},
|
|
239
|
-
children:
|
|
254
|
+
children: L
|
|
240
255
|
})
|
|
241
|
-
] }),
|
|
242
|
-
return
|
|
243
|
-
ref:
|
|
244
|
-
href:
|
|
245
|
-
onClick:
|
|
246
|
-
className: r(
|
|
247
|
-
"aria-label":
|
|
248
|
-
...
|
|
249
|
-
children:
|
|
250
|
-
}) : /* @__PURE__ */
|
|
251
|
-
ref:
|
|
252
|
-
className:
|
|
253
|
-
onClick:
|
|
254
|
-
role:
|
|
255
|
-
tabIndex:
|
|
256
|
-
"aria-label":
|
|
257
|
-
onKeyDown:
|
|
258
|
-
(e.key === "Enter" || e.key === " ") && (e.preventDefault(),
|
|
256
|
+
] }), J = r("rounded-surface p-ds-05b", A === "tint" ? "bg-linear-to-t from-accent-2 to-surface-raised" : "bg-surface-raised", k === "flat" ? "border border-surface-border" : "shadow-raised", H && "cursor-pointer hover:shadow-raised-hover transition-[box-shadow] duration-fast-02 ease-productive-standard group", c);
|
|
257
|
+
return I ? /* @__PURE__ */ u(B, {
|
|
258
|
+
ref: z,
|
|
259
|
+
href: I,
|
|
260
|
+
onClick: F,
|
|
261
|
+
className: r(J, "block no-underline"),
|
|
262
|
+
"aria-label": U,
|
|
263
|
+
...R,
|
|
264
|
+
children: q
|
|
265
|
+
}) : /* @__PURE__ */ u("div", {
|
|
266
|
+
ref: z,
|
|
267
|
+
className: J,
|
|
268
|
+
onClick: F,
|
|
269
|
+
role: F ? "button" : void 0,
|
|
270
|
+
tabIndex: F ? 0 : void 0,
|
|
271
|
+
"aria-label": U,
|
|
272
|
+
onKeyDown: F ? (e) => {
|
|
273
|
+
(e.key === "Enter" || e.key === " ") && (e.preventDefault(), F());
|
|
259
274
|
} : void 0,
|
|
260
|
-
...
|
|
261
|
-
children:
|
|
275
|
+
...R,
|
|
276
|
+
children: q
|
|
262
277
|
});
|
|
263
278
|
});
|
|
264
279
|
y.displayName = "StatCard";
|
package/dist/ui/stat-card.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stat-card.js","names":[],"sources":["../../src/ui/stat-card.tsx"],"sourcesContent":["'use client'\n\nimport { IconMinus, IconTrendingDown, IconTrendingUp } from '@tabler/icons-react'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { Icon } from './icon'\nimport { IconProvider } from './icon-context'\nimport type { IconInput } from './lib/icon-input'\nimport { useLink } from './lib/link-context'\nimport { springs, tweens } from './lib/motion'\nimport { normalizeIcon } from './lib/normalize-icon'\nimport { cn } from './lib/utils'\n\n/**\n * Props for StatCard — a dashboard metric tile displaying a label, a large numeric value,\n * an optional trend delta (with directional arrow icon), and an optional header icon.\n *\n * **Delta direction:** `'up'` renders a green trending-up arrow, `'down'` renders a red\n * trending-down arrow, `'neutral'` renders a grey dash. The `delta.value` is a formatted\n * string (e.g. `\"+8%\"` or `\"−120\"`).\n *\n * **Loading state:** Pass `loading={true}` to render a pulse-skeleton placeholder instead of data.\n *\n * @example\n * // Revenue metric with a positive trend:\n * <StatCard\n * label=\"Monthly Revenue\"\n * value=\"$48,200\"\n * delta={{ value: \"+12%\", direction: \"up\" }}\n * icon={<IconCurrencyDollar />}\n * />\n *\n * @example\n * // Open ticket count with a downward trend (good for support queues):\n * <StatCard\n * label=\"Open Tickets\"\n * value={142}\n * delta={{ value: \"−18\", direction: \"down\" }}\n * />\n *\n * @example\n * // Loading skeleton while data is fetching:\n * <StatCard label=\"Users\" value={0} loading={isFetching} />\n *\n * @example\n * // Simple metric with no trend (stable, neutral):\n * <StatCard\n * label=\"Storage Used\"\n * value=\"4.2 GB\"\n * delta={{ value: \"No change\", direction: \"neutral\" }}\n * />\n * // These are just a few ways — feel free to combine props creatively!\n */\nexport interface StatCardProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Heading text for the metric. `title` is an alias for `label`. */\n label?: string\n /** Alias for `label` — use whichever feels natural. */\n title?: string\n value: string | number\n /** Prefix before value, e.g. \"$\" */\n prefix?: string\n /** Suffix after value, e.g. \"users\" */\n suffix?: string\n delta?: {\n value: string\n direction: 'up' | 'down' | 'neutral'\n }\n icon?: IconInput\n loading?: boolean\n /** Comparison period label shown after delta, e.g. \"vs last month\" */\n comparisonLabel?: string\n /** Secondary metric below main value, e.g. \"of $50,000 target\" */\n secondaryLabel?: string\n /** Progress toward a target (0-100). Renders a thin progress bar below the value. */\n progress?: number\n /** Color accent for the left border decoration */\n accent?: 'default' | 'success' | 'warning' | 'error' | 'info'\n /** Sparkline data points. Renders a mini SVG line chart in the card. */\n sparkline?: number[]\n /** Make the card clickable with hover state */\n onClick?: () => void\n /** Href — makes the card a link (uses the LinkContext Link component) */\n href?: string\n /** Footer content rendered below the card body, e.g. \"View details →\" */\n footer?: React.ReactNode\n}\n\nconst accentBorderMap: Record<NonNullable<StatCardProps['accent']>, string> = {\n default: 'border-l-accent-9',\n success: 'border-l-success-9',\n warning: 'border-l-warning-9',\n error: 'border-l-error-9',\n info: 'border-l-info-9',\n}\n\nfunction buildSparklinePath(raw: number[], width: number, height: number): string {\n const data = raw.filter((v) => Number.isFinite(v))\n if (data.length < 2) return ''\n const min = Math.min(...data)\n const max = Math.max(...data)\n const range = max - min || 1\n const stepX = width / (data.length - 1)\n return data\n .map((v, i) => {\n const x = i * stepX\n const y = height - ((v - min) / range) * height\n return `${i === 0 ? 'M' : 'L'}${x.toFixed(1)},${y.toFixed(1)}`\n })\n .join(' ')\n}\n\nfunction Sparkline({\n data,\n colorClass,\n}: {\n data: number[]\n colorClass: string\n}) {\n const id = React.useId()\n const pathRef = React.useRef<SVGPathElement>(null)\n const [pathLength, setPathLength] = React.useState(0)\n const svgWidth = 80\n const svgHeight = 32\n const path = buildSparklinePath(data, svgWidth, svgHeight)\n\n React.useEffect(() => {\n if (pathRef.current) {\n setPathLength(pathRef.current.getTotalLength())\n }\n }, [path])\n\n if (!path) return null\n\n // Build area fill path (close to bottom-right then bottom-left)\n const areaPath = `${path} L${svgWidth.toFixed(1)},${svgHeight.toFixed(1)} L0,${svgHeight.toFixed(1)} Z`\n\n return (\n <div className={cn('w-20 h-8', colorClass)} aria-hidden=\"true\">\n <svg\n viewBox={`0 0 ${svgWidth} ${svgHeight}`}\n className=\"w-full h-full overflow-visible\"\n preserveAspectRatio=\"none\"\n >\n <defs>\n <linearGradient id={`sparkline-fill-${id}`} x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0%\" stopColor=\"currentColor\" stopOpacity=\"0.1\" />\n <stop offset=\"100%\" stopColor=\"currentColor\" stopOpacity=\"0\" />\n </linearGradient>\n </defs>\n <path d={areaPath} fill={`url(#sparkline-fill-${id})`} />\n <path\n ref={pathRef}\n d={path}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n style={{\n opacity: pathLength === 0 ? 0 : 1,\n strokeDasharray: pathLength,\n strokeDashoffset: pathLength,\n animation: pathLength > 0 ? `sparkline-draw-${id.replace(/:/g, '')} 1s ease-out forwards` : 'none',\n }}\n />\n </svg>\n {pathLength > 0 && (\n <style>{`@keyframes sparkline-draw-${id.replace(/:/g, '')} { to { stroke-dashoffset: 0; } }`}</style>\n )}\n </div>\n )\n}\n\nfunction ProgressBar({ progress, label }: { progress: number; label: string }) {\n const clamped = Math.max(0, Math.min(100, progress))\n const barColor =\n clamped >= 90 ? 'bg-success-9' : clamped >= 70 ? 'bg-warning-9' : 'bg-accent-9'\n\n return (\n <div className=\"h-1 w-full rounded-pill bg-surface-raised mt-ds-04\" role=\"progressbar\" aria-label={`${label} progress`} aria-valuenow={clamped} aria-valuemin={0} aria-valuemax={100}>\n <div\n className={cn('h-full rounded-pill transition-[width] duration-moderate-02 ease-productive-standard', barColor)}\n style={{ width: `${clamped}%` }}\n />\n </div>\n )\n}\n\nconst StatCard = React.forwardRef<HTMLDivElement, StatCardProps>(\n (\n {\n className,\n label,\n title,\n value,\n prefix,\n suffix,\n delta,\n icon,\n loading,\n comparisonLabel,\n secondaryLabel,\n progress,\n accent,\n sparkline,\n onClick,\n href,\n footer,\n ...props\n },\n ref,\n ) => {\n const Link = useLink()\n const resolvedLabel = title ?? label ?? ''\n const isClickable = !!(onClick || href)\n const computedAriaLabel =\n isClickable && !props['aria-label'] ? `View ${resolvedLabel}` : props['aria-label']\n\n if (loading) {\n return (\n <div\n ref={ref}\n className={cn(\n 'rounded-surface border border-surface-border bg-surface-raised shadow-raised p-ds-05b',\n accent && `border-l-[3px] ${accentBorderMap[accent]}`,\n className,\n )}\n {...props}\n >\n <div className=\"h-ds-04 w-24 rounded-control-inner bg-skeleton-base animate-pulse mb-ds-05\" />\n <div className=\"h-ds-sm w-32 rounded-control bg-skeleton-base animate-pulse mb-ds-03\" />\n <div className=\"h-3 w-16 rounded-control-inner bg-skeleton-base animate-pulse\" />\n </div>\n )\n }\n\n const DeltaIcon =\n delta?.direction === 'up'\n ? IconTrendingUp\n : delta?.direction === 'down'\n ? IconTrendingDown\n : IconMinus\n\n const deltaColour =\n delta?.direction === 'up'\n ? 'text-success-11'\n : delta?.direction === 'down'\n ? 'text-error-11'\n : 'text-surface-fg-muted'\n\n const sparklineColor =\n delta?.direction === 'up'\n ? 'text-success-11'\n : delta?.direction === 'down'\n ? 'text-error-11'\n : 'text-accent-11'\n\n const cardContent = (\n <>\n <div className=\"flex items-center justify-between mb-ds-04\">\n <motion.p\n className=\"text-ds-md font-medium text-surface-fg-muted\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n transition={tweens.fade}\n >\n {resolvedLabel}\n </motion.p>\n <div className=\"flex items-center gap-ds-03\">\n {sparkline && sparkline.length >= 2 && (\n <Sparkline data={sparkline} colorClass={sparklineColor} />\n )}\n {icon && (\n <motion.span\n className=\"text-surface-fg-muted\"\n initial={{ opacity: 0, scale: 0.96 }}\n animate={{ opacity: 1, scale: 1 }}\n transition={springs.snappy}\n aria-hidden=\"true\"\n >\n <IconProvider size=\"lg\">{normalizeIcon(icon, 'lg')}</IconProvider>\n </motion.span>\n )}\n </div>\n </div>\n <div className=\"overflow-hidden\">\n <motion.p\n className=\"inline-block text-ds-3xl font-semibold text-surface-fg\"\n initial={{ opacity: 0, y: '100%' }}\n animate={{ opacity: 1, y: 0 }}\n transition={springs.smooth}\n >\n {prefix && (\n <span className=\"text-surface-fg-muted text-ds-lg\">{prefix}</span>\n )}\n <span className=\"tabular-nums\">{value}</span>\n {suffix && (\n <span className=\"text-surface-fg-muted text-ds-lg\">{suffix}</span>\n )}\n </motion.p>\n </div>\n {secondaryLabel && (\n <motion.p\n className=\"text-ds-sm text-surface-fg-subtle mt-ds-01\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n transition={{ ...tweens.fade, delay: 0.1 }}\n >\n {secondaryLabel}\n </motion.p>\n )}\n {progress != null && (\n <motion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n transition={{ ...tweens.fade, delay: 0.15 }}\n >\n <ProgressBar progress={progress} label={resolvedLabel} />\n </motion.div>\n )}\n {delta && (\n <motion.div\n className={cn(\n 'mt-ds-03 flex items-center gap-ds-02 text-ds-sm font-medium',\n deltaColour,\n )}\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ ...springs.smooth, delay: 0.2 }}\n >\n <motion.span\n className=\"inline-flex\"\n initial={{ opacity: 0.5, scale: 1.4 }}\n animate={{ opacity: 1, scale: 1 }}\n transition={springs.bouncy}\n >\n <Icon icon={DeltaIcon} size=\"sm\" />\n </motion.span>\n <span>{delta.value}</span>\n {comparisonLabel && (\n <span className=\"text-surface-fg-subtle font-normal\">{comparisonLabel}</span>\n )}\n </motion.div>\n )}\n {footer && (\n <motion.div\n className=\"mt-ds-04 pt-ds-04 border-t border-surface-border text-ds-sm\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n transition={{ ...tweens.fade, delay: 0.25 }}\n >\n {footer}\n </motion.div>\n )}\n </>\n )\n\n const cardClasses = cn(\n 'rounded-surface border border-surface-border bg-surface-raised shadow-raised p-ds-05b',\n accent && `border-l-[3px] ${accentBorderMap[accent]}`,\n isClickable &&\n 'cursor-pointer hover:shadow-raised-hover hover:border-surface-border-strong transition-[box-shadow,border-color] duration-fast-02 ease-productive-standard group',\n className,\n )\n\n if (href) {\n return (\n <Link\n ref={ref as React.Ref<HTMLAnchorElement>}\n href={href}\n onClick={onClick}\n className={cn(cardClasses, 'block no-underline')}\n aria-label={computedAriaLabel}\n {...(props as React.AnchorHTMLAttributes<HTMLAnchorElement>)}\n >\n {cardContent}\n </Link>\n )\n }\n\n return (\n <div\n ref={ref}\n className={cardClasses}\n onClick={onClick}\n role={onClick ? 'button' : undefined}\n tabIndex={onClick ? 0 : undefined}\n aria-label={computedAriaLabel}\n onKeyDown={\n onClick\n ? (e: React.KeyboardEvent) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n onClick()\n }\n }\n : undefined\n }\n {...props}\n >\n {cardContent}\n </div>\n )\n },\n)\n\nStatCard.displayName = 'StatCard'\n\nexport { StatCard }\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"stat-card.js","names":[],"sources":["../../src/ui/stat-card.tsx"],"sourcesContent":["'use client'\n\nimport { IconMinus, IconTrendingDown, IconTrendingUp } from '@tabler/icons-react'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { Icon } from './icon'\nimport { IconProvider } from './icon-context'\nimport type { IconInput } from './lib/icon-input'\nimport { useLink } from './lib/link-context'\nimport { springs, tweens } from './lib/motion'\nimport { normalizeIcon } from './lib/normalize-icon'\nimport { cn } from './lib/utils'\nimport { StatFlash, type FlashPreset, type FlashSpec, type FlashSpeed } from './stat-flash'\n\n/**\n * Props for StatCard — a dashboard metric tile displaying a label, a large numeric value,\n * an optional trend delta (with directional arrow icon), and an optional header icon.\n *\n * **Surface:** `surface=\"raised\"` (default) is elevation-led — the shadow's own 1px ring is the\n * edge, no border (make-kit rule #6). `surface=\"flat\"` is border-led, no shadow.\n *\n * **Accent:** `accentStyle=\"none\"` (default) is neutral; `\"icon\"` wraps `icon` in an accent chip\n * (`iconFill=\"soft\" | \"solid\"`); `\"tint\"` applies a subtle accent surface wash + accent value.\n * (Replaces the removed `accent` left-rail prop — see CHANGELOG migration.)\n *\n * **Delta direction:** `'up'` renders a green trending-up arrow, `'down'` renders a red\n * trending-down arrow, `'neutral'` renders a grey dash. The `delta.value` is a formatted\n * string (e.g. `\"+8%\"` or `\"−120\"`).\n *\n * **Loading state:** Pass `loading={true}` to render a pulse-skeleton placeholder instead of data.\n *\n * @example\n * // Revenue metric with a positive trend:\n * <StatCard\n * label=\"Monthly Revenue\"\n * value=\"$48,200\"\n * delta={{ value: \"+12%\", direction: \"up\" }}\n * icon={<IconCurrencyDollar />}\n * />\n *\n * @example\n * // Open ticket count with a downward trend (good for support queues):\n * <StatCard\n * label=\"Open Tickets\"\n * value={142}\n * delta={{ value: \"−18\", direction: \"down\" }}\n * />\n *\n * @example\n * // Loading skeleton while data is fetching:\n * <StatCard label=\"Users\" value={0} loading={isFetching} />\n *\n * @example\n * // Simple metric with no trend (stable, neutral):\n * <StatCard\n * label=\"Storage Used\"\n * value=\"4.2 GB\"\n * delta={{ value: \"No change\", direction: \"neutral\" }}\n * />\n * // These are just a few ways — feel free to combine props creatively!\n */\nexport interface StatCardProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Heading text for the metric. `title` is an alias for `label`. */\n label?: string\n /** Alias for `label` — use whichever feels natural. */\n title?: string\n value: string | number\n /** Prefix before value, e.g. \"$\" */\n prefix?: string\n /** Suffix after value, e.g. \"users\" */\n suffix?: string\n delta?: {\n value: string\n direction: 'up' | 'down' | 'neutral'\n }\n icon?: IconInput\n loading?: boolean\n /** Comparison period label shown after delta, e.g. \"vs last month\" */\n comparisonLabel?: string\n /** Secondary metric below main value, e.g. \"of $50,000 target\" */\n secondaryLabel?: string\n /** Progress toward a target (0-100). Renders a thin progress bar below the value. */\n progress?: number\n /** Surface treatment: `raised` (ring-in-shadow, no border — default) or `flat` (border, no shadow). */\n surface?: 'raised' | 'flat'\n /** How the brand accent reads: `none` (neutral — default), `icon` (accent chip around `icon`), or `tint` (subtle accent surface wash + accent value). */\n accentStyle?: 'none' | 'icon' | 'tint'\n /** When `accentStyle=\"icon\"`: `soft` (tinted chip) or `solid` (filled accent chip). @default 'soft' */\n iconFill?: 'soft' | 'solid'\n /**\n * Opt-in entrance flash: the icon chip mounts showing a transient state (e.g. a green up-arrow)\n * then settles to `icon`. A preset (`'up' | 'down' | 'goal' | 'record' | 'alert' | 'live'`) or an\n * explicit `{ tone, icon }`. Requires `icon`; implies the icon chip. Off by default.\n */\n flash?: FlashPreset | FlashSpec\n /** Speed of the entrance flash: `fast` | `normal` | `slow`. @default 'normal' */\n flashSpeed?: FlashSpeed\n /** Sparkline data points. Renders a mini SVG line chart in the card. */\n sparkline?: number[]\n /** Make the card clickable with hover state */\n onClick?: () => void\n /** Href — makes the card a link (uses the LinkContext Link component) */\n href?: string\n /** Footer content rendered below the card body, e.g. \"View details →\" */\n footer?: React.ReactNode\n}\n\nfunction buildSparklinePath(raw: number[], width: number, height: number): string {\n const data = raw.filter((v) => Number.isFinite(v))\n if (data.length < 2) return ''\n const min = Math.min(...data)\n const max = Math.max(...data)\n const range = max - min || 1\n const stepX = width / (data.length - 1)\n return data\n .map((v, i) => {\n const x = i * stepX\n const y = height - ((v - min) / range) * height\n return `${i === 0 ? 'M' : 'L'}${x.toFixed(1)},${y.toFixed(1)}`\n })\n .join(' ')\n}\n\nfunction Sparkline({\n data,\n colorClass,\n}: {\n data: number[]\n colorClass: string\n}) {\n const id = React.useId()\n const pathRef = React.useRef<SVGPathElement>(null)\n const [pathLength, setPathLength] = React.useState(0)\n const svgWidth = 80\n const svgHeight = 32\n const path = buildSparklinePath(data, svgWidth, svgHeight)\n\n React.useEffect(() => {\n if (pathRef.current) {\n setPathLength(pathRef.current.getTotalLength())\n }\n }, [path])\n\n if (!path) return null\n\n // Build area fill path (close to bottom-right then bottom-left)\n const areaPath = `${path} L${svgWidth.toFixed(1)},${svgHeight.toFixed(1)} L0,${svgHeight.toFixed(1)} Z`\n\n return (\n <div className={cn('w-20 h-8', colorClass)} aria-hidden=\"true\">\n <svg\n viewBox={`0 0 ${svgWidth} ${svgHeight}`}\n className=\"w-full h-full overflow-visible\"\n preserveAspectRatio=\"none\"\n >\n <defs>\n <linearGradient id={`sparkline-fill-${id}`} x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0%\" stopColor=\"currentColor\" stopOpacity=\"0.1\" />\n <stop offset=\"100%\" stopColor=\"currentColor\" stopOpacity=\"0\" />\n </linearGradient>\n </defs>\n <path d={areaPath} fill={`url(#sparkline-fill-${id})`} />\n <path\n ref={pathRef}\n d={path}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n style={{\n opacity: pathLength === 0 ? 0 : 1,\n strokeDasharray: pathLength,\n strokeDashoffset: pathLength,\n animation: pathLength > 0 ? `sparkline-draw-${id.replace(/:/g, '')} 1s ease-out forwards` : 'none',\n }}\n />\n </svg>\n {pathLength > 0 && (\n <style>{`@keyframes sparkline-draw-${id.replace(/:/g, '')} { to { stroke-dashoffset: 0; } }`}</style>\n )}\n </div>\n )\n}\n\nfunction ProgressBar({ progress, label }: { progress: number; label: string }) {\n const clamped = Math.max(0, Math.min(100, progress))\n const barColor =\n clamped >= 90 ? 'bg-success-9' : clamped >= 70 ? 'bg-warning-9' : 'bg-accent-9'\n\n return (\n <div className=\"h-1 w-full rounded-pill bg-surface-raised mt-ds-04\" role=\"progressbar\" aria-label={`${label} progress`} aria-valuenow={clamped} aria-valuemin={0} aria-valuemax={100}>\n <div\n className={cn('h-full rounded-pill transition-[width] duration-moderate-02 ease-productive-standard', barColor)}\n style={{ width: `${clamped}%` }}\n />\n </div>\n )\n}\n\nconst StatCard = React.forwardRef<HTMLDivElement, StatCardProps>(\n (\n {\n className,\n label,\n title,\n value,\n prefix,\n suffix,\n delta,\n icon,\n loading,\n comparisonLabel,\n secondaryLabel,\n progress,\n surface = 'raised',\n accentStyle = 'none',\n iconFill = 'soft',\n flash,\n flashSpeed,\n sparkline,\n onClick,\n href,\n footer,\n ...props\n },\n ref,\n ) => {\n const Link = useLink()\n const resolvedLabel = title ?? label ?? ''\n const isClickable = !!(onClick || href)\n const computedAriaLabel =\n isClickable && !props['aria-label'] ? `View ${resolvedLabel}` : props['aria-label']\n\n if (loading) {\n return (\n <div\n ref={ref}\n className={cn(\n 'rounded-surface bg-surface-raised p-ds-05b',\n surface === 'flat' ? 'border border-surface-border' : 'shadow-raised',\n className,\n )}\n {...props}\n >\n <div className=\"h-ds-04 w-24 rounded-control-inner bg-skeleton-base animate-pulse mb-ds-05\" />\n <div className=\"h-ds-sm w-32 rounded-control bg-skeleton-base animate-pulse mb-ds-03\" />\n <div className=\"h-3 w-16 rounded-control-inner bg-skeleton-base animate-pulse\" />\n </div>\n )\n }\n\n const DeltaIcon =\n delta?.direction === 'up'\n ? IconTrendingUp\n : delta?.direction === 'down'\n ? IconTrendingDown\n : IconMinus\n\n const deltaColour =\n delta?.direction === 'up'\n ? 'text-success-11'\n : delta?.direction === 'down'\n ? 'text-error-11'\n : 'text-surface-fg-muted'\n\n const sparklineColor =\n delta?.direction === 'up'\n ? 'text-success-11'\n : delta?.direction === 'down'\n ? 'text-error-11'\n : 'text-accent-11'\n\n const cardContent = (\n <>\n <div className=\"flex items-center justify-between mb-ds-04\">\n <motion.p\n className=\"text-ds-md font-medium text-surface-fg-muted\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n transition={tweens.fade}\n >\n {resolvedLabel}\n </motion.p>\n <div className=\"flex items-center gap-ds-03\">\n {sparkline && sparkline.length >= 2 && (\n <Sparkline data={sparkline} colorClass={sparklineColor} />\n )}\n {icon && flash ? (\n <StatFlash icon={icon} flash={flash} fill={iconFill} speed={flashSpeed} />\n ) : icon ? (\n accentStyle === 'icon' ? (\n <motion.span\n className={cn(\n 'inline-flex items-center justify-center rounded-control p-ds-02',\n iconFill === 'solid'\n ? 'bg-accent-9 text-accent-fg'\n : 'bg-accent-3 text-accent-11',\n )}\n initial={{ opacity: 0, scale: 0.96 }}\n animate={{ opacity: 1, scale: 1 }}\n transition={springs.snappy}\n aria-hidden=\"true\"\n >\n <IconProvider size=\"md\">{normalizeIcon(icon, 'md')}</IconProvider>\n </motion.span>\n ) : (\n <motion.span\n className=\"text-surface-fg-muted\"\n initial={{ opacity: 0, scale: 0.96 }}\n animate={{ opacity: 1, scale: 1 }}\n transition={springs.snappy}\n aria-hidden=\"true\"\n >\n <IconProvider size=\"lg\">{normalizeIcon(icon, 'lg')}</IconProvider>\n </motion.span>\n )\n ) : null}\n </div>\n </div>\n <div className=\"overflow-hidden\">\n <motion.p\n className={cn(\n 'inline-block text-ds-3xl font-semibold',\n accentStyle === 'tint' ? 'text-accent-11' : 'text-surface-fg',\n )}\n initial={{ opacity: 0, y: '100%' }}\n animate={{ opacity: 1, y: 0 }}\n transition={springs.smooth}\n >\n {prefix && (\n <span className=\"text-surface-fg-muted text-ds-lg\">{prefix}</span>\n )}\n <span className=\"tabular-nums\">{value}</span>\n {suffix && (\n <span className=\"text-surface-fg-muted text-ds-lg\">{suffix}</span>\n )}\n </motion.p>\n </div>\n {secondaryLabel && (\n <motion.p\n className=\"text-ds-sm text-surface-fg-subtle mt-ds-01\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n transition={{ ...tweens.fade, delay: 0.1 }}\n >\n {secondaryLabel}\n </motion.p>\n )}\n {progress != null && (\n <motion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n transition={{ ...tweens.fade, delay: 0.15 }}\n >\n <ProgressBar progress={progress} label={resolvedLabel} />\n </motion.div>\n )}\n {delta && (\n <motion.div\n className={cn(\n 'mt-ds-03 flex items-center gap-ds-02 text-ds-sm font-medium',\n deltaColour,\n )}\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ ...springs.smooth, delay: 0.2 }}\n >\n <motion.span\n className=\"inline-flex\"\n initial={{ opacity: 0.5, scale: 1.4 }}\n animate={{ opacity: 1, scale: 1 }}\n transition={springs.bouncy}\n >\n <Icon icon={DeltaIcon} size=\"sm\" />\n </motion.span>\n <span>{delta.value}</span>\n {comparisonLabel && (\n <span className=\"text-surface-fg-subtle font-normal\">{comparisonLabel}</span>\n )}\n </motion.div>\n )}\n {footer && (\n <motion.div\n className=\"mt-ds-04 pt-ds-04 border-t border-surface-border text-ds-sm\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n transition={{ ...tweens.fade, delay: 0.25 }}\n >\n {footer}\n </motion.div>\n )}\n </>\n )\n\n const cardClasses = cn(\n 'rounded-surface p-ds-05b',\n accentStyle === 'tint'\n ? 'bg-linear-to-t from-accent-2 to-surface-raised'\n : 'bg-surface-raised',\n surface === 'flat' ? 'border border-surface-border' : 'shadow-raised',\n isClickable &&\n 'cursor-pointer hover:shadow-raised-hover transition-[box-shadow] duration-fast-02 ease-productive-standard group',\n className,\n )\n\n if (href) {\n return (\n <Link\n ref={ref as React.Ref<HTMLAnchorElement>}\n href={href}\n onClick={onClick}\n className={cn(cardClasses, 'block no-underline')}\n aria-label={computedAriaLabel}\n {...(props as React.AnchorHTMLAttributes<HTMLAnchorElement>)}\n >\n {cardContent}\n </Link>\n )\n }\n\n return (\n <div\n ref={ref}\n className={cardClasses}\n onClick={onClick}\n role={onClick ? 'button' : undefined}\n tabIndex={onClick ? 0 : undefined}\n aria-label={computedAriaLabel}\n onKeyDown={\n onClick\n ? (e: React.KeyboardEvent) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n onClick()\n }\n }\n : undefined\n }\n {...props}\n >\n {cardContent}\n </div>\n )\n },\n)\n\nStatCard.displayName = 'StatCard'\n\nexport { StatCard }\n"],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Transition } from 'framer-motion';
|
|
3
|
+
import { IconInput } from './lib/icon-input';
|
|
4
|
+
export type FlashTone = 'success' | 'error' | 'warning' | 'info' | 'accent';
|
|
5
|
+
/** Named entrance states. Each maps to a tone + glyph. */
|
|
6
|
+
export type FlashPreset = 'up' | 'down' | 'goal' | 'record' | 'alert' | 'live';
|
|
7
|
+
/** Explicit flash: any tone + any glyph. */
|
|
8
|
+
export interface FlashSpec {
|
|
9
|
+
tone: FlashTone;
|
|
10
|
+
icon: IconInput;
|
|
11
|
+
}
|
|
12
|
+
/** Composable speed — coarse presets built from the DS motion tokens. */
|
|
13
|
+
export type FlashSpeed = 'fast' | 'normal' | 'slow';
|
|
14
|
+
export interface StatFlashProps {
|
|
15
|
+
/** Settled identity glyph, shown after the flash resolves. */
|
|
16
|
+
icon: IconInput;
|
|
17
|
+
/** The entrance flash — a named preset or an explicit `{ tone, icon }`. */
|
|
18
|
+
flash: FlashPreset | FlashSpec;
|
|
19
|
+
/** Resting chip style once settled. @default 'soft' */
|
|
20
|
+
fill?: 'soft' | 'solid';
|
|
21
|
+
/** Coarse speed preset (hold + settle + fade), built from DS motion tokens. @default 'normal' */
|
|
22
|
+
speed?: FlashSpeed;
|
|
23
|
+
/** Override how long the flash holds before settling, in ms. Wins over `speed`. */
|
|
24
|
+
holdMs?: number;
|
|
25
|
+
/** Override the settle (icon scale-in) transition. Wins over `speed`. */
|
|
26
|
+
settleTransition?: Transition;
|
|
27
|
+
/** Override the flash enter/exit fade transition. Wins over `speed`. */
|
|
28
|
+
flashTransition?: Transition;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* StatFlash — a chip that mounts showing a transient **state** (a toned glyph: an up-arrow on
|
|
32
|
+
* green, a check, an alert…) then settles into the metric's stable **identity** icon. The pattern
|
|
33
|
+
* is *state → identity*: communicate what just happened, then resolve to what the metric is.
|
|
34
|
+
*
|
|
35
|
+
* Used by `StatCard` via its `flash` prop, but standalone too (list rows, badges, toasts).
|
|
36
|
+
* Honors `prefers-reduced-motion` — renders the settled identity directly, no flash.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* <StatFlash icon={<IconFolder />} flash="up" /> // green up-arrow → folder
|
|
40
|
+
* <StatFlash icon={<IconBolt />} flash={{ tone: 'info', icon: <IconActivity /> }} />
|
|
41
|
+
*/
|
|
42
|
+
export declare function StatFlash({ icon, flash, fill, speed, holdMs, settleTransition, flashTransition, }: StatFlashProps): import("react/jsx-runtime").JSX.Element;
|
|
43
|
+
export declare namespace StatFlash {
|
|
44
|
+
var displayName: string;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=stat-flash.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stat-flash.d.ts","sourceRoot":"","sources":["../../src/ui/stat-flash.tsx"],"names":[],"mappings":"AAUA,OAAO,EAA2B,KAAK,UAAU,EAAoB,MAAM,eAAe,CAAA;AAI1F,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAKjD,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAA;AAE3E,0DAA0D;AAC1D,MAAM,MAAM,WAAW,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAA;AAE9E,4CAA4C;AAC5C,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,SAAS,CAAA;IACf,IAAI,EAAE,SAAS,CAAA;CAChB;AA2BD,yEAAyE;AACzE,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAA;AAQnD,MAAM,WAAW,cAAc;IAC7B,8DAA8D;IAC9D,IAAI,EAAE,SAAS,CAAA;IACf,2EAA2E;IAC3E,KAAK,EAAE,WAAW,GAAG,SAAS,CAAA;IAC9B,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IACvB,iGAAiG;IACjG,KAAK,CAAC,EAAE,UAAU,CAAA;IAClB,mFAAmF;IACnF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,yEAAyE;IACzE,gBAAgB,CAAC,EAAE,UAAU,CAAA;IAC7B,wEAAwE;IACxE,eAAe,CAAC,EAAE,UAAU,CAAA;CAC7B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,EACxB,IAAI,EACJ,KAAK,EACL,IAAa,EACb,KAAgB,EAChB,MAAM,EACN,gBAAgB,EAChB,eAAe,GAChB,EAAE,cAAc,2CA0DhB;yBAlEe,SAAS"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { IconProvider as e } from "./icon-context.js";
|
|
3
|
+
import { springs as t, tweens as n } from "./lib/motion.js";
|
|
4
|
+
import { cn as r } from "./lib/utils.js";
|
|
5
|
+
import { t as i } from "../_chunks/normalize-icon.js";
|
|
6
|
+
import * as a from "react";
|
|
7
|
+
import { jsx as o, jsxs as s } from "react/jsx-runtime";
|
|
8
|
+
import { IconAlertTriangle as c, IconArrowDown as l, IconArrowUp as u, IconBolt as d, IconCheck as f, IconStar as p } from "@tabler/icons-react";
|
|
9
|
+
import { AnimatePresence as m, motion as h, useReducedMotion as g } from "framer-motion";
|
|
10
|
+
//#region src/ui/stat-flash.tsx
|
|
11
|
+
var _ = {
|
|
12
|
+
up: {
|
|
13
|
+
tone: "success",
|
|
14
|
+
Glyph: u
|
|
15
|
+
},
|
|
16
|
+
down: {
|
|
17
|
+
tone: "error",
|
|
18
|
+
Glyph: l
|
|
19
|
+
},
|
|
20
|
+
goal: {
|
|
21
|
+
tone: "success",
|
|
22
|
+
Glyph: f
|
|
23
|
+
},
|
|
24
|
+
record: {
|
|
25
|
+
tone: "accent",
|
|
26
|
+
Glyph: p
|
|
27
|
+
},
|
|
28
|
+
alert: {
|
|
29
|
+
tone: "warning",
|
|
30
|
+
Glyph: c
|
|
31
|
+
},
|
|
32
|
+
live: {
|
|
33
|
+
tone: "info",
|
|
34
|
+
Glyph: d
|
|
35
|
+
}
|
|
36
|
+
}, v = {
|
|
37
|
+
success: "bg-success-9",
|
|
38
|
+
error: "bg-error-9",
|
|
39
|
+
warning: "bg-warning-9",
|
|
40
|
+
info: "bg-info-9",
|
|
41
|
+
accent: "bg-accent-9"
|
|
42
|
+
}, y = {
|
|
43
|
+
success: "text-success-fg",
|
|
44
|
+
error: "text-error-fg",
|
|
45
|
+
warning: "text-warning-fg",
|
|
46
|
+
info: "text-info-fg",
|
|
47
|
+
accent: "text-accent-fg"
|
|
48
|
+
}, b = {
|
|
49
|
+
fast: {
|
|
50
|
+
holdMs: 450,
|
|
51
|
+
settle: t.snappy,
|
|
52
|
+
fade: n.fade
|
|
53
|
+
},
|
|
54
|
+
normal: {
|
|
55
|
+
holdMs: 650,
|
|
56
|
+
settle: t.snappy,
|
|
57
|
+
fade: n.fade
|
|
58
|
+
},
|
|
59
|
+
slow: {
|
|
60
|
+
holdMs: 950,
|
|
61
|
+
settle: t.gentle,
|
|
62
|
+
fade: n.elegant
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
function x({ icon: t, flash: n, fill: c = "soft", speed: l = "normal", holdMs: u, settleTransition: d, flashTransition: f }) {
|
|
66
|
+
let p = g(), x = b[l], C = u ?? x.holdMs, w = d ?? x.settle, T = f ?? x.fade, E = typeof n == "string" ? {
|
|
67
|
+
tone: _[n].tone,
|
|
68
|
+
icon: /* @__PURE__ */ o(S, { preset: n })
|
|
69
|
+
} : n, [D, O] = a.useState(!1);
|
|
70
|
+
return a.useEffect(() => {
|
|
71
|
+
if (p) {
|
|
72
|
+
O(!0);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
let e = setTimeout(() => O(!0), C);
|
|
76
|
+
return () => clearTimeout(e);
|
|
77
|
+
}, [p, C]), /* @__PURE__ */ s("span", {
|
|
78
|
+
className: r("relative inline-flex items-center justify-center overflow-hidden rounded-control p-ds-02", c === "solid" ? "bg-accent-9 text-accent-fg" : "bg-accent-3 text-accent-11"),
|
|
79
|
+
"aria-hidden": "true",
|
|
80
|
+
children: [/* @__PURE__ */ o(h.span, {
|
|
81
|
+
className: "inline-flex",
|
|
82
|
+
initial: p ? !1 : {
|
|
83
|
+
opacity: 0,
|
|
84
|
+
scale: .55
|
|
85
|
+
},
|
|
86
|
+
animate: {
|
|
87
|
+
opacity: +!!D,
|
|
88
|
+
scale: D ? 1 : .55
|
|
89
|
+
},
|
|
90
|
+
transition: w,
|
|
91
|
+
children: /* @__PURE__ */ o(e, {
|
|
92
|
+
size: "md",
|
|
93
|
+
children: i(t, "md")
|
|
94
|
+
})
|
|
95
|
+
}), /* @__PURE__ */ o(m, { children: !D && /* @__PURE__ */ o(h.span, {
|
|
96
|
+
className: r("absolute inset-0 inline-flex items-center justify-center", v[E.tone], y[E.tone]),
|
|
97
|
+
initial: { opacity: 0 },
|
|
98
|
+
animate: { opacity: 1 },
|
|
99
|
+
exit: { opacity: 0 },
|
|
100
|
+
transition: T,
|
|
101
|
+
children: /* @__PURE__ */ o(e, {
|
|
102
|
+
size: "md",
|
|
103
|
+
children: i(E.icon, "md")
|
|
104
|
+
})
|
|
105
|
+
}) })]
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
function S({ preset: e }) {
|
|
109
|
+
let { Glyph: t } = _[e];
|
|
110
|
+
return /* @__PURE__ */ o(t, {});
|
|
111
|
+
}
|
|
112
|
+
x.displayName = "StatFlash";
|
|
113
|
+
//#endregion
|
|
114
|
+
export { x as StatFlash };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stat-flash.js","names":[],"sources":["../../src/ui/stat-flash.tsx"],"sourcesContent":["'use client'\n\nimport {\n IconAlertTriangle,\n IconArrowDown,\n IconArrowUp,\n IconBolt,\n IconCheck,\n IconStar,\n} from '@tabler/icons-react'\nimport { AnimatePresence, motion, type Transition, useReducedMotion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { IconProvider } from './icon-context'\nimport type { IconInput } from './lib/icon-input'\nimport { springs, tweens } from './lib/motion'\nimport { normalizeIcon } from './lib/normalize-icon'\nimport { cn } from './lib/utils'\n\nexport type FlashTone = 'success' | 'error' | 'warning' | 'info' | 'accent'\n\n/** Named entrance states. Each maps to a tone + glyph. */\nexport type FlashPreset = 'up' | 'down' | 'goal' | 'record' | 'alert' | 'live'\n\n/** Explicit flash: any tone + any glyph. */\nexport interface FlashSpec {\n tone: FlashTone\n icon: IconInput\n}\n\nconst FLASH_PRESETS: Record<FlashPreset, { tone: FlashTone; Glyph: React.ComponentType }> = {\n up: { tone: 'success', Glyph: IconArrowUp },\n down: { tone: 'error', Glyph: IconArrowDown },\n goal: { tone: 'success', Glyph: IconCheck },\n record: { tone: 'accent', Glyph: IconStar },\n alert: { tone: 'warning', Glyph: IconAlertTriangle },\n live: { tone: 'info', Glyph: IconBolt },\n}\n\nconst toneBg: Record<FlashTone, string> = {\n success: 'bg-success-9',\n error: 'bg-error-9',\n warning: 'bg-warning-9',\n info: 'bg-info-9',\n accent: 'bg-accent-9',\n}\n\nconst toneFg: Record<FlashTone, string> = {\n success: 'text-success-fg',\n error: 'text-error-fg',\n warning: 'text-warning-fg',\n info: 'text-info-fg',\n accent: 'text-accent-fg',\n}\n\n/** Composable speed — coarse presets built from the DS motion tokens. */\nexport type FlashSpeed = 'fast' | 'normal' | 'slow'\n\nconst SPEEDS: Record<FlashSpeed, { holdMs: number; settle: Transition; fade: Transition }> = {\n fast: { holdMs: 450, settle: springs.snappy, fade: tweens.fade },\n normal: { holdMs: 650, settle: springs.snappy, fade: tweens.fade },\n slow: { holdMs: 950, settle: springs.gentle, fade: tweens.elegant },\n}\n\nexport interface StatFlashProps {\n /** Settled identity glyph, shown after the flash resolves. */\n icon: IconInput\n /** The entrance flash — a named preset or an explicit `{ tone, icon }`. */\n flash: FlashPreset | FlashSpec\n /** Resting chip style once settled. @default 'soft' */\n fill?: 'soft' | 'solid'\n /** Coarse speed preset (hold + settle + fade), built from DS motion tokens. @default 'normal' */\n speed?: FlashSpeed\n /** Override how long the flash holds before settling, in ms. Wins over `speed`. */\n holdMs?: number\n /** Override the settle (icon scale-in) transition. Wins over `speed`. */\n settleTransition?: Transition\n /** Override the flash enter/exit fade transition. Wins over `speed`. */\n flashTransition?: Transition\n}\n\n/**\n * StatFlash — a chip that mounts showing a transient **state** (a toned glyph: an up-arrow on\n * green, a check, an alert…) then settles into the metric's stable **identity** icon. The pattern\n * is *state → identity*: communicate what just happened, then resolve to what the metric is.\n *\n * Used by `StatCard` via its `flash` prop, but standalone too (list rows, badges, toasts).\n * Honors `prefers-reduced-motion` — renders the settled identity directly, no flash.\n *\n * @example\n * <StatFlash icon={<IconFolder />} flash=\"up\" /> // green up-arrow → folder\n * <StatFlash icon={<IconBolt />} flash={{ tone: 'info', icon: <IconActivity /> }} />\n */\nexport function StatFlash({\n icon,\n flash,\n fill = 'soft',\n speed = 'normal',\n holdMs,\n settleTransition,\n flashTransition,\n}: StatFlashProps) {\n const prefersReduced = useReducedMotion()\n const preset = SPEEDS[speed]\n const hold = holdMs ?? preset.holdMs\n const settleT = settleTransition ?? preset.settle\n const fadeT = flashTransition ?? preset.fade\n\n const resolved =\n typeof flash === 'string'\n ? { tone: FLASH_PRESETS[flash].tone, icon: <FlashPresetGlyph preset={flash} /> }\n : flash\n\n const [settled, setSettled] = React.useState(false)\n\n React.useEffect(() => {\n if (prefersReduced) {\n setSettled(true)\n return\n }\n const t = setTimeout(() => setSettled(true), hold)\n return () => clearTimeout(t)\n }, [prefersReduced, hold])\n\n return (\n <span\n className={cn(\n 'relative inline-flex items-center justify-center overflow-hidden rounded-control p-ds-02',\n fill === 'solid' ? 'bg-accent-9 text-accent-fg' : 'bg-accent-3 text-accent-11',\n )}\n aria-hidden=\"true\"\n >\n <motion.span\n className=\"inline-flex\"\n initial={prefersReduced ? false : { opacity: 0, scale: 0.55 }}\n animate={{ opacity: settled ? 1 : 0, scale: settled ? 1 : 0.55 }}\n transition={settleT}\n >\n <IconProvider size=\"md\">{normalizeIcon(icon, 'md')}</IconProvider>\n </motion.span>\n <AnimatePresence>\n {!settled && (\n <motion.span\n className={cn(\n 'absolute inset-0 inline-flex items-center justify-center',\n toneBg[resolved.tone],\n toneFg[resolved.tone],\n )}\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={fadeT}\n >\n <IconProvider size=\"md\">{normalizeIcon(resolved.icon, 'md')}</IconProvider>\n </motion.span>\n )}\n </AnimatePresence>\n </span>\n )\n}\n\nfunction FlashPresetGlyph({ preset }: { preset: FlashPreset }) {\n const { Glyph } = FLASH_PRESETS[preset]\n return <Glyph />\n}\n\nStatFlash.displayName = 'StatFlash'\n"],"mappings":";;;;;;;;;;AA8BA,IAAM,IAAsF;CAC1F,IAAI;EAAE,MAAM;EAAW,OAAO;EAAa;CAC3C,MAAM;EAAE,MAAM;EAAS,OAAO;EAAe;CAC7C,MAAM;EAAE,MAAM;EAAW,OAAO;EAAW;CAC3C,QAAQ;EAAE,MAAM;EAAU,OAAO;EAAU;CAC3C,OAAO;EAAE,MAAM;EAAW,OAAO;EAAmB;CACpD,MAAM;EAAE,MAAM;EAAQ,OAAO;EAAU;CACxC,EAEK,IAAoC;CACxC,SAAS;CACT,OAAO;CACP,SAAS;CACT,MAAM;CACN,QAAQ;CACT,EAEK,IAAoC;CACxC,SAAS;CACT,OAAO;CACP,SAAS;CACT,MAAM;CACN,QAAQ;CACT,EAKK,IAAuF;CAC3F,MAAM;EAAE,QAAQ;EAAK,QAAQ,EAAQ;EAAQ,MAAM,EAAO;EAAM;CAChE,QAAQ;EAAE,QAAQ;EAAK,QAAQ,EAAQ;EAAQ,MAAM,EAAO;EAAM;CAClE,MAAM;EAAE,QAAQ;EAAK,QAAQ,EAAQ;EAAQ,MAAM,EAAO;EAAS;CACpE;AA+BD,SAAgB,EAAU,EACxB,SACA,UACA,UAAO,QACP,WAAQ,UACR,WACA,qBACA,sBACiB;CACjB,IAAM,IAAiB,GAAkB,EACnC,IAAS,EAAO,IAChB,IAAO,KAAU,EAAO,QACxB,IAAU,KAAoB,EAAO,QACrC,IAAQ,KAAmB,EAAO,MAElC,IACJ,OAAO,KAAU,WACb;EAAE,MAAM,EAAc,GAAO;EAAM,MAAM,kBAAC,GAAD,EAAkB,QAAQ,GAAS,CAAA;EAAE,GAC9E,GAEA,CAAC,GAAS,KAAc,EAAM,SAAS,GAAM;AAWnD,QATA,EAAM,gBAAgB;AACpB,MAAI,GAAgB;AAClB,KAAW,GAAK;AAChB;;EAEF,IAAM,IAAI,iBAAiB,EAAW,GAAK,EAAE,EAAK;AAClD,eAAa,aAAa,EAAE;IAC3B,CAAC,GAAgB,EAAK,CAAC,EAGxB,kBAAC,QAAD;EACE,WAAW,EACT,4FACA,MAAS,UAAU,+BAA+B,6BACnD;EACD,eAAY;YALd,CAOE,kBAAC,EAAO,MAAR;GACE,WAAU;GACV,SAAS,IAAiB,KAAQ;IAAE,SAAS;IAAG,OAAO;IAAM;GAC7D,SAAS;IAAE,SAAS;IAAiB,OAAO,IAAU,IAAI;IAAM;GAChE,YAAY;aAEZ,kBAAC,GAAD;IAAc,MAAK;cAAM,EAAc,GAAM,KAAK;IAAgB,CAAA;GACtD,CAAA,EACd,kBAAC,GAAD,EAAA,UACG,CAAC,KACA,kBAAC,EAAO,MAAR;GACE,WAAW,EACT,4DACA,EAAO,EAAS,OAChB,EAAO,EAAS,MACjB;GACD,SAAS,EAAE,SAAS,GAAG;GACvB,SAAS,EAAE,SAAS,GAAG;GACvB,MAAM,EAAE,SAAS,GAAG;GACpB,YAAY;aAEZ,kBAAC,GAAD;IAAc,MAAK;cAAM,EAAc,EAAS,MAAM,KAAK;IAAgB,CAAA;GAC/D,CAAA,EAEA,CAAA,CACb;;;AAIX,SAAS,EAAiB,EAAE,aAAmC;CAC7D,IAAM,EAAE,aAAU,EAAc;AAChC,QAAO,kBAAC,GAAD,EAAS,CAAA;;AAGlB,EAAU,cAAc"}
|
package/dist/ui/toast.js
CHANGED
|
@@ -87,7 +87,7 @@ function k({ type: a, title: o, description: u, action: d, cancel: f, duration:
|
|
|
87
87
|
role: j ? "alert" : "status",
|
|
88
88
|
"aria-live": j ? "assertive" : "polite",
|
|
89
89
|
"aria-atomic": "true",
|
|
90
|
-
className: "group relative flex w-full overflow-hidden rounded-overlay-sm
|
|
90
|
+
className: "group relative flex w-full overflow-hidden rounded-overlay-sm bg-surface-overlay shadow-floating",
|
|
91
91
|
onMouseEnter: () => _(!0),
|
|
92
92
|
onMouseLeave: () => _(!1),
|
|
93
93
|
onFocusCapture: () => y(!0),
|
|
@@ -370,7 +370,7 @@ function I({ files: r, onRetry: a, onRemove: o, selfDismissId: d }) {
|
|
|
370
370
|
role: j ? "alert" : "status",
|
|
371
371
|
"aria-live": j ? "assertive" : "polite",
|
|
372
372
|
"aria-label": "File uploads",
|
|
373
|
-
className: "group relative flex w-full overflow-hidden rounded-overlay-sm
|
|
373
|
+
className: "group relative flex w-full overflow-hidden rounded-overlay-sm bg-surface-overlay shadow-floating",
|
|
374
374
|
onMouseEnter: () => m(!0),
|
|
375
375
|
onMouseLeave: () => m(!1),
|
|
376
376
|
onFocusCapture: () => g(!0),
|