@glyphui/ui 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +137 -2
- package/dist/data-table.d.ts +110 -0
- package/dist/data-table.js +225 -0
- package/dist/field-DXZD0l8w.js +107 -0
- package/dist/field-context-B8cYheXz.js +321 -0
- package/dist/form.d.ts +79 -0
- package/dist/form.js +84 -0
- package/dist/index-BWK4czIG.d.ts +233 -0
- package/dist/index.d.ts +2155 -0
- package/dist/index.js +1529 -0
- package/dist/registry-BGSJoiLq.d.ts +9 -0
- package/dist/states-BxPjCUxz.js +287 -0
- package/dist/tv-DuMh7UH2.d.ts +6 -0
- package/package.json +113 -3
- package/src/components/avatar/avatar.styles.ts +22 -0
- package/src/components/avatar/avatar.tsx +62 -0
- package/src/components/avatar/index.ts +2 -0
- package/src/components/badge/badge.styles.ts +69 -0
- package/src/components/badge/badge.tsx +30 -0
- package/src/components/badge/index.ts +2 -0
- package/src/components/button/button.styles.ts +43 -0
- package/src/components/button/button.tsx +77 -0
- package/src/components/button/index.ts +2 -0
- package/src/components/card/card.styles.ts +32 -0
- package/src/components/card/card.tsx +91 -0
- package/src/components/card/index.ts +11 -0
- package/src/components/checkbox/checkbox.tsx +39 -0
- package/src/components/checkbox/index.ts +1 -0
- package/src/components/container/container.tsx +35 -0
- package/src/components/container/index.ts +1 -0
- package/src/components/eyebrow/eyebrow.styles.ts +14 -0
- package/src/components/eyebrow/eyebrow.tsx +19 -0
- package/src/components/eyebrow/index.ts +2 -0
- package/src/components/field/field-context.ts +40 -0
- package/src/components/field/field.styles.ts +23 -0
- package/src/components/field/field.tsx +109 -0
- package/src/components/field/index.ts +3 -0
- package/src/components/heading/heading.styles.ts +16 -0
- package/src/components/heading/heading.tsx +26 -0
- package/src/components/heading/index.ts +2 -0
- package/src/components/input/index.ts +2 -0
- package/src/components/input/input.styles.ts +36 -0
- package/src/components/input/input.tsx +33 -0
- package/src/components/kbd/index.ts +1 -0
- package/src/components/kbd/kbd.tsx +21 -0
- package/src/components/label/index.ts +1 -0
- package/src/components/label/label.tsx +21 -0
- package/src/components/section/index.ts +1 -0
- package/src/components/section/section.tsx +39 -0
- package/src/components/segmented-control/index.ts +6 -0
- package/src/components/segmented-control/segmented-control.tsx +79 -0
- package/src/components/select/index.ts +1 -0
- package/src/components/select/select.tsx +133 -0
- package/src/components/separator/index.ts +1 -0
- package/src/components/separator/separator.tsx +29 -0
- package/src/components/skeleton/index.ts +1 -0
- package/src/components/skeleton/skeleton.tsx +20 -0
- package/src/components/spinner/index.ts +2 -0
- package/src/components/spinner/spinner.styles.ts +19 -0
- package/src/components/spinner/spinner.tsx +42 -0
- package/src/components/stack/index.ts +1 -0
- package/src/components/stack/stack.tsx +62 -0
- package/src/components/switch/index.ts +1 -0
- package/src/components/switch/switch.tsx +48 -0
- package/src/components/table/index.ts +10 -0
- package/src/components/table/table.tsx +90 -0
- package/src/components/tabs/index.ts +1 -0
- package/src/components/tabs/tabs.tsx +92 -0
- package/src/components/text/index.ts +2 -0
- package/src/components/text/text.styles.ts +32 -0
- package/src/components/text/text.tsx +17 -0
- package/src/components/textarea/index.ts +1 -0
- package/src/components/textarea/textarea.tsx +27 -0
- package/src/components/tooltip/index.ts +1 -0
- package/src/components/tooltip/tooltip.tsx +42 -0
- package/src/hooks/index.ts +4 -0
- package/src/hooks/use-component-config.ts +42 -0
- package/src/hooks/use-controllable-state.ts +27 -0
- package/src/hooks/use-media-query.ts +14 -0
- package/src/hooks/use-scrolled.ts +14 -0
- package/src/index.ts +52 -0
- package/src/internal/icons.tsx +99 -0
- package/src/patterns/app-shell/app-shell-context.ts +27 -0
- package/src/patterns/app-shell/app-shell.styles.ts +127 -0
- package/src/patterns/app-shell/app-shell.tsx +81 -0
- package/src/patterns/app-shell/index.ts +16 -0
- package/src/patterns/app-shell/navbar.tsx +119 -0
- package/src/patterns/app-shell/page-header.tsx +30 -0
- package/src/patterns/app-shell/sidebar.tsx +163 -0
- package/src/patterns/async/async.tsx +83 -0
- package/src/patterns/async/index.ts +10 -0
- package/src/patterns/async/states.tsx +128 -0
- package/src/patterns/data-table/data-table.tsx +367 -0
- package/src/patterns/data-table/index.ts +2 -0
- package/src/patterns/form/form.tsx +150 -0
- package/src/patterns/form/index.ts +13 -0
- package/src/provider/color-mode.tsx +133 -0
- package/src/provider/glyph-context.ts +42 -0
- package/src/provider/glyph-provider.tsx +41 -0
- package/src/provider/index.ts +18 -0
- package/src/provider/theme-scope.tsx +36 -0
- package/src/provider/theme-style.tsx +19 -0
- package/src/register.ts +69 -0
- package/src/themes.ts +56 -0
- package/src/utils/cn.ts +12 -0
- package/src/utils/compose-refs.ts +11 -0
- package/src/utils/registry.ts +9 -0
- package/src/utils/styles.ts +13 -0
- package/src/utils/tv.ts +7 -0
- package/src/utils/tw-merge-config.ts +14 -0
- package/styles.css +178 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1529 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { A as useGlyph, C as popover, D as twMergeConfig, E as tv, O as useComponentConfig, S as focusRing, T as transition, _ as Button, b as Spinner, f as MenuIcon, g as XIcon, j as useLinkComponent, k as GlyphContext, m as PanelLeftIcon, n as useFieldControl, o as CheckIcon, s as ChevronDownIcon, t as FieldContext, v as IconButton, w as themedBorder, x as spinnerStyles, y as buttonStyles } from "./field-context-B8cYheXz.js";
|
|
3
|
+
import { _ as inputStyles, a as Table, c as TableCell, d as TableRow, f as tableStyles, g as fieldControl, h as Input, i as stateStyles, l as TableHead, m as skeletonStyles, n as ErrorState, o as TableBody, p as Skeleton, r as LoadingState, s as TableCaption, t as EmptyState, u as TableHeader, v as Checkbox, y as checkboxStyles } from "./states-BxPjCUxz.js";
|
|
4
|
+
import { i as labelStyles, n as fieldStyles, r as Label, t as Field } from "./field-DXZD0l8w.js";
|
|
5
|
+
import { defineTheme, defineTheme as defineTheme$1, presets as presets$1, scopeSelector, themeToCss, themeToCss as themeToCss$1, tokenVar } from "@glyphui/tokens";
|
|
6
|
+
import { Avatar as Avatar$1, Dialog, Select as Select$1, Separator as Separator$1, Slot, Switch as Switch$1, Tabs as Tabs$1, ToggleGroup, Tooltip as Tooltip$1 } from "radix-ui";
|
|
7
|
+
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
8
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
import { clsx } from "clsx";
|
|
10
|
+
import { extendTailwindMerge } from "tailwind-merge";
|
|
11
|
+
//#region src/components/avatar/avatar.styles.ts
|
|
12
|
+
const avatarStyles = tv({
|
|
13
|
+
slots: {
|
|
14
|
+
root: "relative inline-flex shrink-0 select-none items-center justify-center overflow-hidden rounded-pill bg-muted align-middle",
|
|
15
|
+
image: "size-full object-cover",
|
|
16
|
+
fallback: "flex size-full items-center justify-center font-medium text-muted-foreground",
|
|
17
|
+
group: "flex items-center -space-x-2 [&>[data-slot=avatar]]:ring-2 [&>[data-slot=avatar]]:ring-background"
|
|
18
|
+
},
|
|
19
|
+
variants: { size: {
|
|
20
|
+
xs: {
|
|
21
|
+
root: "size-5",
|
|
22
|
+
fallback: "text-[0.5625rem]"
|
|
23
|
+
},
|
|
24
|
+
sm: {
|
|
25
|
+
root: "size-6",
|
|
26
|
+
fallback: "text-[0.625rem]"
|
|
27
|
+
},
|
|
28
|
+
md: {
|
|
29
|
+
root: "size-8",
|
|
30
|
+
fallback: "text-caption"
|
|
31
|
+
},
|
|
32
|
+
lg: {
|
|
33
|
+
root: "size-11",
|
|
34
|
+
fallback: "text-small"
|
|
35
|
+
}
|
|
36
|
+
} },
|
|
37
|
+
defaultVariants: { size: "md" }
|
|
38
|
+
});
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region src/components/avatar/avatar.tsx
|
|
41
|
+
function initials(name) {
|
|
42
|
+
const parts = name.trim().split(/\s+/);
|
|
43
|
+
return ((parts[0]?.[0] ?? "") + (parts.length > 1 ? parts.at(-1)?.[0] ?? "" : "")).toUpperCase();
|
|
44
|
+
}
|
|
45
|
+
/** A stable hue per name, so fallback avatars are distinguishable but not random. */
|
|
46
|
+
function hue(name) {
|
|
47
|
+
let h = 0;
|
|
48
|
+
for (const char of name) h = (h * 31 + char.charCodeAt(0)) % 360;
|
|
49
|
+
return h;
|
|
50
|
+
}
|
|
51
|
+
function Avatar(inProps) {
|
|
52
|
+
const [props, slots] = useComponentConfig("Avatar", inProps);
|
|
53
|
+
const { name, src, size, className, style, ...rest } = props;
|
|
54
|
+
const s = avatarStyles({ size });
|
|
55
|
+
const tint = {
|
|
56
|
+
"--avatar-hue": hue(name),
|
|
57
|
+
...style
|
|
58
|
+
};
|
|
59
|
+
return /* @__PURE__ */ jsxs(Avatar$1.Root, {
|
|
60
|
+
"data-slot": "avatar",
|
|
61
|
+
className: s.root({ class: [slots.root, className] }),
|
|
62
|
+
style: tint,
|
|
63
|
+
...rest,
|
|
64
|
+
children: [src && /* @__PURE__ */ jsx(Avatar$1.Image, {
|
|
65
|
+
src,
|
|
66
|
+
alt: name,
|
|
67
|
+
className: s.image({ class: slots.image })
|
|
68
|
+
}), /* @__PURE__ */ jsx(Avatar$1.Fallback, {
|
|
69
|
+
className: s.fallback({ class: ["bg-[oklch(68%_0.11_var(--avatar-hue)/0.22)] text-foreground", slots.fallback] }),
|
|
70
|
+
"aria-label": src ? void 0 : name,
|
|
71
|
+
children: initials(name)
|
|
72
|
+
})]
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
function AvatarGroup({ className, ...props }) {
|
|
76
|
+
const [, slots] = useComponentConfig("Avatar", {});
|
|
77
|
+
return /* @__PURE__ */ jsx("div", {
|
|
78
|
+
"data-slot": "avatar-group",
|
|
79
|
+
className: avatarStyles().group({ class: [slots.group, className] }),
|
|
80
|
+
...props
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
//#endregion
|
|
84
|
+
//#region src/components/badge/badge.styles.ts
|
|
85
|
+
const tones = {
|
|
86
|
+
neutral: {
|
|
87
|
+
soft: "bg-muted text-muted-foreground",
|
|
88
|
+
solid: "bg-secondary text-secondary-foreground",
|
|
89
|
+
outline: "border-border-strong text-foreground"
|
|
90
|
+
},
|
|
91
|
+
primary: {
|
|
92
|
+
soft: "bg-primary/14 text-[color-mix(in_oklab,var(--color-primary)_62%,var(--color-foreground))]",
|
|
93
|
+
solid: "bg-primary text-primary-foreground",
|
|
94
|
+
outline: "border-primary/50 text-[color-mix(in_oklab,var(--color-primary)_62%,var(--color-foreground))]"
|
|
95
|
+
},
|
|
96
|
+
success: {
|
|
97
|
+
soft: "bg-success/14 text-[color-mix(in_oklab,var(--color-success)_62%,var(--color-foreground))]",
|
|
98
|
+
solid: "bg-success text-success-foreground",
|
|
99
|
+
outline: "border-success/50 text-[color-mix(in_oklab,var(--color-success)_62%,var(--color-foreground))]"
|
|
100
|
+
},
|
|
101
|
+
warning: {
|
|
102
|
+
soft: "bg-warning/16 text-[color-mix(in_oklab,var(--color-warning)_62%,var(--color-foreground))]",
|
|
103
|
+
solid: "bg-warning text-warning-foreground",
|
|
104
|
+
outline: "border-warning/50 text-[color-mix(in_oklab,var(--color-warning)_62%,var(--color-foreground))]"
|
|
105
|
+
},
|
|
106
|
+
danger: {
|
|
107
|
+
soft: "bg-danger/14 text-[color-mix(in_oklab,var(--color-danger)_62%,var(--color-foreground))]",
|
|
108
|
+
solid: "bg-danger text-danger-foreground",
|
|
109
|
+
outline: "border-danger/50 text-[color-mix(in_oklab,var(--color-danger)_62%,var(--color-foreground))]"
|
|
110
|
+
},
|
|
111
|
+
info: {
|
|
112
|
+
soft: "bg-info/14 text-[color-mix(in_oklab,var(--color-info)_62%,var(--color-foreground))]",
|
|
113
|
+
solid: "bg-info text-info-foreground",
|
|
114
|
+
outline: "border-info/50 text-[color-mix(in_oklab,var(--color-info)_62%,var(--color-foreground))]"
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
const badgeStyles = tv({
|
|
118
|
+
slots: {
|
|
119
|
+
base: [
|
|
120
|
+
"inline-flex h-5.5 shrink-0 items-center gap-1.5 whitespace-nowrap rounded-pill border-transparent px-2",
|
|
121
|
+
"text-caption tabular-nums",
|
|
122
|
+
themedBorder
|
|
123
|
+
],
|
|
124
|
+
dot: "size-1.5 shrink-0 rounded-pill bg-current"
|
|
125
|
+
},
|
|
126
|
+
variants: {
|
|
127
|
+
tone: {
|
|
128
|
+
neutral: {},
|
|
129
|
+
primary: {},
|
|
130
|
+
success: {},
|
|
131
|
+
warning: {},
|
|
132
|
+
danger: {},
|
|
133
|
+
info: {}
|
|
134
|
+
},
|
|
135
|
+
variant: {
|
|
136
|
+
soft: {},
|
|
137
|
+
solid: {},
|
|
138
|
+
outline: { base: "bg-transparent" }
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
compoundVariants: Object.keys(tones).flatMap((tone) => Object.keys(tones[tone]).map((variant) => ({
|
|
142
|
+
tone,
|
|
143
|
+
variant,
|
|
144
|
+
class: { base: tones[tone][variant] }
|
|
145
|
+
}))),
|
|
146
|
+
defaultVariants: {
|
|
147
|
+
tone: "neutral",
|
|
148
|
+
variant: "soft"
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
//#endregion
|
|
152
|
+
//#region src/components/badge/badge.tsx
|
|
153
|
+
function Badge(inProps) {
|
|
154
|
+
const [props, slots] = useComponentConfig("Badge", inProps);
|
|
155
|
+
const { tone, variant, dot, asChild, className, children, ...rest } = props;
|
|
156
|
+
const s = badgeStyles({
|
|
157
|
+
tone,
|
|
158
|
+
variant
|
|
159
|
+
});
|
|
160
|
+
const Comp = asChild ? Slot.Root : "span";
|
|
161
|
+
return /* @__PURE__ */ jsxs(Comp, {
|
|
162
|
+
"data-slot": "badge",
|
|
163
|
+
"data-tone": tone ?? "neutral",
|
|
164
|
+
className: s.base({ class: [slots.base, className] }),
|
|
165
|
+
...rest,
|
|
166
|
+
children: [dot && /* @__PURE__ */ jsx("span", {
|
|
167
|
+
"data-slot": "badge-dot",
|
|
168
|
+
className: s.dot({ class: slots.dot }),
|
|
169
|
+
"aria-hidden": "true"
|
|
170
|
+
}), /* @__PURE__ */ jsx(Slot.Slottable, { children })]
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
//#endregion
|
|
174
|
+
//#region src/components/card/card.styles.ts
|
|
175
|
+
const cardStyles = tv({
|
|
176
|
+
slots: {
|
|
177
|
+
root: [
|
|
178
|
+
"flex flex-col rounded-lg text-foreground",
|
|
179
|
+
themedBorder,
|
|
180
|
+
transition
|
|
181
|
+
],
|
|
182
|
+
header: "flex flex-col gap-1",
|
|
183
|
+
title: "font-display text-h4 text-foreground",
|
|
184
|
+
description: "text-small text-muted-foreground",
|
|
185
|
+
body: "min-w-0",
|
|
186
|
+
footer: "mt-auto flex items-center gap-2 pt-1"
|
|
187
|
+
},
|
|
188
|
+
variants: {
|
|
189
|
+
variant: {
|
|
190
|
+
outline: { root: "border-border bg-surface" },
|
|
191
|
+
elevated: { root: "border-border bg-surface shadow-md" },
|
|
192
|
+
ghost: { root: "border-transparent bg-subtle" },
|
|
193
|
+
interactive: { root: "cursor-pointer border-border bg-surface shadow-sm hover:-translate-y-0.5 hover:border-border-strong hover:shadow-lg" }
|
|
194
|
+
},
|
|
195
|
+
padding: {
|
|
196
|
+
none: { root: "gap-0 p-0" },
|
|
197
|
+
sm: { root: "gap-3 p-4" },
|
|
198
|
+
md: { root: "gap-4 p-6" },
|
|
199
|
+
lg: { root: "gap-5 p-8" }
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
defaultVariants: {
|
|
203
|
+
variant: "elevated",
|
|
204
|
+
padding: "md"
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
//#endregion
|
|
208
|
+
//#region src/components/card/card.tsx
|
|
209
|
+
const CardContext = createContext(null);
|
|
210
|
+
function useCardParts() {
|
|
211
|
+
return useContext(CardContext) ?? {
|
|
212
|
+
s: cardStyles(),
|
|
213
|
+
slots: {}
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
/** A surface that groups related content. Compose with CardHeader, CardTitle, CardBody, CardFooter. */
|
|
217
|
+
function Card(inProps) {
|
|
218
|
+
const [props, slots] = useComponentConfig("Card", inProps);
|
|
219
|
+
const { variant, padding, asChild, className, ...rest } = props;
|
|
220
|
+
const s = cardStyles({
|
|
221
|
+
variant,
|
|
222
|
+
padding
|
|
223
|
+
});
|
|
224
|
+
const Comp = asChild ? Slot.Root : "div";
|
|
225
|
+
return /* @__PURE__ */ jsx(CardContext.Provider, {
|
|
226
|
+
value: {
|
|
227
|
+
s,
|
|
228
|
+
slots
|
|
229
|
+
},
|
|
230
|
+
children: /* @__PURE__ */ jsx(Comp, {
|
|
231
|
+
"data-slot": "card",
|
|
232
|
+
"data-variant": variant,
|
|
233
|
+
className: s.root({ class: [slots.root, className] }),
|
|
234
|
+
...rest
|
|
235
|
+
})
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
function CardHeader({ className, ...props }) {
|
|
239
|
+
const { s, slots } = useCardParts();
|
|
240
|
+
return /* @__PURE__ */ jsx("div", {
|
|
241
|
+
"data-slot": "card-header",
|
|
242
|
+
className: s.header({ class: [slots.header, className] }),
|
|
243
|
+
...props
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
function CardTitle({ asChild, className, ...props }) {
|
|
247
|
+
const { s, slots } = useCardParts();
|
|
248
|
+
const Comp = asChild ? Slot.Root : "h3";
|
|
249
|
+
return /* @__PURE__ */ jsx(Comp, {
|
|
250
|
+
"data-slot": "card-title",
|
|
251
|
+
className: s.title({ class: [slots.title, className] }),
|
|
252
|
+
...props
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
function CardDescription({ className, ...props }) {
|
|
256
|
+
const { s, slots } = useCardParts();
|
|
257
|
+
return /* @__PURE__ */ jsx("p", {
|
|
258
|
+
"data-slot": "card-description",
|
|
259
|
+
className: s.description({ class: [slots.description, className] }),
|
|
260
|
+
...props
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
function CardBody({ className, ...props }) {
|
|
264
|
+
const { s, slots } = useCardParts();
|
|
265
|
+
return /* @__PURE__ */ jsx("div", {
|
|
266
|
+
"data-slot": "card-body",
|
|
267
|
+
className: s.body({ class: [slots.body, className] }),
|
|
268
|
+
...props
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
function CardFooter({ className, ...props }) {
|
|
272
|
+
const { s, slots } = useCardParts();
|
|
273
|
+
return /* @__PURE__ */ jsx("div", {
|
|
274
|
+
"data-slot": "card-footer",
|
|
275
|
+
className: s.footer({ class: [slots.footer, className] }),
|
|
276
|
+
...props
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
//#endregion
|
|
280
|
+
//#region src/components/container/container.tsx
|
|
281
|
+
const containerStyles = tv({
|
|
282
|
+
base: "mx-auto w-full px-4 sm:px-6",
|
|
283
|
+
variants: { size: {
|
|
284
|
+
sm: "max-w-2xl",
|
|
285
|
+
md: "max-w-3xl",
|
|
286
|
+
lg: "max-w-5xl",
|
|
287
|
+
xl: "max-w-7xl",
|
|
288
|
+
full: "max-w-none"
|
|
289
|
+
} },
|
|
290
|
+
defaultVariants: { size: "xl" }
|
|
291
|
+
});
|
|
292
|
+
/** Centers content at a readable max width with consistent side gutters. */
|
|
293
|
+
function Container({ size, asChild, className, ...props }) {
|
|
294
|
+
const Comp = asChild ? Slot.Root : "div";
|
|
295
|
+
return /* @__PURE__ */ jsx(Comp, {
|
|
296
|
+
"data-slot": "container",
|
|
297
|
+
className: containerStyles({
|
|
298
|
+
size,
|
|
299
|
+
class: className
|
|
300
|
+
}),
|
|
301
|
+
...props
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
//#endregion
|
|
305
|
+
//#region src/components/eyebrow/eyebrow.styles.ts
|
|
306
|
+
const eyebrowStyles = tv({
|
|
307
|
+
slots: { base: "font-mono text-caption uppercase tracking-[0.12em]" },
|
|
308
|
+
variants: { tone: {
|
|
309
|
+
muted: { base: "text-muted-foreground" },
|
|
310
|
+
primary: { base: "text-primary" }
|
|
311
|
+
} },
|
|
312
|
+
defaultVariants: { tone: "muted" }
|
|
313
|
+
});
|
|
314
|
+
//#endregion
|
|
315
|
+
//#region src/components/eyebrow/eyebrow.tsx
|
|
316
|
+
/** Small uppercase label that sits above a heading. */
|
|
317
|
+
function Eyebrow(inProps) {
|
|
318
|
+
const [props, slots] = useComponentConfig("Eyebrow", inProps);
|
|
319
|
+
const { tone, asChild, className, ...rest } = props;
|
|
320
|
+
const s = eyebrowStyles({ tone });
|
|
321
|
+
const Comp = asChild ? Slot.Root : "p";
|
|
322
|
+
return /* @__PURE__ */ jsx(Comp, {
|
|
323
|
+
"data-slot": "eyebrow",
|
|
324
|
+
className: s.base({ class: [slots.base, className] }),
|
|
325
|
+
...rest
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
//#endregion
|
|
329
|
+
//#region src/components/heading/heading.styles.ts
|
|
330
|
+
const headingStyles = tv({
|
|
331
|
+
slots: { base: "font-display text-balance text-foreground" },
|
|
332
|
+
variants: { size: {
|
|
333
|
+
display: { base: "text-display" },
|
|
334
|
+
h1: { base: "text-h1" },
|
|
335
|
+
h2: { base: "text-h2" },
|
|
336
|
+
h3: { base: "text-h3" },
|
|
337
|
+
h4: { base: "text-h4" }
|
|
338
|
+
} }
|
|
339
|
+
});
|
|
340
|
+
//#endregion
|
|
341
|
+
//#region src/components/heading/heading.tsx
|
|
342
|
+
const sizeForLevel = {
|
|
343
|
+
1: "h1",
|
|
344
|
+
2: "h2",
|
|
345
|
+
3: "h3",
|
|
346
|
+
4: "h4",
|
|
347
|
+
5: "h4",
|
|
348
|
+
6: "h4"
|
|
349
|
+
};
|
|
350
|
+
/** A heading whose semantic level and visual size are set independently. */
|
|
351
|
+
function Heading(inProps) {
|
|
352
|
+
const [props, slots] = useComponentConfig("Heading", inProps);
|
|
353
|
+
const { level = 2, size, asChild, className, ...rest } = props;
|
|
354
|
+
const s = headingStyles({ size: size ?? sizeForLevel[level] });
|
|
355
|
+
const Comp = asChild ? Slot.Root : `h${level}`;
|
|
356
|
+
return /* @__PURE__ */ jsx(Comp, {
|
|
357
|
+
"data-slot": "heading",
|
|
358
|
+
className: s.base({ class: [slots.base, className] }),
|
|
359
|
+
...rest
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
//#endregion
|
|
363
|
+
//#region src/components/kbd/kbd.tsx
|
|
364
|
+
const kbdStyles = tv({ slots: { base: "inline-flex h-5 min-w-5 items-center justify-center rounded-xs border border-border bg-muted px-1 font-mono text-[0.6875rem] font-medium text-muted-foreground shadow-xs" } });
|
|
365
|
+
/** A keyboard key, e.g. `<Kbd>⌘K</Kbd>`. */
|
|
366
|
+
function Kbd(inProps) {
|
|
367
|
+
const [{ className, ...rest }, slots] = useComponentConfig("Kbd", inProps);
|
|
368
|
+
return /* @__PURE__ */ jsx("kbd", {
|
|
369
|
+
"data-slot": "kbd",
|
|
370
|
+
className: kbdStyles().base({ class: [slots.base, className] }),
|
|
371
|
+
...rest
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
//#endregion
|
|
375
|
+
//#region src/components/section/section.tsx
|
|
376
|
+
const sectionStyles = tv({
|
|
377
|
+
base: "w-full",
|
|
378
|
+
variants: {
|
|
379
|
+
spacing: {
|
|
380
|
+
none: "py-0",
|
|
381
|
+
sm: "py-10 sm:py-12",
|
|
382
|
+
md: "py-16 sm:py-20",
|
|
383
|
+
lg: "py-20 sm:py-28"
|
|
384
|
+
},
|
|
385
|
+
tone: {
|
|
386
|
+
default: "",
|
|
387
|
+
subtle: "bg-subtle"
|
|
388
|
+
},
|
|
389
|
+
divided: { true: "border-t-(length:--glyph-border-width) border-border" }
|
|
390
|
+
},
|
|
391
|
+
defaultVariants: {
|
|
392
|
+
spacing: "md",
|
|
393
|
+
tone: "default"
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
/** A full-width page band with consistent vertical rhythm. Put a Container inside. */
|
|
397
|
+
function Section({ spacing, tone, divided, asChild, className, ...props }) {
|
|
398
|
+
const Comp = asChild ? Slot.Root : "section";
|
|
399
|
+
return /* @__PURE__ */ jsx(Comp, {
|
|
400
|
+
"data-slot": "section",
|
|
401
|
+
className: sectionStyles({
|
|
402
|
+
spacing,
|
|
403
|
+
tone,
|
|
404
|
+
divided,
|
|
405
|
+
class: className
|
|
406
|
+
}),
|
|
407
|
+
...props
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
//#endregion
|
|
411
|
+
//#region src/components/segmented-control/segmented-control.tsx
|
|
412
|
+
const segmentedControlStyles = tv({
|
|
413
|
+
slots: {
|
|
414
|
+
root: ["inline-flex items-center gap-0.5 rounded-md border-border bg-muted p-0.5", themedBorder],
|
|
415
|
+
item: [
|
|
416
|
+
"inline-flex h-full cursor-pointer select-none items-center justify-center gap-1.5 whitespace-nowrap rounded-[calc(var(--radius-md)-2px)] px-3 font-medium text-muted-foreground",
|
|
417
|
+
transition,
|
|
418
|
+
focusRing,
|
|
419
|
+
"hover:text-foreground data-[state=on]:bg-raised data-[state=on]:text-foreground data-[state=on]:shadow-xs",
|
|
420
|
+
"disabled:pointer-events-none disabled:opacity-50 [&>svg]:size-4"
|
|
421
|
+
]
|
|
422
|
+
},
|
|
423
|
+
variants: {
|
|
424
|
+
size: {
|
|
425
|
+
sm: {
|
|
426
|
+
root: "h-(--glyph-control-sm)",
|
|
427
|
+
item: "px-2.5 text-caption"
|
|
428
|
+
},
|
|
429
|
+
md: {
|
|
430
|
+
root: "h-(--glyph-control-md)",
|
|
431
|
+
item: "text-small"
|
|
432
|
+
}
|
|
433
|
+
},
|
|
434
|
+
fullWidth: { true: {
|
|
435
|
+
root: "flex w-full",
|
|
436
|
+
item: "flex-1"
|
|
437
|
+
} }
|
|
438
|
+
},
|
|
439
|
+
defaultVariants: { size: "md" }
|
|
440
|
+
});
|
|
441
|
+
/**
|
|
442
|
+
* Pick exactly one of a few options. `T` is inferred from `options`, so `onValueChange`
|
|
443
|
+
* receives your union type, not `string`.
|
|
444
|
+
*/
|
|
445
|
+
function SegmentedControl(inProps) {
|
|
446
|
+
const [props, slots] = useComponentConfig("SegmentedControl", inProps);
|
|
447
|
+
const { options, value, defaultValue, onValueChange, size, fullWidth, className, ...rest } = props;
|
|
448
|
+
const s = segmentedControlStyles({
|
|
449
|
+
size,
|
|
450
|
+
fullWidth
|
|
451
|
+
});
|
|
452
|
+
return /* @__PURE__ */ jsx(ToggleGroup.Root, {
|
|
453
|
+
type: "single",
|
|
454
|
+
"data-slot": "segmented-control",
|
|
455
|
+
...value !== void 0 && { value },
|
|
456
|
+
...defaultValue !== void 0 && { defaultValue },
|
|
457
|
+
onValueChange: (next) => next && onValueChange?.(next),
|
|
458
|
+
className: s.root({ class: [slots.root, className] }),
|
|
459
|
+
...rest,
|
|
460
|
+
children: options.map((option) => /* @__PURE__ */ jsx(ToggleGroup.Item, {
|
|
461
|
+
value: option.value,
|
|
462
|
+
disabled: option.disabled,
|
|
463
|
+
className: s.item({ class: slots.item }),
|
|
464
|
+
children: option.label
|
|
465
|
+
}, option.value))
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
//#endregion
|
|
469
|
+
//#region src/components/select/select.tsx
|
|
470
|
+
const selectStyles = tv({
|
|
471
|
+
slots: {
|
|
472
|
+
trigger: [
|
|
473
|
+
...fieldControl,
|
|
474
|
+
"flex cursor-pointer items-center justify-between gap-2 px-3 text-left outline-none",
|
|
475
|
+
"focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/20",
|
|
476
|
+
"data-placeholder:text-faint-foreground disabled:cursor-not-allowed disabled:opacity-50",
|
|
477
|
+
"[&>span]:truncate"
|
|
478
|
+
],
|
|
479
|
+
icon: "size-4 shrink-0 text-faint-foreground",
|
|
480
|
+
content: [popover, "max-h-(--radix-select-content-available-height) min-w-(--radix-select-trigger-width)"],
|
|
481
|
+
viewport: "p-1",
|
|
482
|
+
item: ["relative flex h-8 cursor-pointer select-none items-center gap-2 rounded-sm pr-8 pl-2 text-small outline-none", "data-highlighted:bg-muted data-disabled:pointer-events-none data-disabled:opacity-50"],
|
|
483
|
+
indicator: "absolute right-2 flex items-center text-primary [&>svg]:size-4"
|
|
484
|
+
},
|
|
485
|
+
variants: { size: {
|
|
486
|
+
sm: { trigger: "h-(--glyph-control-sm) text-small" },
|
|
487
|
+
md: { trigger: "h-(--glyph-control-md) text-small" },
|
|
488
|
+
lg: { trigger: "h-(--glyph-control-lg) text-body" }
|
|
489
|
+
} },
|
|
490
|
+
defaultVariants: { size: "md" }
|
|
491
|
+
});
|
|
492
|
+
/**
|
|
493
|
+
* A dropdown select. `value` is typed by the `T` you use:
|
|
494
|
+
* `<Select<Plan> value={plan} onValueChange={setPlan}>` only accepts `Plan` values.
|
|
495
|
+
*/
|
|
496
|
+
function Select(inProps) {
|
|
497
|
+
const [props, slots] = useComponentConfig("Select", useFieldControl(inProps));
|
|
498
|
+
const { size, placeholder, id, className, contentClassName, children, onValueChange, "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid, ...rootProps } = props;
|
|
499
|
+
const s = selectStyles({ size });
|
|
500
|
+
return /* @__PURE__ */ jsxs(Select$1.Root, {
|
|
501
|
+
...rootProps,
|
|
502
|
+
...onValueChange && { onValueChange: (v) => onValueChange(v) },
|
|
503
|
+
children: [/* @__PURE__ */ jsxs(Select$1.Trigger, {
|
|
504
|
+
id,
|
|
505
|
+
"data-slot": "select-trigger",
|
|
506
|
+
"aria-label": ariaLabel,
|
|
507
|
+
"aria-describedby": ariaDescribedBy,
|
|
508
|
+
"aria-invalid": ariaInvalid,
|
|
509
|
+
className: s.trigger({ class: [slots.trigger, className] }),
|
|
510
|
+
children: [/* @__PURE__ */ jsx(Select$1.Value, { placeholder }), /* @__PURE__ */ jsx(Select$1.Icon, {
|
|
511
|
+
asChild: true,
|
|
512
|
+
children: /* @__PURE__ */ jsx(ChevronDownIcon, { className: s.icon({ class: slots.icon }) })
|
|
513
|
+
})]
|
|
514
|
+
}), /* @__PURE__ */ jsx(Select$1.Portal, { children: /* @__PURE__ */ jsx(Select$1.Content, {
|
|
515
|
+
position: "popper",
|
|
516
|
+
sideOffset: 4,
|
|
517
|
+
"data-slot": "select-content",
|
|
518
|
+
className: s.content({ class: [slots.content, contentClassName] }),
|
|
519
|
+
children: /* @__PURE__ */ jsx(Select$1.Viewport, {
|
|
520
|
+
className: s.viewport({ class: slots.viewport }),
|
|
521
|
+
children
|
|
522
|
+
})
|
|
523
|
+
}) })]
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
function SelectItem({ className, children, ...props }) {
|
|
527
|
+
const [, slots] = useComponentConfig("Select", {});
|
|
528
|
+
const s = selectStyles();
|
|
529
|
+
return /* @__PURE__ */ jsxs(Select$1.Item, {
|
|
530
|
+
"data-slot": "select-item",
|
|
531
|
+
className: s.item({ class: [slots.item, className] }),
|
|
532
|
+
...props,
|
|
533
|
+
children: [/* @__PURE__ */ jsx(Select$1.ItemText, { children }), /* @__PURE__ */ jsx(Select$1.ItemIndicator, {
|
|
534
|
+
className: s.indicator({ class: slots.indicator }),
|
|
535
|
+
children: /* @__PURE__ */ jsx(CheckIcon, {})
|
|
536
|
+
})]
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
//#endregion
|
|
540
|
+
//#region src/components/separator/separator.tsx
|
|
541
|
+
const separatorStyles = tv({
|
|
542
|
+
slots: { base: "shrink-0 bg-border" },
|
|
543
|
+
variants: { orientation: {
|
|
544
|
+
horizontal: { base: "h-px w-full" },
|
|
545
|
+
vertical: { base: "h-full w-px self-stretch" }
|
|
546
|
+
} },
|
|
547
|
+
defaultVariants: { orientation: "horizontal" }
|
|
548
|
+
});
|
|
549
|
+
function Separator(inProps) {
|
|
550
|
+
const [props, slots] = useComponentConfig("Separator", inProps);
|
|
551
|
+
const { orientation = "horizontal", decorative = true, className, ...rest } = props;
|
|
552
|
+
return /* @__PURE__ */ jsx(Separator$1.Root, {
|
|
553
|
+
"data-slot": "separator",
|
|
554
|
+
orientation,
|
|
555
|
+
decorative,
|
|
556
|
+
className: separatorStyles({ orientation }).base({ class: [slots.base, className] }),
|
|
557
|
+
...rest
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
//#endregion
|
|
561
|
+
//#region src/components/stack/stack.tsx
|
|
562
|
+
const stackStyles = tv({
|
|
563
|
+
base: "flex",
|
|
564
|
+
variants: {
|
|
565
|
+
direction: {
|
|
566
|
+
column: "flex-col",
|
|
567
|
+
row: "flex-row"
|
|
568
|
+
},
|
|
569
|
+
gap: {
|
|
570
|
+
0: "gap-0",
|
|
571
|
+
1: "gap-1",
|
|
572
|
+
2: "gap-2",
|
|
573
|
+
3: "gap-3",
|
|
574
|
+
4: "gap-4",
|
|
575
|
+
5: "gap-5",
|
|
576
|
+
6: "gap-6",
|
|
577
|
+
8: "gap-8",
|
|
578
|
+
10: "gap-10",
|
|
579
|
+
12: "gap-12"
|
|
580
|
+
},
|
|
581
|
+
align: {
|
|
582
|
+
start: "items-start",
|
|
583
|
+
center: "items-center",
|
|
584
|
+
end: "items-end",
|
|
585
|
+
stretch: "items-stretch",
|
|
586
|
+
baseline: "items-baseline"
|
|
587
|
+
},
|
|
588
|
+
justify: {
|
|
589
|
+
start: "justify-start",
|
|
590
|
+
center: "justify-center",
|
|
591
|
+
end: "justify-end",
|
|
592
|
+
between: "justify-between"
|
|
593
|
+
},
|
|
594
|
+
wrap: { true: "flex-wrap" }
|
|
595
|
+
},
|
|
596
|
+
defaultVariants: {
|
|
597
|
+
direction: "column",
|
|
598
|
+
gap: 4
|
|
599
|
+
}
|
|
600
|
+
});
|
|
601
|
+
/** Flex layout with spacing from the scale. `direction="row"` for horizontal. */
|
|
602
|
+
function Stack({ direction, gap, align, justify, wrap, asChild, className, ...props }) {
|
|
603
|
+
const Comp = asChild ? Slot.Root : "div";
|
|
604
|
+
return /* @__PURE__ */ jsx(Comp, {
|
|
605
|
+
"data-slot": "stack",
|
|
606
|
+
className: stackStyles({
|
|
607
|
+
direction,
|
|
608
|
+
gap,
|
|
609
|
+
align,
|
|
610
|
+
justify,
|
|
611
|
+
wrap,
|
|
612
|
+
class: className
|
|
613
|
+
}),
|
|
614
|
+
...props
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
//#endregion
|
|
618
|
+
//#region src/components/switch/switch.tsx
|
|
619
|
+
const switchStyles = tv({
|
|
620
|
+
slots: {
|
|
621
|
+
root: [
|
|
622
|
+
"peer inline-flex shrink-0 cursor-pointer items-center rounded-pill border-transparent bg-input p-px",
|
|
623
|
+
themedBorder,
|
|
624
|
+
transition,
|
|
625
|
+
focusRing,
|
|
626
|
+
"data-[state=checked]:bg-primary disabled:cursor-not-allowed disabled:opacity-50"
|
|
627
|
+
],
|
|
628
|
+
thumb: "pointer-events-none block rounded-pill bg-white shadow-sm transition-transform duration-(--glyph-duration-base) ease-emphasized data-[state=unchecked]:translate-x-0"
|
|
629
|
+
},
|
|
630
|
+
variants: { size: {
|
|
631
|
+
sm: {
|
|
632
|
+
root: "h-4 w-7",
|
|
633
|
+
thumb: "size-3 data-[state=checked]:translate-x-3"
|
|
634
|
+
},
|
|
635
|
+
md: {
|
|
636
|
+
root: "h-5 w-9",
|
|
637
|
+
thumb: "size-4 data-[state=checked]:translate-x-4"
|
|
638
|
+
}
|
|
639
|
+
} },
|
|
640
|
+
defaultVariants: { size: "md" }
|
|
641
|
+
});
|
|
642
|
+
function Switch(inProps) {
|
|
643
|
+
const [{ size, className, ...rest }, slots] = useComponentConfig("Switch", useFieldControl(inProps));
|
|
644
|
+
const s = switchStyles({ size });
|
|
645
|
+
return /* @__PURE__ */ jsx(Switch$1.Root, {
|
|
646
|
+
"data-slot": "switch",
|
|
647
|
+
className: s.root({ class: [slots.root, className] }),
|
|
648
|
+
...rest,
|
|
649
|
+
children: /* @__PURE__ */ jsx(Switch$1.Thumb, { className: s.thumb({ class: slots.thumb }) })
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
//#endregion
|
|
653
|
+
//#region src/components/tabs/tabs.tsx
|
|
654
|
+
const tabsStyles = tv({
|
|
655
|
+
slots: {
|
|
656
|
+
root: "flex flex-col gap-4",
|
|
657
|
+
list: "flex items-center",
|
|
658
|
+
trigger: [
|
|
659
|
+
"relative inline-flex cursor-pointer select-none items-center justify-center gap-2 whitespace-nowrap text-small font-medium text-muted-foreground",
|
|
660
|
+
transition,
|
|
661
|
+
focusRing,
|
|
662
|
+
"hover:text-foreground data-[state=active]:text-foreground disabled:pointer-events-none disabled:opacity-50",
|
|
663
|
+
"[&>svg]:size-4"
|
|
664
|
+
],
|
|
665
|
+
content: "outline-none"
|
|
666
|
+
},
|
|
667
|
+
variants: { variant: {
|
|
668
|
+
underline: {
|
|
669
|
+
list: "gap-5 border-b border-border",
|
|
670
|
+
trigger: "h-9 after:absolute after:inset-x-0 after:-bottom-px after:h-0.5 after:rounded-pill after:bg-transparent data-[state=active]:after:bg-foreground"
|
|
671
|
+
},
|
|
672
|
+
pill: {
|
|
673
|
+
list: ["inline-flex w-fit gap-0.5 rounded-md border-border bg-muted p-0.5", themedBorder],
|
|
674
|
+
trigger: "h-7 rounded-[calc(var(--radius-md)-2px)] px-3 data-[state=active]:bg-raised data-[state=active]:shadow-xs"
|
|
675
|
+
}
|
|
676
|
+
} },
|
|
677
|
+
defaultVariants: { variant: "underline" }
|
|
678
|
+
});
|
|
679
|
+
const TabsContext = createContext({});
|
|
680
|
+
function Tabs(inProps) {
|
|
681
|
+
const [{ variant, className, ...rest }, slots] = useComponentConfig("Tabs", inProps);
|
|
682
|
+
return /* @__PURE__ */ jsx(TabsContext.Provider, {
|
|
683
|
+
value: { variant },
|
|
684
|
+
children: /* @__PURE__ */ jsx(Tabs$1.Root, {
|
|
685
|
+
"data-slot": "tabs",
|
|
686
|
+
className: tabsStyles({ variant }).root({ class: [slots.root, className] }),
|
|
687
|
+
...rest
|
|
688
|
+
})
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
function useTabsParts() {
|
|
692
|
+
const { variant } = useContext(TabsContext);
|
|
693
|
+
const [, slots] = useComponentConfig("Tabs", {});
|
|
694
|
+
return {
|
|
695
|
+
s: tabsStyles({ variant }),
|
|
696
|
+
slots
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
function TabsList({ className, ...props }) {
|
|
700
|
+
const { s, slots } = useTabsParts();
|
|
701
|
+
return /* @__PURE__ */ jsx(Tabs$1.List, {
|
|
702
|
+
"data-slot": "tabs-list",
|
|
703
|
+
className: s.list({ class: [slots.list, className] }),
|
|
704
|
+
...props
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
function TabsTrigger({ className, ...props }) {
|
|
708
|
+
const { s, slots } = useTabsParts();
|
|
709
|
+
return /* @__PURE__ */ jsx(Tabs$1.Trigger, {
|
|
710
|
+
"data-slot": "tabs-trigger",
|
|
711
|
+
className: s.trigger({ class: [slots.trigger, className] }),
|
|
712
|
+
...props
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
function TabsContent({ className, ...props }) {
|
|
716
|
+
const { s, slots } = useTabsParts();
|
|
717
|
+
return /* @__PURE__ */ jsx(Tabs$1.Content, {
|
|
718
|
+
"data-slot": "tabs-content",
|
|
719
|
+
className: s.content({ class: [slots.content, className] }),
|
|
720
|
+
...props
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
//#endregion
|
|
724
|
+
//#region src/components/text/text.styles.ts
|
|
725
|
+
const textStyles = tv({
|
|
726
|
+
slots: { base: "" },
|
|
727
|
+
variants: {
|
|
728
|
+
size: {
|
|
729
|
+
lead: { base: "text-lead" },
|
|
730
|
+
body: { base: "text-body" },
|
|
731
|
+
small: { base: "text-small" },
|
|
732
|
+
caption: { base: "text-caption" }
|
|
733
|
+
},
|
|
734
|
+
tone: {
|
|
735
|
+
default: { base: "text-foreground" },
|
|
736
|
+
muted: { base: "text-muted-foreground" },
|
|
737
|
+
faint: { base: "text-faint-foreground" },
|
|
738
|
+
primary: { base: "text-primary" },
|
|
739
|
+
success: { base: "text-success" },
|
|
740
|
+
warning: { base: "text-warning" },
|
|
741
|
+
danger: { base: "text-danger" }
|
|
742
|
+
},
|
|
743
|
+
weight: {
|
|
744
|
+
regular: { base: "font-normal" },
|
|
745
|
+
medium: { base: "font-medium" },
|
|
746
|
+
semibold: { base: "font-semibold" }
|
|
747
|
+
},
|
|
748
|
+
mono: { true: { base: "font-mono" } },
|
|
749
|
+
truncate: { true: { base: "truncate" } }
|
|
750
|
+
},
|
|
751
|
+
defaultVariants: {
|
|
752
|
+
size: "body",
|
|
753
|
+
tone: "default"
|
|
754
|
+
}
|
|
755
|
+
});
|
|
756
|
+
//#endregion
|
|
757
|
+
//#region src/components/text/text.tsx
|
|
758
|
+
function Text(inProps) {
|
|
759
|
+
const [props, slots] = useComponentConfig("Text", inProps);
|
|
760
|
+
const { size, tone, weight, mono, truncate, asChild, className, ...rest } = props;
|
|
761
|
+
const s = textStyles({
|
|
762
|
+
size,
|
|
763
|
+
tone,
|
|
764
|
+
weight,
|
|
765
|
+
mono,
|
|
766
|
+
truncate
|
|
767
|
+
});
|
|
768
|
+
const Comp = asChild ? Slot.Root : "p";
|
|
769
|
+
return /* @__PURE__ */ jsx(Comp, {
|
|
770
|
+
"data-slot": "text",
|
|
771
|
+
className: s.base({ class: [slots.base, className] }),
|
|
772
|
+
...rest
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
//#endregion
|
|
776
|
+
//#region src/components/textarea/textarea.tsx
|
|
777
|
+
const textareaStyles = tv({ slots: { base: [
|
|
778
|
+
...fieldControl,
|
|
779
|
+
"field-sizing-content min-h-20 resize-y rounded-[min(var(--radius-md),var(--radius-lg))] px-3 py-2 text-small",
|
|
780
|
+
"outline-none placeholder:text-faint-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/20",
|
|
781
|
+
"disabled:cursor-not-allowed disabled:opacity-50"
|
|
782
|
+
] } });
|
|
783
|
+
function Textarea(inProps) {
|
|
784
|
+
const [{ className, ...rest }, slots] = useComponentConfig("Textarea", useFieldControl(inProps));
|
|
785
|
+
return /* @__PURE__ */ jsx("textarea", {
|
|
786
|
+
"data-slot": "textarea",
|
|
787
|
+
className: textareaStyles().base({ class: [slots.base, className] }),
|
|
788
|
+
...rest
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
//#endregion
|
|
792
|
+
//#region src/components/tooltip/tooltip.tsx
|
|
793
|
+
const tooltipStyles = tv({ slots: { content: "z-50 max-w-xs animate-pop-in rounded-sm bg-secondary px-2 py-1 text-caption text-secondary-foreground shadow-md" } });
|
|
794
|
+
/** A short hint on hover or focus. The child must be focusable (a button or link). */
|
|
795
|
+
function Tooltip(inProps) {
|
|
796
|
+
const [props, slots] = useComponentConfig("Tooltip", inProps);
|
|
797
|
+
const { content, children, disabled, delayDuration, className, sideOffset = 6, ...rest } = props;
|
|
798
|
+
if (disabled) return children;
|
|
799
|
+
return /* @__PURE__ */ jsxs(Tooltip$1.Root, {
|
|
800
|
+
...delayDuration !== void 0 && { delayDuration },
|
|
801
|
+
children: [/* @__PURE__ */ jsx(Tooltip$1.Trigger, {
|
|
802
|
+
asChild: true,
|
|
803
|
+
children
|
|
804
|
+
}), /* @__PURE__ */ jsx(Tooltip$1.Portal, { children: /* @__PURE__ */ jsx(Tooltip$1.Content, {
|
|
805
|
+
"data-slot": "tooltip",
|
|
806
|
+
sideOffset,
|
|
807
|
+
className: tooltipStyles().content({ class: [slots.content, className] }),
|
|
808
|
+
...rest,
|
|
809
|
+
children: content
|
|
810
|
+
}) })]
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
//#endregion
|
|
814
|
+
//#region src/hooks/use-controllable-state.ts
|
|
815
|
+
/** State that can be controlled by a parent (`value` + `onChange`) or left uncontrolled. */
|
|
816
|
+
function useControllableState({ value, defaultValue, onChange }) {
|
|
817
|
+
const [internal, setInternal] = useState(defaultValue);
|
|
818
|
+
const isControlled = value !== void 0;
|
|
819
|
+
const current = isControlled ? value : internal;
|
|
820
|
+
const onChangeRef = useRef(onChange);
|
|
821
|
+
onChangeRef.current = onChange;
|
|
822
|
+
return [current, useCallback((next) => {
|
|
823
|
+
const resolved = typeof next === "function" ? next(current) : next;
|
|
824
|
+
if (!isControlled) setInternal(resolved);
|
|
825
|
+
onChangeRef.current?.(resolved);
|
|
826
|
+
}, [current, isControlled])];
|
|
827
|
+
}
|
|
828
|
+
//#endregion
|
|
829
|
+
//#region src/hooks/use-media-query.ts
|
|
830
|
+
/** Subscribes to a CSS media query. Returns `fallback` during SSR. */
|
|
831
|
+
function useMediaQuery(query, fallback = false) {
|
|
832
|
+
return useSyncExternalStore((onChange) => {
|
|
833
|
+
const list = window.matchMedia(query);
|
|
834
|
+
list.addEventListener("change", onChange);
|
|
835
|
+
return () => list.removeEventListener("change", onChange);
|
|
836
|
+
}, () => window.matchMedia(query).matches, () => fallback);
|
|
837
|
+
}
|
|
838
|
+
//#endregion
|
|
839
|
+
//#region src/hooks/use-scrolled.ts
|
|
840
|
+
/** True once the window has scrolled past `threshold` pixels. */
|
|
841
|
+
function useScrolled(threshold = 8, enabled = true) {
|
|
842
|
+
const [scrolled, setScrolled] = useState(false);
|
|
843
|
+
useEffect(() => {
|
|
844
|
+
if (!enabled) return;
|
|
845
|
+
const update = () => setScrolled(window.scrollY > threshold);
|
|
846
|
+
update();
|
|
847
|
+
window.addEventListener("scroll", update, { passive: true });
|
|
848
|
+
return () => window.removeEventListener("scroll", update);
|
|
849
|
+
}, [threshold, enabled]);
|
|
850
|
+
return scrolled;
|
|
851
|
+
}
|
|
852
|
+
//#endregion
|
|
853
|
+
//#region src/patterns/app-shell/app-shell.styles.ts
|
|
854
|
+
const appShellStyles = tv({
|
|
855
|
+
slots: {
|
|
856
|
+
root: "flex w-full bg-background",
|
|
857
|
+
sidebar: "hidden shrink-0 md:flex",
|
|
858
|
+
main: "flex min-w-0 flex-1 flex-col",
|
|
859
|
+
mobileBar: ["flex h-14 shrink-0 items-center gap-2 border-b-border px-3 md:hidden", "border-b-(length:--glyph-border-width)"],
|
|
860
|
+
overlay: "fixed inset-0 z-50 animate-fade-in bg-overlay",
|
|
861
|
+
drawer: "fixed inset-y-0 left-0 z-50 flex w-72 max-w-[85vw] animate-slide-in-left shadow-lg outline-none [&>[data-slot=sidebar]]:w-full"
|
|
862
|
+
},
|
|
863
|
+
variants: { layout: {
|
|
864
|
+
page: {
|
|
865
|
+
root: "min-h-dvh",
|
|
866
|
+
sidebar: "sticky top-0 h-dvh",
|
|
867
|
+
mobileBar: "sticky top-0 z-30 bg-background/80 backdrop-blur-xl"
|
|
868
|
+
},
|
|
869
|
+
embedded: {
|
|
870
|
+
root: "h-full min-h-0 overflow-hidden",
|
|
871
|
+
sidebar: "h-full",
|
|
872
|
+
main: "overflow-y-auto"
|
|
873
|
+
}
|
|
874
|
+
} },
|
|
875
|
+
defaultVariants: { layout: "page" }
|
|
876
|
+
});
|
|
877
|
+
const sidebarStyles = tv({ slots: {
|
|
878
|
+
root: [
|
|
879
|
+
"group/sidebar flex h-full w-60 flex-col overflow-hidden border-r-border bg-subtle",
|
|
880
|
+
"border-r-(length:--glyph-border-width)",
|
|
881
|
+
"transition-[width] duration-(--glyph-duration-base) ease-standard data-[collapsed=true]:w-15"
|
|
882
|
+
],
|
|
883
|
+
header: "flex h-14 shrink-0 items-center gap-2 px-3",
|
|
884
|
+
content: "flex flex-1 flex-col gap-5 overflow-y-auto overflow-x-hidden px-3 py-3",
|
|
885
|
+
section: "flex flex-col gap-0.5",
|
|
886
|
+
sectionLabel: "mb-1 truncate px-2 font-mono text-caption uppercase tracking-[0.1em] text-faint-foreground group-data-[collapsed=true]/sidebar:sr-only",
|
|
887
|
+
item: [
|
|
888
|
+
"relative flex h-8 w-full shrink-0 cursor-pointer items-center gap-2.5 rounded-sm px-2 text-small font-medium text-muted-foreground",
|
|
889
|
+
transition,
|
|
890
|
+
focusRing,
|
|
891
|
+
"hover:bg-muted hover:text-foreground",
|
|
892
|
+
"aria-[current=page]:bg-muted aria-[current=page]:text-foreground",
|
|
893
|
+
"before:absolute before:inset-y-1.5 before:-left-3 before:w-0.5 before:rounded-r-pill before:bg-transparent aria-[current=page]:before:bg-primary",
|
|
894
|
+
"group-data-[collapsed=true]/sidebar:justify-center group-data-[collapsed=true]/sidebar:px-0"
|
|
895
|
+
],
|
|
896
|
+
itemIcon: "flex size-4 shrink-0 items-center justify-center [&>svg]:size-4",
|
|
897
|
+
itemLabel: "min-w-0 flex-1 truncate text-left group-data-[collapsed=true]/sidebar:hidden",
|
|
898
|
+
itemBadge: "ml-auto font-mono text-caption text-faint-foreground tabular-nums group-data-[collapsed=true]/sidebar:hidden",
|
|
899
|
+
footer: ["mt-auto flex shrink-0 items-center gap-2 border-t-border px-3 py-3 group-data-[collapsed=true]/sidebar:justify-center", "border-t-(length:--glyph-border-width)"]
|
|
900
|
+
} });
|
|
901
|
+
const navbarStyles = tv({
|
|
902
|
+
slots: {
|
|
903
|
+
root: [
|
|
904
|
+
"top-0 z-40 w-full border-b-transparent",
|
|
905
|
+
"border-b-(length:--glyph-border-width)",
|
|
906
|
+
"transition-[background-color,border-color,backdrop-filter] duration-(--glyph-duration-base) ease-standard"
|
|
907
|
+
],
|
|
908
|
+
inner: "flex h-16 items-center gap-6",
|
|
909
|
+
brand: "flex shrink-0 items-center gap-2 font-display text-h4 text-foreground",
|
|
910
|
+
links: "hidden items-center gap-1 md:flex",
|
|
911
|
+
link: [
|
|
912
|
+
"rounded-sm px-3 py-1.5 text-small font-medium text-muted-foreground",
|
|
913
|
+
transition,
|
|
914
|
+
focusRing,
|
|
915
|
+
"hover:text-foreground aria-[current=page]:text-foreground"
|
|
916
|
+
],
|
|
917
|
+
actions: "ml-auto hidden items-center gap-2 md:flex",
|
|
918
|
+
menuButton: "ml-auto md:hidden",
|
|
919
|
+
overlay: "fixed inset-0 z-50 animate-fade-in bg-overlay",
|
|
920
|
+
panel: ["fixed inset-x-0 top-0 z-50 max-h-dvh overflow-y-auto border-b-border bg-background px-4 pb-6 shadow-lg outline-none animate-slide-in-top", "border-b-(length:--glyph-border-width)"],
|
|
921
|
+
panelHeader: "flex h-16 items-center justify-between",
|
|
922
|
+
panelLinks: "flex flex-col gap-1",
|
|
923
|
+
panelLink: [
|
|
924
|
+
"rounded-md px-3 py-2.5 text-body font-medium text-muted-foreground",
|
|
925
|
+
focusRing,
|
|
926
|
+
"hover:bg-muted hover:text-foreground aria-[current=page]:bg-muted aria-[current=page]:text-foreground"
|
|
927
|
+
],
|
|
928
|
+
panelActions: ["mt-4 flex flex-col gap-2 border-t-border pt-4", "border-t-(length:--glyph-border-width)"]
|
|
929
|
+
},
|
|
930
|
+
variants: {
|
|
931
|
+
sticky: {
|
|
932
|
+
true: { root: "sticky" },
|
|
933
|
+
false: { root: "relative" }
|
|
934
|
+
},
|
|
935
|
+
blur: {
|
|
936
|
+
true: { root: "data-[scrolled=true]:border-b-border data-[scrolled=true]:bg-background/72 data-[scrolled=true]:backdrop-blur-xl data-[scrolled=true]:backdrop-saturate-150" },
|
|
937
|
+
false: { root: "border-b-border bg-background" }
|
|
938
|
+
}
|
|
939
|
+
},
|
|
940
|
+
defaultVariants: {
|
|
941
|
+
sticky: true,
|
|
942
|
+
blur: true
|
|
943
|
+
}
|
|
944
|
+
});
|
|
945
|
+
const pageHeaderStyles = tv({ slots: {
|
|
946
|
+
root: ["flex flex-col gap-4 border-b-border pb-5 sm:flex-row sm:items-end sm:justify-between", "border-b-(length:--glyph-border-width)"],
|
|
947
|
+
text: "grid min-w-0 gap-1",
|
|
948
|
+
eyebrow: "font-mono text-caption uppercase tracking-[0.12em] text-faint-foreground",
|
|
949
|
+
title: "font-display text-h3 text-foreground",
|
|
950
|
+
description: "max-w-2xl text-small text-muted-foreground",
|
|
951
|
+
actions: "flex shrink-0 flex-wrap items-center gap-2"
|
|
952
|
+
} });
|
|
953
|
+
//#endregion
|
|
954
|
+
//#region src/patterns/app-shell/app-shell-context.ts
|
|
955
|
+
const AppShellContext = createContext(null);
|
|
956
|
+
/** Sidebar state. Works outside an AppShell too (always expanded). */
|
|
957
|
+
function useAppShell() {
|
|
958
|
+
return useContext(AppShellContext) ?? {
|
|
959
|
+
collapsed: false,
|
|
960
|
+
setCollapsed: () => {},
|
|
961
|
+
mobileOpen: false,
|
|
962
|
+
setMobileOpen: () => {},
|
|
963
|
+
inDrawer: false
|
|
964
|
+
};
|
|
965
|
+
}
|
|
966
|
+
//#endregion
|
|
967
|
+
//#region src/patterns/app-shell/sidebar.tsx
|
|
968
|
+
const s = sidebarStyles();
|
|
969
|
+
function useSlots() {
|
|
970
|
+
return useComponentConfig("Sidebar", {})[1];
|
|
971
|
+
}
|
|
972
|
+
/** Navigation sidebar. Place inside `AppShell`'s `sidebar` prop. */
|
|
973
|
+
function Sidebar({ className, ...props }) {
|
|
974
|
+
const { collapsed } = useAppShell();
|
|
975
|
+
const slots = useSlots();
|
|
976
|
+
return /* @__PURE__ */ jsx("aside", {
|
|
977
|
+
"data-slot": "sidebar",
|
|
978
|
+
"data-collapsed": collapsed,
|
|
979
|
+
className: s.root({ class: [slots.root, className] }),
|
|
980
|
+
...props
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
function SidebarHeader({ className, ...props }) {
|
|
984
|
+
const slots = useSlots();
|
|
985
|
+
return /* @__PURE__ */ jsx("div", {
|
|
986
|
+
"data-slot": "sidebar-header",
|
|
987
|
+
className: s.header({ class: [slots.header, className] }),
|
|
988
|
+
...props
|
|
989
|
+
});
|
|
990
|
+
}
|
|
991
|
+
function SidebarContent({ className, ...props }) {
|
|
992
|
+
const slots = useSlots();
|
|
993
|
+
return /* @__PURE__ */ jsx("nav", {
|
|
994
|
+
"data-slot": "sidebar-content",
|
|
995
|
+
"aria-label": "Main",
|
|
996
|
+
className: s.content({ class: [slots.content, className] }),
|
|
997
|
+
...props
|
|
998
|
+
});
|
|
999
|
+
}
|
|
1000
|
+
function SidebarSection({ label, className, children, ...props }) {
|
|
1001
|
+
const slots = useSlots();
|
|
1002
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1003
|
+
"data-slot": "sidebar-section",
|
|
1004
|
+
className: s.section({ class: [slots.section, className] }),
|
|
1005
|
+
...props,
|
|
1006
|
+
children: [label && /* @__PURE__ */ jsx("p", {
|
|
1007
|
+
className: s.sectionLabel({ class: slots.sectionLabel }),
|
|
1008
|
+
children: label
|
|
1009
|
+
}), children]
|
|
1010
|
+
});
|
|
1011
|
+
}
|
|
1012
|
+
function SidebarItem({ href, icon, badge, active, onClick, className, children }) {
|
|
1013
|
+
const { collapsed, inDrawer, setMobileOpen } = useAppShell();
|
|
1014
|
+
const Link = useLinkComponent();
|
|
1015
|
+
const slots = useSlots();
|
|
1016
|
+
const handleClick = (event) => {
|
|
1017
|
+
onClick?.(event);
|
|
1018
|
+
if (inDrawer) setMobileOpen(false);
|
|
1019
|
+
};
|
|
1020
|
+
const content = /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
1021
|
+
icon && /* @__PURE__ */ jsx("span", {
|
|
1022
|
+
className: s.itemIcon({ class: slots.itemIcon }),
|
|
1023
|
+
"aria-hidden": "true",
|
|
1024
|
+
children: icon
|
|
1025
|
+
}),
|
|
1026
|
+
/* @__PURE__ */ jsx("span", {
|
|
1027
|
+
className: s.itemLabel({ class: slots.itemLabel }),
|
|
1028
|
+
children
|
|
1029
|
+
}),
|
|
1030
|
+
badge !== void 0 && /* @__PURE__ */ jsx("span", {
|
|
1031
|
+
className: s.itemBadge({ class: slots.itemBadge }),
|
|
1032
|
+
children: badge
|
|
1033
|
+
})
|
|
1034
|
+
] });
|
|
1035
|
+
const shared = {
|
|
1036
|
+
"data-slot": "sidebar-item",
|
|
1037
|
+
"aria-current": active ? "page" : void 0,
|
|
1038
|
+
className: s.item({ class: [slots.item, className] })
|
|
1039
|
+
};
|
|
1040
|
+
const element = href ? /* @__PURE__ */ jsx(Link, {
|
|
1041
|
+
href,
|
|
1042
|
+
onClick: handleClick,
|
|
1043
|
+
...shared,
|
|
1044
|
+
children: content
|
|
1045
|
+
}) : /* @__PURE__ */ jsx("button", {
|
|
1046
|
+
type: "button",
|
|
1047
|
+
onClick: handleClick,
|
|
1048
|
+
...shared,
|
|
1049
|
+
children: content
|
|
1050
|
+
});
|
|
1051
|
+
return /* @__PURE__ */ jsx(Tooltip, {
|
|
1052
|
+
content: children,
|
|
1053
|
+
side: "right",
|
|
1054
|
+
disabled: !collapsed,
|
|
1055
|
+
children: element
|
|
1056
|
+
});
|
|
1057
|
+
}
|
|
1058
|
+
function SidebarFooter({ className, ...props }) {
|
|
1059
|
+
const slots = useSlots();
|
|
1060
|
+
return /* @__PURE__ */ jsx("div", {
|
|
1061
|
+
"data-slot": "sidebar-footer",
|
|
1062
|
+
className: s.footer({ class: [slots.footer, className] }),
|
|
1063
|
+
...props
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
/** Collapses the sidebar on desktop; opens the navigation drawer on mobile. */
|
|
1067
|
+
function SidebarTrigger(props) {
|
|
1068
|
+
const { collapsed, setCollapsed, setMobileOpen, inDrawer } = useAppShell();
|
|
1069
|
+
const desktop = useMediaQuery("(min-width: 48rem)");
|
|
1070
|
+
if (inDrawer) return null;
|
|
1071
|
+
return /* @__PURE__ */ jsx(IconButton, {
|
|
1072
|
+
icon: /* @__PURE__ */ jsx(PanelLeftIcon, {}),
|
|
1073
|
+
"aria-label": desktop ? collapsed ? "Expand sidebar" : "Collapse sidebar" : "Open navigation",
|
|
1074
|
+
size: "sm",
|
|
1075
|
+
onClick: () => desktop ? setCollapsed(!collapsed) : setMobileOpen(true),
|
|
1076
|
+
...props
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
//#endregion
|
|
1080
|
+
//#region src/patterns/app-shell/app-shell.tsx
|
|
1081
|
+
/**
|
|
1082
|
+
* App layout: sidebar plus main content. Handles desktop collapse to an icon rail,
|
|
1083
|
+
* the mobile drawer (focus-trapped, closes on navigation), and the mobile top bar.
|
|
1084
|
+
*
|
|
1085
|
+
* `layout="page"` fills the viewport; `layout="embedded"` fills its parent.
|
|
1086
|
+
*/
|
|
1087
|
+
function AppShell(inProps) {
|
|
1088
|
+
const [props, slots] = useComponentConfig("AppShell", inProps);
|
|
1089
|
+
const { sidebar, children, collapsed: collapsedProp, defaultCollapsed = false, onCollapsedChange, mobileBrand, layout, className, ...rest } = props;
|
|
1090
|
+
const [collapsed, setCollapsed] = useControllableState({
|
|
1091
|
+
value: collapsedProp,
|
|
1092
|
+
defaultValue: defaultCollapsed,
|
|
1093
|
+
onChange: onCollapsedChange
|
|
1094
|
+
});
|
|
1095
|
+
const [mobileOpen, setMobileOpen] = useState(false);
|
|
1096
|
+
const s = appShellStyles({ layout });
|
|
1097
|
+
const desktop = useMemo(() => ({
|
|
1098
|
+
collapsed,
|
|
1099
|
+
setCollapsed,
|
|
1100
|
+
mobileOpen,
|
|
1101
|
+
setMobileOpen,
|
|
1102
|
+
inDrawer: false
|
|
1103
|
+
}), [
|
|
1104
|
+
collapsed,
|
|
1105
|
+
setCollapsed,
|
|
1106
|
+
mobileOpen
|
|
1107
|
+
]);
|
|
1108
|
+
const drawer = useMemo(() => ({
|
|
1109
|
+
...desktop,
|
|
1110
|
+
collapsed: false,
|
|
1111
|
+
inDrawer: true
|
|
1112
|
+
}), [desktop]);
|
|
1113
|
+
return /* @__PURE__ */ jsx(AppShellContext.Provider, {
|
|
1114
|
+
value: desktop,
|
|
1115
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
1116
|
+
"data-slot": "app-shell",
|
|
1117
|
+
className: s.root({ class: [slots.root, className] }),
|
|
1118
|
+
...rest,
|
|
1119
|
+
children: [
|
|
1120
|
+
/* @__PURE__ */ jsx("div", {
|
|
1121
|
+
className: s.sidebar({ class: slots.sidebar }),
|
|
1122
|
+
children: sidebar
|
|
1123
|
+
}),
|
|
1124
|
+
/* @__PURE__ */ jsx(Dialog.Root, {
|
|
1125
|
+
open: mobileOpen,
|
|
1126
|
+
onOpenChange: setMobileOpen,
|
|
1127
|
+
children: /* @__PURE__ */ jsxs(Dialog.Portal, { children: [/* @__PURE__ */ jsx(Dialog.Overlay, { className: s.overlay({ class: slots.overlay }) }), /* @__PURE__ */ jsxs(Dialog.Content, {
|
|
1128
|
+
className: s.drawer({ class: slots.drawer }),
|
|
1129
|
+
"aria-describedby": void 0,
|
|
1130
|
+
children: [/* @__PURE__ */ jsx(Dialog.Title, {
|
|
1131
|
+
className: "sr-only",
|
|
1132
|
+
children: "Navigation"
|
|
1133
|
+
}), /* @__PURE__ */ jsx(AppShellContext.Provider, {
|
|
1134
|
+
value: drawer,
|
|
1135
|
+
children: sidebar
|
|
1136
|
+
})]
|
|
1137
|
+
})] })
|
|
1138
|
+
}),
|
|
1139
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1140
|
+
"data-slot": "app-shell-main",
|
|
1141
|
+
className: s.main({ class: slots.main }),
|
|
1142
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
1143
|
+
className: s.mobileBar({ class: slots.mobileBar }),
|
|
1144
|
+
children: [/* @__PURE__ */ jsx(SidebarTrigger, {}), mobileBrand]
|
|
1145
|
+
}), children]
|
|
1146
|
+
})
|
|
1147
|
+
]
|
|
1148
|
+
})
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
//#endregion
|
|
1152
|
+
//#region src/patterns/app-shell/navbar.tsx
|
|
1153
|
+
/**
|
|
1154
|
+
* Site header from data: brand, links, actions. Responsive out of the box: links collapse
|
|
1155
|
+
* into a focus-trapped menu panel on small screens.
|
|
1156
|
+
*/
|
|
1157
|
+
function Navbar(inProps) {
|
|
1158
|
+
const [props, slots] = useComponentConfig("Navbar", inProps);
|
|
1159
|
+
const { brand, links = [], actions, sticky = true, blurOnScroll = true, containerSize, className, ...rest } = props;
|
|
1160
|
+
const [open, setOpen] = useState(false);
|
|
1161
|
+
const scrolled = useScrolled(8, blurOnScroll);
|
|
1162
|
+
const Link = useLinkComponent();
|
|
1163
|
+
const s = navbarStyles({
|
|
1164
|
+
sticky,
|
|
1165
|
+
blur: blurOnScroll
|
|
1166
|
+
});
|
|
1167
|
+
return /* @__PURE__ */ jsx("header", {
|
|
1168
|
+
"data-slot": "navbar",
|
|
1169
|
+
"data-scrolled": scrolled,
|
|
1170
|
+
className: s.root({ class: [slots.root, className] }),
|
|
1171
|
+
...rest,
|
|
1172
|
+
children: /* @__PURE__ */ jsxs(Container, {
|
|
1173
|
+
size: containerSize,
|
|
1174
|
+
className: s.inner({ class: slots.inner }),
|
|
1175
|
+
children: [
|
|
1176
|
+
/* @__PURE__ */ jsx("div", {
|
|
1177
|
+
className: s.brand({ class: slots.brand }),
|
|
1178
|
+
children: brand
|
|
1179
|
+
}),
|
|
1180
|
+
links.length > 0 && /* @__PURE__ */ jsx("nav", {
|
|
1181
|
+
"aria-label": "Main",
|
|
1182
|
+
className: s.links({ class: slots.links }),
|
|
1183
|
+
children: links.map((link) => /* @__PURE__ */ jsx(Link, {
|
|
1184
|
+
href: link.href,
|
|
1185
|
+
"aria-current": link.active ? "page" : void 0,
|
|
1186
|
+
className: s.link({ class: slots.link }),
|
|
1187
|
+
children: link.label
|
|
1188
|
+
}, link.href))
|
|
1189
|
+
}),
|
|
1190
|
+
actions && /* @__PURE__ */ jsx("div", {
|
|
1191
|
+
className: s.actions({ class: slots.actions }),
|
|
1192
|
+
children: actions
|
|
1193
|
+
}),
|
|
1194
|
+
/* @__PURE__ */ jsxs(Dialog.Root, {
|
|
1195
|
+
open,
|
|
1196
|
+
onOpenChange: setOpen,
|
|
1197
|
+
children: [/* @__PURE__ */ jsx(Dialog.Trigger, {
|
|
1198
|
+
asChild: true,
|
|
1199
|
+
children: /* @__PURE__ */ jsx(IconButton, {
|
|
1200
|
+
icon: /* @__PURE__ */ jsx(MenuIcon, {}),
|
|
1201
|
+
"aria-label": "Open menu",
|
|
1202
|
+
className: s.menuButton({ class: slots.menuButton })
|
|
1203
|
+
})
|
|
1204
|
+
}), /* @__PURE__ */ jsxs(Dialog.Portal, { children: [/* @__PURE__ */ jsx(Dialog.Overlay, { className: s.overlay({ class: slots.overlay }) }), /* @__PURE__ */ jsxs(Dialog.Content, {
|
|
1205
|
+
className: s.panel({ class: slots.panel }),
|
|
1206
|
+
"aria-describedby": void 0,
|
|
1207
|
+
children: [
|
|
1208
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1209
|
+
className: s.panelHeader({ class: slots.panelHeader }),
|
|
1210
|
+
children: [/* @__PURE__ */ jsx(Dialog.Title, {
|
|
1211
|
+
asChild: true,
|
|
1212
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
1213
|
+
className: s.brand({ class: slots.brand }),
|
|
1214
|
+
children: brand
|
|
1215
|
+
})
|
|
1216
|
+
}), /* @__PURE__ */ jsx(Dialog.Close, {
|
|
1217
|
+
asChild: true,
|
|
1218
|
+
children: /* @__PURE__ */ jsx(IconButton, {
|
|
1219
|
+
icon: /* @__PURE__ */ jsx(XIcon, {}),
|
|
1220
|
+
"aria-label": "Close menu"
|
|
1221
|
+
})
|
|
1222
|
+
})]
|
|
1223
|
+
}),
|
|
1224
|
+
/* @__PURE__ */ jsx("nav", {
|
|
1225
|
+
"aria-label": "Main",
|
|
1226
|
+
className: s.panelLinks({ class: slots.panelLinks }),
|
|
1227
|
+
children: links.map((link) => /* @__PURE__ */ jsx(Link, {
|
|
1228
|
+
href: link.href,
|
|
1229
|
+
"aria-current": link.active ? "page" : void 0,
|
|
1230
|
+
className: s.panelLink({ class: slots.panelLink }),
|
|
1231
|
+
onClick: () => setOpen(false),
|
|
1232
|
+
children: link.label
|
|
1233
|
+
}, link.href))
|
|
1234
|
+
}),
|
|
1235
|
+
actions && /* @__PURE__ */ jsx("div", {
|
|
1236
|
+
className: s.panelActions({ class: slots.panelActions }),
|
|
1237
|
+
children: actions
|
|
1238
|
+
})
|
|
1239
|
+
]
|
|
1240
|
+
})] })]
|
|
1241
|
+
})
|
|
1242
|
+
]
|
|
1243
|
+
})
|
|
1244
|
+
});
|
|
1245
|
+
}
|
|
1246
|
+
//#endregion
|
|
1247
|
+
//#region src/patterns/app-shell/page-header.tsx
|
|
1248
|
+
/** Title block at the top of a page: optional eyebrow, title, description, actions. */
|
|
1249
|
+
function PageHeader(inProps) {
|
|
1250
|
+
const [props, slots] = useComponentConfig("PageHeader", inProps);
|
|
1251
|
+
const { title, description, eyebrow, actions, className, ...rest } = props;
|
|
1252
|
+
const s = pageHeaderStyles();
|
|
1253
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1254
|
+
"data-slot": "page-header",
|
|
1255
|
+
className: s.root({ class: [slots.root, className] }),
|
|
1256
|
+
...rest,
|
|
1257
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
1258
|
+
className: s.text({ class: slots.text }),
|
|
1259
|
+
children: [
|
|
1260
|
+
eyebrow && /* @__PURE__ */ jsx("p", {
|
|
1261
|
+
className: s.eyebrow({ class: slots.eyebrow }),
|
|
1262
|
+
children: eyebrow
|
|
1263
|
+
}),
|
|
1264
|
+
/* @__PURE__ */ jsx("h1", {
|
|
1265
|
+
className: s.title({ class: slots.title }),
|
|
1266
|
+
children: title
|
|
1267
|
+
}),
|
|
1268
|
+
description && /* @__PURE__ */ jsx("p", {
|
|
1269
|
+
className: s.description({ class: slots.description }),
|
|
1270
|
+
children: description
|
|
1271
|
+
})
|
|
1272
|
+
]
|
|
1273
|
+
}), actions && /* @__PURE__ */ jsx("div", {
|
|
1274
|
+
className: s.actions({ class: slots.actions }),
|
|
1275
|
+
children: actions
|
|
1276
|
+
})]
|
|
1277
|
+
});
|
|
1278
|
+
}
|
|
1279
|
+
//#endregion
|
|
1280
|
+
//#region src/patterns/async/async.tsx
|
|
1281
|
+
const defaultIsEmpty = (data) => Array.isArray(data) && data.length === 0;
|
|
1282
|
+
/**
|
|
1283
|
+
* Renders the right UI for every state of an async result: loading, error (with retry),
|
|
1284
|
+
* empty, or your content. One convention for every data-driven screen.
|
|
1285
|
+
*
|
|
1286
|
+
* ```tsx
|
|
1287
|
+
* <Async result={useQuery(...)} empty={<EmptyState title="No invoices yet" />}>
|
|
1288
|
+
* {(invoices) => <InvoiceList invoices={invoices} />}
|
|
1289
|
+
* </Async>
|
|
1290
|
+
* ```
|
|
1291
|
+
*/
|
|
1292
|
+
function Async({ result, children, isEmpty = defaultIsEmpty, loading, empty, error }) {
|
|
1293
|
+
if (result.status === "pending") return loading ?? /* @__PURE__ */ jsx(LoadingState, {});
|
|
1294
|
+
if (result.status === "error") {
|
|
1295
|
+
if (typeof error === "function") return error(result.error, result.refetch);
|
|
1296
|
+
return error ?? /* @__PURE__ */ jsx(ErrorState, {
|
|
1297
|
+
error: result.error,
|
|
1298
|
+
onRetry: result.refetch
|
|
1299
|
+
});
|
|
1300
|
+
}
|
|
1301
|
+
const data = result.data;
|
|
1302
|
+
if (data === void 0 || data === null || isEmpty(data)) return empty ?? /* @__PURE__ */ jsx(EmptyState, { title: "Nothing here yet" });
|
|
1303
|
+
return children(data);
|
|
1304
|
+
}
|
|
1305
|
+
/**
|
|
1306
|
+
* Runs an async function and tracks its state as an `AsyncResult`. Re-runs when `deps`
|
|
1307
|
+
* change; stale responses are ignored. For caching and deduping, use TanStack Query.
|
|
1308
|
+
*/
|
|
1309
|
+
function useAsync(fn, deps) {
|
|
1310
|
+
const [state, setState] = useState({ status: "pending" });
|
|
1311
|
+
const [run, setRun] = useState(0);
|
|
1312
|
+
useEffect(() => {
|
|
1313
|
+
let active = true;
|
|
1314
|
+
setState((prev) => ({
|
|
1315
|
+
...prev,
|
|
1316
|
+
status: "pending"
|
|
1317
|
+
}));
|
|
1318
|
+
fn().then((data) => active && setState({
|
|
1319
|
+
status: "success",
|
|
1320
|
+
data
|
|
1321
|
+
}), (error) => active && setState({
|
|
1322
|
+
status: "error",
|
|
1323
|
+
error
|
|
1324
|
+
}));
|
|
1325
|
+
return () => {
|
|
1326
|
+
active = false;
|
|
1327
|
+
};
|
|
1328
|
+
}, [...deps, run]);
|
|
1329
|
+
const refetch = useCallback(() => setRun((n) => n + 1), []);
|
|
1330
|
+
return {
|
|
1331
|
+
...state,
|
|
1332
|
+
refetch
|
|
1333
|
+
};
|
|
1334
|
+
}
|
|
1335
|
+
//#endregion
|
|
1336
|
+
//#region src/provider/color-mode.tsx
|
|
1337
|
+
const ColorModeContext = createContext(null);
|
|
1338
|
+
const DARK_QUERY = "(prefers-color-scheme: dark)";
|
|
1339
|
+
const isBrowser = typeof window !== "undefined";
|
|
1340
|
+
function readStoredMode(storageKey) {
|
|
1341
|
+
if (!isBrowser || !storageKey) return null;
|
|
1342
|
+
try {
|
|
1343
|
+
const value = window.localStorage.getItem(storageKey);
|
|
1344
|
+
return value === "light" || value === "dark" || value === "system" ? value : null;
|
|
1345
|
+
} catch {
|
|
1346
|
+
return null;
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
function systemMode() {
|
|
1350
|
+
return isBrowser && window.matchMedia(DARK_QUERY).matches ? "dark" : "light";
|
|
1351
|
+
}
|
|
1352
|
+
/** Disables transitions for one frame so a mode switch doesn't animate every color. */
|
|
1353
|
+
function suppressTransitions() {
|
|
1354
|
+
const style = document.createElement("style");
|
|
1355
|
+
style.textContent = "*,*::before,*::after{transition:none!important}";
|
|
1356
|
+
document.head.appendChild(style);
|
|
1357
|
+
return () => {
|
|
1358
|
+
window.getComputedStyle(document.body);
|
|
1359
|
+
requestAnimationFrame(() => style.remove());
|
|
1360
|
+
};
|
|
1361
|
+
}
|
|
1362
|
+
function ColorModeProvider({ children, defaultMode = "system", storageKey = "glyph-mode" }) {
|
|
1363
|
+
const [mode, setModeState] = useState(() => readStoredMode(storageKey) ?? defaultMode);
|
|
1364
|
+
const [system, setSystem] = useState(systemMode);
|
|
1365
|
+
useEffect(() => {
|
|
1366
|
+
const query = window.matchMedia(DARK_QUERY);
|
|
1367
|
+
const onChange = () => setSystem(query.matches ? "dark" : "light");
|
|
1368
|
+
query.addEventListener("change", onChange);
|
|
1369
|
+
return () => query.removeEventListener("change", onChange);
|
|
1370
|
+
}, []);
|
|
1371
|
+
const resolvedMode = mode === "system" ? system : mode;
|
|
1372
|
+
useEffect(() => {
|
|
1373
|
+
const root = document.documentElement;
|
|
1374
|
+
if (root.dataset.mode === resolvedMode) return;
|
|
1375
|
+
const restore = suppressTransitions();
|
|
1376
|
+
root.dataset.mode = resolvedMode;
|
|
1377
|
+
root.style.colorScheme = resolvedMode;
|
|
1378
|
+
restore();
|
|
1379
|
+
}, [resolvedMode]);
|
|
1380
|
+
const setMode = useCallback((next) => {
|
|
1381
|
+
setModeState(next);
|
|
1382
|
+
if (!storageKey) return;
|
|
1383
|
+
try {
|
|
1384
|
+
window.localStorage.setItem(storageKey, next);
|
|
1385
|
+
} catch {}
|
|
1386
|
+
}, [storageKey]);
|
|
1387
|
+
const value = useMemo(() => ({
|
|
1388
|
+
mode,
|
|
1389
|
+
resolvedMode,
|
|
1390
|
+
setMode
|
|
1391
|
+
}), [
|
|
1392
|
+
mode,
|
|
1393
|
+
resolvedMode,
|
|
1394
|
+
setMode
|
|
1395
|
+
]);
|
|
1396
|
+
return /* @__PURE__ */ jsx(ColorModeContext.Provider, {
|
|
1397
|
+
value,
|
|
1398
|
+
children
|
|
1399
|
+
});
|
|
1400
|
+
}
|
|
1401
|
+
/** Read and change the color mode. */
|
|
1402
|
+
function useColorMode() {
|
|
1403
|
+
const value = useContext(ColorModeContext);
|
|
1404
|
+
if (!value) throw new Error("useColorMode must be used inside <GlyphProvider>.");
|
|
1405
|
+
return value;
|
|
1406
|
+
}
|
|
1407
|
+
/**
|
|
1408
|
+
* Inline script that sets `data-mode` on `<html>` before first paint, so a dark-mode
|
|
1409
|
+
* visitor never sees a light flash. Put it in `<head>`.
|
|
1410
|
+
*/
|
|
1411
|
+
function getColorModeScript({ defaultMode = "system", storageKey = "glyph-mode" } = {}) {
|
|
1412
|
+
return `(function(){try{var d=${JSON.stringify(defaultMode)},m=null;${storageKey ? `m=localStorage.getItem(${JSON.stringify(storageKey)});` : ""}if(m!=='light'&&m!=='dark'&&m!=='system')m=d;if(m==='system')m=matchMedia('${DARK_QUERY}').matches?'dark':'light';var r=document.documentElement;r.dataset.mode=m;r.style.colorScheme=m}catch(e){}})()`;
|
|
1413
|
+
}
|
|
1414
|
+
/** `<script>` element version of `getColorModeScript` for SSR frameworks. */
|
|
1415
|
+
function ColorModeScript(props) {
|
|
1416
|
+
return /* @__PURE__ */ jsx("script", {
|
|
1417
|
+
dangerouslySetInnerHTML: { __html: getColorModeScript(props) },
|
|
1418
|
+
suppressHydrationWarning: true
|
|
1419
|
+
});
|
|
1420
|
+
}
|
|
1421
|
+
//#endregion
|
|
1422
|
+
//#region src/themes.ts
|
|
1423
|
+
/**
|
|
1424
|
+
* Built-in themes: the token presets from @glyphui/tokens plus per-component recipes.
|
|
1425
|
+
* Extend any of them in your theme file: `defineTheme({ name: 'acme', extends: presets.glyph, ... })`.
|
|
1426
|
+
*/
|
|
1427
|
+
/** Precise, dark-first, one electric-blue accent, layered soft shadows, Geist. */
|
|
1428
|
+
const glyph = presets$1.glyph;
|
|
1429
|
+
/** Zero radius, heavy borders, hard offset shadows, loud color. */
|
|
1430
|
+
const brutalist = defineTheme$1({
|
|
1431
|
+
name: "brutalist",
|
|
1432
|
+
extends: presets$1.brutalist,
|
|
1433
|
+
components: {
|
|
1434
|
+
Button: { className: { base: "font-bold uppercase tracking-wide shadow-sm hover:-translate-x-px hover:-translate-y-px hover:shadow-md active:translate-x-[3px] active:translate-y-[3px] active:shadow-none" } },
|
|
1435
|
+
Badge: { className: { base: "font-bold uppercase" } },
|
|
1436
|
+
Card: { defaultVariants: { variant: "elevated" } },
|
|
1437
|
+
Heading: { className: { base: "uppercase" } },
|
|
1438
|
+
Input: { className: { root: "shadow-sm focus-within:ring-0 focus-within:shadow-md" } }
|
|
1439
|
+
}
|
|
1440
|
+
});
|
|
1441
|
+
/** Serif display type, warm paper neutrals, deep green ink, hairlines instead of shadows. */
|
|
1442
|
+
const editorial = defineTheme$1({
|
|
1443
|
+
name: "editorial",
|
|
1444
|
+
extends: presets$1.editorial,
|
|
1445
|
+
components: {
|
|
1446
|
+
Card: { defaultVariants: { variant: "outline" } },
|
|
1447
|
+
Eyebrow: { className: { base: "font-sans tracking-[0.18em]" } },
|
|
1448
|
+
Button: { className: { base: "tracking-wide" } }
|
|
1449
|
+
}
|
|
1450
|
+
});
|
|
1451
|
+
/** Pill shapes, lavender and mint, chunky rounded type, springy motion. */
|
|
1452
|
+
const playful = defineTheme$1({
|
|
1453
|
+
name: "playful",
|
|
1454
|
+
extends: presets$1.playful,
|
|
1455
|
+
components: {
|
|
1456
|
+
Button: { className: { base: "font-bold hover:-translate-y-0.5 active:translate-y-0 active:scale-[0.97]" } },
|
|
1457
|
+
Card: { defaultVariants: { variant: "elevated" } },
|
|
1458
|
+
Badge: { className: { base: "font-semibold" } }
|
|
1459
|
+
}
|
|
1460
|
+
});
|
|
1461
|
+
const presets = {
|
|
1462
|
+
glyph,
|
|
1463
|
+
brutalist,
|
|
1464
|
+
editorial,
|
|
1465
|
+
playful
|
|
1466
|
+
};
|
|
1467
|
+
//#endregion
|
|
1468
|
+
//#region src/provider/theme-style.tsx
|
|
1469
|
+
/** Renders a theme's CSS variables. Re-renders replace the previous rules in place. */
|
|
1470
|
+
function ThemeStyle({ theme, selector }) {
|
|
1471
|
+
const css = useMemo(() => themeToCss$1(theme, selector ? { selector } : {}), [theme, selector]);
|
|
1472
|
+
return /* @__PURE__ */ jsx("style", {
|
|
1473
|
+
"data-glyph-theme-style": theme.name,
|
|
1474
|
+
dangerouslySetInnerHTML: { __html: css }
|
|
1475
|
+
});
|
|
1476
|
+
}
|
|
1477
|
+
//#endregion
|
|
1478
|
+
//#region src/provider/glyph-provider.tsx
|
|
1479
|
+
/** Provides the theme, color mode and app settings. Render once at the root. */
|
|
1480
|
+
function GlyphProvider({ children, theme = glyph, defaultMode, storageKey, linkComponent = "a" }) {
|
|
1481
|
+
const value = useMemo(() => ({
|
|
1482
|
+
theme,
|
|
1483
|
+
linkComponent
|
|
1484
|
+
}), [theme, linkComponent]);
|
|
1485
|
+
return /* @__PURE__ */ jsx(ColorModeProvider, {
|
|
1486
|
+
...defaultMode !== void 0 && { defaultMode },
|
|
1487
|
+
...storageKey !== void 0 && { storageKey },
|
|
1488
|
+
children: /* @__PURE__ */ jsxs(GlyphContext.Provider, {
|
|
1489
|
+
value,
|
|
1490
|
+
children: [/* @__PURE__ */ jsx(ThemeStyle, { theme }), /* @__PURE__ */ jsx(Tooltip$1.Provider, {
|
|
1491
|
+
delayDuration: 300,
|
|
1492
|
+
children
|
|
1493
|
+
})]
|
|
1494
|
+
})
|
|
1495
|
+
});
|
|
1496
|
+
}
|
|
1497
|
+
//#endregion
|
|
1498
|
+
//#region src/utils/cn.ts
|
|
1499
|
+
const twMerge = extendTailwindMerge(twMergeConfig);
|
|
1500
|
+
/** Joins class names and resolves Tailwind conflicts (last one wins). */
|
|
1501
|
+
function cn(...inputs) {
|
|
1502
|
+
return twMerge(clsx(inputs));
|
|
1503
|
+
}
|
|
1504
|
+
//#endregion
|
|
1505
|
+
//#region src/provider/theme-scope.tsx
|
|
1506
|
+
/** Renders a subtree with a different theme, e.g. to compare themes side by side. */
|
|
1507
|
+
function ThemeScope({ theme, mode, asChild, children, ...props }) {
|
|
1508
|
+
const parent = useContext(GlyphContext);
|
|
1509
|
+
const value = useMemo(() => ({
|
|
1510
|
+
theme,
|
|
1511
|
+
linkComponent: parent?.linkComponent ?? "a"
|
|
1512
|
+
}), [theme, parent?.linkComponent]);
|
|
1513
|
+
const Comp = asChild ? Slot.Root : "div";
|
|
1514
|
+
return /* @__PURE__ */ jsxs(GlyphContext.Provider, {
|
|
1515
|
+
value,
|
|
1516
|
+
children: [/* @__PURE__ */ jsx(ThemeStyle, {
|
|
1517
|
+
theme,
|
|
1518
|
+
selector: scopeSelector(theme)
|
|
1519
|
+
}), /* @__PURE__ */ jsx(Comp, {
|
|
1520
|
+
"data-glyph-theme": theme.name,
|
|
1521
|
+
"data-mode": mode,
|
|
1522
|
+
...props,
|
|
1523
|
+
className: cn("bg-background font-sans text-foreground", props.className),
|
|
1524
|
+
children
|
|
1525
|
+
})]
|
|
1526
|
+
});
|
|
1527
|
+
}
|
|
1528
|
+
//#endregion
|
|
1529
|
+
export { AppShell, Async, Avatar, AvatarGroup, Badge, Button, Card, CardBody, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, ColorModeProvider, ColorModeScript, Container, EmptyState, ErrorState, Eyebrow, Field, FieldContext, GlyphProvider, Heading, IconButton, Input, Kbd, Label, LoadingState, Navbar, PageHeader, Section, SegmentedControl, Select, SelectItem, Separator, Sidebar, SidebarContent, SidebarFooter, SidebarHeader, SidebarItem, SidebarSection, SidebarTrigger, Skeleton, Spinner, Stack, Switch, Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Text, Textarea, ThemeScope, Tooltip, appShellStyles, avatarStyles, badgeStyles, brutalist, buttonStyles, cardStyles, checkboxStyles, cn, containerStyles, defineTheme, editorial, eyebrowStyles, fieldControl, fieldStyles, getColorModeScript, glyph, headingStyles, inputStyles, kbdStyles, labelStyles, navbarStyles, pageHeaderStyles, playful, presets, sectionStyles, segmentedControlStyles, selectStyles, separatorStyles, sidebarStyles, skeletonStyles, spinnerStyles, stackStyles, stateStyles, switchStyles, tableStyles, tabsStyles, textStyles, textareaStyles, themeToCss, tokenVar, tooltipStyles, tv, useAppShell, useAsync, useColorMode, useComponentConfig, useControllableState, useFieldControl, useGlyph, useLinkComponent, useMediaQuery, useScrolled };
|