@windforge/ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1195 -0
- package/dist/index.js +3628 -0
- package/package.json +66 -0
- package/src/catalog.ts +654 -0
- package/src/components/accordion.tsx +91 -0
- package/src/components/alert.tsx +58 -0
- package/src/components/autocomplete.tsx +174 -0
- package/src/components/avatar.tsx +60 -0
- package/src/components/badge.tsx +37 -0
- package/src/components/breadcrumb.tsx +62 -0
- package/src/components/button-group.tsx +23 -0
- package/src/components/button.tsx +53 -0
- package/src/components/calendar.tsx +61 -0
- package/src/components/card.tsx +72 -0
- package/src/components/chart.tsx +130 -0
- package/src/components/checkbox.tsx +27 -0
- package/src/components/chip.tsx +75 -0
- package/src/components/code-block.tsx +126 -0
- package/src/components/command.tsx +139 -0
- package/src/components/data-table.tsx +194 -0
- package/src/components/date-picker.tsx +77 -0
- package/src/components/dialog.tsx +57 -0
- package/src/components/dropdown-menu.tsx +186 -0
- package/src/components/form-field.tsx +97 -0
- package/src/components/input.tsx +29 -0
- package/src/components/label.tsx +18 -0
- package/src/components/layout.tsx +179 -0
- package/src/components/link.tsx +37 -0
- package/src/components/modal.tsx +67 -0
- package/src/components/multi-select.tsx +175 -0
- package/src/components/pagination.tsx +72 -0
- package/src/components/popover.tsx +25 -0
- package/src/components/progress.tsx +31 -0
- package/src/components/radio-group.tsx +34 -0
- package/src/components/select.tsx +134 -0
- package/src/components/separator.tsx +21 -0
- package/src/components/sheet.tsx +80 -0
- package/src/components/skeleton.tsx +11 -0
- package/src/components/slider.tsx +28 -0
- package/src/components/stepper.tsx +69 -0
- package/src/components/switch.tsx +33 -0
- package/src/components/table.tsx +121 -0
- package/src/components/tabs.tsx +90 -0
- package/src/components/text.tsx +109 -0
- package/src/components/textarea.tsx +27 -0
- package/src/components/toast.tsx +107 -0
- package/src/components/toggle-button.tsx +103 -0
- package/src/components/tooltip.tsx +26 -0
- package/src/icons/forge-icon.tsx +55 -0
- package/src/icons/icon-set.ts +60 -0
- package/src/icons/svg-icon.tsx +43 -0
- package/src/index.ts +80 -0
- package/src/layouts/app-bar.tsx +95 -0
- package/src/layouts/app-shell.tsx +80 -0
- package/src/layouts/side-nav.tsx +196 -0
- package/src/layouts/theme-provider.tsx +128 -0
- package/src/lib/recipes.ts +50 -0
- package/src/lib/types.ts +3 -0
- package/src/lib/use-media-query.ts +18 -0
- package/src/lib/utils.ts +10 -0
- package/tailwind-preset.cjs +77 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,3628 @@
|
|
|
1
|
+
// src/components/layout.tsx
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
4
|
+
import { cva } from "class-variance-authority";
|
|
5
|
+
|
|
6
|
+
// src/lib/utils.ts
|
|
7
|
+
import { clsx } from "clsx";
|
|
8
|
+
import { twMerge } from "tailwind-merge";
|
|
9
|
+
function cn(...inputs) {
|
|
10
|
+
return twMerge(clsx(inputs));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// src/components/layout.tsx
|
|
14
|
+
import { jsx } from "react/jsx-runtime";
|
|
15
|
+
var surface = cva("", {
|
|
16
|
+
variants: {
|
|
17
|
+
padding: {
|
|
18
|
+
none: "p-none",
|
|
19
|
+
xs: "p-xs",
|
|
20
|
+
sm: "p-sm",
|
|
21
|
+
md: "p-md",
|
|
22
|
+
lg: "p-lg",
|
|
23
|
+
xl: "p-xl",
|
|
24
|
+
"2xl": "p-2xl",
|
|
25
|
+
nbsp: "p-nbsp",
|
|
26
|
+
card: "p-card",
|
|
27
|
+
gutter: "p-gutter",
|
|
28
|
+
section: "p-section",
|
|
29
|
+
page: "p-page"
|
|
30
|
+
},
|
|
31
|
+
paddingX: {
|
|
32
|
+
none: "px-none",
|
|
33
|
+
xs: "px-xs",
|
|
34
|
+
sm: "px-sm",
|
|
35
|
+
md: "px-md",
|
|
36
|
+
lg: "px-lg",
|
|
37
|
+
xl: "px-xl",
|
|
38
|
+
"2xl": "px-2xl",
|
|
39
|
+
nbsp: "px-nbsp",
|
|
40
|
+
card: "px-card",
|
|
41
|
+
gutter: "px-gutter",
|
|
42
|
+
section: "px-section",
|
|
43
|
+
page: "px-page"
|
|
44
|
+
},
|
|
45
|
+
paddingY: {
|
|
46
|
+
none: "py-none",
|
|
47
|
+
xs: "py-xs",
|
|
48
|
+
sm: "py-sm",
|
|
49
|
+
md: "py-md",
|
|
50
|
+
lg: "py-lg",
|
|
51
|
+
xl: "py-xl",
|
|
52
|
+
"2xl": "py-2xl",
|
|
53
|
+
nbsp: "py-nbsp",
|
|
54
|
+
card: "py-card",
|
|
55
|
+
gutter: "py-gutter",
|
|
56
|
+
section: "py-section",
|
|
57
|
+
page: "py-page"
|
|
58
|
+
},
|
|
59
|
+
background: {
|
|
60
|
+
none: "",
|
|
61
|
+
surface: "bg-surface",
|
|
62
|
+
subtle: "bg-surface-subtle",
|
|
63
|
+
inset: "bg-surface-inset",
|
|
64
|
+
inverse: "bg-surface-inverse text-inverse",
|
|
65
|
+
brand: "bg-brand-subtle"
|
|
66
|
+
},
|
|
67
|
+
border: { none: "", default: "border border-border", subtle: "border border-subtle", strong: "border border-strong" },
|
|
68
|
+
borderRadius: { none: "rounded-none", sm: "rounded-sm", md: "rounded-md", lg: "rounded-lg", xl: "rounded-xl", "2xl": "rounded-2xl", full: "rounded-full" },
|
|
69
|
+
boxShadow: { none: "shadow-none", sm: "shadow-sm", md: "shadow-md", lg: "shadow-lg", xl: "shadow-xl" },
|
|
70
|
+
// Page-width constraint (absorbs the old Container). Pair with className="mx-auto" to center.
|
|
71
|
+
maxWidth: { sm: "max-w-2xl", md: "max-w-4xl", lg: "max-w-6xl", xl: "max-w-7xl", prose: "max-w-prose", full: "max-w-none" }
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
var flex = cva("flex", {
|
|
75
|
+
variants: {
|
|
76
|
+
direction: { row: "flex-row", column: "flex-col" },
|
|
77
|
+
gap: {
|
|
78
|
+
none: "gap-none",
|
|
79
|
+
xs: "gap-xs",
|
|
80
|
+
sm: "gap-sm",
|
|
81
|
+
md: "gap-md",
|
|
82
|
+
lg: "gap-lg",
|
|
83
|
+
xl: "gap-xl",
|
|
84
|
+
"2xl": "gap-2xl",
|
|
85
|
+
nbsp: "gap-nbsp",
|
|
86
|
+
card: "gap-card",
|
|
87
|
+
gutter: "gap-gutter",
|
|
88
|
+
section: "gap-section",
|
|
89
|
+
page: "gap-page"
|
|
90
|
+
},
|
|
91
|
+
align: { start: "items-start", center: "items-center", end: "items-end", stretch: "items-stretch", baseline: "items-baseline" },
|
|
92
|
+
justify: { start: "justify-start", center: "justify-center", end: "justify-end", between: "justify-between", around: "justify-around" },
|
|
93
|
+
wrap: { true: "flex-wrap", false: "flex-nowrap" }
|
|
94
|
+
},
|
|
95
|
+
defaultVariants: { direction: "column", gap: "md" }
|
|
96
|
+
});
|
|
97
|
+
var Box = React.forwardRef(
|
|
98
|
+
({ className, asChild, padding, paddingX, paddingY, background, border, borderRadius, boxShadow, maxWidth, ...props }, ref) => {
|
|
99
|
+
const Comp = asChild ? Slot : "div";
|
|
100
|
+
return /* @__PURE__ */ jsx(
|
|
101
|
+
Comp,
|
|
102
|
+
{
|
|
103
|
+
ref,
|
|
104
|
+
className: cn(surface({ padding, paddingX, paddingY, background, border, borderRadius, boxShadow, maxWidth }), className),
|
|
105
|
+
...props
|
|
106
|
+
}
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
Box.displayName = "Box";
|
|
111
|
+
var Stack = React.forwardRef(
|
|
112
|
+
({
|
|
113
|
+
className,
|
|
114
|
+
asChild,
|
|
115
|
+
direction,
|
|
116
|
+
gap,
|
|
117
|
+
align,
|
|
118
|
+
justify,
|
|
119
|
+
wrap,
|
|
120
|
+
padding,
|
|
121
|
+
paddingX,
|
|
122
|
+
paddingY,
|
|
123
|
+
background,
|
|
124
|
+
border,
|
|
125
|
+
borderRadius,
|
|
126
|
+
boxShadow,
|
|
127
|
+
maxWidth,
|
|
128
|
+
...props
|
|
129
|
+
}, ref) => {
|
|
130
|
+
const Comp = asChild ? Slot : "div";
|
|
131
|
+
return /* @__PURE__ */ jsx(
|
|
132
|
+
Comp,
|
|
133
|
+
{
|
|
134
|
+
ref,
|
|
135
|
+
className: cn(
|
|
136
|
+
flex({ direction, gap, align, justify, wrap }),
|
|
137
|
+
surface({ padding, paddingX, paddingY, background, border, borderRadius, boxShadow, maxWidth }),
|
|
138
|
+
className
|
|
139
|
+
),
|
|
140
|
+
...props
|
|
141
|
+
}
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
);
|
|
145
|
+
Stack.displayName = "Stack";
|
|
146
|
+
var grid = cva("grid", {
|
|
147
|
+
variants: {
|
|
148
|
+
cols: {
|
|
149
|
+
1: "grid-cols-1",
|
|
150
|
+
2: "grid-cols-2",
|
|
151
|
+
3: "grid-cols-3",
|
|
152
|
+
4: "grid-cols-4",
|
|
153
|
+
5: "grid-cols-5",
|
|
154
|
+
6: "grid-cols-6",
|
|
155
|
+
12: "grid-cols-12"
|
|
156
|
+
},
|
|
157
|
+
// responsive breakpoint at md — the common "stack on mobile, grid on desktop"
|
|
158
|
+
mdCols: {
|
|
159
|
+
1: "md:grid-cols-1",
|
|
160
|
+
2: "md:grid-cols-2",
|
|
161
|
+
3: "md:grid-cols-3",
|
|
162
|
+
4: "md:grid-cols-4",
|
|
163
|
+
5: "md:grid-cols-5",
|
|
164
|
+
6: "md:grid-cols-6",
|
|
165
|
+
12: "md:grid-cols-12"
|
|
166
|
+
},
|
|
167
|
+
gap: {
|
|
168
|
+
none: "gap-none",
|
|
169
|
+
xs: "gap-xs",
|
|
170
|
+
sm: "gap-sm",
|
|
171
|
+
md: "gap-md",
|
|
172
|
+
lg: "gap-lg",
|
|
173
|
+
xl: "gap-xl",
|
|
174
|
+
"2xl": "gap-2xl",
|
|
175
|
+
nbsp: "gap-nbsp",
|
|
176
|
+
card: "gap-card",
|
|
177
|
+
gutter: "gap-gutter",
|
|
178
|
+
section: "gap-section",
|
|
179
|
+
page: "gap-page"
|
|
180
|
+
},
|
|
181
|
+
align: { start: "items-start", center: "items-center", end: "items-end", stretch: "items-stretch" },
|
|
182
|
+
justify: { start: "justify-items-start", center: "justify-items-center", end: "justify-items-end", stretch: "justify-items-stretch" }
|
|
183
|
+
},
|
|
184
|
+
defaultVariants: { cols: 1, gap: "md" }
|
|
185
|
+
});
|
|
186
|
+
var Grid = React.forwardRef(
|
|
187
|
+
({ className, asChild, cols, mdCols, gap, align, justify, ...props }, ref) => {
|
|
188
|
+
const Comp = asChild ? Slot : "div";
|
|
189
|
+
return /* @__PURE__ */ jsx(Comp, { ref, className: cn(grid({ cols, mdCols, gap, align, justify }), className), ...props });
|
|
190
|
+
}
|
|
191
|
+
);
|
|
192
|
+
Grid.displayName = "Grid";
|
|
193
|
+
var layoutVocabulary = {
|
|
194
|
+
spacing: ["none", "xs", "sm", "md", "lg", "xl", "2xl", "nbsp", "card", "gutter", "section", "page"],
|
|
195
|
+
direction: ["row", "column"],
|
|
196
|
+
align: ["start", "center", "end", "stretch", "baseline"],
|
|
197
|
+
justify: ["start", "center", "end", "between", "around"],
|
|
198
|
+
background: ["none", "surface", "subtle", "inset", "inverse", "brand"],
|
|
199
|
+
border: ["none", "default", "subtle", "strong"],
|
|
200
|
+
borderRadius: ["none", "sm", "md", "lg", "xl", "2xl", "full"],
|
|
201
|
+
boxShadow: ["none", "sm", "md", "lg", "xl"],
|
|
202
|
+
maxWidth: ["sm", "md", "lg", "xl", "prose", "full"],
|
|
203
|
+
gridCols: [1, 2, 3, 4, 5, 6, 12]
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
// src/catalog.ts
|
|
207
|
+
var catalog = {
|
|
208
|
+
// ── layout primitives ──────────────────────────────────────────────────────
|
|
209
|
+
Box: {
|
|
210
|
+
summary: "A padded, optionally-surfaced container \u2014 the on-system replacement for a bare <div>.",
|
|
211
|
+
props: {
|
|
212
|
+
padding: layoutVocabulary.spacing,
|
|
213
|
+
paddingX: layoutVocabulary.spacing,
|
|
214
|
+
paddingY: layoutVocabulary.spacing,
|
|
215
|
+
background: layoutVocabulary.background,
|
|
216
|
+
border: layoutVocabulary.border,
|
|
217
|
+
borderRadius: layoutVocabulary.borderRadius,
|
|
218
|
+
boxShadow: layoutVocabulary.boxShadow,
|
|
219
|
+
maxWidth: layoutVocabulary.maxWidth
|
|
220
|
+
},
|
|
221
|
+
flags: {
|
|
222
|
+
asChild: "Render as a child element (polymorphic, replaces MUI component=)"
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
Stack: {
|
|
226
|
+
summary: "A flex Box \u2014 the primary way to arrange children in a row or column with on-scale gap.",
|
|
227
|
+
props: {
|
|
228
|
+
direction: layoutVocabulary.direction,
|
|
229
|
+
gap: layoutVocabulary.spacing,
|
|
230
|
+
align: layoutVocabulary.align,
|
|
231
|
+
justify: layoutVocabulary.justify,
|
|
232
|
+
padding: layoutVocabulary.spacing,
|
|
233
|
+
paddingX: layoutVocabulary.spacing,
|
|
234
|
+
paddingY: layoutVocabulary.spacing,
|
|
235
|
+
background: layoutVocabulary.background,
|
|
236
|
+
border: layoutVocabulary.border,
|
|
237
|
+
borderRadius: layoutVocabulary.borderRadius,
|
|
238
|
+
boxShadow: layoutVocabulary.boxShadow,
|
|
239
|
+
maxWidth: layoutVocabulary.maxWidth
|
|
240
|
+
},
|
|
241
|
+
flags: {
|
|
242
|
+
wrap: "flex-wrap when true",
|
|
243
|
+
asChild: "Render as a child element (polymorphic)"
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
Grid: {
|
|
247
|
+
summary: "A closed-vocabulary CSS grid with responsive column counts and on-scale gap.",
|
|
248
|
+
props: {
|
|
249
|
+
cols: layoutVocabulary.gridCols.map(String),
|
|
250
|
+
mdCols: layoutVocabulary.gridCols.map(String),
|
|
251
|
+
gap: layoutVocabulary.spacing,
|
|
252
|
+
align: ["start", "center", "end", "stretch"],
|
|
253
|
+
justify: ["start", "center", "end", "stretch"]
|
|
254
|
+
},
|
|
255
|
+
flags: {
|
|
256
|
+
asChild: "Render as a child element (polymorphic)"
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
// ── typography ─────────────────────────────────────────────────────────────
|
|
260
|
+
Text: {
|
|
261
|
+
summary: "The typography primitive \u2014 renders a <p> by default; use asChild for semantics.",
|
|
262
|
+
props: {
|
|
263
|
+
size: ["sm", "base", "lg", "xl", "2xl", "3xl", "4xl", "5xl", "6xl"],
|
|
264
|
+
weight: ["light", "regular", "medium", "semibold", "bold"],
|
|
265
|
+
tone: ["default", "muted", "subtle", "disabled", "inverse", "brand", "link", "gradient"],
|
|
266
|
+
align: ["left", "center", "right"],
|
|
267
|
+
variant: ["inline-code"]
|
|
268
|
+
},
|
|
269
|
+
flags: {
|
|
270
|
+
truncate: "Adds CSS truncation (single line ellipsis)",
|
|
271
|
+
mono: "Switches to monospace font",
|
|
272
|
+
span: "Render inline as a <span> instead of a block <p>",
|
|
273
|
+
asChild: "Render as the wrapped child element for semantic control"
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
H1: {
|
|
277
|
+
summary: "Semantic headings: H1\u2013H6 render the matching <h1>\u2013<h6> with a sensible size default per level; override size freely. (H2\u2026H6 share this API.)",
|
|
278
|
+
props: {
|
|
279
|
+
size: ["sm", "base", "lg", "xl", "2xl", "3xl", "4xl", "5xl", "6xl"],
|
|
280
|
+
weight: ["light", "regular", "medium", "semibold", "bold"],
|
|
281
|
+
tone: ["default", "muted", "subtle", "disabled", "inverse", "brand", "link"],
|
|
282
|
+
align: ["left", "center", "right"]
|
|
283
|
+
},
|
|
284
|
+
flags: {
|
|
285
|
+
truncate: "Adds CSS truncation"
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
// ── action ─────────────────────────────────────────────────────────────────
|
|
289
|
+
Button: {
|
|
290
|
+
summary: "The primary action element. Hierarchy is expressed through variant, never hue.",
|
|
291
|
+
props: {
|
|
292
|
+
variant: ["primary", "secondary", "tertiary", "link", "destructive"],
|
|
293
|
+
size: ["sm", "md", "lg", "icon"]
|
|
294
|
+
},
|
|
295
|
+
flags: {
|
|
296
|
+
asChild: "Render as a child element (e.g. router link)"
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
ButtonGroup: {
|
|
300
|
+
summary: "Joins a row of Buttons into a segmented control with shared edges and a single outer radius.",
|
|
301
|
+
flags: {
|
|
302
|
+
"children (Button elements)": "Any Button variant; the group collapses the seams"
|
|
303
|
+
}
|
|
304
|
+
},
|
|
305
|
+
ToggleButton: {
|
|
306
|
+
summary: "A pressable button inside ToggleButtonGroup that tracks selected state.",
|
|
307
|
+
flags: {
|
|
308
|
+
value: "String identifier used by ToggleButtonGroup to track selection"
|
|
309
|
+
}
|
|
310
|
+
},
|
|
311
|
+
ToggleButtonGroup: {
|
|
312
|
+
summary: 'Segmented control; type="single" allows one selection, type="multiple" allows many.',
|
|
313
|
+
props: {
|
|
314
|
+
type: ["single", "multiple"]
|
|
315
|
+
}
|
|
316
|
+
},
|
|
317
|
+
Link: {
|
|
318
|
+
summary: "An inline anchor styled in the link color. Use asChild to wrap a router link.",
|
|
319
|
+
props: {
|
|
320
|
+
underline: ["hover", "always", "none"]
|
|
321
|
+
},
|
|
322
|
+
flags: {
|
|
323
|
+
asChild: "Render as a child element, keeping Link styling"
|
|
324
|
+
}
|
|
325
|
+
},
|
|
326
|
+
// ── display / status ───────────────────────────────────────────────────────
|
|
327
|
+
Badge: {
|
|
328
|
+
summary: "A static label pill for status, category, or count. Non-interactive.",
|
|
329
|
+
props: {
|
|
330
|
+
variant: ["brand", "subtle", "neutral", "outline", "success", "warning", "error", "info"],
|
|
331
|
+
size: ["sm", "md"]
|
|
332
|
+
}
|
|
333
|
+
},
|
|
334
|
+
Chip: {
|
|
335
|
+
summary: "An interactive pill for filtering or selection. Distinct from Badge. Pass onDelete for a removable chip.",
|
|
336
|
+
props: {
|
|
337
|
+
size: ["sm", "md"]
|
|
338
|
+
},
|
|
339
|
+
flags: {
|
|
340
|
+
selected: "Boolean \u2014 shows the neutral selected (inverse) fill when true",
|
|
341
|
+
clickable: "Set implicitly when onClick is provided",
|
|
342
|
+
onDelete: "Renders a trailing \u2715 dismiss button",
|
|
343
|
+
disabled: "Dims and disables interaction",
|
|
344
|
+
icon: "Optional leading ReactNode (e.g. an icon)"
|
|
345
|
+
}
|
|
346
|
+
},
|
|
347
|
+
Alert: {
|
|
348
|
+
summary: "An inline status message. Icon carries the hue; body text stays neutral.",
|
|
349
|
+
props: {
|
|
350
|
+
variant: ["neutral", "info", "success", "warning", "error"]
|
|
351
|
+
},
|
|
352
|
+
flags: {
|
|
353
|
+
title: "Heading (ReactNode)",
|
|
354
|
+
description: "Body text (ReactNode)",
|
|
355
|
+
icon: "Leading status icon (a lucide element)",
|
|
356
|
+
actions: "Trailing actions, typically buttons (ReactNode)"
|
|
357
|
+
}
|
|
358
|
+
},
|
|
359
|
+
Avatar: {
|
|
360
|
+
summary: "A circular image or fallback initials display. The fallback text scales with size.",
|
|
361
|
+
props: {
|
|
362
|
+
size: ["sm", "md", "lg", "xl"]
|
|
363
|
+
},
|
|
364
|
+
flags: {
|
|
365
|
+
"AvatarImage src": "Image URL; falls back to AvatarFallback when unresolvable",
|
|
366
|
+
"AvatarFallback": "Text shown while image loads or if it fails"
|
|
367
|
+
}
|
|
368
|
+
},
|
|
369
|
+
// ── surface / container ────────────────────────────────────────────────────
|
|
370
|
+
Card: {
|
|
371
|
+
summary: "A Box preset with surface background, default border, 2xl radius, and sm shadow. Accepts all Box props except className/style.",
|
|
372
|
+
props: {
|
|
373
|
+
padding: layoutVocabulary.spacing,
|
|
374
|
+
paddingX: layoutVocabulary.spacing,
|
|
375
|
+
paddingY: layoutVocabulary.spacing,
|
|
376
|
+
background: layoutVocabulary.background,
|
|
377
|
+
border: layoutVocabulary.border,
|
|
378
|
+
borderRadius: layoutVocabulary.borderRadius,
|
|
379
|
+
boxShadow: layoutVocabulary.boxShadow
|
|
380
|
+
},
|
|
381
|
+
flags: {
|
|
382
|
+
title: "Heading (ReactNode)",
|
|
383
|
+
description: "Sub-heading body text (ReactNode)",
|
|
384
|
+
headerAction: "Trailing header content opposite the title, e.g. a Badge (ReactNode)",
|
|
385
|
+
footer: "Footer content, typically buttons (ReactNode)",
|
|
386
|
+
children: "Card body content"
|
|
387
|
+
}
|
|
388
|
+
},
|
|
389
|
+
// ── form fields ────────────────────────────────────────────────────────────
|
|
390
|
+
Input: {
|
|
391
|
+
summary: "A styled text input. Pass standard HTML input props (type, placeholder, disabled, \u2026).",
|
|
392
|
+
flags: {
|
|
393
|
+
type: "HTML input type (text, email, password, number, \u2026)",
|
|
394
|
+
placeholder: "Placeholder text",
|
|
395
|
+
disabled: "Dims and disables the field",
|
|
396
|
+
invalid: "Error state \u2014 red outline + aria-invalid (usually set by FormField)"
|
|
397
|
+
}
|
|
398
|
+
},
|
|
399
|
+
Textarea: {
|
|
400
|
+
summary: "A styled multiline text input. Pass standard HTML textarea props.",
|
|
401
|
+
flags: {
|
|
402
|
+
placeholder: "Placeholder text",
|
|
403
|
+
disabled: "Dims and disables the field",
|
|
404
|
+
rows: "Number of visible text rows",
|
|
405
|
+
invalid: "Error state \u2014 red outline + aria-invalid (usually set by FormField)"
|
|
406
|
+
}
|
|
407
|
+
},
|
|
408
|
+
Label: {
|
|
409
|
+
summary: "A form label that connects to a field via htmlFor (Radix Label under the hood)."
|
|
410
|
+
},
|
|
411
|
+
FormField: {
|
|
412
|
+
summary: "Renders a labelled control and wires the label, description, required marker, and error state with correct ids and ARIA. Renders an Input by default; pass a child to wrap Textarea/Select/Autocomplete. The on-system way to build accessible forms.",
|
|
413
|
+
flags: {
|
|
414
|
+
label: "Field label (ReactNode); rendered as a <Label> bound to the control",
|
|
415
|
+
description: "Helper text below the label \u2014 normal foreground, meant to be read",
|
|
416
|
+
placeholder: "Placeholder forwarded onto the control (a prop on the child wins)",
|
|
417
|
+
type: "Input type for the default control (text, email, password, \u2026)",
|
|
418
|
+
error: "Error message (ReactNode); its presence sets the control to invalid",
|
|
419
|
+
required: "Adds a required marker and aria-required",
|
|
420
|
+
children: "Escape hatch \u2014 a non-Input control to wrap; omit to render an Input"
|
|
421
|
+
}
|
|
422
|
+
},
|
|
423
|
+
Select: {
|
|
424
|
+
summary: "A styled dropdown select. Pass an options array, or compose the primitives for groups/labels/custom triggers.",
|
|
425
|
+
flags: {
|
|
426
|
+
options: "Array of { value: string; label: ReactNode; disabled?: boolean } \u2014 the props API",
|
|
427
|
+
placeholder: "Placeholder shown before a value is chosen",
|
|
428
|
+
value: "Controlled value",
|
|
429
|
+
onValueChange: "Callback with the selected value",
|
|
430
|
+
disabled: "Disables the trigger",
|
|
431
|
+
invalid: "Error state \u2014 red trigger outline + aria-invalid (usually set by FormField)",
|
|
432
|
+
"SelectTrigger": "Escape hatch: the visible trigger element",
|
|
433
|
+
"SelectContent": "Escape hatch: the dropdown panel",
|
|
434
|
+
"SelectItem": "Escape hatch: an individual option (value, children)",
|
|
435
|
+
"SelectGroup": "Escape hatch: groups items under a SelectLabel",
|
|
436
|
+
"SelectLabel": "Escape hatch: a non-selectable group heading",
|
|
437
|
+
"SelectValue": "Escape hatch: renders the current value inside SelectTrigger"
|
|
438
|
+
}
|
|
439
|
+
},
|
|
440
|
+
Autocomplete: {
|
|
441
|
+
summary: "A searchable single-select combobox with keyboard navigation.",
|
|
442
|
+
flags: {
|
|
443
|
+
options: "Array of { value: string; label: string }",
|
|
444
|
+
value: "Controlled current value (string | null)",
|
|
445
|
+
onValueChange: "Called with the new value or null on clear",
|
|
446
|
+
placeholder: "Input placeholder text",
|
|
447
|
+
emptyText: "Message shown when filter yields no results",
|
|
448
|
+
disabled: "Dims and disables the field",
|
|
449
|
+
invalid: "Error state \u2014 red field outline + aria-invalid (usually set by FormField)"
|
|
450
|
+
}
|
|
451
|
+
},
|
|
452
|
+
MultiSelect: {
|
|
453
|
+
summary: "A searchable multi-value combobox. Selected values render as removable tags; the dropdown filters as you type. Controlled via value/onValueChange.",
|
|
454
|
+
flags: {
|
|
455
|
+
options: "Array of { value: string; label: string }",
|
|
456
|
+
value: "Controlled selected values (string[])",
|
|
457
|
+
onValueChange: "Called with the new value array",
|
|
458
|
+
placeholder: "Field placeholder when nothing is selected",
|
|
459
|
+
emptyText: "Message shown when the filter yields no results",
|
|
460
|
+
disabled: "Dims and disables the field",
|
|
461
|
+
invalid: "Error state \u2014 red outline + aria-invalid (usually set by FormField)"
|
|
462
|
+
}
|
|
463
|
+
},
|
|
464
|
+
DatePicker: {
|
|
465
|
+
summary: "A single-date field: a styled trigger that opens the Calendar in a Popover. Controlled via value/onValueChange. For ranges, use Calendar directly.",
|
|
466
|
+
flags: {
|
|
467
|
+
value: "Selected Date (or undefined)",
|
|
468
|
+
onValueChange: "Called with the chosen Date (or undefined)",
|
|
469
|
+
placeholder: "Trigger text before a date is chosen",
|
|
470
|
+
disabled: "Dims and disables the trigger",
|
|
471
|
+
invalid: "Error state \u2014 red outline + aria-invalid (usually set by FormField)",
|
|
472
|
+
formatOptions: "Intl.DateTimeFormatOptions for the displayed value"
|
|
473
|
+
}
|
|
474
|
+
},
|
|
475
|
+
Calendar: {
|
|
476
|
+
summary: 'The date grid (react-day-picker), token-styled with no vendor CSS. Pass any react-day-picker prop \u2014 mode="single|range|multiple", selected, onSelect.',
|
|
477
|
+
props: {
|
|
478
|
+
mode: ["single", "multiple", "range"]
|
|
479
|
+
},
|
|
480
|
+
flags: {
|
|
481
|
+
selected: "Selected date(s) \u2014 shape depends on mode",
|
|
482
|
+
onSelect: "Selection callback \u2014 shape depends on mode",
|
|
483
|
+
showOutsideDays: "Render days from adjacent months (default true)"
|
|
484
|
+
}
|
|
485
|
+
},
|
|
486
|
+
Command: {
|
|
487
|
+
summary: "A fast, filterable command menu / palette (cmdk). Compose Command > CommandInput + CommandList (CommandEmpty/CommandGroup/CommandItem/CommandSeparator), or use CommandDialog for a \u2318K palette.",
|
|
488
|
+
flags: {
|
|
489
|
+
"CommandInput": "The search field",
|
|
490
|
+
"CommandList": "Scrollable results region",
|
|
491
|
+
"CommandEmpty": "Shown when nothing matches",
|
|
492
|
+
"CommandGroup": "A labelled group (heading prop)",
|
|
493
|
+
"CommandItem": "A selectable row (onSelect, value)",
|
|
494
|
+
"CommandSeparator": "A divider between groups",
|
|
495
|
+
"CommandShortcut": "Trailing keyboard hint",
|
|
496
|
+
"CommandDialog": "The palette in a centered overlay (open, onOpenChange)"
|
|
497
|
+
}
|
|
498
|
+
},
|
|
499
|
+
Checkbox: {
|
|
500
|
+
summary: "A binary toggle. Pair with Label for an accessible label.",
|
|
501
|
+
flags: {
|
|
502
|
+
checked: "Controlled checked state",
|
|
503
|
+
onCheckedChange: "Callback with the new boolean",
|
|
504
|
+
disabled: "Disables the checkbox"
|
|
505
|
+
}
|
|
506
|
+
},
|
|
507
|
+
RadioGroup: {
|
|
508
|
+
summary: "A group of mutually exclusive RadioGroupItem options.",
|
|
509
|
+
flags: {
|
|
510
|
+
value: "Controlled value",
|
|
511
|
+
onValueChange: "Callback with the selected value",
|
|
512
|
+
"RadioGroupItem value": "The string value for each item; pair with Label"
|
|
513
|
+
}
|
|
514
|
+
},
|
|
515
|
+
Switch: {
|
|
516
|
+
summary: "A toggle switch (on/off). Pair with Label for an accessible label.",
|
|
517
|
+
flags: {
|
|
518
|
+
checked: "Controlled checked state",
|
|
519
|
+
onCheckedChange: "Callback with the new boolean",
|
|
520
|
+
disabled: "Disables the switch"
|
|
521
|
+
}
|
|
522
|
+
},
|
|
523
|
+
Slider: {
|
|
524
|
+
summary: "A range slider for numeric input.",
|
|
525
|
+
flags: {
|
|
526
|
+
value: "Controlled value array e.g. [50]",
|
|
527
|
+
onValueChange: "Callback with the new value array",
|
|
528
|
+
min: "Minimum value (default 0)",
|
|
529
|
+
max: "Maximum value (default 100)",
|
|
530
|
+
step: "Step increment",
|
|
531
|
+
disabled: "Disables the slider"
|
|
532
|
+
}
|
|
533
|
+
},
|
|
534
|
+
// ── navigation / disclosure ────────────────────────────────────────────────
|
|
535
|
+
Tabs: {
|
|
536
|
+
summary: "Tabbed content switcher. Pass an items array, or compose TabsList/TabsTrigger/TabsContent by hand.",
|
|
537
|
+
flags: {
|
|
538
|
+
items: "Array of { value, label, content, disabled? } \u2014 the props API",
|
|
539
|
+
defaultValue: "Initially active tab value",
|
|
540
|
+
value: "Controlled active tab value",
|
|
541
|
+
onValueChange: "Callback with new tab value",
|
|
542
|
+
"TabsList / TabsTrigger / TabsContent": "Escape hatch primitives for hand-composition"
|
|
543
|
+
}
|
|
544
|
+
},
|
|
545
|
+
Accordion: {
|
|
546
|
+
summary: 'Vertically stacked collapsible sections. Pass an items array, or compose the primitives. type="single" or "multiple".',
|
|
547
|
+
props: {
|
|
548
|
+
type: ["single", "multiple"]
|
|
549
|
+
},
|
|
550
|
+
flags: {
|
|
551
|
+
items: "Array of { value, trigger, content, disabled? } \u2014 the props API",
|
|
552
|
+
collapsible: 'When type="single", allows deselecting the open item',
|
|
553
|
+
"AccordionItem / AccordionTrigger / AccordionContent": "Escape hatch primitives for hand-composition"
|
|
554
|
+
}
|
|
555
|
+
},
|
|
556
|
+
Breadcrumb: {
|
|
557
|
+
summary: "Navigation trail. Pass an items array; the last item is the current (non-link) page.",
|
|
558
|
+
flags: {
|
|
559
|
+
items: "Array of { label: ReactNode, href?: string }; the final item renders as the current page",
|
|
560
|
+
separator: "Divider between crumbs; defaults to a ChevronRight (ReactNode)"
|
|
561
|
+
}
|
|
562
|
+
},
|
|
563
|
+
Pagination: {
|
|
564
|
+
summary: "Controlled page navigation with previous/next buttons, a window of sibling pages, and ellipses.",
|
|
565
|
+
flags: {
|
|
566
|
+
page: "1-based current page number",
|
|
567
|
+
count: "Total number of pages",
|
|
568
|
+
onPageChange: "Callback with the new page number",
|
|
569
|
+
siblingCount: "Number of pages to show on each side of the current (default 1)"
|
|
570
|
+
}
|
|
571
|
+
},
|
|
572
|
+
Stepper: {
|
|
573
|
+
summary: "Linear progress through a sequence of named steps.",
|
|
574
|
+
props: {
|
|
575
|
+
orientation: ["horizontal", "vertical"]
|
|
576
|
+
},
|
|
577
|
+
flags: {
|
|
578
|
+
steps: "Array of { label: string; description?: string }",
|
|
579
|
+
activeStep: "0-based index of the current step"
|
|
580
|
+
}
|
|
581
|
+
},
|
|
582
|
+
// ── overlays ───────────────────────────────────────────────────────────────
|
|
583
|
+
Dialog: {
|
|
584
|
+
summary: "A convenience overlay with title, description, and footer actions \u2014 built on Modal.",
|
|
585
|
+
props: {
|
|
586
|
+
size: ["sm", "md", "lg", "xl"]
|
|
587
|
+
},
|
|
588
|
+
flags: {
|
|
589
|
+
trigger: "The element that opens the dialog",
|
|
590
|
+
title: "Heading text (required)",
|
|
591
|
+
description: "Body text below the title",
|
|
592
|
+
actions: "ReactNode for the footer, typically buttons",
|
|
593
|
+
open: "Controlled open state",
|
|
594
|
+
onOpenChange: "Callback when open state changes"
|
|
595
|
+
}
|
|
596
|
+
},
|
|
597
|
+
Modal: {
|
|
598
|
+
summary: "Low-level composable overlay (Radix Dialog). Use Dialog for the common case.",
|
|
599
|
+
props: {
|
|
600
|
+
size: ["sm", "md", "lg", "xl"]
|
|
601
|
+
},
|
|
602
|
+
flags: {
|
|
603
|
+
"ModalTrigger": "The trigger element; use asChild to keep your button",
|
|
604
|
+
"ModalContent": "The panel; size prop sets max-width",
|
|
605
|
+
"ModalClose": "Programmatic close trigger; use asChild",
|
|
606
|
+
hideClose: "Hide the default corner \u2715 on ModalContent"
|
|
607
|
+
}
|
|
608
|
+
},
|
|
609
|
+
Popover: {
|
|
610
|
+
summary: "A small floating panel anchored to a trigger. Compose: Popover > PopoverTrigger + PopoverContent.",
|
|
611
|
+
flags: {
|
|
612
|
+
"PopoverTrigger asChild": "Attach the trigger to your own element",
|
|
613
|
+
"PopoverContent align": "Alignment relative to the trigger (start|center|end)",
|
|
614
|
+
"PopoverContent sideOffset": "Distance from the trigger in px (default 6)"
|
|
615
|
+
}
|
|
616
|
+
},
|
|
617
|
+
Sheet: {
|
|
618
|
+
summary: "A slide-in drawer anchored to a screen edge.",
|
|
619
|
+
props: {
|
|
620
|
+
side: ["top", "bottom", "left", "right"]
|
|
621
|
+
},
|
|
622
|
+
flags: {
|
|
623
|
+
"SheetTrigger asChild": "Attach the trigger to your own element",
|
|
624
|
+
"SheetContent title": "Accessible sheet title (ReactNode)",
|
|
625
|
+
"SheetContent description": "Supporting body text (ReactNode)",
|
|
626
|
+
"SheetContent footer": "Action row pinned to the bottom (ReactNode)",
|
|
627
|
+
"SheetClose asChild": "Programmatic close trigger"
|
|
628
|
+
}
|
|
629
|
+
},
|
|
630
|
+
DropdownMenu: {
|
|
631
|
+
summary: "A contextual menu anchored to a trigger. Pass trigger + items for the common case, or compose the primitives for checkboxes, radios, and sub-menus.",
|
|
632
|
+
flags: {
|
|
633
|
+
trigger: "The element that opens the menu (rendered via asChild)",
|
|
634
|
+
items: 'Array of { label, icon?, shortcut?, onSelect?, disabled? } | { type: "separator" } | { type: "label", label } \u2014 the props API',
|
|
635
|
+
"DropdownMenuTrigger asChild": "Escape hatch: use your own element as the trigger",
|
|
636
|
+
"DropdownMenuContent": "Escape hatch: the floating menu panel",
|
|
637
|
+
"DropdownMenuItem": "Escape hatch: a clickable item; inset adds left padding",
|
|
638
|
+
"DropdownMenuCheckboxItem": "Escape hatch: a checkable item",
|
|
639
|
+
"DropdownMenuRadioGroup / DropdownMenuRadioItem": "Escape hatch: radio group + items",
|
|
640
|
+
"DropdownMenuLabel / DropdownMenuSeparator / DropdownMenuShortcut": "Escape hatch: label, divider, shortcut",
|
|
641
|
+
"DropdownMenuSub / DropdownMenuSubTrigger / DropdownMenuSubContent": "Escape hatch: nested sub-menus"
|
|
642
|
+
}
|
|
643
|
+
},
|
|
644
|
+
Tooltip: {
|
|
645
|
+
summary: "A floating label on hover/focus. Wrap with TooltipProvider at the app root.",
|
|
646
|
+
flags: {
|
|
647
|
+
"TooltipProvider": "Render once near the app root to share delay",
|
|
648
|
+
"TooltipTrigger asChild": "Attach the tooltip to your own element",
|
|
649
|
+
"TooltipContent": "The tooltip bubble; sideOffset adjusts distance"
|
|
650
|
+
}
|
|
651
|
+
},
|
|
652
|
+
// ── feedback / progress ────────────────────────────────────────────────────
|
|
653
|
+
Progress: {
|
|
654
|
+
summary: "A horizontal progress bar. value is 0\u2013100.",
|
|
655
|
+
flags: {
|
|
656
|
+
value: "Number 0\u2013100 representing completion percentage",
|
|
657
|
+
indeterminate: "Looping animation for work of unknown duration; ignores value"
|
|
658
|
+
}
|
|
659
|
+
},
|
|
660
|
+
Skeleton: {
|
|
661
|
+
summary: "An animated placeholder while content loads. Size with width/height via className on Box.",
|
|
662
|
+
flags: {
|
|
663
|
+
"className (via Box wrapper)": "Use Box className to size the skeleton"
|
|
664
|
+
}
|
|
665
|
+
},
|
|
666
|
+
Toast: {
|
|
667
|
+
summary: "Transient status notifications. Call toast({...}) imperatively; render <Toaster /> once near root.",
|
|
668
|
+
props: {
|
|
669
|
+
variant: ["neutral", "success", "error", "warning", "info"]
|
|
670
|
+
},
|
|
671
|
+
flags: {
|
|
672
|
+
title: "Heading text",
|
|
673
|
+
description: "Body text",
|
|
674
|
+
duration: "Auto-dismiss time in ms (default 5000; 0 = permanent)",
|
|
675
|
+
action: "ReactNode action slot inside the toast",
|
|
676
|
+
"Toaster": "Place once near the app root to render active toasts"
|
|
677
|
+
}
|
|
678
|
+
},
|
|
679
|
+
// ── data display ───────────────────────────────────────────────────────────
|
|
680
|
+
Table: {
|
|
681
|
+
summary: "A semantic table. Pass columns + data for the common case, or compose the primitives for full control.",
|
|
682
|
+
flags: {
|
|
683
|
+
columns: "Array of { header, accessor: key | (row) => ReactNode, align? } \u2014 the props API",
|
|
684
|
+
data: "Array of row objects rendered against columns",
|
|
685
|
+
caption: "Caption rendered below the table (ReactNode)",
|
|
686
|
+
"TableHeader / TableBody / TableFooter": "Escape hatch: thead / tbody / tfoot wrappers",
|
|
687
|
+
"TableRow": "Escape hatch: tr with hover and selected states",
|
|
688
|
+
"TableHead / TableCell": 'Escape hatch: th / td; both take align="left|center|right"',
|
|
689
|
+
"TableCaption": "Escape hatch: caption rendered below the table"
|
|
690
|
+
}
|
|
691
|
+
},
|
|
692
|
+
DataTable: {
|
|
693
|
+
summary: "The batteries-included table: column sorting, row selection, and optional client-side pagination, built on the Table primitives. Pass columns + data + rowKey.",
|
|
694
|
+
flags: {
|
|
695
|
+
columns: "Array of { header, accessor, align?, sortable?, sortAccessor? }",
|
|
696
|
+
data: "Array of row objects",
|
|
697
|
+
rowKey: "(row) => string \u2014 stable id for selection and keys (required)",
|
|
698
|
+
selectable: "Adds a select-all + per-row checkbox column",
|
|
699
|
+
selected: "Controlled selection (array of ids); omit for uncontrolled",
|
|
700
|
+
onSelectedChange: "Callback with the new selected id array",
|
|
701
|
+
pageSize: "Enable client-side pagination at this page size",
|
|
702
|
+
caption: "Caption rendered below the table",
|
|
703
|
+
emptyState: "Content shown when there are no rows"
|
|
704
|
+
}
|
|
705
|
+
},
|
|
706
|
+
Chart: {
|
|
707
|
+
summary: "A token-driven ECharts surface. Pass a standard ECharts option; palette, axes, tooltip, and fonts are themed from the live --wf-* tokens and re-skin with the brand and color mode automatically. Auto-resizes.",
|
|
708
|
+
flags: {
|
|
709
|
+
option: "A standard ECharts option object (xAxis/yAxis/series/\u2026)",
|
|
710
|
+
height: "Pixel or CSS height; width fills the container (default 320)",
|
|
711
|
+
notMerge: "Replace the previous option wholesale instead of merging (default true)",
|
|
712
|
+
onEvents: "Map of ECharts event name \u2192 handler"
|
|
713
|
+
}
|
|
714
|
+
},
|
|
715
|
+
// ── utility / decoration ──────────────────────────────────────────────────
|
|
716
|
+
Separator: {
|
|
717
|
+
summary: "A horizontal or vertical visual divider.",
|
|
718
|
+
props: {
|
|
719
|
+
orientation: ["horizontal", "vertical"]
|
|
720
|
+
}
|
|
721
|
+
},
|
|
722
|
+
CodeBlock: {
|
|
723
|
+
summary: "A syntax-highlighted code surface. Highlighting runs locally (prism-react-renderer, no API); the theme is built from --wf-* tokens, so it re-skins with mode and brand automatically.",
|
|
724
|
+
flags: {
|
|
725
|
+
code: "The source string to render",
|
|
726
|
+
language: "Prism language id (default 'tsx')",
|
|
727
|
+
filename: "Optional header filename, shown with the language label",
|
|
728
|
+
showLineNumbers: "Render a 1-based line-number gutter",
|
|
729
|
+
highlightLines: "Array of 1-based line numbers to emphasize",
|
|
730
|
+
wrap: "Soft-wrap long lines instead of scrolling",
|
|
731
|
+
maxHeight: "Max height before vertical scroll, e.g. '24rem'",
|
|
732
|
+
copyable: "Show the copy button (default true)"
|
|
733
|
+
}
|
|
734
|
+
},
|
|
735
|
+
// ── app chrome / layout ────────────────────────────────────────────────────
|
|
736
|
+
AppShell: {
|
|
737
|
+
summary: "The guaranteed-layout frame: fixed sidebar on desktop, Sheet drawer on mobile, sticky header, scrollable main. Pass header and sidebar as slots.",
|
|
738
|
+
flags: {
|
|
739
|
+
header: "Top-bar slot, typically an <AppBar>",
|
|
740
|
+
sidebar: "Side-navigation slot, typically a <SideNav>",
|
|
741
|
+
fullBleed: "Drop the default main padding for edge-to-edge content"
|
|
742
|
+
}
|
|
743
|
+
},
|
|
744
|
+
AppBar: {
|
|
745
|
+
summary: "The sticky top bar slot for AppShell: menu toggle, logo, title, and actions. Drop a <ModeToggle/> into actions for the light/dark/system switch.",
|
|
746
|
+
props: {
|
|
747
|
+
variant: ["glass", "solid"],
|
|
748
|
+
color: ["surface", "subtle", "inset"]
|
|
749
|
+
},
|
|
750
|
+
flags: {
|
|
751
|
+
title: "Optional title text",
|
|
752
|
+
logo: "Optional logo ReactNode",
|
|
753
|
+
actions: "Right-aligned action ReactNode(s) \u2014 e.g. a <ModeToggle/>",
|
|
754
|
+
onMenuClick: "Override the menu-button handler (defaults to toggling the nav)"
|
|
755
|
+
}
|
|
756
|
+
},
|
|
757
|
+
SideNav: {
|
|
758
|
+
summary: "The data-driven side navigation: pass a typed items tree (item | group | section | divider); renders active state, nesting, and mobile drawer integration.",
|
|
759
|
+
flags: {
|
|
760
|
+
items: "NavItem[] \u2014 the navigation tree (item/group/section/divider)",
|
|
761
|
+
activePath: "The currently active path, highlighted in the tree",
|
|
762
|
+
onNavigate: "Called with a path when a leaf is activated",
|
|
763
|
+
logo: "Optional logo ReactNode pinned to the nav header"
|
|
764
|
+
}
|
|
765
|
+
},
|
|
766
|
+
ThemeProvider: {
|
|
767
|
+
summary: "Provides color mode (light/dark/system) and optional runtime --wf-* token overrides for live re-skinning. Wrap the app once near the root.",
|
|
768
|
+
flags: {
|
|
769
|
+
defaultMode: "Initial color mode: light | dark | system (default system)",
|
|
770
|
+
tokens: "A --wf-* override map, or (mode) => map for mode-aware brand swaps",
|
|
771
|
+
persist: "Persist the chosen mode to localStorage (default true)"
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
};
|
|
775
|
+
|
|
776
|
+
// src/lib/use-media-query.ts
|
|
777
|
+
import * as React2 from "react";
|
|
778
|
+
function useMediaQuery(query) {
|
|
779
|
+
const subscribe = React2.useCallback(
|
|
780
|
+
(onChange) => {
|
|
781
|
+
const mq = window.matchMedia(query);
|
|
782
|
+
mq.addEventListener("change", onChange);
|
|
783
|
+
return () => mq.removeEventListener("change", onChange);
|
|
784
|
+
},
|
|
785
|
+
[query]
|
|
786
|
+
);
|
|
787
|
+
return React2.useSyncExternalStore(
|
|
788
|
+
subscribe,
|
|
789
|
+
() => window.matchMedia(query).matches,
|
|
790
|
+
() => false
|
|
791
|
+
);
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
// src/lib/recipes.ts
|
|
795
|
+
var focusRing = "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background";
|
|
796
|
+
var focusRingInset = "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring";
|
|
797
|
+
var focusRingField = "focus-visible:outline-none focus-visible:border-focus focus-visible:ring-2 focus-visible:ring-ring";
|
|
798
|
+
var floatingPanel = "z-50 rounded-lg border border-border bg-surface text-primary shadow-md outline-none";
|
|
799
|
+
var menuItem = "relative flex cursor-pointer select-none items-center rounded-md text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50";
|
|
800
|
+
var overlayBackdrop = "fixed inset-0 z-50 bg-overlay animate-fade-in";
|
|
801
|
+
var dismissButton = "absolute right-4 top-4 rounded-md p-1 text-secondary opacity-70 transition-opacity hover:opacity-100";
|
|
802
|
+
|
|
803
|
+
// src/components/button.tsx
|
|
804
|
+
import * as React3 from "react";
|
|
805
|
+
import { Slot as Slot2 } from "@radix-ui/react-slot";
|
|
806
|
+
import { cva as cva2 } from "class-variance-authority";
|
|
807
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
808
|
+
var buttonVariants = cva2(
|
|
809
|
+
"inline-flex items-center justify-center gap-nbsp whitespace-nowrap rounded-lg font-medium transition-colors duration-normal " + focusRing + " disabled:pointer-events-none disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0",
|
|
810
|
+
{
|
|
811
|
+
variants: {
|
|
812
|
+
variant: {
|
|
813
|
+
primary: "bg-brand text-contrast hover:bg-brand-hover active:bg-brand-active shadow-xs",
|
|
814
|
+
secondary: "border-[length:var(--wf-border-width-control)] border-strong bg-surface text-primary hover:bg-surface-subtle active:bg-surface-inset",
|
|
815
|
+
tertiary: "text-primary hover:bg-surface-inset active:bg-surface-inset",
|
|
816
|
+
link: "text-link underline-offset-4 hover:underline hover:text-link-hover",
|
|
817
|
+
destructive: "border-[length:var(--wf-border-width-control)] border-error bg-surface text-error hover:bg-error-subtle active:bg-error-muted"
|
|
818
|
+
},
|
|
819
|
+
size: {
|
|
820
|
+
sm: "h-8 px-3 text-sm",
|
|
821
|
+
md: "h-10 px-4 text-sm",
|
|
822
|
+
lg: "h-12 px-6 text-base",
|
|
823
|
+
icon: "h-10 w-10"
|
|
824
|
+
}
|
|
825
|
+
},
|
|
826
|
+
defaultVariants: { variant: "primary", size: "md" }
|
|
827
|
+
}
|
|
828
|
+
);
|
|
829
|
+
var Button = React3.forwardRef(
|
|
830
|
+
({ variant, size, asChild = false, ...props }, ref) => {
|
|
831
|
+
const Comp = asChild ? Slot2 : "button";
|
|
832
|
+
return /* @__PURE__ */ jsx2(Comp, { ref, className: cn(buttonVariants({ variant, size })), ...props });
|
|
833
|
+
}
|
|
834
|
+
);
|
|
835
|
+
Button.displayName = "Button";
|
|
836
|
+
|
|
837
|
+
// src/components/button-group.tsx
|
|
838
|
+
import * as React4 from "react";
|
|
839
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
840
|
+
var ButtonGroup = React4.forwardRef(
|
|
841
|
+
({ ...props }, ref) => /* @__PURE__ */ jsx3(
|
|
842
|
+
"div",
|
|
843
|
+
{
|
|
844
|
+
ref,
|
|
845
|
+
role: "group",
|
|
846
|
+
className: cn(
|
|
847
|
+
"inline-flex isolate",
|
|
848
|
+
"[&>*]:rounded-none [&>*:first-child]:rounded-l-lg [&>*:last-child]:rounded-r-lg",
|
|
849
|
+
"[&>*:not(:first-child)]:-ml-px hover:[&>*]:z-10 focus-visible:[&>*]:z-10"
|
|
850
|
+
),
|
|
851
|
+
...props
|
|
852
|
+
}
|
|
853
|
+
)
|
|
854
|
+
);
|
|
855
|
+
ButtonGroup.displayName = "ButtonGroup";
|
|
856
|
+
|
|
857
|
+
// src/components/toggle-button.tsx
|
|
858
|
+
import * as React5 from "react";
|
|
859
|
+
import { cva as cva3 } from "class-variance-authority";
|
|
860
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
861
|
+
var ToggleGroupContext = React5.createContext(null);
|
|
862
|
+
function ToggleButtonGroup({
|
|
863
|
+
type = "single",
|
|
864
|
+
value,
|
|
865
|
+
onValueChange,
|
|
866
|
+
children,
|
|
867
|
+
...props
|
|
868
|
+
}) {
|
|
869
|
+
const contextValue = React5.useMemo(() => {
|
|
870
|
+
if (type === "multiple") {
|
|
871
|
+
const values = value ?? [];
|
|
872
|
+
return {
|
|
873
|
+
isSelected: (itemValue) => values.includes(itemValue),
|
|
874
|
+
toggle: (itemValue) => onValueChange(
|
|
875
|
+
values.includes(itemValue) ? values.filter((existing) => existing !== itemValue) : [...values, itemValue]
|
|
876
|
+
)
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
return {
|
|
880
|
+
isSelected: (itemValue) => value === itemValue,
|
|
881
|
+
toggle: (itemValue) => onValueChange(value === itemValue ? null : itemValue)
|
|
882
|
+
};
|
|
883
|
+
}, [type, value, onValueChange]);
|
|
884
|
+
return /* @__PURE__ */ jsx4(ToggleGroupContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx4(
|
|
885
|
+
"div",
|
|
886
|
+
{
|
|
887
|
+
role: "group",
|
|
888
|
+
className: cn(
|
|
889
|
+
"inline-flex isolate",
|
|
890
|
+
"[&>*]:rounded-none [&>*:first-child]:rounded-l-lg [&>*:last-child]:rounded-r-lg [&>*:not(:first-child)]:-ml-px"
|
|
891
|
+
),
|
|
892
|
+
...props,
|
|
893
|
+
children
|
|
894
|
+
}
|
|
895
|
+
) });
|
|
896
|
+
}
|
|
897
|
+
var toggleButton = cva3(
|
|
898
|
+
"inline-flex h-10 items-center justify-center gap-2 border border-strong px-4 text-sm font-medium transition-colors disabled:opacity-50 disabled:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 " + focusRingInset,
|
|
899
|
+
{
|
|
900
|
+
variants: {
|
|
901
|
+
selected: {
|
|
902
|
+
true: "z-10 bg-surface-inverse text-inverse border-transparent",
|
|
903
|
+
false: "bg-surface text-secondary hover:bg-surface-subtle hover:text-primary"
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
);
|
|
908
|
+
var ToggleButton = React5.forwardRef(
|
|
909
|
+
({ value, onClick, ...props }, ref) => {
|
|
910
|
+
const group = React5.useContext(ToggleGroupContext);
|
|
911
|
+
const selected = group?.isSelected(value) ?? false;
|
|
912
|
+
return /* @__PURE__ */ jsx4(
|
|
913
|
+
"button",
|
|
914
|
+
{
|
|
915
|
+
ref,
|
|
916
|
+
type: "button",
|
|
917
|
+
"aria-pressed": selected,
|
|
918
|
+
className: cn(toggleButton({ selected })),
|
|
919
|
+
onClick: (event) => {
|
|
920
|
+
group?.toggle(value);
|
|
921
|
+
onClick?.(event);
|
|
922
|
+
},
|
|
923
|
+
...props
|
|
924
|
+
}
|
|
925
|
+
);
|
|
926
|
+
}
|
|
927
|
+
);
|
|
928
|
+
ToggleButton.displayName = "ToggleButton";
|
|
929
|
+
|
|
930
|
+
// src/components/badge.tsx
|
|
931
|
+
import { cva as cva4 } from "class-variance-authority";
|
|
932
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
933
|
+
var badgeVariants = cva4(
|
|
934
|
+
"inline-flex items-center gap-1 rounded-lg border font-medium transition-colors [&_svg]:size-3",
|
|
935
|
+
{
|
|
936
|
+
variants: {
|
|
937
|
+
variant: {
|
|
938
|
+
brand: "border-transparent bg-brand text-contrast",
|
|
939
|
+
subtle: "border-transparent bg-brand-subtle text-brand",
|
|
940
|
+
neutral: "border-border bg-surface-subtle text-secondary",
|
|
941
|
+
outline: "border-strong text-primary",
|
|
942
|
+
success: "border-transparent bg-success-subtle text-primary",
|
|
943
|
+
warning: "border-transparent bg-warning-subtle text-primary",
|
|
944
|
+
error: "border-transparent bg-error-subtle text-primary",
|
|
945
|
+
info: "border-transparent bg-info-subtle text-primary"
|
|
946
|
+
},
|
|
947
|
+
size: {
|
|
948
|
+
sm: "px-2 py-0.5 text-sm",
|
|
949
|
+
md: "px-2.5 py-0.5 text-sm"
|
|
950
|
+
}
|
|
951
|
+
},
|
|
952
|
+
defaultVariants: { variant: "neutral", size: "md" }
|
|
953
|
+
}
|
|
954
|
+
);
|
|
955
|
+
function Badge({ variant, size, ...props }) {
|
|
956
|
+
return /* @__PURE__ */ jsx5("span", { className: cn(badgeVariants({ variant, size })), ...props });
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
// src/components/chip.tsx
|
|
960
|
+
import * as React6 from "react";
|
|
961
|
+
import { X } from "lucide-react";
|
|
962
|
+
import { cva as cva5 } from "class-variance-authority";
|
|
963
|
+
import { jsx as jsx6, jsxs } from "react/jsx-runtime";
|
|
964
|
+
var chip = cva5(
|
|
965
|
+
"inline-flex items-center gap-1.5 rounded-full border text-sm font-medium transition-colors [&_svg]:size-3.5 disabled:opacity-50 disabled:pointer-events-none",
|
|
966
|
+
{
|
|
967
|
+
variants: {
|
|
968
|
+
selected: {
|
|
969
|
+
true: "border-transparent bg-surface-inverse text-inverse",
|
|
970
|
+
false: "border-strong bg-surface text-primary hover:bg-surface-subtle"
|
|
971
|
+
},
|
|
972
|
+
clickable: { true: "cursor-pointer " + focusRingInset, false: "" },
|
|
973
|
+
size: {
|
|
974
|
+
sm: "px-2 py-0.5",
|
|
975
|
+
md: "px-3 py-1"
|
|
976
|
+
}
|
|
977
|
+
},
|
|
978
|
+
defaultVariants: { selected: false, clickable: false, size: "md" }
|
|
979
|
+
}
|
|
980
|
+
);
|
|
981
|
+
var Chip = React6.forwardRef(
|
|
982
|
+
({ selected, size, onClick, onDelete, disabled, icon, children, ...props }, ref) => {
|
|
983
|
+
const clickable = !!onClick;
|
|
984
|
+
const Comp = clickable ? "button" : "span";
|
|
985
|
+
return /* @__PURE__ */ jsxs(
|
|
986
|
+
Comp,
|
|
987
|
+
{
|
|
988
|
+
ref,
|
|
989
|
+
className: cn(chip({ selected, clickable, size })),
|
|
990
|
+
...clickable ? { type: "button", onClick, disabled, "aria-pressed": !!selected } : {},
|
|
991
|
+
...props,
|
|
992
|
+
children: [
|
|
993
|
+
icon,
|
|
994
|
+
children,
|
|
995
|
+
onDelete && /* @__PURE__ */ jsx6(
|
|
996
|
+
"button",
|
|
997
|
+
{
|
|
998
|
+
type: "button",
|
|
999
|
+
"aria-label": "Remove",
|
|
1000
|
+
disabled,
|
|
1001
|
+
onClick: (event) => {
|
|
1002
|
+
event.stopPropagation();
|
|
1003
|
+
onDelete();
|
|
1004
|
+
},
|
|
1005
|
+
className: cn("-mr-1 ml-0.5 rounded-full p-0.5 opacity-70 hover:opacity-100", focusRingInset),
|
|
1006
|
+
children: /* @__PURE__ */ jsx6(X, {})
|
|
1007
|
+
}
|
|
1008
|
+
)
|
|
1009
|
+
]
|
|
1010
|
+
}
|
|
1011
|
+
);
|
|
1012
|
+
}
|
|
1013
|
+
);
|
|
1014
|
+
Chip.displayName = "Chip";
|
|
1015
|
+
|
|
1016
|
+
// src/components/text.tsx
|
|
1017
|
+
import * as React7 from "react";
|
|
1018
|
+
import { Slot as Slot3 } from "@radix-ui/react-slot";
|
|
1019
|
+
import { cva as cva6 } from "class-variance-authority";
|
|
1020
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
1021
|
+
var text = cva6("", {
|
|
1022
|
+
variants: {
|
|
1023
|
+
size: {
|
|
1024
|
+
sm: "text-sm",
|
|
1025
|
+
base: "text-base",
|
|
1026
|
+
lg: "text-lg",
|
|
1027
|
+
xl: "text-xl",
|
|
1028
|
+
"2xl": "text-2xl",
|
|
1029
|
+
"3xl": "text-3xl",
|
|
1030
|
+
"4xl": "text-4xl",
|
|
1031
|
+
"5xl": "text-5xl",
|
|
1032
|
+
"6xl": "text-6xl"
|
|
1033
|
+
},
|
|
1034
|
+
weight: {
|
|
1035
|
+
light: "font-light",
|
|
1036
|
+
regular: "font-normal",
|
|
1037
|
+
medium: "font-medium",
|
|
1038
|
+
semibold: "font-semibold",
|
|
1039
|
+
bold: "font-bold"
|
|
1040
|
+
},
|
|
1041
|
+
tone: {
|
|
1042
|
+
default: "text-primary",
|
|
1043
|
+
muted: "text-secondary",
|
|
1044
|
+
subtle: "text-tertiary",
|
|
1045
|
+
disabled: "text-disabled",
|
|
1046
|
+
inverse: "text-inverse",
|
|
1047
|
+
brand: "text-brand",
|
|
1048
|
+
link: "text-link",
|
|
1049
|
+
// A brand→secondary gradient clipped to the glyphs — for hero/display type.
|
|
1050
|
+
gradient: "bg-gradient-to-r from-brand to-brand-secondary bg-clip-text text-transparent"
|
|
1051
|
+
},
|
|
1052
|
+
align: { left: "text-left", center: "text-center", right: "text-right" },
|
|
1053
|
+
truncate: { true: "truncate", false: "" },
|
|
1054
|
+
mono: { true: "font-mono", false: "" },
|
|
1055
|
+
// `variant="inline-code"` renders a <code> element styled as inline code:
|
|
1056
|
+
// monospace, a subtle fill, em-sized so it scales with the surrounding text.
|
|
1057
|
+
// Listed last so its text-size wins; no horizontal padding so it hugs the text.
|
|
1058
|
+
variant: {
|
|
1059
|
+
"inline-code": "rounded border border-subtle bg-surface-inset py-0.5 font-mono text-[0.875em] text-primary"
|
|
1060
|
+
}
|
|
1061
|
+
},
|
|
1062
|
+
defaultVariants: { size: "base", tone: "default" }
|
|
1063
|
+
});
|
|
1064
|
+
var Text = React7.forwardRef(
|
|
1065
|
+
({ asChild, span, size, weight, tone, align, truncate, mono, variant, ...props }, ref) => {
|
|
1066
|
+
const Comp = asChild ? Slot3 : span ? "span" : variant === "inline-code" ? "code" : "p";
|
|
1067
|
+
return /* @__PURE__ */ jsx7(
|
|
1068
|
+
Comp,
|
|
1069
|
+
{
|
|
1070
|
+
ref,
|
|
1071
|
+
className: cn(text({ size, weight, tone, align, truncate, mono, variant })),
|
|
1072
|
+
...props
|
|
1073
|
+
}
|
|
1074
|
+
);
|
|
1075
|
+
}
|
|
1076
|
+
);
|
|
1077
|
+
Text.displayName = "Text";
|
|
1078
|
+
var HEADING_SIZE = {
|
|
1079
|
+
1: "3xl",
|
|
1080
|
+
2: "2xl",
|
|
1081
|
+
3: "xl",
|
|
1082
|
+
4: "lg",
|
|
1083
|
+
5: "base",
|
|
1084
|
+
6: "sm"
|
|
1085
|
+
};
|
|
1086
|
+
function createHeading(level) {
|
|
1087
|
+
const Tag2 = `h${level}`;
|
|
1088
|
+
const Component = React7.forwardRef(
|
|
1089
|
+
({ size, weight = "bold", tone, align, truncate, ...props }, ref) => /* @__PURE__ */ jsx7(
|
|
1090
|
+
Tag2,
|
|
1091
|
+
{
|
|
1092
|
+
ref,
|
|
1093
|
+
className: cn(text({ size: size ?? HEADING_SIZE[level], weight, tone, align, truncate }), "tracking-tight"),
|
|
1094
|
+
...props
|
|
1095
|
+
}
|
|
1096
|
+
)
|
|
1097
|
+
);
|
|
1098
|
+
Component.displayName = `H${level}`;
|
|
1099
|
+
return Component;
|
|
1100
|
+
}
|
|
1101
|
+
var H1 = createHeading(1);
|
|
1102
|
+
var H2 = createHeading(2);
|
|
1103
|
+
var H3 = createHeading(3);
|
|
1104
|
+
var H4 = createHeading(4);
|
|
1105
|
+
var H5 = createHeading(5);
|
|
1106
|
+
var H6 = createHeading(6);
|
|
1107
|
+
|
|
1108
|
+
// src/components/link.tsx
|
|
1109
|
+
import * as React8 from "react";
|
|
1110
|
+
import { Slot as Slot4 } from "@radix-ui/react-slot";
|
|
1111
|
+
import { cva as cva7 } from "class-variance-authority";
|
|
1112
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
1113
|
+
var link = cva7(
|
|
1114
|
+
"text-link underline-offset-2 hover:text-link-hover hover:underline rounded-sm transition-colors " + focusRingInset,
|
|
1115
|
+
{
|
|
1116
|
+
variants: {
|
|
1117
|
+
underline: { hover: "", always: "underline", none: "no-underline hover:no-underline" }
|
|
1118
|
+
},
|
|
1119
|
+
defaultVariants: { underline: "hover" }
|
|
1120
|
+
}
|
|
1121
|
+
);
|
|
1122
|
+
var Link = React8.forwardRef(
|
|
1123
|
+
({ asChild, underline, ...props }, ref) => {
|
|
1124
|
+
const Comp = asChild ? Slot4 : "a";
|
|
1125
|
+
return /* @__PURE__ */ jsx8(Comp, { ref, className: cn(link({ underline })), ...props });
|
|
1126
|
+
}
|
|
1127
|
+
);
|
|
1128
|
+
Link.displayName = "Link";
|
|
1129
|
+
|
|
1130
|
+
// src/components/card.tsx
|
|
1131
|
+
import * as React9 from "react";
|
|
1132
|
+
import { jsx as jsx9, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1133
|
+
var SECTION_PADDING = {
|
|
1134
|
+
none: "p-none",
|
|
1135
|
+
xs: "p-xs",
|
|
1136
|
+
sm: "p-sm",
|
|
1137
|
+
md: "p-md",
|
|
1138
|
+
lg: "p-lg",
|
|
1139
|
+
xl: "p-xl",
|
|
1140
|
+
"2xl": "p-2xl",
|
|
1141
|
+
nbsp: "p-nbsp",
|
|
1142
|
+
card: "p-card",
|
|
1143
|
+
gutter: "p-gutter",
|
|
1144
|
+
section: "p-section",
|
|
1145
|
+
page: "p-page"
|
|
1146
|
+
};
|
|
1147
|
+
var Card = React9.forwardRef(
|
|
1148
|
+
({ title, description, headerAction, footer, children, padding = "card", ...props }, ref) => {
|
|
1149
|
+
const hasHeader = title != null || description != null || headerAction != null;
|
|
1150
|
+
const pad = SECTION_PADDING[padding];
|
|
1151
|
+
return /* @__PURE__ */ jsxs2(
|
|
1152
|
+
Box,
|
|
1153
|
+
{
|
|
1154
|
+
ref,
|
|
1155
|
+
background: "surface",
|
|
1156
|
+
border: "default",
|
|
1157
|
+
borderRadius: "2xl",
|
|
1158
|
+
boxShadow: "sm",
|
|
1159
|
+
className: cn("text-primary"),
|
|
1160
|
+
...props,
|
|
1161
|
+
children: [
|
|
1162
|
+
hasHeader && /* @__PURE__ */ jsxs2("div", { className: cn("flex flex-col gap-1.5", pad), children: [
|
|
1163
|
+
(title != null || headerAction != null) && /* @__PURE__ */ jsxs2("div", { className: cn("flex items-center justify-between gap-3"), children: [
|
|
1164
|
+
title != null && /* @__PURE__ */ jsx9("div", { className: cn("text-lg font-semibold leading-snug tracking-tight"), children: title }),
|
|
1165
|
+
headerAction
|
|
1166
|
+
] }),
|
|
1167
|
+
description != null && /* @__PURE__ */ jsx9("div", { className: cn("text-sm text-primary"), children: description })
|
|
1168
|
+
] }),
|
|
1169
|
+
children != null && /* @__PURE__ */ jsx9("div", { className: cn(pad, hasHeader && "pt-0"), children }),
|
|
1170
|
+
footer != null && /* @__PURE__ */ jsx9("div", { className: cn("flex items-center gap-3", pad, (hasHeader || children != null) && "pt-0"), children: footer })
|
|
1171
|
+
]
|
|
1172
|
+
}
|
|
1173
|
+
);
|
|
1174
|
+
}
|
|
1175
|
+
);
|
|
1176
|
+
Card.displayName = "Card";
|
|
1177
|
+
|
|
1178
|
+
// src/components/input.tsx
|
|
1179
|
+
import * as React10 from "react";
|
|
1180
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
1181
|
+
var Input = React10.forwardRef(
|
|
1182
|
+
({ type, invalid, ...props }, ref) => /* @__PURE__ */ jsx10(
|
|
1183
|
+
"input",
|
|
1184
|
+
{
|
|
1185
|
+
type,
|
|
1186
|
+
ref,
|
|
1187
|
+
"aria-invalid": invalid || void 0,
|
|
1188
|
+
className: cn(
|
|
1189
|
+
"flex h-10 w-full rounded-lg border border-strong bg-surface px-3 py-2 text-sm text-primary",
|
|
1190
|
+
"transition-colors placeholder:text-tertiary",
|
|
1191
|
+
focusRingField,
|
|
1192
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
1193
|
+
"file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-primary",
|
|
1194
|
+
invalid && "border-error focus-visible:border-error"
|
|
1195
|
+
),
|
|
1196
|
+
...props
|
|
1197
|
+
}
|
|
1198
|
+
)
|
|
1199
|
+
);
|
|
1200
|
+
Input.displayName = "Input";
|
|
1201
|
+
|
|
1202
|
+
// src/components/textarea.tsx
|
|
1203
|
+
import * as React11 from "react";
|
|
1204
|
+
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
1205
|
+
var Textarea = React11.forwardRef(
|
|
1206
|
+
({ invalid, ...props }, ref) => /* @__PURE__ */ jsx11(
|
|
1207
|
+
"textarea",
|
|
1208
|
+
{
|
|
1209
|
+
ref,
|
|
1210
|
+
"aria-invalid": invalid || void 0,
|
|
1211
|
+
className: cn(
|
|
1212
|
+
"flex min-h-20 w-full rounded-lg border border-strong bg-surface px-3 py-2 text-sm text-primary",
|
|
1213
|
+
"transition-colors placeholder:text-tertiary",
|
|
1214
|
+
focusRingField,
|
|
1215
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
1216
|
+
invalid && "border-error focus-visible:border-error"
|
|
1217
|
+
),
|
|
1218
|
+
...props
|
|
1219
|
+
}
|
|
1220
|
+
)
|
|
1221
|
+
);
|
|
1222
|
+
Textarea.displayName = "Textarea";
|
|
1223
|
+
|
|
1224
|
+
// src/components/label.tsx
|
|
1225
|
+
import * as React12 from "react";
|
|
1226
|
+
import * as LabelPrimitive from "@radix-ui/react-label";
|
|
1227
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
1228
|
+
var Label = React12.forwardRef(({ ...props }, ref) => /* @__PURE__ */ jsx12(
|
|
1229
|
+
LabelPrimitive.Root,
|
|
1230
|
+
{
|
|
1231
|
+
ref,
|
|
1232
|
+
className: cn(
|
|
1233
|
+
"text-sm font-medium leading-none text-primary peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
1234
|
+
),
|
|
1235
|
+
...props
|
|
1236
|
+
}
|
|
1237
|
+
));
|
|
1238
|
+
Label.displayName = LabelPrimitive.Root.displayName;
|
|
1239
|
+
|
|
1240
|
+
// src/components/form-field.tsx
|
|
1241
|
+
import * as React13 from "react";
|
|
1242
|
+
import { CircleAlert } from "lucide-react";
|
|
1243
|
+
import { jsx as jsx13, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1244
|
+
var FormField = React13.forwardRef(
|
|
1245
|
+
({ label, description, placeholder, type, error, required, children, ...props }, ref) => {
|
|
1246
|
+
const uid = React13.useId();
|
|
1247
|
+
const field = children ?? /* @__PURE__ */ jsx13(Input, {});
|
|
1248
|
+
const childProps = field.props;
|
|
1249
|
+
const fieldId = childProps.id ?? `${uid}-field`;
|
|
1250
|
+
const descId = description ? `${uid}-desc` : void 0;
|
|
1251
|
+
const errId = error ? `${uid}-err` : void 0;
|
|
1252
|
+
const describedBy = [childProps["aria-describedby"], descId, errId].filter(Boolean).join(" ") || void 0;
|
|
1253
|
+
const invalid = error != null || childProps.invalid;
|
|
1254
|
+
const control = React13.cloneElement(field, {
|
|
1255
|
+
id: fieldId,
|
|
1256
|
+
invalid: invalid || void 0,
|
|
1257
|
+
placeholder: childProps.placeholder ?? placeholder,
|
|
1258
|
+
type: childProps.type ?? type,
|
|
1259
|
+
"aria-describedby": describedBy,
|
|
1260
|
+
"aria-required": required || void 0
|
|
1261
|
+
});
|
|
1262
|
+
return /* @__PURE__ */ jsxs3("div", { ref, className: cn("flex flex-col gap-1.5"), ...props, children: [
|
|
1263
|
+
label != null && /* @__PURE__ */ jsxs3(Label, { htmlFor: fieldId, children: [
|
|
1264
|
+
label,
|
|
1265
|
+
required && /* @__PURE__ */ jsx13("span", { "aria-hidden": "true", className: "text-error", children: " *" })
|
|
1266
|
+
] }),
|
|
1267
|
+
control,
|
|
1268
|
+
description != null && error == null && /* @__PURE__ */ jsx13(Text, { span: true, size: "sm", id: descId, children: description }),
|
|
1269
|
+
error != null && /* @__PURE__ */ jsxs3("div", { id: errId, className: "flex items-start gap-1.5 text-sm text-primary", children: [
|
|
1270
|
+
/* @__PURE__ */ jsx13(CircleAlert, { className: "mt-0.5 size-3.5 shrink-0 text-error", "aria-hidden": "true" }),
|
|
1271
|
+
/* @__PURE__ */ jsx13("span", { children: error })
|
|
1272
|
+
] })
|
|
1273
|
+
] });
|
|
1274
|
+
}
|
|
1275
|
+
);
|
|
1276
|
+
FormField.displayName = "FormField";
|
|
1277
|
+
|
|
1278
|
+
// src/components/separator.tsx
|
|
1279
|
+
import * as React14 from "react";
|
|
1280
|
+
import * as SeparatorPrimitive from "@radix-ui/react-separator";
|
|
1281
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
1282
|
+
var Separator = React14.forwardRef(({ orientation = "horizontal", decorative = true, ...props }, ref) => /* @__PURE__ */ jsx14(
|
|
1283
|
+
SeparatorPrimitive.Root,
|
|
1284
|
+
{
|
|
1285
|
+
ref,
|
|
1286
|
+
decorative,
|
|
1287
|
+
orientation,
|
|
1288
|
+
className: cn(
|
|
1289
|
+
"shrink-0 bg-border",
|
|
1290
|
+
orientation === "horizontal" ? "h-px w-full" : "h-full w-px"
|
|
1291
|
+
),
|
|
1292
|
+
...props
|
|
1293
|
+
}
|
|
1294
|
+
));
|
|
1295
|
+
Separator.displayName = SeparatorPrimitive.Root.displayName;
|
|
1296
|
+
|
|
1297
|
+
// src/components/skeleton.tsx
|
|
1298
|
+
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
1299
|
+
function Skeleton({ ...props }) {
|
|
1300
|
+
return /* @__PURE__ */ jsx15("div", { className: cn("h-full w-full animate-pulse rounded-md bg-surface-inset"), ...props });
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
// src/components/code-block.tsx
|
|
1304
|
+
import * as React15 from "react";
|
|
1305
|
+
import { Highlight } from "prism-react-renderer";
|
|
1306
|
+
import { Check, Copy } from "lucide-react";
|
|
1307
|
+
import { jsx as jsx16, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1308
|
+
var prismTheme = {
|
|
1309
|
+
plain: { color: "var(--wf-color-text-primary)", backgroundColor: "transparent" },
|
|
1310
|
+
styles: [
|
|
1311
|
+
{ types: ["comment", "prolog", "doctype", "cdata"], style: { color: "var(--wf-color-text-tertiary)", fontStyle: "italic" } },
|
|
1312
|
+
{ types: ["punctuation"], style: { color: "var(--wf-color-text-secondary)" } },
|
|
1313
|
+
{ types: ["tag", "keyword", "selector", "operator", "builtin"], style: { color: "var(--wf-color-brand-primary)" } },
|
|
1314
|
+
{ types: ["function", "class-name", "function-variable"], style: { color: "var(--wf-color-status-info-default)" } },
|
|
1315
|
+
{ types: ["attr-name", "property", "variable", "entity"], style: { color: "var(--wf-color-status-error-default)" } },
|
|
1316
|
+
{ types: ["string", "char", "attr-value", "inserted", "regex"], style: { color: "var(--wf-color-status-success-default)" } },
|
|
1317
|
+
{ types: ["number", "boolean", "constant", "symbol"], style: { color: "var(--wf-color-status-warning-default)" } }
|
|
1318
|
+
]
|
|
1319
|
+
};
|
|
1320
|
+
function CodeBlock({
|
|
1321
|
+
code,
|
|
1322
|
+
language = "tsx",
|
|
1323
|
+
filename,
|
|
1324
|
+
showLineNumbers,
|
|
1325
|
+
highlightLines,
|
|
1326
|
+
wrap,
|
|
1327
|
+
maxHeight,
|
|
1328
|
+
copyable = true
|
|
1329
|
+
}) {
|
|
1330
|
+
const [copied, setCopied] = React15.useState(false);
|
|
1331
|
+
const text2 = code.replace(/\n+$/, "");
|
|
1332
|
+
const highlightSet = React15.useMemo(() => new Set(highlightLines ?? []), [highlightLines]);
|
|
1333
|
+
const copy = () => {
|
|
1334
|
+
void navigator.clipboard?.writeText(text2);
|
|
1335
|
+
setCopied(true);
|
|
1336
|
+
window.setTimeout(() => setCopied(false), 1400);
|
|
1337
|
+
};
|
|
1338
|
+
const copyButton = /* @__PURE__ */ jsx16(
|
|
1339
|
+
"button",
|
|
1340
|
+
{
|
|
1341
|
+
type: "button",
|
|
1342
|
+
onClick: copy,
|
|
1343
|
+
"aria-label": copied ? "Copied" : "Copy code",
|
|
1344
|
+
className: cn(
|
|
1345
|
+
"inline-flex size-8 items-center justify-center rounded-md text-secondary opacity-70 transition-opacity",
|
|
1346
|
+
"hover:bg-surface-subtle hover:opacity-100 [&_svg]:size-4",
|
|
1347
|
+
focusRingInset
|
|
1348
|
+
),
|
|
1349
|
+
children: copied ? /* @__PURE__ */ jsx16(Check, { className: "text-success" }) : /* @__PURE__ */ jsx16(Copy, {})
|
|
1350
|
+
}
|
|
1351
|
+
);
|
|
1352
|
+
return /* @__PURE__ */ jsxs4("div", { className: "relative overflow-hidden rounded-xl border border-border bg-surface-inset text-sm", children: [
|
|
1353
|
+
filename != null && /* @__PURE__ */ jsxs4("div", { className: "flex items-center justify-between gap-2 border-b border-border px-4 py-2", children: [
|
|
1354
|
+
/* @__PURE__ */ jsx16("span", { className: "truncate font-mono text-sm text-secondary", children: filename }),
|
|
1355
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [
|
|
1356
|
+
/* @__PURE__ */ jsx16("span", { className: "select-none text-sm uppercase tracking-wide text-tertiary", children: language }),
|
|
1357
|
+
copyable && copyButton
|
|
1358
|
+
] })
|
|
1359
|
+
] }),
|
|
1360
|
+
filename == null && copyable && /* @__PURE__ */ jsx16("div", { className: "absolute right-2 top-2 z-10", children: copyButton }),
|
|
1361
|
+
/* @__PURE__ */ jsx16(Highlight, { code: text2, language, theme: prismTheme, children: ({ style, tokens, getLineProps, getTokenProps }) => /* @__PURE__ */ jsx16(
|
|
1362
|
+
"pre",
|
|
1363
|
+
{
|
|
1364
|
+
className: cn("m-0 overflow-auto py-3 font-mono text-sm leading-relaxed", filename == null && copyable && "pr-12"),
|
|
1365
|
+
style: { ...style, background: "transparent", maxHeight },
|
|
1366
|
+
children: tokens.map((line, i) => {
|
|
1367
|
+
const lineProps = getLineProps({ line });
|
|
1368
|
+
const isHot = highlightSet.has(i + 1);
|
|
1369
|
+
return /* @__PURE__ */ jsxs4(
|
|
1370
|
+
"div",
|
|
1371
|
+
{
|
|
1372
|
+
...lineProps,
|
|
1373
|
+
className: cn(lineProps.className, "flex px-4", wrap && "whitespace-pre-wrap", isHot && "bg-brand-subtle"),
|
|
1374
|
+
children: [
|
|
1375
|
+
showLineNumbers && /* @__PURE__ */ jsx16("span", { className: "mr-4 inline-block w-6 shrink-0 select-none text-right text-tertiary", children: i + 1 }),
|
|
1376
|
+
/* @__PURE__ */ jsx16("span", { className: cn("flex-1", wrap && "break-words"), children: line.map((token, key) => /* @__PURE__ */ jsx16("span", { ...getTokenProps({ token }) }, key)) })
|
|
1377
|
+
]
|
|
1378
|
+
},
|
|
1379
|
+
i
|
|
1380
|
+
);
|
|
1381
|
+
})
|
|
1382
|
+
}
|
|
1383
|
+
) })
|
|
1384
|
+
] });
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
// src/components/alert.tsx
|
|
1388
|
+
import * as React16 from "react";
|
|
1389
|
+
import { cva as cva8 } from "class-variance-authority";
|
|
1390
|
+
import { jsx as jsx17, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1391
|
+
var alertVariants = cva8(
|
|
1392
|
+
"relative w-full rounded-xl border p-4 flex gap-3 text-primary [&_svg]:size-5 [&_svg]:shrink-0 [&_svg]:mt-0.5",
|
|
1393
|
+
{
|
|
1394
|
+
variants: {
|
|
1395
|
+
variant: {
|
|
1396
|
+
neutral: "border-border bg-surface [&_svg]:text-secondary",
|
|
1397
|
+
info: "border-info bg-info-subtle [&_svg]:text-info",
|
|
1398
|
+
success: "border-success bg-success-subtle [&_svg]:text-success",
|
|
1399
|
+
warning: "border-warning bg-warning-subtle [&_svg]:text-warning",
|
|
1400
|
+
error: "border-error bg-error-subtle [&_svg]:text-error"
|
|
1401
|
+
}
|
|
1402
|
+
},
|
|
1403
|
+
defaultVariants: { variant: "neutral" }
|
|
1404
|
+
}
|
|
1405
|
+
);
|
|
1406
|
+
var Alert = React16.forwardRef(
|
|
1407
|
+
({ variant, title, description, icon, actions, children, ...props }, ref) => /* @__PURE__ */ jsxs5("div", { ref, role: "alert", className: cn(alertVariants({ variant })), ...props, children: [
|
|
1408
|
+
icon,
|
|
1409
|
+
/* @__PURE__ */ jsxs5("div", { className: "min-w-0 flex-1", children: [
|
|
1410
|
+
title && /* @__PURE__ */ jsx17("h5", { className: cn("mb-0.5 font-semibold leading-snug"), children: title }),
|
|
1411
|
+
description && /* @__PURE__ */ jsx17("div", { className: cn("text-sm text-primary"), children: description }),
|
|
1412
|
+
children,
|
|
1413
|
+
actions && /* @__PURE__ */ jsx17("div", { className: "mt-3 flex flex-wrap items-center gap-3", children: actions })
|
|
1414
|
+
] })
|
|
1415
|
+
] })
|
|
1416
|
+
);
|
|
1417
|
+
Alert.displayName = "Alert";
|
|
1418
|
+
|
|
1419
|
+
// src/components/switch.tsx
|
|
1420
|
+
import * as React17 from "react";
|
|
1421
|
+
import * as SwitchPrimitives from "@radix-ui/react-switch";
|
|
1422
|
+
import { jsx as jsx18 } from "react/jsx-runtime";
|
|
1423
|
+
var Switch = React17.forwardRef(({ ...props }, ref) => /* @__PURE__ */ jsx18(
|
|
1424
|
+
SwitchPrimitives.Root,
|
|
1425
|
+
{
|
|
1426
|
+
ref,
|
|
1427
|
+
className: cn(
|
|
1428
|
+
"peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors",
|
|
1429
|
+
focusRing,
|
|
1430
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
1431
|
+
// Selected state matches every other control (neutral inverse fill), not a hue.
|
|
1432
|
+
"data-[state=checked]:bg-surface-inverse data-[state=unchecked]:bg-surface-track"
|
|
1433
|
+
),
|
|
1434
|
+
...props,
|
|
1435
|
+
children: /* @__PURE__ */ jsx18(
|
|
1436
|
+
SwitchPrimitives.Thumb,
|
|
1437
|
+
{
|
|
1438
|
+
className: cn(
|
|
1439
|
+
"pointer-events-none block h-5 w-5 rounded-full bg-white shadow-sm ring-0 transition-transform",
|
|
1440
|
+
"data-[state=checked]:bg-inverse",
|
|
1441
|
+
"data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0"
|
|
1442
|
+
)
|
|
1443
|
+
}
|
|
1444
|
+
)
|
|
1445
|
+
}
|
|
1446
|
+
));
|
|
1447
|
+
Switch.displayName = SwitchPrimitives.Root.displayName;
|
|
1448
|
+
|
|
1449
|
+
// src/components/checkbox.tsx
|
|
1450
|
+
import * as React18 from "react";
|
|
1451
|
+
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
|
|
1452
|
+
import { Check as Check2 } from "lucide-react";
|
|
1453
|
+
import { jsx as jsx19 } from "react/jsx-runtime";
|
|
1454
|
+
var Checkbox = React18.forwardRef(({ ...props }, ref) => /* @__PURE__ */ jsx19(
|
|
1455
|
+
CheckboxPrimitive.Root,
|
|
1456
|
+
{
|
|
1457
|
+
ref,
|
|
1458
|
+
className: cn(
|
|
1459
|
+
"peer h-5 w-5 shrink-0 rounded-sm border border-strong transition-colors",
|
|
1460
|
+
focusRing,
|
|
1461
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
1462
|
+
"data-[state=checked]:border-transparent data-[state=checked]:bg-surface-inverse data-[state=checked]:text-inverse"
|
|
1463
|
+
),
|
|
1464
|
+
...props,
|
|
1465
|
+
children: /* @__PURE__ */ jsx19(CheckboxPrimitive.Indicator, { className: cn("flex items-center justify-center text-current"), children: /* @__PURE__ */ jsx19(Check2, { className: "h-3.5 w-3.5", strokeWidth: 3 }) })
|
|
1466
|
+
}
|
|
1467
|
+
));
|
|
1468
|
+
Checkbox.displayName = CheckboxPrimitive.Root.displayName;
|
|
1469
|
+
|
|
1470
|
+
// src/components/radio-group.tsx
|
|
1471
|
+
import * as React19 from "react";
|
|
1472
|
+
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
|
|
1473
|
+
import { Circle } from "lucide-react";
|
|
1474
|
+
import { jsx as jsx20 } from "react/jsx-runtime";
|
|
1475
|
+
var RadioGroup = React19.forwardRef(({ ...props }, ref) => /* @__PURE__ */ jsx20(RadioGroupPrimitive.Root, { ref, className: cn("grid gap-2.5"), ...props }));
|
|
1476
|
+
RadioGroup.displayName = RadioGroupPrimitive.Root.displayName;
|
|
1477
|
+
var RadioGroupItem = React19.forwardRef(({ ...props }, ref) => /* @__PURE__ */ jsx20(
|
|
1478
|
+
RadioGroupPrimitive.Item,
|
|
1479
|
+
{
|
|
1480
|
+
ref,
|
|
1481
|
+
className: cn(
|
|
1482
|
+
"aspect-square h-5 w-5 rounded-full border border-strong text-primary transition-colors",
|
|
1483
|
+
focusRing,
|
|
1484
|
+
"disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:border-strong"
|
|
1485
|
+
),
|
|
1486
|
+
...props,
|
|
1487
|
+
children: /* @__PURE__ */ jsx20(RadioGroupPrimitive.Indicator, { className: "flex items-center justify-center", children: /* @__PURE__ */ jsx20(Circle, { className: "h-2.5 w-2.5 fill-primary text-primary" }) })
|
|
1488
|
+
}
|
|
1489
|
+
));
|
|
1490
|
+
RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName;
|
|
1491
|
+
|
|
1492
|
+
// src/components/tabs.tsx
|
|
1493
|
+
import * as React20 from "react";
|
|
1494
|
+
import * as TabsPrimitive from "@radix-ui/react-tabs";
|
|
1495
|
+
import { jsx as jsx21, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1496
|
+
var Tabs = React20.forwardRef(({ items, children, ...props }, ref) => {
|
|
1497
|
+
if (!items) return /* @__PURE__ */ jsx21(TabsPrimitive.Root, { ref, ...props, children });
|
|
1498
|
+
return /* @__PURE__ */ jsxs6(TabsPrimitive.Root, { ref, ...props, children: [
|
|
1499
|
+
/* @__PURE__ */ jsx21(TabsList, { children: items.map((t) => /* @__PURE__ */ jsx21(TabsTrigger, { value: t.value, disabled: t.disabled, children: t.label }, t.value)) }),
|
|
1500
|
+
items.map((t) => /* @__PURE__ */ jsx21(TabsContent, { value: t.value, children: t.content }, t.value))
|
|
1501
|
+
] });
|
|
1502
|
+
});
|
|
1503
|
+
Tabs.displayName = "Tabs";
|
|
1504
|
+
var TabsList = React20.forwardRef(({ ...props }, ref) => /* @__PURE__ */ jsx21(
|
|
1505
|
+
TabsPrimitive.List,
|
|
1506
|
+
{
|
|
1507
|
+
ref,
|
|
1508
|
+
className: cn(
|
|
1509
|
+
"inline-flex h-10 items-center justify-center gap-1 rounded-lg bg-surface-inset p-1 text-secondary"
|
|
1510
|
+
),
|
|
1511
|
+
...props
|
|
1512
|
+
}
|
|
1513
|
+
));
|
|
1514
|
+
TabsList.displayName = TabsPrimitive.List.displayName;
|
|
1515
|
+
var TabsTrigger = React20.forwardRef(({ ...props }, ref) => /* @__PURE__ */ jsx21(
|
|
1516
|
+
TabsPrimitive.Trigger,
|
|
1517
|
+
{
|
|
1518
|
+
ref,
|
|
1519
|
+
className: cn(
|
|
1520
|
+
"inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1.5 text-sm font-medium transition-all",
|
|
1521
|
+
focusRing,
|
|
1522
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
1523
|
+
"data-[state=active]:bg-surface data-[state=active]:text-primary data-[state=active]:shadow-sm"
|
|
1524
|
+
),
|
|
1525
|
+
...props
|
|
1526
|
+
}
|
|
1527
|
+
));
|
|
1528
|
+
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
|
|
1529
|
+
var TabsContent = React20.forwardRef(({ ...props }, ref) => /* @__PURE__ */ jsx21(
|
|
1530
|
+
TabsPrimitive.Content,
|
|
1531
|
+
{
|
|
1532
|
+
ref,
|
|
1533
|
+
className: cn("mt-lg focus-visible:outline-none"),
|
|
1534
|
+
...props
|
|
1535
|
+
}
|
|
1536
|
+
));
|
|
1537
|
+
TabsContent.displayName = TabsPrimitive.Content.displayName;
|
|
1538
|
+
|
|
1539
|
+
// src/components/tooltip.tsx
|
|
1540
|
+
import * as React21 from "react";
|
|
1541
|
+
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
1542
|
+
import { jsx as jsx22 } from "react/jsx-runtime";
|
|
1543
|
+
var TooltipProvider = TooltipPrimitive.Provider;
|
|
1544
|
+
var Tooltip = TooltipPrimitive.Root;
|
|
1545
|
+
var TooltipTrigger = TooltipPrimitive.Trigger;
|
|
1546
|
+
var TooltipContent = React21.forwardRef(({ sideOffset = 6, ...props }, ref) => /* @__PURE__ */ jsx22(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsx22(
|
|
1547
|
+
TooltipPrimitive.Content,
|
|
1548
|
+
{
|
|
1549
|
+
ref,
|
|
1550
|
+
sideOffset,
|
|
1551
|
+
className: cn(
|
|
1552
|
+
"z-50 overflow-hidden rounded-md bg-surface-inverse px-2.5 py-1.5 text-sm font-medium text-inverse shadow-md",
|
|
1553
|
+
"animate-scale-in"
|
|
1554
|
+
),
|
|
1555
|
+
...props
|
|
1556
|
+
}
|
|
1557
|
+
) }));
|
|
1558
|
+
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
|
1559
|
+
|
|
1560
|
+
// src/components/select.tsx
|
|
1561
|
+
import * as React22 from "react";
|
|
1562
|
+
import * as SelectPrimitive from "@radix-ui/react-select";
|
|
1563
|
+
import { Check as Check3, ChevronDown, ChevronUp } from "lucide-react";
|
|
1564
|
+
import { jsx as jsx23, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1565
|
+
var SelectGroup = SelectPrimitive.Group;
|
|
1566
|
+
var SelectValue = SelectPrimitive.Value;
|
|
1567
|
+
function Select({ options, placeholder, invalid, id, children, ...props }) {
|
|
1568
|
+
if (!options) return /* @__PURE__ */ jsx23(SelectPrimitive.Root, { ...props, children });
|
|
1569
|
+
return /* @__PURE__ */ jsxs7(SelectPrimitive.Root, { ...props, children: [
|
|
1570
|
+
/* @__PURE__ */ jsx23(SelectTrigger, { invalid, id, "aria-describedby": props["aria-describedby"], children: /* @__PURE__ */ jsx23(SelectValue, { placeholder }) }),
|
|
1571
|
+
/* @__PURE__ */ jsx23(SelectContent, { children: options.map((o) => /* @__PURE__ */ jsx23(SelectItem, { value: o.value, disabled: o.disabled, children: o.label }, o.value)) })
|
|
1572
|
+
] });
|
|
1573
|
+
}
|
|
1574
|
+
var SelectTrigger = React22.forwardRef(({ children, invalid, ...props }, ref) => /* @__PURE__ */ jsxs7(
|
|
1575
|
+
SelectPrimitive.Trigger,
|
|
1576
|
+
{
|
|
1577
|
+
ref,
|
|
1578
|
+
"aria-invalid": invalid || void 0,
|
|
1579
|
+
className: cn(
|
|
1580
|
+
"flex h-10 w-full items-center justify-between gap-2 rounded-lg border border-strong bg-surface px-3 py-2 text-sm text-primary",
|
|
1581
|
+
"placeholder:text-tertiary focus:outline-none focus:border-focus focus:ring-2 focus:ring-ring",
|
|
1582
|
+
"disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1 data-[placeholder]:text-tertiary",
|
|
1583
|
+
invalid && "border-error focus:border-error"
|
|
1584
|
+
),
|
|
1585
|
+
...props,
|
|
1586
|
+
children: [
|
|
1587
|
+
children,
|
|
1588
|
+
/* @__PURE__ */ jsx23(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx23(ChevronDown, { className: "h-4 w-4 opacity-60" }) })
|
|
1589
|
+
]
|
|
1590
|
+
}
|
|
1591
|
+
));
|
|
1592
|
+
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
|
1593
|
+
var SelectContent = React22.forwardRef(({ children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx23(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs7(
|
|
1594
|
+
SelectPrimitive.Content,
|
|
1595
|
+
{
|
|
1596
|
+
ref,
|
|
1597
|
+
position,
|
|
1598
|
+
className: cn(
|
|
1599
|
+
floatingPanel,
|
|
1600
|
+
"relative max-h-96 min-w-32 overflow-hidden animate-scale-in",
|
|
1601
|
+
position === "popper" && "data-[side=bottom]:translate-y-1"
|
|
1602
|
+
),
|
|
1603
|
+
...props,
|
|
1604
|
+
children: [
|
|
1605
|
+
/* @__PURE__ */ jsx23(SelectPrimitive.ScrollUpButton, { className: "flex items-center justify-center py-1", children: /* @__PURE__ */ jsx23(ChevronUp, { className: "h-4 w-4" }) }),
|
|
1606
|
+
/* @__PURE__ */ jsx23(
|
|
1607
|
+
SelectPrimitive.Viewport,
|
|
1608
|
+
{
|
|
1609
|
+
className: cn("p-1", position === "popper" && "w-full min-w-[var(--radix-select-trigger-width)]"),
|
|
1610
|
+
children
|
|
1611
|
+
}
|
|
1612
|
+
),
|
|
1613
|
+
/* @__PURE__ */ jsx23(SelectPrimitive.ScrollDownButton, { className: "flex items-center justify-center py-1", children: /* @__PURE__ */ jsx23(ChevronDown, { className: "h-4 w-4" }) })
|
|
1614
|
+
]
|
|
1615
|
+
}
|
|
1616
|
+
) }));
|
|
1617
|
+
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
|
1618
|
+
var SelectLabel = React22.forwardRef(({ ...props }, ref) => /* @__PURE__ */ jsx23(SelectPrimitive.Label, { ref, className: cn("px-2 py-1.5 text-sm font-semibold text-tertiary"), ...props }));
|
|
1619
|
+
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
|
1620
|
+
var SelectItem = React22.forwardRef(({ children, ...props }, ref) => /* @__PURE__ */ jsxs7(
|
|
1621
|
+
SelectPrimitive.Item,
|
|
1622
|
+
{
|
|
1623
|
+
ref,
|
|
1624
|
+
className: cn(menuItem, "w-full py-1.5 pl-8 pr-2 focus:bg-surface-inset focus:text-primary"),
|
|
1625
|
+
...props,
|
|
1626
|
+
children: [
|
|
1627
|
+
/* @__PURE__ */ jsx23("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx23(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx23(Check3, { className: "h-4 w-4" }) }) }),
|
|
1628
|
+
/* @__PURE__ */ jsx23(SelectPrimitive.ItemText, { children })
|
|
1629
|
+
]
|
|
1630
|
+
}
|
|
1631
|
+
));
|
|
1632
|
+
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
|
1633
|
+
|
|
1634
|
+
// src/components/autocomplete.tsx
|
|
1635
|
+
import * as React23 from "react";
|
|
1636
|
+
import * as Popover from "@radix-ui/react-popover";
|
|
1637
|
+
import { Check as Check4, ChevronsUpDown } from "lucide-react";
|
|
1638
|
+
import { jsx as jsx24, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1639
|
+
function Autocomplete({
|
|
1640
|
+
options,
|
|
1641
|
+
value,
|
|
1642
|
+
onValueChange,
|
|
1643
|
+
placeholder = "Search\u2026",
|
|
1644
|
+
emptyText = "No results",
|
|
1645
|
+
disabled,
|
|
1646
|
+
id,
|
|
1647
|
+
invalid,
|
|
1648
|
+
"aria-describedby": ariaDescribedBy
|
|
1649
|
+
}) {
|
|
1650
|
+
const [open, setOpen] = React23.useState(false);
|
|
1651
|
+
const [query, setQuery] = React23.useState("");
|
|
1652
|
+
const [highlight, setHighlight] = React23.useState(0);
|
|
1653
|
+
const anchorRef = React23.useRef(null);
|
|
1654
|
+
const listId = React23.useId();
|
|
1655
|
+
const selected = options.find((option) => option.value === value) ?? null;
|
|
1656
|
+
const display = open ? query : selected?.label ?? "";
|
|
1657
|
+
const filtered = React23.useMemo(
|
|
1658
|
+
() => open && query ? options.filter((option) => option.label.toLowerCase().includes(query.toLowerCase())) : options,
|
|
1659
|
+
[open, query, options]
|
|
1660
|
+
);
|
|
1661
|
+
const optionId = (index) => `${listId}-opt-${index}`;
|
|
1662
|
+
React23.useEffect(() => {
|
|
1663
|
+
setHighlight((current) => Math.min(current, Math.max(0, filtered.length - 1)));
|
|
1664
|
+
}, [filtered.length]);
|
|
1665
|
+
const choose = (opt) => {
|
|
1666
|
+
onValueChange?.(opt.value);
|
|
1667
|
+
setQuery("");
|
|
1668
|
+
setOpen(false);
|
|
1669
|
+
};
|
|
1670
|
+
const onKeyDown = (event) => {
|
|
1671
|
+
if (event.key === "ArrowDown") {
|
|
1672
|
+
event.preventDefault();
|
|
1673
|
+
if (!open) setOpen(true);
|
|
1674
|
+
setHighlight((current) => Math.min(current + 1, filtered.length - 1));
|
|
1675
|
+
} else if (event.key === "ArrowUp") {
|
|
1676
|
+
event.preventDefault();
|
|
1677
|
+
setHighlight((current) => Math.max(current - 1, 0));
|
|
1678
|
+
} else if (event.key === "Enter") {
|
|
1679
|
+
if (open && filtered[highlight]) {
|
|
1680
|
+
event.preventDefault();
|
|
1681
|
+
choose(filtered[highlight]);
|
|
1682
|
+
}
|
|
1683
|
+
} else if (event.key === "Escape") {
|
|
1684
|
+
setOpen(false);
|
|
1685
|
+
}
|
|
1686
|
+
};
|
|
1687
|
+
return /* @__PURE__ */ jsxs8(Popover.Root, { open, onOpenChange: setOpen, children: [
|
|
1688
|
+
/* @__PURE__ */ jsx24(Popover.Anchor, { asChild: true, children: /* @__PURE__ */ jsxs8(
|
|
1689
|
+
"div",
|
|
1690
|
+
{
|
|
1691
|
+
ref: anchorRef,
|
|
1692
|
+
className: cn(
|
|
1693
|
+
"flex h-10 items-center gap-2 rounded-lg border border-strong bg-surface px-3 text-sm text-primary",
|
|
1694
|
+
"focus-within:border-focus focus-within:ring-2 focus-within:ring-ring",
|
|
1695
|
+
disabled && "opacity-50 pointer-events-none",
|
|
1696
|
+
invalid && "border-error focus-within:border-error"
|
|
1697
|
+
),
|
|
1698
|
+
children: [
|
|
1699
|
+
/* @__PURE__ */ jsx24(
|
|
1700
|
+
"input",
|
|
1701
|
+
{
|
|
1702
|
+
id,
|
|
1703
|
+
role: "combobox",
|
|
1704
|
+
"aria-expanded": open,
|
|
1705
|
+
"aria-controls": listId,
|
|
1706
|
+
"aria-invalid": invalid || void 0,
|
|
1707
|
+
"aria-describedby": ariaDescribedBy,
|
|
1708
|
+
"aria-autocomplete": "list",
|
|
1709
|
+
"aria-activedescendant": open && filtered[highlight] ? optionId(highlight) : void 0,
|
|
1710
|
+
disabled,
|
|
1711
|
+
value: display,
|
|
1712
|
+
placeholder,
|
|
1713
|
+
onChange: (event) => {
|
|
1714
|
+
setQuery(event.target.value);
|
|
1715
|
+
setHighlight(0);
|
|
1716
|
+
setOpen(true);
|
|
1717
|
+
},
|
|
1718
|
+
onFocus: () => setOpen(true),
|
|
1719
|
+
onKeyDown,
|
|
1720
|
+
className: "w-full bg-transparent placeholder:text-tertiary outline-none"
|
|
1721
|
+
}
|
|
1722
|
+
),
|
|
1723
|
+
/* @__PURE__ */ jsx24(ChevronsUpDown, { className: "size-4 shrink-0 text-tertiary" })
|
|
1724
|
+
]
|
|
1725
|
+
}
|
|
1726
|
+
) }),
|
|
1727
|
+
/* @__PURE__ */ jsx24(Popover.Portal, { children: /* @__PURE__ */ jsx24(
|
|
1728
|
+
Popover.Content,
|
|
1729
|
+
{
|
|
1730
|
+
role: "listbox",
|
|
1731
|
+
id: listId,
|
|
1732
|
+
align: "start",
|
|
1733
|
+
sideOffset: 4,
|
|
1734
|
+
onOpenAutoFocus: (event) => event.preventDefault(),
|
|
1735
|
+
onCloseAutoFocus: (event) => event.preventDefault(),
|
|
1736
|
+
onInteractOutside: (event) => {
|
|
1737
|
+
const target = event.detail.originalEvent.target;
|
|
1738
|
+
if (target && anchorRef.current?.contains(target)) event.preventDefault();
|
|
1739
|
+
},
|
|
1740
|
+
className: cn(
|
|
1741
|
+
floatingPanel,
|
|
1742
|
+
"max-h-60 w-[var(--radix-popover-trigger-width)] overflow-auto p-1 shadow-lg animate-scale-in"
|
|
1743
|
+
),
|
|
1744
|
+
children: filtered.length === 0 ? /* @__PURE__ */ jsx24("div", { className: "px-2 py-1.5 text-sm text-tertiary", children: emptyText }) : filtered.map((option, index) => {
|
|
1745
|
+
const isSelected = option.value === value;
|
|
1746
|
+
return /* @__PURE__ */ jsxs8(
|
|
1747
|
+
"div",
|
|
1748
|
+
{
|
|
1749
|
+
id: optionId(index),
|
|
1750
|
+
role: "option",
|
|
1751
|
+
"aria-selected": isSelected,
|
|
1752
|
+
onMouseEnter: () => setHighlight(index),
|
|
1753
|
+
onMouseDown: (event) => {
|
|
1754
|
+
event.preventDefault();
|
|
1755
|
+
choose(option);
|
|
1756
|
+
},
|
|
1757
|
+
className: cn(
|
|
1758
|
+
menuItem,
|
|
1759
|
+
"gap-2 px-2 py-1.5",
|
|
1760
|
+
index === highlight ? "bg-surface-inset text-primary" : "text-secondary"
|
|
1761
|
+
),
|
|
1762
|
+
children: [
|
|
1763
|
+
/* @__PURE__ */ jsx24(Check4, { className: cn("size-4 shrink-0", isSelected ? "opacity-100 text-primary" : "opacity-0") }),
|
|
1764
|
+
option.label
|
|
1765
|
+
]
|
|
1766
|
+
},
|
|
1767
|
+
option.value
|
|
1768
|
+
);
|
|
1769
|
+
})
|
|
1770
|
+
}
|
|
1771
|
+
) })
|
|
1772
|
+
] });
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
// src/components/modal.tsx
|
|
1776
|
+
import * as React24 from "react";
|
|
1777
|
+
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
1778
|
+
import { X as X2 } from "lucide-react";
|
|
1779
|
+
import { cva as cva9 } from "class-variance-authority";
|
|
1780
|
+
import { jsx as jsx25, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1781
|
+
var Modal = DialogPrimitive.Root;
|
|
1782
|
+
var ModalTrigger = DialogPrimitive.Trigger;
|
|
1783
|
+
var ModalClose = DialogPrimitive.Close;
|
|
1784
|
+
var ModalPortal = DialogPrimitive.Portal;
|
|
1785
|
+
var ModalOverlay = React24.forwardRef(({ ...props }, ref) => /* @__PURE__ */ jsx25(DialogPrimitive.Overlay, { ref, className: cn(overlayBackdrop), ...props }));
|
|
1786
|
+
ModalOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
|
1787
|
+
var modalContent = cva9(
|
|
1788
|
+
"fixed left-1/2 top-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 gap-4 rounded-2xl border border-border bg-surface p-6 shadow-xl animate-scale-in",
|
|
1789
|
+
{
|
|
1790
|
+
variants: {
|
|
1791
|
+
size: { sm: "max-w-sm", md: "max-w-lg", lg: "max-w-2xl", xl: "max-w-4xl" }
|
|
1792
|
+
},
|
|
1793
|
+
defaultVariants: { size: "md" }
|
|
1794
|
+
}
|
|
1795
|
+
);
|
|
1796
|
+
var ModalContent = React24.forwardRef(({ children, size, hideClose, ...props }, ref) => /* @__PURE__ */ jsxs9(ModalPortal, { children: [
|
|
1797
|
+
/* @__PURE__ */ jsx25(ModalOverlay, {}),
|
|
1798
|
+
/* @__PURE__ */ jsxs9(DialogPrimitive.Content, { ref, className: cn(modalContent({ size })), ...props, children: [
|
|
1799
|
+
children,
|
|
1800
|
+
!hideClose && /* @__PURE__ */ jsx25(
|
|
1801
|
+
DialogPrimitive.Close,
|
|
1802
|
+
{
|
|
1803
|
+
className: cn(dismissButton, "hover:bg-surface-inset focus:outline-none", focusRingInset),
|
|
1804
|
+
"aria-label": "Close",
|
|
1805
|
+
children: /* @__PURE__ */ jsx25(X2, { className: "h-4 w-4" })
|
|
1806
|
+
}
|
|
1807
|
+
)
|
|
1808
|
+
] })
|
|
1809
|
+
] }));
|
|
1810
|
+
ModalContent.displayName = DialogPrimitive.Content.displayName;
|
|
1811
|
+
|
|
1812
|
+
// src/components/dialog.tsx
|
|
1813
|
+
import * as DialogPrimitive2 from "@radix-ui/react-dialog";
|
|
1814
|
+
import { jsx as jsx26, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1815
|
+
function Dialog({
|
|
1816
|
+
title,
|
|
1817
|
+
description,
|
|
1818
|
+
actions,
|
|
1819
|
+
trigger,
|
|
1820
|
+
children,
|
|
1821
|
+
size,
|
|
1822
|
+
open,
|
|
1823
|
+
defaultOpen,
|
|
1824
|
+
onOpenChange
|
|
1825
|
+
}) {
|
|
1826
|
+
return /* @__PURE__ */ jsxs10(Modal, { open, defaultOpen, onOpenChange, children: [
|
|
1827
|
+
trigger && /* @__PURE__ */ jsx26(ModalTrigger, { asChild: true, children: trigger }),
|
|
1828
|
+
/* @__PURE__ */ jsxs10(ModalContent, { size, children: [
|
|
1829
|
+
/* @__PURE__ */ jsxs10("div", { className: cn("flex flex-col gap-1.5 text-left"), children: [
|
|
1830
|
+
/* @__PURE__ */ jsx26(DialogPrimitive2.Title, { className: cn("text-lg font-semibold leading-snug tracking-tight"), children: title }),
|
|
1831
|
+
description && /* @__PURE__ */ jsx26(DialogPrimitive2.Description, { className: cn("text-sm text-primary"), children: description })
|
|
1832
|
+
] }),
|
|
1833
|
+
children,
|
|
1834
|
+
actions && /* @__PURE__ */ jsx26("div", { className: cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end"), children: actions })
|
|
1835
|
+
] })
|
|
1836
|
+
] });
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
// src/components/pagination.tsx
|
|
1840
|
+
import * as React25 from "react";
|
|
1841
|
+
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
1842
|
+
import { jsx as jsx27, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1843
|
+
var DOTS = "dots";
|
|
1844
|
+
function range(start, end) {
|
|
1845
|
+
return Array.from({ length: end - start + 1 }, (_, i) => start + i);
|
|
1846
|
+
}
|
|
1847
|
+
function usePageItems(page, count, siblingCount) {
|
|
1848
|
+
return React25.useMemo(() => {
|
|
1849
|
+
const total = siblingCount * 2 + 5;
|
|
1850
|
+
if (count <= total) return range(1, count);
|
|
1851
|
+
const left = Math.max(page - siblingCount, 1);
|
|
1852
|
+
const right = Math.min(page + siblingCount, count);
|
|
1853
|
+
const showLeftDots = left > 2;
|
|
1854
|
+
const showRightDots = right < count - 1;
|
|
1855
|
+
if (!showLeftDots && showRightDots) return [...range(1, 3 + siblingCount * 2), DOTS, count];
|
|
1856
|
+
if (showLeftDots && !showRightDots) return [1, DOTS, ...range(count - (2 + siblingCount * 2), count)];
|
|
1857
|
+
return [1, DOTS, ...range(left, right), DOTS, count];
|
|
1858
|
+
}, [page, count, siblingCount]);
|
|
1859
|
+
}
|
|
1860
|
+
var cell = "inline-flex h-9 min-w-9 items-center justify-center rounded-md px-2 text-sm font-medium transition-colors disabled:opacity-40 disabled:pointer-events-none [&_svg]:size-4 " + focusRingInset;
|
|
1861
|
+
function Pagination({ page, count, onPageChange, siblingCount = 1, ...props }) {
|
|
1862
|
+
const pageItems = usePageItems(page, count, siblingCount);
|
|
1863
|
+
const goToPage = (targetPage) => onPageChange(Math.min(count, Math.max(1, targetPage)));
|
|
1864
|
+
return /* @__PURE__ */ jsxs11("nav", { role: "navigation", "aria-label": "Pagination", className: cn("flex items-center gap-1"), ...props, children: [
|
|
1865
|
+
/* @__PURE__ */ jsx27("button", { type: "button", className: cn(cell, "text-secondary hover:bg-surface-inset hover:text-primary"), onClick: () => goToPage(page - 1), disabled: page <= 1, "aria-label": "Previous page", children: /* @__PURE__ */ jsx27(ChevronLeft, {}) }),
|
|
1866
|
+
pageItems.map(
|
|
1867
|
+
(pageItem, index) => pageItem === DOTS ? /* @__PURE__ */ jsx27("span", { className: "inline-flex h-9 min-w-9 items-center justify-center text-tertiary", children: "\u2026" }, `dots-${index}`) : /* @__PURE__ */ jsx27(
|
|
1868
|
+
"button",
|
|
1869
|
+
{
|
|
1870
|
+
type: "button",
|
|
1871
|
+
"aria-current": pageItem === page ? "page" : void 0,
|
|
1872
|
+
onClick: () => goToPage(pageItem),
|
|
1873
|
+
className: cn(cell, pageItem === page ? "bg-surface-inverse text-inverse" : "text-secondary hover:bg-surface-inset hover:text-primary"),
|
|
1874
|
+
children: pageItem
|
|
1875
|
+
},
|
|
1876
|
+
pageItem
|
|
1877
|
+
)
|
|
1878
|
+
),
|
|
1879
|
+
/* @__PURE__ */ jsx27("button", { type: "button", className: cn(cell, "text-secondary hover:bg-surface-inset hover:text-primary"), onClick: () => goToPage(page + 1), disabled: page >= count, "aria-label": "Next page", children: /* @__PURE__ */ jsx27(ChevronRight, {}) })
|
|
1880
|
+
] });
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1883
|
+
// src/components/stepper.tsx
|
|
1884
|
+
import { Check as Check5 } from "lucide-react";
|
|
1885
|
+
import { jsx as jsx28, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1886
|
+
function StepDot({ state, stepNumber }) {
|
|
1887
|
+
return /* @__PURE__ */ jsx28(
|
|
1888
|
+
"span",
|
|
1889
|
+
{
|
|
1890
|
+
className: cn(
|
|
1891
|
+
"grid size-8 shrink-0 place-items-center rounded-full border text-sm font-semibold transition-colors [&_svg]:size-4",
|
|
1892
|
+
state === "complete" && "border-transparent bg-surface-inverse text-inverse",
|
|
1893
|
+
state === "active" && "border-strong bg-surface-inset text-primary",
|
|
1894
|
+
state === "upcoming" && "border-strong bg-surface text-tertiary"
|
|
1895
|
+
),
|
|
1896
|
+
children: state === "complete" ? /* @__PURE__ */ jsx28(Check5, {}) : stepNumber
|
|
1897
|
+
}
|
|
1898
|
+
);
|
|
1899
|
+
}
|
|
1900
|
+
function Stepper({ steps, activeStep, orientation = "horizontal", ...props }) {
|
|
1901
|
+
const vertical = orientation === "vertical";
|
|
1902
|
+
const stateOf = (index) => index < activeStep ? "complete" : index === activeStep ? "active" : "upcoming";
|
|
1903
|
+
return /* @__PURE__ */ jsx28("ol", { className: cn(vertical ? "flex flex-col" : "flex items-start"), ...props, children: steps.map((step, index) => {
|
|
1904
|
+
const state = stateOf(index);
|
|
1905
|
+
const isLast = index === steps.length - 1;
|
|
1906
|
+
return /* @__PURE__ */ jsxs12(
|
|
1907
|
+
"li",
|
|
1908
|
+
{
|
|
1909
|
+
"aria-current": state === "active" ? "step" : void 0,
|
|
1910
|
+
className: cn("flex", vertical ? "gap-3" : "flex-1 items-start", !isLast && !vertical && "pr-2"),
|
|
1911
|
+
children: [
|
|
1912
|
+
/* @__PURE__ */ jsxs12("div", { className: cn("flex", vertical ? "flex-col items-center" : "flex-col items-center", vertical && "self-stretch"), children: [
|
|
1913
|
+
/* @__PURE__ */ jsx28(StepDot, { state, stepNumber: index + 1 }),
|
|
1914
|
+
!isLast && vertical && /* @__PURE__ */ jsx28("span", { className: cn("my-1 w-px flex-1", index < activeStep ? "bg-surface-inverse" : "bg-border") })
|
|
1915
|
+
] }),
|
|
1916
|
+
/* @__PURE__ */ jsxs12("div", { className: cn(vertical ? "pb-6 pt-1" : "mt-2 text-center", vertical ? "text-left" : "flex-1"), children: [
|
|
1917
|
+
/* @__PURE__ */ jsx28("div", { className: cn("text-sm font-medium", state === "upcoming" ? "text-secondary" : "text-primary"), children: step.label }),
|
|
1918
|
+
step.description && /* @__PURE__ */ jsx28("div", { className: "text-sm text-tertiary", children: step.description })
|
|
1919
|
+
] }),
|
|
1920
|
+
!isLast && !vertical && /* @__PURE__ */ jsx28("span", { className: cn("mt-4 h-px flex-1", index < activeStep ? "bg-surface-inverse" : "bg-border") })
|
|
1921
|
+
]
|
|
1922
|
+
},
|
|
1923
|
+
step.label
|
|
1924
|
+
);
|
|
1925
|
+
}) });
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
// src/components/toast.tsx
|
|
1929
|
+
import * as React26 from "react";
|
|
1930
|
+
import { createPortal } from "react-dom";
|
|
1931
|
+
import { CheckCircle2, XCircle, AlertTriangle, Info, X as X3 } from "lucide-react";
|
|
1932
|
+
import { jsx as jsx29, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1933
|
+
var toasts = [];
|
|
1934
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
1935
|
+
var emit = () => listeners.forEach((listener) => listener());
|
|
1936
|
+
var nextId = 0;
|
|
1937
|
+
function toast(options) {
|
|
1938
|
+
const id = nextId++;
|
|
1939
|
+
const duration = options.duration ?? 5e3;
|
|
1940
|
+
toasts = [...toasts, { variant: "neutral", ...options, duration, id }];
|
|
1941
|
+
emit();
|
|
1942
|
+
if (duration > 0) window.setTimeout(() => dismissToast(id), duration);
|
|
1943
|
+
return id;
|
|
1944
|
+
}
|
|
1945
|
+
function dismissToast(id) {
|
|
1946
|
+
toasts = toasts.filter((existing) => existing.id !== id);
|
|
1947
|
+
emit();
|
|
1948
|
+
}
|
|
1949
|
+
function useToasts() {
|
|
1950
|
+
return React26.useSyncExternalStore(
|
|
1951
|
+
(onStoreChange) => {
|
|
1952
|
+
listeners.add(onStoreChange);
|
|
1953
|
+
return () => listeners.delete(onStoreChange);
|
|
1954
|
+
},
|
|
1955
|
+
() => toasts,
|
|
1956
|
+
() => toasts
|
|
1957
|
+
);
|
|
1958
|
+
}
|
|
1959
|
+
var ICONS = {
|
|
1960
|
+
neutral: null,
|
|
1961
|
+
success: /* @__PURE__ */ jsx29(CheckCircle2, { className: "text-success" }),
|
|
1962
|
+
error: /* @__PURE__ */ jsx29(XCircle, { className: "text-error" }),
|
|
1963
|
+
warning: /* @__PURE__ */ jsx29(AlertTriangle, { className: "text-warning" }),
|
|
1964
|
+
info: /* @__PURE__ */ jsx29(Info, { className: "text-info" })
|
|
1965
|
+
};
|
|
1966
|
+
function ToastItem({ toast: toast2 }) {
|
|
1967
|
+
const urgent = toast2.variant === "error" || toast2.variant === "warning";
|
|
1968
|
+
return /* @__PURE__ */ jsxs13(
|
|
1969
|
+
"li",
|
|
1970
|
+
{
|
|
1971
|
+
role: urgent ? "alert" : "status",
|
|
1972
|
+
"aria-live": urgent ? "assertive" : "polite",
|
|
1973
|
+
className: "pointer-events-auto flex w-80 gap-3 rounded-xl border border-border bg-surface p-4 text-primary shadow-lg animate-scale-in [&_svg]:size-5 [&_svg]:shrink-0",
|
|
1974
|
+
children: [
|
|
1975
|
+
toast2.variant && toast2.variant !== "neutral" && /* @__PURE__ */ jsx29("span", { className: "mt-0.5", children: ICONS[toast2.variant] }),
|
|
1976
|
+
/* @__PURE__ */ jsxs13("div", { className: "min-w-0 flex-1", children: [
|
|
1977
|
+
toast2.title && /* @__PURE__ */ jsx29("div", { className: "font-semibold leading-snug", children: toast2.title }),
|
|
1978
|
+
toast2.description && /* @__PURE__ */ jsx29("div", { className: "text-sm text-primary", children: toast2.description }),
|
|
1979
|
+
toast2.action && /* @__PURE__ */ jsx29("div", { className: "mt-2", children: toast2.action })
|
|
1980
|
+
] }),
|
|
1981
|
+
/* @__PURE__ */ jsx29(
|
|
1982
|
+
"button",
|
|
1983
|
+
{
|
|
1984
|
+
type: "button",
|
|
1985
|
+
"aria-label": "Dismiss",
|
|
1986
|
+
onClick: () => dismissToast(toast2.id),
|
|
1987
|
+
className: cn("-mr-1 -mt-1 h-fit rounded-md p-1 text-secondary opacity-70 transition-opacity hover:opacity-100", focusRingInset),
|
|
1988
|
+
children: /* @__PURE__ */ jsx29(X3, { className: "h-4 w-4" })
|
|
1989
|
+
}
|
|
1990
|
+
)
|
|
1991
|
+
]
|
|
1992
|
+
}
|
|
1993
|
+
);
|
|
1994
|
+
}
|
|
1995
|
+
var emptySubscribe = () => () => {
|
|
1996
|
+
};
|
|
1997
|
+
function Toaster() {
|
|
1998
|
+
const activeToasts = useToasts();
|
|
1999
|
+
const mounted = React26.useSyncExternalStore(emptySubscribe, () => true, () => false);
|
|
2000
|
+
if (!mounted) return null;
|
|
2001
|
+
return createPortal(
|
|
2002
|
+
/* @__PURE__ */ jsx29("ol", { className: "pointer-events-none fixed bottom-4 right-4 z-[100] flex w-80 flex-col gap-2", children: activeToasts.map((toast2) => /* @__PURE__ */ jsx29(ToastItem, { toast: toast2 }, toast2.id)) }),
|
|
2003
|
+
document.body
|
|
2004
|
+
);
|
|
2005
|
+
}
|
|
2006
|
+
|
|
2007
|
+
// src/components/accordion.tsx
|
|
2008
|
+
import * as React27 from "react";
|
|
2009
|
+
import * as AccordionPrimitive from "@radix-ui/react-accordion";
|
|
2010
|
+
import { ChevronDown as ChevronDown2 } from "lucide-react";
|
|
2011
|
+
import { jsx as jsx30, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
2012
|
+
var Accordion = React27.forwardRef(({ items, children, ...props }, ref) => {
|
|
2013
|
+
if (!items) return /* @__PURE__ */ jsx30(AccordionPrimitive.Root, { ref, ...props, children });
|
|
2014
|
+
return /* @__PURE__ */ jsx30(AccordionPrimitive.Root, { ref, ...props, children: items.map((item) => /* @__PURE__ */ jsxs14(AccordionItem, { value: item.value, disabled: item.disabled, children: [
|
|
2015
|
+
/* @__PURE__ */ jsx30(AccordionTrigger, { children: item.trigger }),
|
|
2016
|
+
/* @__PURE__ */ jsx30(AccordionContent, { children: item.content })
|
|
2017
|
+
] }, item.value)) });
|
|
2018
|
+
});
|
|
2019
|
+
Accordion.displayName = "Accordion";
|
|
2020
|
+
var AccordionItem = React27.forwardRef(({ ...props }, ref) => /* @__PURE__ */ jsx30(AccordionPrimitive.Item, { ref, className: cn("border-b border-border"), ...props }));
|
|
2021
|
+
AccordionItem.displayName = "AccordionItem";
|
|
2022
|
+
var AccordionTrigger = React27.forwardRef(({ children, ...props }, ref) => /* @__PURE__ */ jsx30(AccordionPrimitive.Header, { className: "flex", children: /* @__PURE__ */ jsxs14(
|
|
2023
|
+
AccordionPrimitive.Trigger,
|
|
2024
|
+
{
|
|
2025
|
+
ref,
|
|
2026
|
+
className: cn(
|
|
2027
|
+
"flex flex-1 items-center justify-between py-4 text-sm font-medium text-primary transition-all",
|
|
2028
|
+
"rounded-sm hover:text-link",
|
|
2029
|
+
focusRingInset,
|
|
2030
|
+
"[&[data-state=open]>svg]:rotate-180"
|
|
2031
|
+
),
|
|
2032
|
+
...props,
|
|
2033
|
+
children: [
|
|
2034
|
+
children,
|
|
2035
|
+
/* @__PURE__ */ jsx30(ChevronDown2, { className: "h-4 w-4 shrink-0 text-secondary transition-transform duration-200" })
|
|
2036
|
+
]
|
|
2037
|
+
}
|
|
2038
|
+
) }));
|
|
2039
|
+
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
|
|
2040
|
+
var AccordionContent = React27.forwardRef(({ children, ...props }, ref) => /* @__PURE__ */ jsx30(
|
|
2041
|
+
AccordionPrimitive.Content,
|
|
2042
|
+
{
|
|
2043
|
+
ref,
|
|
2044
|
+
className: "overflow-hidden text-sm text-primary data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down",
|
|
2045
|
+
...props,
|
|
2046
|
+
children: /* @__PURE__ */ jsx30("div", { className: cn("pb-4 pt-0"), children })
|
|
2047
|
+
}
|
|
2048
|
+
));
|
|
2049
|
+
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
|
|
2050
|
+
|
|
2051
|
+
// src/components/avatar.tsx
|
|
2052
|
+
import * as React28 from "react";
|
|
2053
|
+
import * as AvatarPrimitive from "@radix-ui/react-avatar";
|
|
2054
|
+
import { cva as cva10 } from "class-variance-authority";
|
|
2055
|
+
import { jsx as jsx31 } from "react/jsx-runtime";
|
|
2056
|
+
var avatar = cva10("relative flex shrink-0 overflow-hidden rounded-full", {
|
|
2057
|
+
variants: {
|
|
2058
|
+
size: {
|
|
2059
|
+
sm: "h-8 w-8",
|
|
2060
|
+
md: "h-10 w-10",
|
|
2061
|
+
lg: "h-12 w-12",
|
|
2062
|
+
xl: "h-16 w-16"
|
|
2063
|
+
}
|
|
2064
|
+
},
|
|
2065
|
+
defaultVariants: { size: "md" }
|
|
2066
|
+
});
|
|
2067
|
+
var FALLBACK_TEXT = {
|
|
2068
|
+
sm: "text-sm",
|
|
2069
|
+
md: "text-sm",
|
|
2070
|
+
lg: "text-base",
|
|
2071
|
+
xl: "text-lg"
|
|
2072
|
+
};
|
|
2073
|
+
var AvatarSizeContext = React28.createContext("md");
|
|
2074
|
+
var Avatar = React28.forwardRef(({ size, ...props }, ref) => /* @__PURE__ */ jsx31(AvatarSizeContext.Provider, { value: size ?? "md", children: /* @__PURE__ */ jsx31(AvatarPrimitive.Root, { ref, className: cn(avatar({ size })), ...props }) }));
|
|
2075
|
+
Avatar.displayName = AvatarPrimitive.Root.displayName;
|
|
2076
|
+
var AvatarImage = React28.forwardRef(({ ...props }, ref) => /* @__PURE__ */ jsx31(AvatarPrimitive.Image, { ref, className: cn("aspect-square h-full w-full object-cover"), ...props }));
|
|
2077
|
+
AvatarImage.displayName = AvatarPrimitive.Image.displayName;
|
|
2078
|
+
var AvatarFallback = React28.forwardRef(({ ...props }, ref) => {
|
|
2079
|
+
const size = React28.useContext(AvatarSizeContext);
|
|
2080
|
+
return /* @__PURE__ */ jsx31(
|
|
2081
|
+
AvatarPrimitive.Fallback,
|
|
2082
|
+
{
|
|
2083
|
+
ref,
|
|
2084
|
+
className: cn("flex h-full w-full items-center justify-center rounded-full bg-surface-inset font-medium text-primary", FALLBACK_TEXT[size]),
|
|
2085
|
+
...props
|
|
2086
|
+
}
|
|
2087
|
+
);
|
|
2088
|
+
});
|
|
2089
|
+
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
|
|
2090
|
+
|
|
2091
|
+
// src/components/progress.tsx
|
|
2092
|
+
import * as React29 from "react";
|
|
2093
|
+
import * as ProgressPrimitive from "@radix-ui/react-progress";
|
|
2094
|
+
import { jsx as jsx32 } from "react/jsx-runtime";
|
|
2095
|
+
var Progress = React29.forwardRef(({ value = 0, indeterminate, ...props }, ref) => /* @__PURE__ */ jsx32(
|
|
2096
|
+
ProgressPrimitive.Root,
|
|
2097
|
+
{
|
|
2098
|
+
ref,
|
|
2099
|
+
value: indeterminate ? null : value,
|
|
2100
|
+
className: cn("relative h-2 w-full overflow-hidden rounded-full bg-surface-track"),
|
|
2101
|
+
...props,
|
|
2102
|
+
children: /* @__PURE__ */ jsx32(
|
|
2103
|
+
ProgressPrimitive.Indicator,
|
|
2104
|
+
{
|
|
2105
|
+
className: cn(
|
|
2106
|
+
"h-full bg-surface-inverse",
|
|
2107
|
+
indeterminate ? "w-2/5 rounded-full animate-progress-indeterminate" : "w-full flex-1 transition-transform duration-slow"
|
|
2108
|
+
),
|
|
2109
|
+
style: indeterminate ? void 0 : { transform: `translateX(-${100 - (value || 0)}%)` }
|
|
2110
|
+
}
|
|
2111
|
+
)
|
|
2112
|
+
}
|
|
2113
|
+
));
|
|
2114
|
+
Progress.displayName = ProgressPrimitive.Root.displayName;
|
|
2115
|
+
|
|
2116
|
+
// src/components/slider.tsx
|
|
2117
|
+
import * as React30 from "react";
|
|
2118
|
+
import * as SliderPrimitive from "@radix-ui/react-slider";
|
|
2119
|
+
import { jsx as jsx33, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2120
|
+
var Slider = React30.forwardRef(({ ...props }, ref) => /* @__PURE__ */ jsxs15(
|
|
2121
|
+
SliderPrimitive.Root,
|
|
2122
|
+
{
|
|
2123
|
+
ref,
|
|
2124
|
+
className: cn("relative flex w-full touch-none select-none items-center"),
|
|
2125
|
+
...props,
|
|
2126
|
+
children: [
|
|
2127
|
+
/* @__PURE__ */ jsx33(SliderPrimitive.Track, { className: "relative h-2 w-full grow overflow-hidden rounded-full bg-surface-track", children: /* @__PURE__ */ jsx33(SliderPrimitive.Range, { className: "absolute h-full bg-surface-inverse" }) }),
|
|
2128
|
+
/* @__PURE__ */ jsx33(
|
|
2129
|
+
SliderPrimitive.Thumb,
|
|
2130
|
+
{
|
|
2131
|
+
className: cn(
|
|
2132
|
+
"block h-5 w-5 rounded-full border-2 border-strong bg-surface shadow-sm transition-colors",
|
|
2133
|
+
focusRing,
|
|
2134
|
+
"disabled:pointer-events-none disabled:opacity-50"
|
|
2135
|
+
)
|
|
2136
|
+
}
|
|
2137
|
+
)
|
|
2138
|
+
]
|
|
2139
|
+
}
|
|
2140
|
+
));
|
|
2141
|
+
Slider.displayName = SliderPrimitive.Root.displayName;
|
|
2142
|
+
|
|
2143
|
+
// src/components/popover.tsx
|
|
2144
|
+
import * as React31 from "react";
|
|
2145
|
+
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
2146
|
+
import { jsx as jsx34 } from "react/jsx-runtime";
|
|
2147
|
+
var Popover2 = PopoverPrimitive.Root;
|
|
2148
|
+
var PopoverTrigger = PopoverPrimitive.Trigger;
|
|
2149
|
+
var PopoverAnchor = PopoverPrimitive.Anchor;
|
|
2150
|
+
var PopoverContent = React31.forwardRef(({ align = "center", sideOffset = 6, ...props }, ref) => /* @__PURE__ */ jsx34(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx34(
|
|
2151
|
+
PopoverPrimitive.Content,
|
|
2152
|
+
{
|
|
2153
|
+
ref,
|
|
2154
|
+
align,
|
|
2155
|
+
sideOffset,
|
|
2156
|
+
className: cn(floatingPanel, "w-72 rounded-xl p-4 shadow-lg animate-scale-in"),
|
|
2157
|
+
...props
|
|
2158
|
+
}
|
|
2159
|
+
) }));
|
|
2160
|
+
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
|
|
2161
|
+
|
|
2162
|
+
// src/components/dropdown-menu.tsx
|
|
2163
|
+
import * as React32 from "react";
|
|
2164
|
+
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
|
2165
|
+
import { Check as Check6, ChevronRight as ChevronRight2, Circle as Circle2 } from "lucide-react";
|
|
2166
|
+
import { jsx as jsx35, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
2167
|
+
var DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
|
2168
|
+
var DropdownMenuGroup = DropdownMenuPrimitive.Group;
|
|
2169
|
+
var DropdownMenuPortal = DropdownMenuPrimitive.Portal;
|
|
2170
|
+
var DropdownMenuSub = DropdownMenuPrimitive.Sub;
|
|
2171
|
+
var DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
|
|
2172
|
+
var DropdownMenuContent = React32.forwardRef(({ sideOffset = 6, ...props }, ref) => /* @__PURE__ */ jsx35(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ jsx35(
|
|
2173
|
+
DropdownMenuPrimitive.Content,
|
|
2174
|
+
{
|
|
2175
|
+
ref,
|
|
2176
|
+
sideOffset,
|
|
2177
|
+
className: cn(floatingPanel, "min-w-48 overflow-hidden p-1 animate-scale-in"),
|
|
2178
|
+
...props
|
|
2179
|
+
}
|
|
2180
|
+
) }));
|
|
2181
|
+
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
|
|
2182
|
+
var DropdownMenuItem = React32.forwardRef(({ inset, ...props }, ref) => /* @__PURE__ */ jsx35(
|
|
2183
|
+
DropdownMenuPrimitive.Item,
|
|
2184
|
+
{
|
|
2185
|
+
ref,
|
|
2186
|
+
className: cn(
|
|
2187
|
+
menuItem,
|
|
2188
|
+
"gap-2 px-2 py-1.5 focus:bg-surface-inset focus:text-primary [&_svg]:size-4",
|
|
2189
|
+
inset && "pl-8"
|
|
2190
|
+
),
|
|
2191
|
+
...props
|
|
2192
|
+
}
|
|
2193
|
+
));
|
|
2194
|
+
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
|
|
2195
|
+
var DropdownMenuCheckboxItem = React32.forwardRef(({ children, checked, ...props }, ref) => /* @__PURE__ */ jsxs16(
|
|
2196
|
+
DropdownMenuPrimitive.CheckboxItem,
|
|
2197
|
+
{
|
|
2198
|
+
ref,
|
|
2199
|
+
checked,
|
|
2200
|
+
className: cn(menuItem, "py-1.5 pl-8 pr-2 focus:bg-surface-inset focus:text-primary"),
|
|
2201
|
+
...props,
|
|
2202
|
+
children: [
|
|
2203
|
+
/* @__PURE__ */ jsx35("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx35(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx35(Check6, { className: "h-4 w-4" }) }) }),
|
|
2204
|
+
children
|
|
2205
|
+
]
|
|
2206
|
+
}
|
|
2207
|
+
));
|
|
2208
|
+
DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName;
|
|
2209
|
+
var DropdownMenuRadioItem = React32.forwardRef(({ children, ...props }, ref) => /* @__PURE__ */ jsxs16(
|
|
2210
|
+
DropdownMenuPrimitive.RadioItem,
|
|
2211
|
+
{
|
|
2212
|
+
ref,
|
|
2213
|
+
className: cn(menuItem, "py-1.5 pl-8 pr-2 focus:bg-surface-inset focus:text-primary"),
|
|
2214
|
+
...props,
|
|
2215
|
+
children: [
|
|
2216
|
+
/* @__PURE__ */ jsx35("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx35(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx35(Circle2, { className: "h-2 w-2 fill-current" }) }) }),
|
|
2217
|
+
children
|
|
2218
|
+
]
|
|
2219
|
+
}
|
|
2220
|
+
));
|
|
2221
|
+
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
|
|
2222
|
+
var DropdownMenuLabel = React32.forwardRef(({ inset, ...props }, ref) => /* @__PURE__ */ jsx35(
|
|
2223
|
+
DropdownMenuPrimitive.Label,
|
|
2224
|
+
{
|
|
2225
|
+
ref,
|
|
2226
|
+
className: cn("px-2 py-1.5 text-sm font-semibold text-tertiary", inset && "pl-8"),
|
|
2227
|
+
...props
|
|
2228
|
+
}
|
|
2229
|
+
));
|
|
2230
|
+
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
|
|
2231
|
+
var DropdownMenuSeparator = React32.forwardRef(({ ...props }, ref) => /* @__PURE__ */ jsx35(DropdownMenuPrimitive.Separator, { ref, className: cn("-mx-1 my-1 h-px bg-border"), ...props }));
|
|
2232
|
+
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
|
|
2233
|
+
function DropdownMenuShortcut({ ...props }) {
|
|
2234
|
+
return /* @__PURE__ */ jsx35("span", { className: cn("ml-auto text-sm tracking-widest text-tertiary"), ...props });
|
|
2235
|
+
}
|
|
2236
|
+
var DropdownMenuSubTrigger = React32.forwardRef(({ inset, children, ...props }, ref) => /* @__PURE__ */ jsxs16(
|
|
2237
|
+
DropdownMenuPrimitive.SubTrigger,
|
|
2238
|
+
{
|
|
2239
|
+
ref,
|
|
2240
|
+
className: cn(menuItem, "px-2 py-1.5 focus:bg-surface-inset focus:text-primary", inset && "pl-8"),
|
|
2241
|
+
...props,
|
|
2242
|
+
children: [
|
|
2243
|
+
children,
|
|
2244
|
+
/* @__PURE__ */ jsx35(ChevronRight2, { className: "ml-auto h-4 w-4" })
|
|
2245
|
+
]
|
|
2246
|
+
}
|
|
2247
|
+
));
|
|
2248
|
+
DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName;
|
|
2249
|
+
var DropdownMenuSubContent = React32.forwardRef(({ ...props }, ref) => /* @__PURE__ */ jsx35(
|
|
2250
|
+
DropdownMenuPrimitive.SubContent,
|
|
2251
|
+
{
|
|
2252
|
+
ref,
|
|
2253
|
+
className: cn(floatingPanel, "min-w-32 overflow-hidden p-1 animate-scale-in"),
|
|
2254
|
+
...props
|
|
2255
|
+
}
|
|
2256
|
+
));
|
|
2257
|
+
DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName;
|
|
2258
|
+
function DropdownMenu({ trigger, items, children, ...props }) {
|
|
2259
|
+
if (!items) return /* @__PURE__ */ jsx35(DropdownMenuPrimitive.Root, { ...props, children });
|
|
2260
|
+
return /* @__PURE__ */ jsxs16(DropdownMenuPrimitive.Root, { ...props, children: [
|
|
2261
|
+
trigger && /* @__PURE__ */ jsx35(DropdownMenuTrigger, { asChild: true, children: trigger }),
|
|
2262
|
+
/* @__PURE__ */ jsx35(DropdownMenuContent, { children: items.map((item, i) => {
|
|
2263
|
+
if (item.type === "separator") return /* @__PURE__ */ jsx35(DropdownMenuSeparator, {}, i);
|
|
2264
|
+
if (item.type === "label") return /* @__PURE__ */ jsx35(DropdownMenuLabel, { children: item.label }, i);
|
|
2265
|
+
return /* @__PURE__ */ jsxs16(DropdownMenuItem, { onSelect: item.onSelect, disabled: item.disabled, children: [
|
|
2266
|
+
item.icon,
|
|
2267
|
+
item.label,
|
|
2268
|
+
item.shortcut && /* @__PURE__ */ jsx35(DropdownMenuShortcut, { children: item.shortcut })
|
|
2269
|
+
] }, i);
|
|
2270
|
+
}) })
|
|
2271
|
+
] });
|
|
2272
|
+
}
|
|
2273
|
+
|
|
2274
|
+
// src/components/table.tsx
|
|
2275
|
+
import * as React33 from "react";
|
|
2276
|
+
import { Fragment, jsx as jsx36, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
2277
|
+
var alignClass = { left: "text-left", center: "text-center", right: "text-right" };
|
|
2278
|
+
function TableInner({ columns, data, caption, children, ...props }, ref) {
|
|
2279
|
+
return /* @__PURE__ */ jsx36("div", { className: "relative w-full overflow-auto rounded-xl border border-border", children: /* @__PURE__ */ jsxs17("table", { ref, className: cn("w-full caption-bottom text-sm"), ...props, children: [
|
|
2280
|
+
caption != null && /* @__PURE__ */ jsx36(TableCaption, { children: caption }),
|
|
2281
|
+
columns && data ? /* @__PURE__ */ jsxs17(Fragment, { children: [
|
|
2282
|
+
/* @__PURE__ */ jsx36(TableHeader, { children: /* @__PURE__ */ jsx36(TableRow, { children: columns.map((col, i) => /* @__PURE__ */ jsx36(TableHead, { align: col.align, children: col.header }, i)) }) }),
|
|
2283
|
+
/* @__PURE__ */ jsx36(TableBody, { children: data.map((row, r) => /* @__PURE__ */ jsx36(TableRow, { children: columns.map((col, c) => /* @__PURE__ */ jsx36(TableCell, { align: col.align, children: typeof col.accessor === "function" ? col.accessor(row) : row[col.accessor] }, c)) }, r)) })
|
|
2284
|
+
] }) : children
|
|
2285
|
+
] }) });
|
|
2286
|
+
}
|
|
2287
|
+
var Table = React33.forwardRef(TableInner);
|
|
2288
|
+
Table.displayName = "Table";
|
|
2289
|
+
var TableHeader = React33.forwardRef(
|
|
2290
|
+
({ ...props }, ref) => /* @__PURE__ */ jsx36("thead", { ref, className: cn("[&_tr]:border-b [&_tr]:border-border"), ...props })
|
|
2291
|
+
);
|
|
2292
|
+
TableHeader.displayName = "TableHeader";
|
|
2293
|
+
var TableBody = React33.forwardRef(
|
|
2294
|
+
({ ...props }, ref) => /* @__PURE__ */ jsx36("tbody", { ref, className: cn("[&_tr:last-child]:border-0"), ...props })
|
|
2295
|
+
);
|
|
2296
|
+
TableBody.displayName = "TableBody";
|
|
2297
|
+
var TableFooter = React33.forwardRef(
|
|
2298
|
+
({ ...props }, ref) => /* @__PURE__ */ jsx36("tfoot", { ref, className: cn("border-t border-border bg-surface-subtle font-medium"), ...props })
|
|
2299
|
+
);
|
|
2300
|
+
TableFooter.displayName = "TableFooter";
|
|
2301
|
+
var TableRow = React33.forwardRef(
|
|
2302
|
+
({ ...props }, ref) => /* @__PURE__ */ jsx36("tr", { ref, className: cn("border-b border-border transition-colors hover:bg-surface-subtle data-[state=selected]:bg-surface-inset"), ...props })
|
|
2303
|
+
);
|
|
2304
|
+
TableRow.displayName = "TableRow";
|
|
2305
|
+
var TableHead = React33.forwardRef(({ align = "left", ...props }, ref) => /* @__PURE__ */ jsx36("th", { ref, className: cn("h-11 px-4 align-middle text-sm font-semibold uppercase tracking-wide text-tertiary", alignClass[align]), ...props }));
|
|
2306
|
+
TableHead.displayName = "TableHead";
|
|
2307
|
+
var TableCell = React33.forwardRef(({ align = "left", ...props }, ref) => /* @__PURE__ */ jsx36("td", { ref, className: cn("px-4 py-3 align-middle", alignClass[align]), ...props }));
|
|
2308
|
+
TableCell.displayName = "TableCell";
|
|
2309
|
+
var TableCaption = React33.forwardRef(
|
|
2310
|
+
({ ...props }, ref) => /* @__PURE__ */ jsx36("caption", { ref, className: cn("mt-4 text-sm text-secondary"), ...props })
|
|
2311
|
+
);
|
|
2312
|
+
TableCaption.displayName = "TableCaption";
|
|
2313
|
+
|
|
2314
|
+
// src/components/data-table.tsx
|
|
2315
|
+
import * as React34 from "react";
|
|
2316
|
+
import { ChevronUp as ChevronUp2, ChevronDown as ChevronDown3, ChevronsUpDown as ChevronsUpDown2 } from "lucide-react";
|
|
2317
|
+
import { jsx as jsx37, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
2318
|
+
function sortValue(col, row) {
|
|
2319
|
+
if (col.sortAccessor) return col.sortAccessor(row);
|
|
2320
|
+
if (typeof col.accessor !== "function") {
|
|
2321
|
+
const v = row[col.accessor];
|
|
2322
|
+
return typeof v === "number" ? v : String(v ?? "");
|
|
2323
|
+
}
|
|
2324
|
+
return "";
|
|
2325
|
+
}
|
|
2326
|
+
function DataTable({
|
|
2327
|
+
columns,
|
|
2328
|
+
data,
|
|
2329
|
+
rowKey,
|
|
2330
|
+
caption,
|
|
2331
|
+
selectable,
|
|
2332
|
+
selected,
|
|
2333
|
+
onSelectedChange,
|
|
2334
|
+
pageSize,
|
|
2335
|
+
emptyState
|
|
2336
|
+
}) {
|
|
2337
|
+
const [sort, setSort] = React34.useState(null);
|
|
2338
|
+
const [page, setPage] = React34.useState(1);
|
|
2339
|
+
const [internalSel, setInternalSel] = React34.useState([]);
|
|
2340
|
+
const sel = selected ?? internalSel;
|
|
2341
|
+
const setSel = (ids) => {
|
|
2342
|
+
onSelectedChange?.(ids);
|
|
2343
|
+
if (selected === void 0) setInternalSel(ids);
|
|
2344
|
+
};
|
|
2345
|
+
const sorted = React34.useMemo(() => {
|
|
2346
|
+
if (!sort) return [...data];
|
|
2347
|
+
const col = columns[sort.index];
|
|
2348
|
+
const dir = sort.dir === "asc" ? 1 : -1;
|
|
2349
|
+
return [...data].sort((a, b) => {
|
|
2350
|
+
const av = sortValue(col, a);
|
|
2351
|
+
const bv = sortValue(col, b);
|
|
2352
|
+
if (av < bv) return -1 * dir;
|
|
2353
|
+
if (av > bv) return 1 * dir;
|
|
2354
|
+
return 0;
|
|
2355
|
+
});
|
|
2356
|
+
}, [data, sort, columns]);
|
|
2357
|
+
const pageCount = pageSize ? Math.max(1, Math.ceil(sorted.length / pageSize)) : 1;
|
|
2358
|
+
const current = Math.min(page, pageCount);
|
|
2359
|
+
const rows = pageSize ? sorted.slice((current - 1) * pageSize, current * pageSize) : sorted;
|
|
2360
|
+
const pageIds = rows.map(rowKey);
|
|
2361
|
+
const allOnPageSelected = pageIds.length > 0 && pageIds.every((id) => sel.includes(id));
|
|
2362
|
+
const toggleAll = () => setSel(allOnPageSelected ? sel.filter((id) => !pageIds.includes(id)) : [.../* @__PURE__ */ new Set([...sel, ...pageIds])]);
|
|
2363
|
+
const toggleRow = (id) => setSel(sel.includes(id) ? sel.filter((x) => x !== id) : [...sel, id]);
|
|
2364
|
+
const toggleSort = (index) => setSort(
|
|
2365
|
+
(s) => s?.index !== index ? { index, dir: "asc" } : s.dir === "asc" ? { index, dir: "desc" } : null
|
|
2366
|
+
);
|
|
2367
|
+
const colCount = columns.length + (selectable ? 1 : 0);
|
|
2368
|
+
return /* @__PURE__ */ jsxs18("div", { className: "flex flex-col gap-3", children: [
|
|
2369
|
+
/* @__PURE__ */ jsxs18(Table, { caption, children: [
|
|
2370
|
+
/* @__PURE__ */ jsx37(TableHeader, { children: /* @__PURE__ */ jsxs18(TableRow, { children: [
|
|
2371
|
+
selectable && /* @__PURE__ */ jsx37(TableHead, { align: "center", children: /* @__PURE__ */ jsx37(
|
|
2372
|
+
Checkbox,
|
|
2373
|
+
{
|
|
2374
|
+
checked: allOnPageSelected,
|
|
2375
|
+
onCheckedChange: toggleAll,
|
|
2376
|
+
"aria-label": "Select all rows on this page"
|
|
2377
|
+
}
|
|
2378
|
+
) }),
|
|
2379
|
+
columns.map((col, i) => {
|
|
2380
|
+
const active = sort?.index === i;
|
|
2381
|
+
const SortIcon = !active ? ChevronsUpDown2 : sort.dir === "asc" ? ChevronUp2 : ChevronDown3;
|
|
2382
|
+
return /* @__PURE__ */ jsx37(TableHead, { align: col.align, children: col.sortable ? /* @__PURE__ */ jsxs18(
|
|
2383
|
+
"button",
|
|
2384
|
+
{
|
|
2385
|
+
type: "button",
|
|
2386
|
+
onClick: () => toggleSort(i),
|
|
2387
|
+
"aria-label": `Sort by ${typeof col.header === "string" ? col.header : "column"}`,
|
|
2388
|
+
className: cn(
|
|
2389
|
+
"inline-flex items-center gap-1 rounded-sm transition-colors hover:text-primary",
|
|
2390
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
2391
|
+
active ? "text-primary" : "text-tertiary"
|
|
2392
|
+
),
|
|
2393
|
+
children: [
|
|
2394
|
+
col.header,
|
|
2395
|
+
/* @__PURE__ */ jsx37(SortIcon, { className: "size-3.5 shrink-0", "aria-hidden": "true" })
|
|
2396
|
+
]
|
|
2397
|
+
}
|
|
2398
|
+
) : col.header }, i);
|
|
2399
|
+
})
|
|
2400
|
+
] }) }),
|
|
2401
|
+
/* @__PURE__ */ jsx37(TableBody, { children: rows.length === 0 ? /* @__PURE__ */ jsx37(TableRow, { children: /* @__PURE__ */ jsx37(TableCell, { align: "center", colSpan: colCount, children: /* @__PURE__ */ jsx37("div", { className: "py-6 text-sm text-secondary", children: emptyState ?? "No results." }) }) }) : rows.map((row) => {
|
|
2402
|
+
const id = rowKey(row);
|
|
2403
|
+
const isSelected = sel.includes(id);
|
|
2404
|
+
return /* @__PURE__ */ jsxs18(TableRow, { "data-state": isSelected ? "selected" : void 0, children: [
|
|
2405
|
+
selectable && /* @__PURE__ */ jsx37(TableCell, { align: "center", children: /* @__PURE__ */ jsx37(
|
|
2406
|
+
Checkbox,
|
|
2407
|
+
{
|
|
2408
|
+
checked: isSelected,
|
|
2409
|
+
onCheckedChange: () => toggleRow(id),
|
|
2410
|
+
"aria-label": "Select row"
|
|
2411
|
+
}
|
|
2412
|
+
) }),
|
|
2413
|
+
columns.map((col, c) => /* @__PURE__ */ jsx37(TableCell, { align: col.align, children: typeof col.accessor === "function" ? col.accessor(row) : row[col.accessor] }, c))
|
|
2414
|
+
] }, id);
|
|
2415
|
+
}) })
|
|
2416
|
+
] }),
|
|
2417
|
+
pageSize && pageCount > 1 && /* @__PURE__ */ jsxs18("div", { className: "flex items-center justify-between", children: [
|
|
2418
|
+
/* @__PURE__ */ jsxs18("span", { className: "text-sm text-secondary", children: [
|
|
2419
|
+
selectable && sel.length > 0 ? `${sel.length} selected \xB7 ` : "",
|
|
2420
|
+
sorted.length,
|
|
2421
|
+
" ",
|
|
2422
|
+
sorted.length === 1 ? "row" : "rows"
|
|
2423
|
+
] }),
|
|
2424
|
+
/* @__PURE__ */ jsx37(Pagination, { page: current, count: pageCount, onPageChange: setPage })
|
|
2425
|
+
] })
|
|
2426
|
+
] });
|
|
2427
|
+
}
|
|
2428
|
+
|
|
2429
|
+
// src/components/breadcrumb.tsx
|
|
2430
|
+
import * as React35 from "react";
|
|
2431
|
+
import { ChevronRight as ChevronRight3 } from "lucide-react";
|
|
2432
|
+
import { jsx as jsx38, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
2433
|
+
var Breadcrumb = React35.forwardRef(
|
|
2434
|
+
({ items, separator, ...props }, ref) => /* @__PURE__ */ jsx38("nav", { ref, "aria-label": "breadcrumb", ...props, children: /* @__PURE__ */ jsx38("ol", { className: cn("flex flex-wrap items-center gap-1.5 text-sm text-secondary"), children: items.map((item, index) => {
|
|
2435
|
+
const isLast = index === items.length - 1;
|
|
2436
|
+
return /* @__PURE__ */ jsxs19(React35.Fragment, { children: [
|
|
2437
|
+
/* @__PURE__ */ jsx38("li", { className: cn("inline-flex items-center gap-1.5"), children: isLast || item.href == null ? /* @__PURE__ */ jsx38(Text, { span: true, size: "sm", weight: "medium", role: "link", "aria-disabled": "true", "aria-current": "page", children: item.label }) : /* @__PURE__ */ jsx38("a", { href: item.href, className: cn("transition-colors hover:text-link cursor-pointer"), children: item.label }) }),
|
|
2438
|
+
!isLast && /* @__PURE__ */ jsx38("li", { role: "presentation", "aria-hidden": "true", className: cn("[&_svg]:size-3.5 text-tertiary"), children: separator ?? /* @__PURE__ */ jsx38(ChevronRight3, {}) })
|
|
2439
|
+
] }, index);
|
|
2440
|
+
}) }) })
|
|
2441
|
+
);
|
|
2442
|
+
Breadcrumb.displayName = "Breadcrumb";
|
|
2443
|
+
|
|
2444
|
+
// src/components/sheet.tsx
|
|
2445
|
+
import * as React36 from "react";
|
|
2446
|
+
import * as DialogPrimitive3 from "@radix-ui/react-dialog";
|
|
2447
|
+
import { cva as cva11 } from "class-variance-authority";
|
|
2448
|
+
import { X as X4 } from "lucide-react";
|
|
2449
|
+
import { jsx as jsx39, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
2450
|
+
var Sheet = DialogPrimitive3.Root;
|
|
2451
|
+
var SheetTrigger = DialogPrimitive3.Trigger;
|
|
2452
|
+
var SheetClose = DialogPrimitive3.Close;
|
|
2453
|
+
var SheetPortal = DialogPrimitive3.Portal;
|
|
2454
|
+
var SheetOverlay = React36.forwardRef(({ ...props }, ref) => /* @__PURE__ */ jsx39(DialogPrimitive3.Overlay, { ref, className: cn(overlayBackdrop), ...props }));
|
|
2455
|
+
SheetOverlay.displayName = DialogPrimitive3.Overlay.displayName;
|
|
2456
|
+
var sheetVariants = cva11(
|
|
2457
|
+
"fixed z-50 gap-4 bg-surface shadow-xl transition ease-in-out",
|
|
2458
|
+
{
|
|
2459
|
+
variants: {
|
|
2460
|
+
side: {
|
|
2461
|
+
top: "inset-x-0 top-0 border-b border-border",
|
|
2462
|
+
bottom: "inset-x-0 bottom-0 border-t border-border",
|
|
2463
|
+
left: "inset-y-0 left-0 h-full w-3/4 max-w-sm border-r border-border",
|
|
2464
|
+
right: "inset-y-0 right-0 h-full w-3/4 max-w-sm border-l border-border"
|
|
2465
|
+
}
|
|
2466
|
+
},
|
|
2467
|
+
defaultVariants: { side: "right" }
|
|
2468
|
+
}
|
|
2469
|
+
);
|
|
2470
|
+
var SheetContent = React36.forwardRef(({ side = "right", title, description, footer, children, ...props }, ref) => /* @__PURE__ */ jsxs20(SheetPortal, { children: [
|
|
2471
|
+
/* @__PURE__ */ jsx39(SheetOverlay, {}),
|
|
2472
|
+
/* @__PURE__ */ jsxs20(DialogPrimitive3.Content, { ref, className: cn(sheetVariants({ side }), "flex flex-col p-6 animate-fade-in"), ...props, children: [
|
|
2473
|
+
(title != null || description != null) && /* @__PURE__ */ jsxs20("div", { className: cn("flex flex-col gap-1.5"), children: [
|
|
2474
|
+
title != null && /* @__PURE__ */ jsx39(DialogPrimitive3.Title, { className: cn("text-lg font-semibold text-primary"), children: title }),
|
|
2475
|
+
description != null && /* @__PURE__ */ jsx39(DialogPrimitive3.Description, { className: cn("text-sm text-primary"), children: description })
|
|
2476
|
+
] }),
|
|
2477
|
+
children,
|
|
2478
|
+
footer != null && /* @__PURE__ */ jsx39("div", { className: cn("mt-auto flex flex-col gap-2"), children: footer }),
|
|
2479
|
+
/* @__PURE__ */ jsx39(
|
|
2480
|
+
DialogPrimitive3.Close,
|
|
2481
|
+
{
|
|
2482
|
+
className: cn(dismissButton, "focus:outline-none", focusRingInset),
|
|
2483
|
+
"aria-label": "Close",
|
|
2484
|
+
children: /* @__PURE__ */ jsx39(X4, { className: "h-4 w-4" })
|
|
2485
|
+
}
|
|
2486
|
+
)
|
|
2487
|
+
] })
|
|
2488
|
+
] }));
|
|
2489
|
+
SheetContent.displayName = DialogPrimitive3.Content.displayName;
|
|
2490
|
+
|
|
2491
|
+
// src/components/multi-select.tsx
|
|
2492
|
+
import * as React37 from "react";
|
|
2493
|
+
import * as Popover3 from "@radix-ui/react-popover";
|
|
2494
|
+
import { Check as Check7, ChevronsUpDown as ChevronsUpDown3, X as X5 } from "lucide-react";
|
|
2495
|
+
import { jsx as jsx40, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
2496
|
+
function MultiSelect({
|
|
2497
|
+
options,
|
|
2498
|
+
value,
|
|
2499
|
+
onValueChange,
|
|
2500
|
+
placeholder = "Select\u2026",
|
|
2501
|
+
emptyText = "No results",
|
|
2502
|
+
disabled,
|
|
2503
|
+
invalid,
|
|
2504
|
+
id,
|
|
2505
|
+
"aria-describedby": ariaDescribedBy
|
|
2506
|
+
}) {
|
|
2507
|
+
const [open, setOpen] = React37.useState(false);
|
|
2508
|
+
const [query, setQuery] = React37.useState("");
|
|
2509
|
+
const [highlight, setHighlight] = React37.useState(0);
|
|
2510
|
+
const inputRef = React37.useRef(null);
|
|
2511
|
+
const anchorRef = React37.useRef(null);
|
|
2512
|
+
const listId = React37.useId();
|
|
2513
|
+
const selectedOptions = value.map((v) => options.find((o) => o.value === v)).filter((o) => !!o);
|
|
2514
|
+
const filtered = React37.useMemo(
|
|
2515
|
+
() => query ? options.filter((o) => o.label.toLowerCase().includes(query.toLowerCase())) : options,
|
|
2516
|
+
[query, options]
|
|
2517
|
+
);
|
|
2518
|
+
React37.useEffect(() => {
|
|
2519
|
+
setHighlight((c) => Math.min(c, Math.max(0, filtered.length - 1)));
|
|
2520
|
+
}, [filtered.length]);
|
|
2521
|
+
const toggle = (v) => onValueChange(value.includes(v) ? value.filter((x) => x !== v) : [...value, v]);
|
|
2522
|
+
const onKeyDown = (event) => {
|
|
2523
|
+
if (event.key === "ArrowDown") {
|
|
2524
|
+
event.preventDefault();
|
|
2525
|
+
if (!open) setOpen(true);
|
|
2526
|
+
setHighlight((c) => Math.min(c + 1, filtered.length - 1));
|
|
2527
|
+
} else if (event.key === "ArrowUp") {
|
|
2528
|
+
event.preventDefault();
|
|
2529
|
+
setHighlight((c) => Math.max(c - 1, 0));
|
|
2530
|
+
} else if (event.key === "Enter" && open && filtered[highlight]) {
|
|
2531
|
+
event.preventDefault();
|
|
2532
|
+
toggle(filtered[highlight].value);
|
|
2533
|
+
} else if (event.key === "Backspace" && !query && value.length) {
|
|
2534
|
+
onValueChange(value.slice(0, -1));
|
|
2535
|
+
} else if (event.key === "Escape") {
|
|
2536
|
+
setOpen(false);
|
|
2537
|
+
}
|
|
2538
|
+
};
|
|
2539
|
+
const optionId = (i) => `${listId}-opt-${i}`;
|
|
2540
|
+
return /* @__PURE__ */ jsxs21(Popover3.Root, { open, onOpenChange: setOpen, children: [
|
|
2541
|
+
/* @__PURE__ */ jsx40(Popover3.Anchor, { asChild: true, children: /* @__PURE__ */ jsxs21(
|
|
2542
|
+
"div",
|
|
2543
|
+
{
|
|
2544
|
+
ref: anchorRef,
|
|
2545
|
+
className: cn(
|
|
2546
|
+
"flex min-h-10 w-full flex-wrap items-center gap-1.5 rounded-lg border border-strong bg-surface px-2 py-1.5 text-sm text-primary",
|
|
2547
|
+
"focus-within:border-focus focus-within:ring-2 focus-within:ring-ring",
|
|
2548
|
+
disabled && "pointer-events-none opacity-50",
|
|
2549
|
+
invalid && "border-error focus-within:border-error"
|
|
2550
|
+
),
|
|
2551
|
+
onMouseDown: (e) => {
|
|
2552
|
+
if (e.target === e.currentTarget) inputRef.current?.focus();
|
|
2553
|
+
},
|
|
2554
|
+
children: [
|
|
2555
|
+
selectedOptions.map((o) => /* @__PURE__ */ jsxs21(
|
|
2556
|
+
"span",
|
|
2557
|
+
{
|
|
2558
|
+
className: "inline-flex items-center gap-1 rounded-full border border-transparent bg-surface-inset px-2 py-0.5 text-sm text-primary",
|
|
2559
|
+
children: [
|
|
2560
|
+
o.label,
|
|
2561
|
+
/* @__PURE__ */ jsx40(
|
|
2562
|
+
"button",
|
|
2563
|
+
{
|
|
2564
|
+
type: "button",
|
|
2565
|
+
"aria-label": `Remove ${o.label}`,
|
|
2566
|
+
onMouseDown: (e) => {
|
|
2567
|
+
e.preventDefault();
|
|
2568
|
+
e.stopPropagation();
|
|
2569
|
+
},
|
|
2570
|
+
onClick: () => toggle(o.value),
|
|
2571
|
+
className: "rounded-full opacity-70 transition-opacity hover:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring [&_svg]:size-3",
|
|
2572
|
+
children: /* @__PURE__ */ jsx40(X5, {})
|
|
2573
|
+
}
|
|
2574
|
+
)
|
|
2575
|
+
]
|
|
2576
|
+
},
|
|
2577
|
+
o.value
|
|
2578
|
+
)),
|
|
2579
|
+
/* @__PURE__ */ jsx40(
|
|
2580
|
+
"input",
|
|
2581
|
+
{
|
|
2582
|
+
ref: inputRef,
|
|
2583
|
+
id,
|
|
2584
|
+
role: "combobox",
|
|
2585
|
+
"aria-expanded": open,
|
|
2586
|
+
"aria-controls": listId,
|
|
2587
|
+
"aria-autocomplete": "list",
|
|
2588
|
+
"aria-invalid": invalid || void 0,
|
|
2589
|
+
"aria-describedby": ariaDescribedBy,
|
|
2590
|
+
"aria-activedescendant": open && filtered[highlight] ? optionId(highlight) : void 0,
|
|
2591
|
+
disabled,
|
|
2592
|
+
value: query,
|
|
2593
|
+
placeholder: selectedOptions.length ? "" : placeholder,
|
|
2594
|
+
onChange: (e) => {
|
|
2595
|
+
setQuery(e.target.value);
|
|
2596
|
+
setHighlight(0);
|
|
2597
|
+
setOpen(true);
|
|
2598
|
+
},
|
|
2599
|
+
onFocus: () => setOpen(true),
|
|
2600
|
+
onKeyDown,
|
|
2601
|
+
className: "min-w-16 flex-1 bg-transparent placeholder:text-tertiary outline-none"
|
|
2602
|
+
}
|
|
2603
|
+
),
|
|
2604
|
+
/* @__PURE__ */ jsx40(ChevronsUpDown3, { className: "size-4 shrink-0 text-tertiary" })
|
|
2605
|
+
]
|
|
2606
|
+
}
|
|
2607
|
+
) }),
|
|
2608
|
+
/* @__PURE__ */ jsx40(Popover3.Portal, { children: /* @__PURE__ */ jsx40(
|
|
2609
|
+
Popover3.Content,
|
|
2610
|
+
{
|
|
2611
|
+
role: "listbox",
|
|
2612
|
+
"aria-multiselectable": true,
|
|
2613
|
+
id: listId,
|
|
2614
|
+
align: "start",
|
|
2615
|
+
sideOffset: 4,
|
|
2616
|
+
onOpenAutoFocus: (e) => e.preventDefault(),
|
|
2617
|
+
onCloseAutoFocus: (e) => e.preventDefault(),
|
|
2618
|
+
onInteractOutside: (event) => {
|
|
2619
|
+
const target = event.detail.originalEvent.target;
|
|
2620
|
+
if (target && anchorRef.current?.contains(target)) event.preventDefault();
|
|
2621
|
+
},
|
|
2622
|
+
className: cn(floatingPanel, "max-h-60 w-[var(--radix-popover-trigger-width)] overflow-auto p-1 shadow-lg animate-scale-in"),
|
|
2623
|
+
children: filtered.length === 0 ? /* @__PURE__ */ jsx40("div", { className: "px-2 py-1.5 text-sm text-tertiary", children: emptyText }) : filtered.map((option, index) => {
|
|
2624
|
+
const isSelected = value.includes(option.value);
|
|
2625
|
+
return /* @__PURE__ */ jsxs21(
|
|
2626
|
+
"div",
|
|
2627
|
+
{
|
|
2628
|
+
id: optionId(index),
|
|
2629
|
+
role: "option",
|
|
2630
|
+
"aria-selected": isSelected,
|
|
2631
|
+
onMouseEnter: () => setHighlight(index),
|
|
2632
|
+
onMouseDown: (e) => {
|
|
2633
|
+
e.preventDefault();
|
|
2634
|
+
toggle(option.value);
|
|
2635
|
+
},
|
|
2636
|
+
className: cn(menuItem, "gap-2 px-2 py-1.5", index === highlight ? "bg-surface-inset text-primary" : "text-secondary"),
|
|
2637
|
+
children: [
|
|
2638
|
+
/* @__PURE__ */ jsx40(Check7, { className: cn("size-4 shrink-0", isSelected ? "opacity-100 text-primary" : "opacity-0") }),
|
|
2639
|
+
option.label
|
|
2640
|
+
]
|
|
2641
|
+
},
|
|
2642
|
+
option.value
|
|
2643
|
+
);
|
|
2644
|
+
})
|
|
2645
|
+
}
|
|
2646
|
+
) })
|
|
2647
|
+
] });
|
|
2648
|
+
}
|
|
2649
|
+
|
|
2650
|
+
// src/components/command.tsx
|
|
2651
|
+
import * as React38 from "react";
|
|
2652
|
+
import * as DialogPrimitive4 from "@radix-ui/react-dialog";
|
|
2653
|
+
import { Command as CommandPrimitive } from "cmdk";
|
|
2654
|
+
import { Search } from "lucide-react";
|
|
2655
|
+
import { jsx as jsx41, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
2656
|
+
var Command = React38.forwardRef(({ ...props }, ref) => /* @__PURE__ */ jsx41(
|
|
2657
|
+
CommandPrimitive,
|
|
2658
|
+
{
|
|
2659
|
+
ref,
|
|
2660
|
+
className: cn("flex h-full w-full flex-col overflow-hidden rounded-xl bg-surface text-primary"),
|
|
2661
|
+
...props
|
|
2662
|
+
}
|
|
2663
|
+
));
|
|
2664
|
+
Command.displayName = "Command";
|
|
2665
|
+
var CommandInput = React38.forwardRef(({ ...props }, ref) => /* @__PURE__ */ jsxs22("div", { className: "flex items-center gap-2 border-b border-border px-3", children: [
|
|
2666
|
+
/* @__PURE__ */ jsx41(Search, { className: "size-4 shrink-0 text-tertiary" }),
|
|
2667
|
+
/* @__PURE__ */ jsx41(
|
|
2668
|
+
CommandPrimitive.Input,
|
|
2669
|
+
{
|
|
2670
|
+
ref,
|
|
2671
|
+
className: cn(
|
|
2672
|
+
"flex h-11 w-full bg-transparent py-3 text-sm text-primary outline-none placeholder:text-tertiary",
|
|
2673
|
+
"disabled:cursor-not-allowed disabled:opacity-50"
|
|
2674
|
+
),
|
|
2675
|
+
...props
|
|
2676
|
+
}
|
|
2677
|
+
)
|
|
2678
|
+
] }));
|
|
2679
|
+
CommandInput.displayName = "CommandInput";
|
|
2680
|
+
var CommandList = React38.forwardRef(({ ...props }, ref) => /* @__PURE__ */ jsx41(CommandPrimitive.List, { ref, className: cn("max-h-80 overflow-y-auto overflow-x-hidden p-1"), ...props }));
|
|
2681
|
+
CommandList.displayName = "CommandList";
|
|
2682
|
+
var CommandEmpty = React38.forwardRef(({ ...props }, ref) => /* @__PURE__ */ jsx41(CommandPrimitive.Empty, { ref, className: cn("py-6 text-center text-sm text-tertiary"), ...props }));
|
|
2683
|
+
CommandEmpty.displayName = "CommandEmpty";
|
|
2684
|
+
var CommandGroup = React38.forwardRef(({ ...props }, ref) => /* @__PURE__ */ jsx41(
|
|
2685
|
+
CommandPrimitive.Group,
|
|
2686
|
+
{
|
|
2687
|
+
ref,
|
|
2688
|
+
className: cn(
|
|
2689
|
+
"overflow-hidden p-1 text-primary",
|
|
2690
|
+
"[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-sm [&_[cmdk-group-heading]]:font-semibold [&_[cmdk-group-heading]]:text-tertiary"
|
|
2691
|
+
),
|
|
2692
|
+
...props
|
|
2693
|
+
}
|
|
2694
|
+
));
|
|
2695
|
+
CommandGroup.displayName = "CommandGroup";
|
|
2696
|
+
var CommandSeparator = React38.forwardRef(({ ...props }, ref) => /* @__PURE__ */ jsx41(CommandPrimitive.Separator, { ref, className: cn("-mx-1 my-1 h-px bg-border"), ...props }));
|
|
2697
|
+
CommandSeparator.displayName = "CommandSeparator";
|
|
2698
|
+
var CommandItem = React38.forwardRef(({ ...props }, ref) => /* @__PURE__ */ jsx41(
|
|
2699
|
+
CommandPrimitive.Item,
|
|
2700
|
+
{
|
|
2701
|
+
ref,
|
|
2702
|
+
className: cn(
|
|
2703
|
+
menuItem,
|
|
2704
|
+
"gap-2 px-2 py-2 text-primary [&_svg]:size-4 [&_svg]:text-secondary",
|
|
2705
|
+
"data-[selected=true]:bg-surface-inset data-[selected=true]:text-primary"
|
|
2706
|
+
),
|
|
2707
|
+
...props
|
|
2708
|
+
}
|
|
2709
|
+
));
|
|
2710
|
+
CommandItem.displayName = "CommandItem";
|
|
2711
|
+
function CommandShortcut({ ...props }) {
|
|
2712
|
+
return /* @__PURE__ */ jsx41("span", { className: cn("ml-auto text-sm tracking-widest text-tertiary"), ...props });
|
|
2713
|
+
}
|
|
2714
|
+
function CommandDialog({ open, onOpenChange, children, label = "Command palette" }) {
|
|
2715
|
+
return /* @__PURE__ */ jsx41(DialogPrimitive4.Root, { open, onOpenChange, children: /* @__PURE__ */ jsxs22(DialogPrimitive4.Portal, { children: [
|
|
2716
|
+
/* @__PURE__ */ jsx41(DialogPrimitive4.Overlay, { className: cn(overlayBackdrop) }),
|
|
2717
|
+
/* @__PURE__ */ jsxs22(
|
|
2718
|
+
DialogPrimitive4.Content,
|
|
2719
|
+
{
|
|
2720
|
+
className: cn(
|
|
2721
|
+
"fixed left-1/2 top-[15%] z-50 w-full max-w-2xl -translate-x-1/2 overflow-hidden",
|
|
2722
|
+
"rounded-2xl border border-border bg-surface shadow-xl animate-scale-in"
|
|
2723
|
+
),
|
|
2724
|
+
children: [
|
|
2725
|
+
/* @__PURE__ */ jsx41(DialogPrimitive4.Title, { className: "sr-only", children: label }),
|
|
2726
|
+
/* @__PURE__ */ jsx41(Command, { children })
|
|
2727
|
+
]
|
|
2728
|
+
}
|
|
2729
|
+
)
|
|
2730
|
+
] }) });
|
|
2731
|
+
}
|
|
2732
|
+
|
|
2733
|
+
// src/components/calendar.tsx
|
|
2734
|
+
import { DayPicker } from "react-day-picker";
|
|
2735
|
+
import { ChevronLeft as ChevronLeft2, ChevronRight as ChevronRight4 } from "lucide-react";
|
|
2736
|
+
import { jsx as jsx42 } from "react/jsx-runtime";
|
|
2737
|
+
var navButton = cn(
|
|
2738
|
+
"inline-flex h-7 w-7 items-center justify-center rounded-md border border-strong bg-surface text-primary",
|
|
2739
|
+
"transition-colors hover:bg-surface-subtle disabled:opacity-40 disabled:pointer-events-none",
|
|
2740
|
+
focusRingInset
|
|
2741
|
+
);
|
|
2742
|
+
function Calendar({ showOutsideDays = true, classNames, ...props }) {
|
|
2743
|
+
return /* @__PURE__ */ jsx42(
|
|
2744
|
+
DayPicker,
|
|
2745
|
+
{
|
|
2746
|
+
showOutsideDays,
|
|
2747
|
+
className: "p-3",
|
|
2748
|
+
classNames: {
|
|
2749
|
+
months: "flex flex-col gap-4",
|
|
2750
|
+
month: "flex flex-col gap-3",
|
|
2751
|
+
month_caption: "relative flex h-7 items-center justify-center",
|
|
2752
|
+
caption_label: "text-sm font-medium text-primary",
|
|
2753
|
+
nav: "absolute inset-x-0 flex items-center justify-between",
|
|
2754
|
+
button_previous: navButton,
|
|
2755
|
+
button_next: navButton,
|
|
2756
|
+
month_grid: "w-full border-collapse",
|
|
2757
|
+
weekdays: "flex",
|
|
2758
|
+
weekday: "w-9 text-sm font-normal text-tertiary",
|
|
2759
|
+
week: "mt-1 flex w-full",
|
|
2760
|
+
day: "relative h-9 w-9 p-0 text-center text-sm text-primary",
|
|
2761
|
+
day_button: cn(
|
|
2762
|
+
"inline-flex h-9 w-9 items-center justify-center rounded-md transition-colors",
|
|
2763
|
+
"hover:bg-surface-inset",
|
|
2764
|
+
focusRingInset
|
|
2765
|
+
),
|
|
2766
|
+
today: "font-semibold underline underline-offset-4",
|
|
2767
|
+
selected: "[&_button]:bg-surface-inverse [&_button]:text-inverse [&_button:hover]:bg-surface-inverse",
|
|
2768
|
+
outside: "text-disabled",
|
|
2769
|
+
disabled: "text-disabled opacity-50",
|
|
2770
|
+
range_start: "rounded-l-md bg-surface-inset [&_button]:bg-surface-inverse [&_button]:text-inverse",
|
|
2771
|
+
range_end: "rounded-r-md bg-surface-inset [&_button]:bg-surface-inverse [&_button]:text-inverse",
|
|
2772
|
+
range_middle: "rounded-none bg-surface-inset [&_button]:bg-transparent [&_button]:text-primary",
|
|
2773
|
+
hidden: "invisible",
|
|
2774
|
+
...classNames
|
|
2775
|
+
},
|
|
2776
|
+
components: {
|
|
2777
|
+
Chevron: ({ orientation }) => orientation === "left" ? /* @__PURE__ */ jsx42(ChevronLeft2, { className: "size-4" }) : /* @__PURE__ */ jsx42(ChevronRight4, { className: "size-4" })
|
|
2778
|
+
},
|
|
2779
|
+
...props
|
|
2780
|
+
}
|
|
2781
|
+
);
|
|
2782
|
+
}
|
|
2783
|
+
Calendar.displayName = "Calendar";
|
|
2784
|
+
|
|
2785
|
+
// src/components/date-picker.tsx
|
|
2786
|
+
import * as React39 from "react";
|
|
2787
|
+
import * as PopoverPrimitive2 from "@radix-ui/react-popover";
|
|
2788
|
+
import { Calendar as CalendarIcon } from "lucide-react";
|
|
2789
|
+
import { jsx as jsx43, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
2790
|
+
function DatePicker({
|
|
2791
|
+
value,
|
|
2792
|
+
onValueChange,
|
|
2793
|
+
placeholder = "Pick a date",
|
|
2794
|
+
disabled,
|
|
2795
|
+
invalid,
|
|
2796
|
+
id,
|
|
2797
|
+
"aria-describedby": ariaDescribedBy,
|
|
2798
|
+
formatOptions = { dateStyle: "medium" }
|
|
2799
|
+
}) {
|
|
2800
|
+
const [open, setOpen] = React39.useState(false);
|
|
2801
|
+
const label = value ? new Intl.DateTimeFormat(void 0, formatOptions).format(value) : null;
|
|
2802
|
+
return /* @__PURE__ */ jsxs23(Popover2, { open, onOpenChange: setOpen, children: [
|
|
2803
|
+
/* @__PURE__ */ jsx43(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs23(
|
|
2804
|
+
"button",
|
|
2805
|
+
{
|
|
2806
|
+
type: "button",
|
|
2807
|
+
id,
|
|
2808
|
+
disabled,
|
|
2809
|
+
"aria-invalid": invalid || void 0,
|
|
2810
|
+
"aria-describedby": ariaDescribedBy,
|
|
2811
|
+
className: cn(
|
|
2812
|
+
"flex h-10 w-full items-center gap-2 rounded-lg border border-strong bg-surface px-3 text-left text-sm",
|
|
2813
|
+
"transition-colors hover:bg-surface-subtle",
|
|
2814
|
+
focusRingField,
|
|
2815
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
2816
|
+
invalid && "border-error focus-visible:border-error"
|
|
2817
|
+
),
|
|
2818
|
+
children: [
|
|
2819
|
+
/* @__PURE__ */ jsx43(CalendarIcon, { className: "size-4 shrink-0 text-tertiary" }),
|
|
2820
|
+
/* @__PURE__ */ jsx43("span", { className: cn("flex-1", label ? "text-primary" : "text-tertiary"), children: label ?? placeholder })
|
|
2821
|
+
]
|
|
2822
|
+
}
|
|
2823
|
+
) }),
|
|
2824
|
+
/* @__PURE__ */ jsx43(PopoverPrimitive2.Portal, { children: /* @__PURE__ */ jsx43(
|
|
2825
|
+
PopoverPrimitive2.Content,
|
|
2826
|
+
{
|
|
2827
|
+
align: "start",
|
|
2828
|
+
sideOffset: 6,
|
|
2829
|
+
className: cn(floatingPanel, "w-auto rounded-xl shadow-lg animate-scale-in"),
|
|
2830
|
+
children: /* @__PURE__ */ jsx43(
|
|
2831
|
+
Calendar,
|
|
2832
|
+
{
|
|
2833
|
+
mode: "single",
|
|
2834
|
+
selected: value,
|
|
2835
|
+
onSelect: (date) => {
|
|
2836
|
+
onValueChange?.(date);
|
|
2837
|
+
setOpen(false);
|
|
2838
|
+
},
|
|
2839
|
+
autoFocus: true
|
|
2840
|
+
}
|
|
2841
|
+
)
|
|
2842
|
+
}
|
|
2843
|
+
) })
|
|
2844
|
+
] });
|
|
2845
|
+
}
|
|
2846
|
+
|
|
2847
|
+
// src/components/chart.tsx
|
|
2848
|
+
import * as React40 from "react";
|
|
2849
|
+
import * as echarts from "echarts/core";
|
|
2850
|
+
import { LineChart, BarChart, PieChart, ScatterChart } from "echarts/charts";
|
|
2851
|
+
import {
|
|
2852
|
+
TitleComponent,
|
|
2853
|
+
TooltipComponent,
|
|
2854
|
+
GridComponent,
|
|
2855
|
+
LegendComponent,
|
|
2856
|
+
DataZoomComponent,
|
|
2857
|
+
MarkLineComponent,
|
|
2858
|
+
MarkAreaComponent
|
|
2859
|
+
} from "echarts/components";
|
|
2860
|
+
import { CanvasRenderer } from "echarts/renderers";
|
|
2861
|
+
import { jsx as jsx44 } from "react/jsx-runtime";
|
|
2862
|
+
echarts.use([
|
|
2863
|
+
LineChart,
|
|
2864
|
+
BarChart,
|
|
2865
|
+
PieChart,
|
|
2866
|
+
ScatterChart,
|
|
2867
|
+
TitleComponent,
|
|
2868
|
+
TooltipComponent,
|
|
2869
|
+
GridComponent,
|
|
2870
|
+
LegendComponent,
|
|
2871
|
+
DataZoomComponent,
|
|
2872
|
+
MarkLineComponent,
|
|
2873
|
+
MarkAreaComponent,
|
|
2874
|
+
CanvasRenderer
|
|
2875
|
+
]);
|
|
2876
|
+
var cssVar = (name) => getComputedStyle(document.documentElement).getPropertyValue(name).trim();
|
|
2877
|
+
function buildTheme() {
|
|
2878
|
+
const text2 = cssVar("--wf-color-text-primary");
|
|
2879
|
+
const muted = cssVar("--wf-color-text-secondary");
|
|
2880
|
+
const faint = cssVar("--wf-color-text-tertiary");
|
|
2881
|
+
const line = cssVar("--wf-color-border-default");
|
|
2882
|
+
const subtle = cssVar("--wf-color-border-subtle");
|
|
2883
|
+
const surface2 = cssVar("--wf-color-background-paper");
|
|
2884
|
+
const font = cssVar("--wf-font-sans");
|
|
2885
|
+
const palette = [
|
|
2886
|
+
cssVar("--wf-color-brand-primary"),
|
|
2887
|
+
cssVar("--wf-color-status-info-default"),
|
|
2888
|
+
cssVar("--wf-color-status-success-default"),
|
|
2889
|
+
cssVar("--wf-color-status-warning-default"),
|
|
2890
|
+
cssVar("--wf-color-status-error-default"),
|
|
2891
|
+
cssVar("--wf-color-brand-secondary")
|
|
2892
|
+
].filter(Boolean);
|
|
2893
|
+
const axis = {
|
|
2894
|
+
axisLine: { lineStyle: { color: line } },
|
|
2895
|
+
axisTick: { lineStyle: { color: line } },
|
|
2896
|
+
axisLabel: { color: muted },
|
|
2897
|
+
splitLine: { lineStyle: { color: subtle } }
|
|
2898
|
+
};
|
|
2899
|
+
return {
|
|
2900
|
+
color: palette,
|
|
2901
|
+
backgroundColor: "transparent",
|
|
2902
|
+
textStyle: { color: text2, fontFamily: font },
|
|
2903
|
+
title: { textStyle: { color: text2 }, subtextStyle: { color: faint } },
|
|
2904
|
+
legend: { textStyle: { color: muted } },
|
|
2905
|
+
tooltip: {
|
|
2906
|
+
backgroundColor: surface2,
|
|
2907
|
+
borderColor: line,
|
|
2908
|
+
textStyle: { color: text2, fontFamily: font },
|
|
2909
|
+
axisPointer: { lineStyle: { color: muted }, crossStyle: { color: muted } }
|
|
2910
|
+
},
|
|
2911
|
+
categoryAxis: axis,
|
|
2912
|
+
valueAxis: axis,
|
|
2913
|
+
logAxis: axis,
|
|
2914
|
+
timeAxis: axis,
|
|
2915
|
+
// Series labels (notably pie's outside labels) don't inherit the global
|
|
2916
|
+
// textStyle — pin them to the foreground token so they stay readable in both
|
|
2917
|
+
// modes, and tint the connector lines with the border token.
|
|
2918
|
+
pie: {
|
|
2919
|
+
label: { color: text2, fontFamily: font },
|
|
2920
|
+
labelLine: { lineStyle: { color: line } }
|
|
2921
|
+
}
|
|
2922
|
+
};
|
|
2923
|
+
}
|
|
2924
|
+
function Chart({ option, height = 320, notMerge = true, onEvents }) {
|
|
2925
|
+
const elRef = React40.useRef(null);
|
|
2926
|
+
const chartRef = React40.useRef(null);
|
|
2927
|
+
const [themeKey, bump] = React40.useReducer((x) => x + 1, 0);
|
|
2928
|
+
React40.useEffect(() => {
|
|
2929
|
+
const mo = new MutationObserver(() => bump());
|
|
2930
|
+
mo.observe(document.documentElement, { attributes: true, attributeFilter: ["class", "style"] });
|
|
2931
|
+
return () => mo.disconnect();
|
|
2932
|
+
}, []);
|
|
2933
|
+
React40.useEffect(() => {
|
|
2934
|
+
if (!elRef.current) return;
|
|
2935
|
+
const chart = echarts.init(elRef.current, buildTheme());
|
|
2936
|
+
chartRef.current = chart;
|
|
2937
|
+
chart.setOption(option, notMerge);
|
|
2938
|
+
if (onEvents) for (const [name, handler] of Object.entries(onEvents)) chart.on(name, handler);
|
|
2939
|
+
const ro = new ResizeObserver(() => chart.resize());
|
|
2940
|
+
ro.observe(elRef.current);
|
|
2941
|
+
return () => {
|
|
2942
|
+
ro.disconnect();
|
|
2943
|
+
chart.dispose();
|
|
2944
|
+
chartRef.current = null;
|
|
2945
|
+
};
|
|
2946
|
+
}, [themeKey]);
|
|
2947
|
+
React40.useEffect(() => {
|
|
2948
|
+
chartRef.current?.setOption(option, notMerge);
|
|
2949
|
+
}, [option, notMerge]);
|
|
2950
|
+
return /* @__PURE__ */ jsx44("div", { ref: elRef, role: "img", style: { height, width: "100%" } });
|
|
2951
|
+
}
|
|
2952
|
+
|
|
2953
|
+
// src/icons/forge-icon.tsx
|
|
2954
|
+
import * as React41 from "react";
|
|
2955
|
+
import { jsx as jsx45, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
2956
|
+
var WINDFORGE_PATH = "M403.1,144.03 403.39,140.32 401.1,134.61 349.97,85.76 345.97,84.05 340.26,84.62 328.27,95.47 324.84,95.47 283.14,58.34 270,52.06 262,52.63 252.86,57.77 200.02,115.75 196.59,122.04 199.44,131.75 261.71,190.02 256,202.02 236.01,230.58 236.29,232.58 260.28,236.01 290.56,242.86 327.69,257.14 351.69,273.14 390.53,310.27 394.53,311.98 399.67,311.41 403.39,308.27 455.37,248.86 461.09,236.86 461.09,229.44 456.52,219.72 394.82,160.88 394.82,157.46 403.1,144.03Z M138.03,155.46 150.6,154.89 159.74,157.17 180.31,169.74 177.16,161.45 169.74,150.03 156.31,137.75 143.17,130.89 130.04,127.46 110.61,127.46 95.76,131.46 78.62,141.17 66.34,153.46 56.06,171.17 50.91,192.3 50.91,204.3 53.77,219.72 57.77,230.58 66.34,244.86 86.05,263.43 102.61,272.57 115.18,277.14 148.89,283.42 183.73,284.56 246.57,279.99 272.85,280.56 295.13,283.42 316.27,288.56 334.55,295.42 355.69,306.27 372.82,318.27 373.68,317.41 351.12,293.7 339.69,283.99 320.84,271.42 297.99,260.57 257.43,249.14 167.17,236.58 139.75,228.01 124.89,219.44 115.47,210.01 111.47,203.16 108.61,193.45 109.18,180.88 113.75,170.02 120.89,162.31 131.18,156.6 138.03,155.46Z M277.42,296.56 276.28,294.28 248.29,293.13 182.59,297.13 88.62,402.53 84.05,412.24 83.48,424.81 85.76,432.24 92.05,442.52 102.04,451.95 116.32,458.8 129.46,459.94 143.75,454.23 277.42,296.56Z";
|
|
2957
|
+
var ForgeIcon = React41.forwardRef(
|
|
2958
|
+
({ className, ...props }, ref) => {
|
|
2959
|
+
const gradId = `forge-${React41.useId().replace(/:/g, "")}`;
|
|
2960
|
+
return /* @__PURE__ */ jsxs24(
|
|
2961
|
+
"svg",
|
|
2962
|
+
{
|
|
2963
|
+
ref,
|
|
2964
|
+
viewBox: "0 0 512 512",
|
|
2965
|
+
role: "img",
|
|
2966
|
+
"aria-label": "Windforge",
|
|
2967
|
+
className: cn("h-6 w-6", className),
|
|
2968
|
+
...props,
|
|
2969
|
+
children: [
|
|
2970
|
+
/* @__PURE__ */ jsx45("defs", { children: /* @__PURE__ */ jsxs24("linearGradient", { id: gradId, x1: "0", y1: "1", x2: "1", y2: "0", children: [
|
|
2971
|
+
/* @__PURE__ */ jsx45("stop", { offset: "0", stopColor: "var(--wf-color-brand-primary-active)" }),
|
|
2972
|
+
/* @__PURE__ */ jsx45("stop", { offset: "1", stopColor: "var(--wf-color-brand-secondary)" })
|
|
2973
|
+
] }) }),
|
|
2974
|
+
/* @__PURE__ */ jsx45("path", { fill: `url(#${gradId})`, fillRule: "evenodd", d: WINDFORGE_PATH })
|
|
2975
|
+
]
|
|
2976
|
+
}
|
|
2977
|
+
);
|
|
2978
|
+
}
|
|
2979
|
+
);
|
|
2980
|
+
ForgeIcon.displayName = "ForgeIcon";
|
|
2981
|
+
function WindforgeLogo({ className, markOnly = false }) {
|
|
2982
|
+
return /* @__PURE__ */ jsxs24("span", { className: cn("inline-flex items-center gap-2.5", className), children: [
|
|
2983
|
+
/* @__PURE__ */ jsx45(ForgeIcon, { className: "h-8 w-8 drop-shadow-sm" }),
|
|
2984
|
+
!markOnly && /* @__PURE__ */ jsx45("span", { className: "text-base font-bold tracking-tight text-primary leading-none", children: "WindForge" })
|
|
2985
|
+
] });
|
|
2986
|
+
}
|
|
2987
|
+
|
|
2988
|
+
// src/icons/svg-icon.tsx
|
|
2989
|
+
import * as React42 from "react";
|
|
2990
|
+
import { jsx as jsx46 } from "react/jsx-runtime";
|
|
2991
|
+
var SVGIcon = React42.forwardRef(
|
|
2992
|
+
({ viewBox = "0 0 24 24", label, children, ...props }, ref) => /* @__PURE__ */ jsx46(
|
|
2993
|
+
"svg",
|
|
2994
|
+
{
|
|
2995
|
+
ref,
|
|
2996
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2997
|
+
viewBox,
|
|
2998
|
+
width: 24,
|
|
2999
|
+
height: 24,
|
|
3000
|
+
fill: "none",
|
|
3001
|
+
stroke: "currentColor",
|
|
3002
|
+
strokeWidth: 2,
|
|
3003
|
+
strokeLinecap: "round",
|
|
3004
|
+
strokeLinejoin: "round",
|
|
3005
|
+
role: label ? "img" : void 0,
|
|
3006
|
+
"aria-label": label,
|
|
3007
|
+
"aria-hidden": label ? void 0 : true,
|
|
3008
|
+
...props,
|
|
3009
|
+
children
|
|
3010
|
+
}
|
|
3011
|
+
)
|
|
3012
|
+
);
|
|
3013
|
+
SVGIcon.displayName = "SVGIcon";
|
|
3014
|
+
|
|
3015
|
+
// src/icons/icon-set.ts
|
|
3016
|
+
import {
|
|
3017
|
+
ArrowUp,
|
|
3018
|
+
ArrowDown,
|
|
3019
|
+
ArrowLeft,
|
|
3020
|
+
ArrowRight,
|
|
3021
|
+
ArrowUpRight,
|
|
3022
|
+
ChevronUp as ChevronUp3,
|
|
3023
|
+
ChevronDown as ChevronDown4,
|
|
3024
|
+
ChevronLeft as ChevronLeft3,
|
|
3025
|
+
ChevronRight as ChevronRight5,
|
|
3026
|
+
ChevronsUpDown as ChevronsUpDown4,
|
|
3027
|
+
ChevronsLeft,
|
|
3028
|
+
ChevronsRight,
|
|
3029
|
+
CornerDownLeft,
|
|
3030
|
+
Menu,
|
|
3031
|
+
MoreHorizontal,
|
|
3032
|
+
MoreVertical,
|
|
3033
|
+
Home,
|
|
3034
|
+
Search as Search2,
|
|
3035
|
+
LayoutDashboard,
|
|
3036
|
+
LayoutGrid,
|
|
3037
|
+
PanelLeft,
|
|
3038
|
+
PanelRight,
|
|
3039
|
+
PanelTop,
|
|
3040
|
+
Map,
|
|
3041
|
+
MapPin,
|
|
3042
|
+
Globe,
|
|
3043
|
+
ExternalLink,
|
|
3044
|
+
Link as Link2,
|
|
3045
|
+
Filter,
|
|
3046
|
+
Plus,
|
|
3047
|
+
Minus,
|
|
3048
|
+
X as X6,
|
|
3049
|
+
Check as Check8,
|
|
3050
|
+
Copy as Copy2,
|
|
3051
|
+
ClipboardCheck,
|
|
3052
|
+
Pencil,
|
|
3053
|
+
Trash2,
|
|
3054
|
+
Save,
|
|
3055
|
+
Download,
|
|
3056
|
+
Upload,
|
|
3057
|
+
Share2,
|
|
3058
|
+
RefreshCw,
|
|
3059
|
+
RotateCw,
|
|
3060
|
+
Settings,
|
|
3061
|
+
SlidersHorizontal,
|
|
3062
|
+
Send,
|
|
3063
|
+
Eye,
|
|
3064
|
+
EyeOff,
|
|
3065
|
+
Maximize2,
|
|
3066
|
+
Info as Info2,
|
|
3067
|
+
AlertTriangle as AlertTriangle2,
|
|
3068
|
+
AlertCircle,
|
|
3069
|
+
CheckCircle2 as CheckCircle22,
|
|
3070
|
+
XCircle as XCircle2,
|
|
3071
|
+
HelpCircle,
|
|
3072
|
+
Ban,
|
|
3073
|
+
ShieldCheck,
|
|
3074
|
+
Shield,
|
|
3075
|
+
Bell,
|
|
3076
|
+
BellRing,
|
|
3077
|
+
BellOff,
|
|
3078
|
+
LoaderCircle,
|
|
3079
|
+
Star,
|
|
3080
|
+
Heart,
|
|
3081
|
+
Bookmark,
|
|
3082
|
+
Flag,
|
|
3083
|
+
ThumbsUp,
|
|
3084
|
+
File,
|
|
3085
|
+
FileText,
|
|
3086
|
+
Folder,
|
|
3087
|
+
FolderOpen,
|
|
3088
|
+
Image as Image2,
|
|
3089
|
+
Paperclip,
|
|
3090
|
+
Inbox,
|
|
3091
|
+
Archive,
|
|
3092
|
+
Package,
|
|
3093
|
+
Box as Box2,
|
|
3094
|
+
Boxes,
|
|
3095
|
+
Database,
|
|
3096
|
+
Server,
|
|
3097
|
+
Cloud,
|
|
3098
|
+
Calendar as Calendar2,
|
|
3099
|
+
Clock,
|
|
3100
|
+
Mail,
|
|
3101
|
+
MessageSquare,
|
|
3102
|
+
MessageCircle,
|
|
3103
|
+
Phone,
|
|
3104
|
+
AtSign,
|
|
3105
|
+
Hash,
|
|
3106
|
+
Megaphone,
|
|
3107
|
+
Rss,
|
|
3108
|
+
Play,
|
|
3109
|
+
Pause,
|
|
3110
|
+
SkipForward,
|
|
3111
|
+
SkipBack,
|
|
3112
|
+
Volume2,
|
|
3113
|
+
VolumeX,
|
|
3114
|
+
Mic,
|
|
3115
|
+
MicOff,
|
|
3116
|
+
Camera,
|
|
3117
|
+
Video,
|
|
3118
|
+
Music,
|
|
3119
|
+
Bold,
|
|
3120
|
+
Italic,
|
|
3121
|
+
Underline,
|
|
3122
|
+
AlignLeft,
|
|
3123
|
+
AlignCenter,
|
|
3124
|
+
AlignRight,
|
|
3125
|
+
AlignJustify,
|
|
3126
|
+
List as List2,
|
|
3127
|
+
ListOrdered,
|
|
3128
|
+
ListChecks,
|
|
3129
|
+
Type,
|
|
3130
|
+
Code2,
|
|
3131
|
+
Quote,
|
|
3132
|
+
User,
|
|
3133
|
+
Users,
|
|
3134
|
+
UserPlus,
|
|
3135
|
+
CircleUser,
|
|
3136
|
+
LogIn,
|
|
3137
|
+
LogOut,
|
|
3138
|
+
CreditCard,
|
|
3139
|
+
ShoppingCart,
|
|
3140
|
+
DollarSign,
|
|
3141
|
+
Wallet,
|
|
3142
|
+
Gift,
|
|
3143
|
+
Briefcase,
|
|
3144
|
+
Building2,
|
|
3145
|
+
ToggleLeft,
|
|
3146
|
+
ToggleRight,
|
|
3147
|
+
Circle as Circle3,
|
|
3148
|
+
CircleDot,
|
|
3149
|
+
CircleDashed,
|
|
3150
|
+
Square,
|
|
3151
|
+
CheckSquare,
|
|
3152
|
+
Shapes,
|
|
3153
|
+
Tag,
|
|
3154
|
+
Palette,
|
|
3155
|
+
SwatchBook,
|
|
3156
|
+
Sun,
|
|
3157
|
+
Moon,
|
|
3158
|
+
MonitorSmartphone,
|
|
3159
|
+
Sparkles,
|
|
3160
|
+
Rocket,
|
|
3161
|
+
Zap,
|
|
3162
|
+
Layers,
|
|
3163
|
+
Ruler,
|
|
3164
|
+
Gauge,
|
|
3165
|
+
Accessibility,
|
|
3166
|
+
Lock,
|
|
3167
|
+
Unlock,
|
|
3168
|
+
Bot,
|
|
3169
|
+
Terminal,
|
|
3170
|
+
GitBranch,
|
|
3171
|
+
TrendingUp,
|
|
3172
|
+
Activity
|
|
3173
|
+
} from "lucide-react";
|
|
3174
|
+
var commonIconGroups = [
|
|
3175
|
+
{ label: "Arrows & chevrons", icons: { ArrowUp, ArrowDown, ArrowLeft, ArrowRight, ArrowUpRight, ChevronUp: ChevronUp3, ChevronDown: ChevronDown4, ChevronLeft: ChevronLeft3, ChevronRight: ChevronRight5, ChevronsUpDown: ChevronsUpDown4, ChevronsLeft, ChevronsRight, CornerDownLeft } },
|
|
3176
|
+
{ label: "Navigation & layout", icons: { Menu, MoreHorizontal, MoreVertical, Home, Search: Search2, LayoutDashboard, LayoutGrid, PanelLeft, PanelRight, PanelTop, Map, MapPin, Globe, ExternalLink, Link: Link2, Filter } },
|
|
3177
|
+
{ label: "Actions", icons: { Plus, Minus, X: X6, Check: Check8, Copy: Copy2, ClipboardCheck, Pencil, Trash2, Save, Download, Upload, Share2, RefreshCw, RotateCw, Settings, SlidersHorizontal, Send, Eye, EyeOff, Maximize2 } },
|
|
3178
|
+
{ label: "Status & feedback", icons: { Info: Info2, AlertTriangle: AlertTriangle2, AlertCircle, CheckCircle2: CheckCircle22, XCircle: XCircle2, HelpCircle, Ban, ShieldCheck, Shield, Bell, BellRing, BellOff, LoaderCircle, Star, Heart, Bookmark, Flag, ThumbsUp } },
|
|
3179
|
+
{ label: "Files & data", icons: { File, FileText, Folder, FolderOpen, Image: Image2, Paperclip, Inbox, Archive, Package, Box: Box2, Boxes, Database, Server, Cloud, Calendar: Calendar2, Clock } },
|
|
3180
|
+
{ label: "Communication", icons: { Mail, MessageSquare, MessageCircle, Phone, AtSign, Hash, Megaphone, Rss } },
|
|
3181
|
+
{ label: "Media", icons: { Play, Pause, SkipForward, SkipBack, Volume2, VolumeX, Mic, MicOff, Camera, Video, Music } },
|
|
3182
|
+
{ label: "Text & formatting", icons: { Bold, Italic, Underline, AlignLeft, AlignCenter, AlignRight, AlignJustify, List: List2, ListOrdered, ListChecks, Type, Code2, Quote } },
|
|
3183
|
+
{ label: "People & commerce", icons: { User, Users, UserPlus, CircleUser, LogIn, LogOut, CreditCard, ShoppingCart, DollarSign, Wallet, Gift, Briefcase, Building2 } },
|
|
3184
|
+
{ label: "Toggles & shapes", icons: { ToggleLeft, ToggleRight, Circle: Circle3, CircleDot, CircleDashed, Square, CheckSquare, Shapes, Tag } },
|
|
3185
|
+
{ label: "Theming & system", icons: { Palette, SwatchBook, Sun, Moon, MonitorSmartphone, Sparkles, Rocket, Zap, Layers, Ruler, Gauge, Accessibility, Lock, Unlock, Bot, Terminal, GitBranch, TrendingUp, Activity } }
|
|
3186
|
+
];
|
|
3187
|
+
var commonIcons = Object.assign({}, ...commonIconGroups.map((g) => g.icons));
|
|
3188
|
+
|
|
3189
|
+
// src/layouts/theme-provider.tsx
|
|
3190
|
+
import * as React43 from "react";
|
|
3191
|
+
import { jsx as jsx47 } from "react/jsx-runtime";
|
|
3192
|
+
var MODE_KEY = "wf-mode";
|
|
3193
|
+
function themeInitScript(storageKey = MODE_KEY) {
|
|
3194
|
+
return `(function(){try{var m=localStorage.getItem('${storageKey}')||'system';var d=m==='dark'||(m==='system'&&window.matchMedia('(prefers-color-scheme: dark)').matches);var e=document.documentElement;e.classList.toggle('dark',d);e.style.colorScheme=d?'dark':'light';}catch(e){}})()`;
|
|
3195
|
+
}
|
|
3196
|
+
var canUseDOM = typeof window !== "undefined";
|
|
3197
|
+
var readStoredMode = () => {
|
|
3198
|
+
try {
|
|
3199
|
+
return canUseDOM ? localStorage.getItem(MODE_KEY) : null;
|
|
3200
|
+
} catch {
|
|
3201
|
+
return null;
|
|
3202
|
+
}
|
|
3203
|
+
};
|
|
3204
|
+
var prefersDark = () => canUseDOM && window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
3205
|
+
var resolveMode = (mode) => mode === "system" ? prefersDark() ? "dark" : "light" : mode;
|
|
3206
|
+
var nextMode = (mode) => mode === "light" ? "dark" : mode === "dark" ? "system" : "light";
|
|
3207
|
+
var ThemeContext = React43.createContext(null);
|
|
3208
|
+
function ThemeProvider({ children, defaultMode = "system", tokens, persist = true }) {
|
|
3209
|
+
const [mode, setModeState] = React43.useState(
|
|
3210
|
+
() => persist && readStoredMode() || defaultMode
|
|
3211
|
+
);
|
|
3212
|
+
const [resolvedMode, setResolvedMode] = React43.useState(() => resolveMode(mode));
|
|
3213
|
+
const applyMode = (mode2) => {
|
|
3214
|
+
const resolved = resolveMode(mode2);
|
|
3215
|
+
setResolvedMode(resolved);
|
|
3216
|
+
if (!canUseDOM) return;
|
|
3217
|
+
const root = document.documentElement;
|
|
3218
|
+
const noTransitions = document.createElement("style");
|
|
3219
|
+
noTransitions.textContent = "*,*::before,*::after{transition:none!important}";
|
|
3220
|
+
document.head.appendChild(noTransitions);
|
|
3221
|
+
root.classList.toggle("dark", resolved === "dark");
|
|
3222
|
+
root.style.colorScheme = resolved;
|
|
3223
|
+
void root.offsetHeight;
|
|
3224
|
+
window.setTimeout(() => noTransitions.remove(), 1);
|
|
3225
|
+
};
|
|
3226
|
+
const setMode = (mode2) => {
|
|
3227
|
+
setModeState(mode2);
|
|
3228
|
+
if (persist) {
|
|
3229
|
+
try {
|
|
3230
|
+
localStorage.setItem(MODE_KEY, mode2);
|
|
3231
|
+
} catch {
|
|
3232
|
+
}
|
|
3233
|
+
}
|
|
3234
|
+
applyMode(mode2);
|
|
3235
|
+
};
|
|
3236
|
+
React43.useEffect(() => {
|
|
3237
|
+
if (mode !== "system") return;
|
|
3238
|
+
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
3239
|
+
const onSystemChange = () => applyMode("system");
|
|
3240
|
+
mediaQuery.addEventListener("change", onSystemChange);
|
|
3241
|
+
return () => mediaQuery.removeEventListener("change", onSystemChange);
|
|
3242
|
+
}, [mode]);
|
|
3243
|
+
const value = { mode, resolvedMode, setMode, cycleMode: () => setMode(nextMode(mode)) };
|
|
3244
|
+
const resolvedTokens = typeof tokens === "function" ? tokens(resolvedMode) : tokens;
|
|
3245
|
+
const tokenSig = resolvedTokens ? JSON.stringify(resolvedTokens) : "";
|
|
3246
|
+
React43.useLayoutEffect(() => {
|
|
3247
|
+
if (!canUseDOM || !tokenSig) return;
|
|
3248
|
+
const map = JSON.parse(tokenSig);
|
|
3249
|
+
const root = document.documentElement;
|
|
3250
|
+
const keys = Object.keys(map);
|
|
3251
|
+
for (const key of keys) root.style.setProperty(key, map[key]);
|
|
3252
|
+
return () => {
|
|
3253
|
+
for (const key of keys) root.style.removeProperty(key);
|
|
3254
|
+
};
|
|
3255
|
+
}, [tokenSig]);
|
|
3256
|
+
return /* @__PURE__ */ jsx47(ThemeContext.Provider, { value, children });
|
|
3257
|
+
}
|
|
3258
|
+
function useTheme() {
|
|
3259
|
+
const ctx = React43.useContext(ThemeContext);
|
|
3260
|
+
if (!ctx) throw new Error("useTheme must be used within a <ThemeProvider>");
|
|
3261
|
+
return ctx;
|
|
3262
|
+
}
|
|
3263
|
+
|
|
3264
|
+
// src/layouts/app-shell.tsx
|
|
3265
|
+
import * as React44 from "react";
|
|
3266
|
+
import { jsx as jsx48, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
3267
|
+
var NAV_WIDTH = 256;
|
|
3268
|
+
var AppShellContext = React44.createContext(null);
|
|
3269
|
+
var useAppShell = () => React44.useContext(AppShellContext);
|
|
3270
|
+
function AppShell({ header, sidebar, children, fullBleed = false, defaultNavOpen = true }) {
|
|
3271
|
+
const isMobile = useMediaQuery("(max-width: 1023px)");
|
|
3272
|
+
const [navOpen, setNavOpen] = React44.useState(defaultNavOpen);
|
|
3273
|
+
const [drawerOpen, setDrawerOpen] = React44.useState(false);
|
|
3274
|
+
const ctx = React44.useMemo(
|
|
3275
|
+
() => ({
|
|
3276
|
+
toggleNav: () => isMobile ? setDrawerOpen((o) => !o) : setNavOpen((o) => !o),
|
|
3277
|
+
closeNav: () => setDrawerOpen(false)
|
|
3278
|
+
}),
|
|
3279
|
+
[isMobile]
|
|
3280
|
+
);
|
|
3281
|
+
const desktopNavVisible = !isMobile && navOpen;
|
|
3282
|
+
return /* @__PURE__ */ jsx48(AppShellContext.Provider, { value: ctx, children: /* @__PURE__ */ jsxs25("div", { className: "flex min-h-screen bg-background text-primary", children: [
|
|
3283
|
+
desktopNavVisible && sidebar && /* @__PURE__ */ jsx48("aside", { className: "fixed inset-y-0 left-0 z-20 shrink-0", style: { width: NAV_WIDTH }, children: sidebar }),
|
|
3284
|
+
/* @__PURE__ */ jsx48(Sheet, { open: isMobile && drawerOpen, onOpenChange: setDrawerOpen, children: /* @__PURE__ */ jsx48(SheetContent, { side: "left", children: /* @__PURE__ */ jsx48(Box, { className: "-m-6 h-[calc(100%+3rem)] w-64", children: sidebar }) }) }),
|
|
3285
|
+
/* @__PURE__ */ jsxs25(
|
|
3286
|
+
"div",
|
|
3287
|
+
{
|
|
3288
|
+
className: "flex min-w-0 flex-1 flex-col transition-[margin] duration-normal",
|
|
3289
|
+
style: { marginLeft: desktopNavVisible ? NAV_WIDTH : 0 },
|
|
3290
|
+
children: [
|
|
3291
|
+
header,
|
|
3292
|
+
/* @__PURE__ */ jsx48("main", { className: cn("flex-1 overflow-auto", !fullBleed && "px-gutter py-lg md:px-xl"), children })
|
|
3293
|
+
]
|
|
3294
|
+
}
|
|
3295
|
+
)
|
|
3296
|
+
] }) });
|
|
3297
|
+
}
|
|
3298
|
+
|
|
3299
|
+
// src/layouts/side-nav.tsx
|
|
3300
|
+
import * as React45 from "react";
|
|
3301
|
+
import * as AccordionPrimitive2 from "@radix-ui/react-accordion";
|
|
3302
|
+
import { ChevronDown as ChevronDown5 } from "lucide-react";
|
|
3303
|
+
import { Fragment as Fragment3, jsx as jsx49, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
3304
|
+
var NavList = ({ className, ...props }) => /* @__PURE__ */ jsx49("ul", { className: cn("flex flex-col", className), ...props });
|
|
3305
|
+
var navButtonBase = "group relative flex w-full items-center gap-2.5 rounded-md px-3 py-1.5 text-left text-sm font-medium transition-colors disabled:opacity-40 disabled:pointer-events-none [&_svg]:size-[18px] [&_svg]:shrink-0 focus-visible:ring-inset " + focusRingInset;
|
|
3306
|
+
var NavButton = React45.forwardRef(
|
|
3307
|
+
({ className, active, ...props }, ref) => /* @__PURE__ */ jsx49(
|
|
3308
|
+
"button",
|
|
3309
|
+
{
|
|
3310
|
+
ref,
|
|
3311
|
+
type: "button",
|
|
3312
|
+
className: cn(
|
|
3313
|
+
navButtonBase,
|
|
3314
|
+
active ? "bg-surface-inset text-primary" : "text-secondary hover:bg-surface-inset hover:text-primary",
|
|
3315
|
+
className
|
|
3316
|
+
),
|
|
3317
|
+
...props
|
|
3318
|
+
}
|
|
3319
|
+
)
|
|
3320
|
+
);
|
|
3321
|
+
NavButton.displayName = "NavButton";
|
|
3322
|
+
var NavSubheader = ({ className, ...props }) => /* @__PURE__ */ jsx49(
|
|
3323
|
+
"li",
|
|
3324
|
+
{
|
|
3325
|
+
className: cn(
|
|
3326
|
+
"mt-md px-3 pb-1.5 pt-1 text-sm font-semibold uppercase tracking-wider text-tertiary first:mt-none",
|
|
3327
|
+
className
|
|
3328
|
+
),
|
|
3329
|
+
...props
|
|
3330
|
+
}
|
|
3331
|
+
);
|
|
3332
|
+
var indent = (depth) => ({
|
|
3333
|
+
paddingLeft: depth > 0 ? `calc(0.75rem + ${depth} * var(--wf-spacing-md))` : void 0
|
|
3334
|
+
});
|
|
3335
|
+
function NavLeaf({
|
|
3336
|
+
item,
|
|
3337
|
+
active,
|
|
3338
|
+
depth,
|
|
3339
|
+
onNavigate
|
|
3340
|
+
}) {
|
|
3341
|
+
return /* @__PURE__ */ jsx49("li", { children: /* @__PURE__ */ jsxs26(
|
|
3342
|
+
NavButton,
|
|
3343
|
+
{
|
|
3344
|
+
active,
|
|
3345
|
+
disabled: item.disabled,
|
|
3346
|
+
"aria-current": active ? "page" : void 0,
|
|
3347
|
+
style: indent(depth),
|
|
3348
|
+
onClick: () => {
|
|
3349
|
+
item.onClick?.();
|
|
3350
|
+
if (item.path) onNavigate(item.path);
|
|
3351
|
+
},
|
|
3352
|
+
children: [
|
|
3353
|
+
active && /* @__PURE__ */ jsx49("span", { className: "absolute left-0 top-1/2 h-5 w-0.5 -translate-y-1/2 rounded-full bg-surface-inverse" }),
|
|
3354
|
+
item.icon,
|
|
3355
|
+
/* @__PURE__ */ jsx49("span", { className: "truncate", children: item.label }),
|
|
3356
|
+
item.badge && /* @__PURE__ */ jsx49("span", { className: "ml-auto", children: item.badge })
|
|
3357
|
+
]
|
|
3358
|
+
}
|
|
3359
|
+
) });
|
|
3360
|
+
}
|
|
3361
|
+
function NavGroup({
|
|
3362
|
+
item,
|
|
3363
|
+
activePath,
|
|
3364
|
+
depth,
|
|
3365
|
+
onNavigate
|
|
3366
|
+
}) {
|
|
3367
|
+
const containsActive = React45.useMemo(() => hasActivePath(item.children, activePath), [item.children, activePath]);
|
|
3368
|
+
const defaultValue = item.defaultOpen ?? containsActive ? "group" : void 0;
|
|
3369
|
+
return /* @__PURE__ */ jsx49("li", { children: /* @__PURE__ */ jsx49(AccordionPrimitive2.Root, { type: "single", collapsible: true, defaultValue, children: /* @__PURE__ */ jsxs26(AccordionPrimitive2.Item, { value: "group", children: [
|
|
3370
|
+
/* @__PURE__ */ jsx49(AccordionPrimitive2.Header, { className: "flex", children: /* @__PURE__ */ jsx49(AccordionPrimitive2.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs26(
|
|
3371
|
+
NavButton,
|
|
3372
|
+
{
|
|
3373
|
+
style: indent(depth),
|
|
3374
|
+
className: "data-[state=open]:[&_svg:last-of-type]:rotate-180",
|
|
3375
|
+
children: [
|
|
3376
|
+
item.icon,
|
|
3377
|
+
/* @__PURE__ */ jsx49("span", { className: "truncate", children: item.label }),
|
|
3378
|
+
/* @__PURE__ */ jsx49(ChevronDown5, { className: "ml-auto !size-4 transition-transform duration-200" })
|
|
3379
|
+
]
|
|
3380
|
+
}
|
|
3381
|
+
) }) }),
|
|
3382
|
+
/* @__PURE__ */ jsx49(AccordionPrimitive2.Content, { className: "overflow-hidden data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down", children: /* @__PURE__ */ jsx49(NavList, { children: item.children.map((child, index) => /* @__PURE__ */ jsx49(NavRenderer, { item: child, activePath, depth: depth + 1, onNavigate }, index)) }) })
|
|
3383
|
+
] }) }) });
|
|
3384
|
+
}
|
|
3385
|
+
function NavRenderer({
|
|
3386
|
+
item,
|
|
3387
|
+
activePath,
|
|
3388
|
+
depth,
|
|
3389
|
+
onNavigate
|
|
3390
|
+
}) {
|
|
3391
|
+
if (item.type === "divider") return /* @__PURE__ */ jsx49("li", { role: "separator", className: "my-sm h-px bg-border" });
|
|
3392
|
+
if (item.type === "section") {
|
|
3393
|
+
return /* @__PURE__ */ jsxs26(Fragment3, { children: [
|
|
3394
|
+
/* @__PURE__ */ jsx49(NavSubheader, { children: item.label }),
|
|
3395
|
+
item.items.map((child, index) => /* @__PURE__ */ jsx49(NavRenderer, { item: child, activePath, depth, onNavigate }, index))
|
|
3396
|
+
] });
|
|
3397
|
+
}
|
|
3398
|
+
if (item.type === "group") {
|
|
3399
|
+
return /* @__PURE__ */ jsx49(NavGroup, { item, activePath, depth, onNavigate });
|
|
3400
|
+
}
|
|
3401
|
+
return /* @__PURE__ */ jsx49(NavLeaf, { item, active: !!item.path && activePath === item.path, depth, onNavigate });
|
|
3402
|
+
}
|
|
3403
|
+
function hasActivePath(items, activePath) {
|
|
3404
|
+
return items.some((item) => {
|
|
3405
|
+
if (item.type === "group") return hasActivePath(item.children, activePath);
|
|
3406
|
+
if (item.type === "section") return hasActivePath(item.items, activePath);
|
|
3407
|
+
if (!item.type || item.type === "item") return item.path === activePath;
|
|
3408
|
+
return false;
|
|
3409
|
+
});
|
|
3410
|
+
}
|
|
3411
|
+
function SideNav({ items, activePath = "/", onNavigate = () => {
|
|
3412
|
+
}, logo }) {
|
|
3413
|
+
const appShell = useAppShell();
|
|
3414
|
+
const handleNavigate = (path) => {
|
|
3415
|
+
onNavigate(path);
|
|
3416
|
+
appShell?.closeNav();
|
|
3417
|
+
};
|
|
3418
|
+
return /* @__PURE__ */ jsxs26("nav", { "aria-label": "Main navigation", className: "flex h-full flex-col border-r border-border bg-surface-subtle", children: [
|
|
3419
|
+
logo && /* @__PURE__ */ jsx49("div", { className: "flex h-16 shrink-0 items-center border-b border-border px-4", children: logo }),
|
|
3420
|
+
/* @__PURE__ */ jsx49(NavList, { className: "flex-1 overflow-y-auto px-sm py-sm", children: items.map((item, index) => /* @__PURE__ */ jsx49(NavRenderer, { item, activePath, depth: 0, onNavigate: handleNavigate }, index)) })
|
|
3421
|
+
] });
|
|
3422
|
+
}
|
|
3423
|
+
|
|
3424
|
+
// src/layouts/app-bar.tsx
|
|
3425
|
+
import { cva as cva12 } from "class-variance-authority";
|
|
3426
|
+
import { Menu as Menu2, Sun as Sun2, Moon as Moon2, MonitorSmartphone as MonitorSmartphone2 } from "lucide-react";
|
|
3427
|
+
import { jsx as jsx50, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
3428
|
+
var appBarVariants = cva12(
|
|
3429
|
+
"sticky top-0 z-30 flex h-16 items-center gap-2 border-b border-border px-4",
|
|
3430
|
+
{
|
|
3431
|
+
variants: {
|
|
3432
|
+
variant: { glass: "backdrop-blur-md", solid: "" },
|
|
3433
|
+
color: { surface: "", subtle: "", inset: "" }
|
|
3434
|
+
},
|
|
3435
|
+
compoundVariants: [
|
|
3436
|
+
{ variant: "glass", color: "surface", class: "bg-surface/80" },
|
|
3437
|
+
{ variant: "glass", color: "subtle", class: "bg-surface-subtle/80" },
|
|
3438
|
+
{ variant: "glass", color: "inset", class: "bg-surface-inset/80" },
|
|
3439
|
+
{ variant: "solid", color: "surface", class: "bg-surface" },
|
|
3440
|
+
{ variant: "solid", color: "subtle", class: "bg-surface-subtle" },
|
|
3441
|
+
{ variant: "solid", color: "inset", class: "bg-surface-inset" }
|
|
3442
|
+
],
|
|
3443
|
+
defaultVariants: { variant: "glass", color: "surface" }
|
|
3444
|
+
}
|
|
3445
|
+
);
|
|
3446
|
+
var modeIcon = {
|
|
3447
|
+
light: /* @__PURE__ */ jsx50(Sun2, { className: "h-5 w-5" }),
|
|
3448
|
+
dark: /* @__PURE__ */ jsx50(Moon2, { className: "h-5 w-5" }),
|
|
3449
|
+
system: /* @__PURE__ */ jsx50(MonitorSmartphone2, { className: "h-5 w-5" })
|
|
3450
|
+
};
|
|
3451
|
+
var modeLabel = {
|
|
3452
|
+
light: "Light \u2014 switch to dark",
|
|
3453
|
+
dark: "Dark \u2014 switch to system",
|
|
3454
|
+
system: "System \u2014 switch to light"
|
|
3455
|
+
};
|
|
3456
|
+
function ModeToggle() {
|
|
3457
|
+
const { mode, cycleMode } = useTheme();
|
|
3458
|
+
return /* @__PURE__ */ jsx50(TooltipProvider, { children: /* @__PURE__ */ jsxs27(Tooltip, { children: [
|
|
3459
|
+
/* @__PURE__ */ jsx50(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx50(Button, { variant: "tertiary", size: "icon", onClick: cycleMode, "aria-label": modeLabel[mode], children: modeIcon[mode] }) }),
|
|
3460
|
+
/* @__PURE__ */ jsx50(TooltipContent, { children: modeLabel[mode] })
|
|
3461
|
+
] }) });
|
|
3462
|
+
}
|
|
3463
|
+
function AppBar({
|
|
3464
|
+
title,
|
|
3465
|
+
logo,
|
|
3466
|
+
actions,
|
|
3467
|
+
onMenuClick,
|
|
3468
|
+
variant,
|
|
3469
|
+
color
|
|
3470
|
+
}) {
|
|
3471
|
+
const appShell = useAppShell();
|
|
3472
|
+
const handleMenu = onMenuClick ?? appShell?.toggleNav;
|
|
3473
|
+
return /* @__PURE__ */ jsxs27("header", { className: cn(appBarVariants({ variant, color })), children: [
|
|
3474
|
+
handleMenu && /* @__PURE__ */ jsx50(Button, { variant: "tertiary", size: "icon", onClick: handleMenu, "aria-label": "Toggle navigation", children: /* @__PURE__ */ jsx50(Menu2, { className: "h-5 w-5" }) }),
|
|
3475
|
+
logo,
|
|
3476
|
+
title && /* @__PURE__ */ jsx50("span", { className: "text-sm font-semibold text-secondary", children: title }),
|
|
3477
|
+
/* @__PURE__ */ jsx50("div", { className: "ml-auto flex items-center gap-1", children: actions })
|
|
3478
|
+
] });
|
|
3479
|
+
}
|
|
3480
|
+
|
|
3481
|
+
// src/index.ts
|
|
3482
|
+
export * from "@windforge/tokens";
|
|
3483
|
+
export {
|
|
3484
|
+
Accordion,
|
|
3485
|
+
AccordionContent,
|
|
3486
|
+
AccordionItem,
|
|
3487
|
+
AccordionTrigger,
|
|
3488
|
+
Alert,
|
|
3489
|
+
AppBar,
|
|
3490
|
+
AppShell,
|
|
3491
|
+
Autocomplete,
|
|
3492
|
+
Avatar,
|
|
3493
|
+
AvatarFallback,
|
|
3494
|
+
AvatarImage,
|
|
3495
|
+
Badge,
|
|
3496
|
+
Box,
|
|
3497
|
+
Breadcrumb,
|
|
3498
|
+
Button,
|
|
3499
|
+
ButtonGroup,
|
|
3500
|
+
Calendar,
|
|
3501
|
+
Card,
|
|
3502
|
+
Chart,
|
|
3503
|
+
Checkbox,
|
|
3504
|
+
Chip,
|
|
3505
|
+
CodeBlock,
|
|
3506
|
+
Command,
|
|
3507
|
+
CommandDialog,
|
|
3508
|
+
CommandEmpty,
|
|
3509
|
+
CommandGroup,
|
|
3510
|
+
CommandInput,
|
|
3511
|
+
CommandItem,
|
|
3512
|
+
CommandList,
|
|
3513
|
+
CommandSeparator,
|
|
3514
|
+
CommandShortcut,
|
|
3515
|
+
DataTable,
|
|
3516
|
+
DatePicker,
|
|
3517
|
+
Dialog,
|
|
3518
|
+
DropdownMenu,
|
|
3519
|
+
DropdownMenuCheckboxItem,
|
|
3520
|
+
DropdownMenuContent,
|
|
3521
|
+
DropdownMenuGroup,
|
|
3522
|
+
DropdownMenuItem,
|
|
3523
|
+
DropdownMenuLabel,
|
|
3524
|
+
DropdownMenuPortal,
|
|
3525
|
+
DropdownMenuRadioGroup,
|
|
3526
|
+
DropdownMenuRadioItem,
|
|
3527
|
+
DropdownMenuSeparator,
|
|
3528
|
+
DropdownMenuShortcut,
|
|
3529
|
+
DropdownMenuSub,
|
|
3530
|
+
DropdownMenuSubContent,
|
|
3531
|
+
DropdownMenuSubTrigger,
|
|
3532
|
+
DropdownMenuTrigger,
|
|
3533
|
+
ForgeIcon,
|
|
3534
|
+
FormField,
|
|
3535
|
+
Grid,
|
|
3536
|
+
H1,
|
|
3537
|
+
H2,
|
|
3538
|
+
H3,
|
|
3539
|
+
H4,
|
|
3540
|
+
H5,
|
|
3541
|
+
H6,
|
|
3542
|
+
Input,
|
|
3543
|
+
Label,
|
|
3544
|
+
Link,
|
|
3545
|
+
Modal,
|
|
3546
|
+
ModalClose,
|
|
3547
|
+
ModalContent,
|
|
3548
|
+
ModalOverlay,
|
|
3549
|
+
ModalPortal,
|
|
3550
|
+
ModalTrigger,
|
|
3551
|
+
ModeToggle,
|
|
3552
|
+
MultiSelect,
|
|
3553
|
+
Pagination,
|
|
3554
|
+
Popover2 as Popover,
|
|
3555
|
+
PopoverAnchor,
|
|
3556
|
+
PopoverContent,
|
|
3557
|
+
PopoverTrigger,
|
|
3558
|
+
Progress,
|
|
3559
|
+
RadioGroup,
|
|
3560
|
+
RadioGroupItem,
|
|
3561
|
+
SVGIcon,
|
|
3562
|
+
Select,
|
|
3563
|
+
SelectContent,
|
|
3564
|
+
SelectGroup,
|
|
3565
|
+
SelectItem,
|
|
3566
|
+
SelectLabel,
|
|
3567
|
+
SelectTrigger,
|
|
3568
|
+
SelectValue,
|
|
3569
|
+
Separator,
|
|
3570
|
+
Sheet,
|
|
3571
|
+
SheetClose,
|
|
3572
|
+
SheetContent,
|
|
3573
|
+
SheetPortal,
|
|
3574
|
+
SheetTrigger,
|
|
3575
|
+
SideNav,
|
|
3576
|
+
Skeleton,
|
|
3577
|
+
Slider,
|
|
3578
|
+
Stack,
|
|
3579
|
+
Stepper,
|
|
3580
|
+
Switch,
|
|
3581
|
+
Table,
|
|
3582
|
+
TableBody,
|
|
3583
|
+
TableCaption,
|
|
3584
|
+
TableCell,
|
|
3585
|
+
TableFooter,
|
|
3586
|
+
TableHead,
|
|
3587
|
+
TableHeader,
|
|
3588
|
+
TableRow,
|
|
3589
|
+
Tabs,
|
|
3590
|
+
TabsContent,
|
|
3591
|
+
TabsList,
|
|
3592
|
+
TabsTrigger,
|
|
3593
|
+
Text,
|
|
3594
|
+
Textarea,
|
|
3595
|
+
ThemeProvider,
|
|
3596
|
+
Toaster,
|
|
3597
|
+
ToggleButton,
|
|
3598
|
+
ToggleButtonGroup,
|
|
3599
|
+
Tooltip,
|
|
3600
|
+
TooltipContent,
|
|
3601
|
+
TooltipProvider,
|
|
3602
|
+
TooltipTrigger,
|
|
3603
|
+
WindforgeLogo,
|
|
3604
|
+
badgeVariants,
|
|
3605
|
+
buttonVariants,
|
|
3606
|
+
catalog,
|
|
3607
|
+
chip as chipVariants,
|
|
3608
|
+
cn,
|
|
3609
|
+
commonIconGroups,
|
|
3610
|
+
commonIcons,
|
|
3611
|
+
dismissButton,
|
|
3612
|
+
dismissToast,
|
|
3613
|
+
floatingPanel,
|
|
3614
|
+
focusRing,
|
|
3615
|
+
focusRingField,
|
|
3616
|
+
focusRingInset,
|
|
3617
|
+
layoutVocabulary,
|
|
3618
|
+
link as linkVariants,
|
|
3619
|
+
menuItem,
|
|
3620
|
+
modalContent,
|
|
3621
|
+
overlayBackdrop,
|
|
3622
|
+
text as textVariants,
|
|
3623
|
+
themeInitScript,
|
|
3624
|
+
toast,
|
|
3625
|
+
useAppShell,
|
|
3626
|
+
useMediaQuery,
|
|
3627
|
+
useTheme
|
|
3628
|
+
};
|