@craftzbay/ui 0.9.0 → 0.11.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/README.md +82 -10
- package/dist-lib/__tests__/helpers/color.d.ts +9 -0
- package/dist-lib/__tests__/helpers/theme.d.ts +18 -0
- package/dist-lib/components/ui/Accordion.js +71 -0
- package/dist-lib/components/ui/Accordion.js.map +1 -0
- package/dist-lib/components/ui/Alert.d.ts +17 -1
- package/dist-lib/components/ui/Alert.js +79 -0
- package/dist-lib/components/ui/Alert.js.map +1 -0
- package/dist-lib/components/ui/Avatar.d.ts +3 -3
- package/dist-lib/components/ui/Avatar.js +94 -0
- package/dist-lib/components/ui/Avatar.js.map +1 -0
- package/dist-lib/components/ui/Badge.d.ts +4 -2
- package/dist-lib/components/ui/Badge.js +76 -0
- package/dist-lib/components/ui/Badge.js.map +1 -0
- package/dist-lib/components/ui/Breadcrumbs.d.ts +2 -0
- package/dist-lib/components/ui/Breadcrumbs.js +52 -0
- package/dist-lib/components/ui/Breadcrumbs.js.map +1 -0
- package/dist-lib/components/ui/Button.d.ts +6 -4
- package/dist-lib/components/ui/Button.js +131 -0
- package/dist-lib/components/ui/Button.js.map +1 -0
- package/dist-lib/components/ui/Calendar.d.ts +7 -1
- package/dist-lib/components/ui/Calendar.js +139 -0
- package/dist-lib/components/ui/Calendar.js.map +1 -0
- package/dist-lib/components/ui/Card.d.ts +3 -1
- package/dist-lib/components/ui/Card.js +97 -0
- package/dist-lib/components/ui/Card.js.map +1 -0
- package/dist-lib/components/ui/Carousel.d.ts +11 -7
- package/dist-lib/components/ui/Carousel.js +174 -0
- package/dist-lib/components/ui/Carousel.js.map +1 -0
- package/dist-lib/components/ui/Chart.d.ts +68 -7
- package/dist-lib/components/ui/Chart.js +487 -0
- package/dist-lib/components/ui/Chart.js.map +1 -0
- package/dist-lib/components/ui/Checkbox.js +70 -0
- package/dist-lib/components/ui/Checkbox.js.map +1 -0
- package/dist-lib/components/ui/Combobox.d.ts +9 -0
- package/dist-lib/components/ui/Combobox.js +195 -0
- package/dist-lib/components/ui/Combobox.js.map +1 -0
- package/dist-lib/components/ui/CommandPalette.d.ts +9 -6
- package/dist-lib/components/ui/CommandPalette.js +179 -0
- package/dist-lib/components/ui/CommandPalette.js.map +1 -0
- package/dist-lib/components/ui/ContextMenu.d.ts +7 -2
- package/dist-lib/components/ui/ContextMenu.js +165 -0
- package/dist-lib/components/ui/ContextMenu.js.map +1 -0
- package/dist-lib/components/ui/DataGrid.d.ts +17 -4
- package/dist-lib/components/ui/DataGrid.js +99 -0
- package/dist-lib/components/ui/DataGrid.js.map +1 -0
- package/dist-lib/components/ui/DatePicker.d.ts +16 -13
- package/dist-lib/components/ui/DatePicker.js +193 -0
- package/dist-lib/components/ui/DatePicker.js.map +1 -0
- package/dist-lib/components/ui/DesignSystemProvider.d.ts +102 -41
- package/dist-lib/components/ui/DesignSystemProvider.js +135 -0
- package/dist-lib/components/ui/DesignSystemProvider.js.map +1 -0
- package/dist-lib/components/ui/Dialog.d.ts +19 -4
- package/dist-lib/components/ui/Dialog.js +165 -0
- package/dist-lib/components/ui/Dialog.js.map +1 -0
- package/dist-lib/components/ui/Drawer.d.ts +23 -2
- package/dist-lib/components/ui/Drawer.js +132 -0
- package/dist-lib/components/ui/Drawer.js.map +1 -0
- package/dist-lib/components/ui/DropdownMenu.js +169 -0
- package/dist-lib/components/ui/DropdownMenu.js.map +1 -0
- package/dist-lib/components/ui/EmptyState.d.ts +2 -0
- package/dist-lib/components/ui/EmptyState.js +46 -0
- package/dist-lib/components/ui/EmptyState.js.map +1 -0
- package/dist-lib/components/ui/ErrorState.d.ts +13 -2
- package/dist-lib/components/ui/ErrorState.js +69 -0
- package/dist-lib/components/ui/ErrorState.js.map +1 -0
- package/dist-lib/components/ui/FileUpload.d.ts +3 -0
- package/dist-lib/components/ui/FileUpload.js +111 -0
- package/dist-lib/components/ui/FileUpload.js.map +1 -0
- package/dist-lib/components/ui/Form.d.ts +16 -2
- package/dist-lib/components/ui/Form.js +131 -0
- package/dist-lib/components/ui/Form.js.map +1 -0
- package/dist-lib/components/ui/Icon.d.ts +1 -1
- package/dist-lib/components/ui/Icon.js +32 -0
- package/dist-lib/components/ui/Icon.js.map +1 -0
- package/dist-lib/components/ui/IconButton.d.ts +3 -3
- package/dist-lib/components/ui/IconButton.js +64 -0
- package/dist-lib/components/ui/IconButton.js.map +1 -0
- package/dist-lib/components/ui/Input.d.ts +9 -4
- package/dist-lib/components/ui/Input.js +149 -0
- package/dist-lib/components/ui/Input.js.map +1 -0
- package/dist-lib/components/ui/Kbd.js +24 -0
- package/dist-lib/components/ui/Kbd.js.map +1 -0
- package/dist-lib/components/ui/MultiSelect.d.ts +3 -1
- package/dist-lib/components/ui/MultiSelect.js +210 -0
- package/dist-lib/components/ui/MultiSelect.js.map +1 -0
- package/dist-lib/components/ui/Pagination.d.ts +23 -0
- package/dist-lib/components/ui/Pagination.js +153 -0
- package/dist-lib/components/ui/Pagination.js.map +1 -0
- package/dist-lib/components/ui/Popover.js +35 -0
- package/dist-lib/components/ui/Popover.js.map +1 -0
- package/dist-lib/components/ui/Progress.d.ts +3 -3
- package/dist-lib/components/ui/Progress.js +100 -0
- package/dist-lib/components/ui/Progress.js.map +1 -0
- package/dist-lib/components/ui/RadioGroup.d.ts +1 -1
- package/dist-lib/components/ui/RadioGroup.js +81 -0
- package/dist-lib/components/ui/RadioGroup.js.map +1 -0
- package/dist-lib/components/ui/RelativeTime.d.ts +19 -0
- package/dist-lib/components/ui/RelativeTime.js +31 -0
- package/dist-lib/components/ui/RelativeTime.js.map +1 -0
- package/dist-lib/components/ui/ScrollArea.d.ts +9 -1
- package/dist-lib/components/ui/ScrollArea.js +60 -0
- package/dist-lib/components/ui/ScrollArea.js.map +1 -0
- package/dist-lib/components/ui/Select.d.ts +6 -1
- package/dist-lib/components/ui/Select.js +139 -0
- package/dist-lib/components/ui/Select.js.map +1 -0
- package/dist-lib/components/ui/Separator.js +26 -0
- package/dist-lib/components/ui/Separator.js.map +1 -0
- package/dist-lib/components/ui/Sheet.d.ts +10 -1
- package/dist-lib/components/ui/Sheet.js +136 -0
- package/dist-lib/components/ui/Sheet.js.map +1 -0
- package/dist-lib/components/ui/Sidebar.d.ts +29 -14
- package/dist-lib/components/ui/Sidebar.js +212 -0
- package/dist-lib/components/ui/Sidebar.js.map +1 -0
- package/dist-lib/components/ui/Skeleton.d.ts +13 -2
- package/dist-lib/components/ui/Skeleton.js +29 -0
- package/dist-lib/components/ui/Skeleton.js.map +1 -0
- package/dist-lib/components/ui/Slider.js +64 -0
- package/dist-lib/components/ui/Slider.js.map +1 -0
- package/dist-lib/components/ui/Snackbar.d.ts +1 -1
- package/dist-lib/components/ui/Snackbar.js +57 -0
- package/dist-lib/components/ui/Snackbar.js.map +1 -0
- package/dist-lib/components/ui/Spinner.js +54 -0
- package/dist-lib/components/ui/Spinner.js.map +1 -0
- package/dist-lib/components/ui/Stepper.d.ts +6 -0
- package/dist-lib/components/ui/Stepper.js +111 -0
- package/dist-lib/components/ui/Stepper.js.map +1 -0
- package/dist-lib/components/ui/Switch.js +80 -0
- package/dist-lib/components/ui/Switch.js.map +1 -0
- package/dist-lib/components/ui/Table.d.ts +41 -5
- package/dist-lib/components/ui/Table.js +179 -0
- package/dist-lib/components/ui/Table.js.map +1 -0
- package/dist-lib/components/ui/Tabs.d.ts +1 -1
- package/dist-lib/components/ui/Tabs.js +91 -0
- package/dist-lib/components/ui/Tabs.js.map +1 -0
- package/dist-lib/components/ui/TagInput.d.ts +17 -2
- package/dist-lib/components/ui/TagInput.js +111 -0
- package/dist-lib/components/ui/TagInput.js.map +1 -0
- package/dist-lib/components/ui/Textarea.js +88 -0
- package/dist-lib/components/ui/Textarea.js.map +1 -0
- package/dist-lib/components/ui/Timeline.d.ts +16 -6
- package/dist-lib/components/ui/Timeline.js +56 -0
- package/dist-lib/components/ui/Timeline.js.map +1 -0
- package/dist-lib/components/ui/Toast.d.ts +1 -1
- package/dist-lib/components/ui/Toast.js +158 -0
- package/dist-lib/components/ui/Toast.js.map +1 -0
- package/dist-lib/components/ui/Tooltip.d.ts +4 -1
- package/dist-lib/components/ui/Tooltip.js +60 -0
- package/dist-lib/components/ui/Tooltip.js.map +1 -0
- package/dist-lib/components/ui/TopNav.d.ts +7 -3
- package/dist-lib/components/ui/TopNav.js +62 -0
- package/dist-lib/components/ui/TopNav.js.map +1 -0
- package/dist-lib/components/ui/Tree.d.ts +34 -7
- package/dist-lib/components/ui/Tree.js +170 -0
- package/dist-lib/components/ui/Tree.js.map +1 -0
- package/dist-lib/hooks/use-debounce.d.ts +19 -0
- package/dist-lib/hooks/use-debounce.js +36 -0
- package/dist-lib/hooks/use-debounce.js.map +1 -0
- package/dist-lib/hooks/use-delayed-loading.d.ts +21 -0
- package/dist-lib/hooks/use-delayed-loading.js +20 -0
- package/dist-lib/hooks/use-delayed-loading.js.map +1 -0
- package/dist-lib/hooks/use-field-ids.d.ts +30 -0
- package/dist-lib/hooks/use-field-ids.js +18 -0
- package/dist-lib/hooks/use-field-ids.js.map +1 -0
- package/dist-lib/hooks/use-media-query.d.ts +3 -1
- package/dist-lib/hooks/use-media-query.js +22 -0
- package/dist-lib/hooks/use-media-query.js.map +1 -0
- package/dist-lib/hooks/use-modifier-key.d.ts +13 -0
- package/dist-lib/hooks/use-modifier-key.js +19 -0
- package/dist-lib/hooks/use-modifier-key.js.map +1 -0
- package/dist-lib/hooks/use-strings.d.ts +5 -0
- package/dist-lib/hooks/use-strings.js +12 -0
- package/dist-lib/hooks/use-strings.js.map +1 -0
- package/dist-lib/hooks/use-toast.d.ts +7 -2
- package/dist-lib/hooks/use-toast.js +67 -0
- package/dist-lib/hooks/use-toast.js.map +1 -0
- package/dist-lib/icon.d.ts +8 -0
- package/dist-lib/icon.js +7 -0
- package/dist-lib/icon.js.map +1 -0
- package/dist-lib/icons/index.d.ts +80 -88
- package/dist-lib/icons/index.js +163 -0
- package/dist-lib/icons/index.js.map +1 -0
- package/dist-lib/illustrations/index.d.ts +1 -0
- package/dist-lib/illustrations/index.js +114 -0
- package/dist-lib/illustrations/index.js.map +1 -0
- package/dist-lib/index.d.ts +68 -59
- package/dist-lib/index.js +288 -4530
- package/dist-lib/index.js.map +1 -1
- package/dist-lib/lib/format.d.ts +65 -0
- package/dist-lib/lib/format.js +95 -0
- package/dist-lib/lib/format.js.map +1 -0
- package/dist-lib/lib/return-focus.d.ts +11 -0
- package/dist-lib/lib/return-focus.js +12 -0
- package/dist-lib/lib/return-focus.js.map +1 -0
- package/dist-lib/lib/strings.d.ts +190 -0
- package/dist-lib/lib/strings.js +141 -0
- package/dist-lib/lib/strings.js.map +1 -0
- package/dist-lib/lib/strings.mn.d.ts +3 -0
- package/dist-lib/lib/strings.mn.js +115 -0
- package/dist-lib/lib/strings.mn.js.map +1 -0
- package/dist-lib/lib/utils.d.ts +0 -1
- package/dist-lib/lib/utils.js +10 -0
- package/dist-lib/lib/utils.js.map +1 -0
- package/dist-lib/styles.css +1 -1
- package/dist-lib/theme.css +840 -0
- package/package.json +38 -8
- package/dist-lib/index.cjs +0 -8
- package/dist-lib/index.cjs.map +0 -1
package/README.md
CHANGED
|
@@ -15,19 +15,81 @@ primitives — Button through DataGrid — plus composed patterns
|
|
|
15
15
|
## Install
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
-
pnpm add @craftzbay/ui
|
|
18
|
+
pnpm add @craftzbay/ui # peers: react ^18 || ^19, react-dom ^18 || ^19
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
+
**1. Fonts** — Geist + Geist Mono are referenced by the tokens but not bundled (Inter is only a fallback in the stack). Google Fonts serves the `cyrillic-ext` subset automatically; self-hosting (≤4 woff2, weights 400/500/600) is equally fine. Add to `<head>`:
|
|
22
|
+
|
|
23
|
+
```html
|
|
24
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
25
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
26
|
+
<link
|
|
27
|
+
href="https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500&display=swap"
|
|
28
|
+
rel="stylesheet"
|
|
29
|
+
/>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**2. CSS** — pick one:
|
|
33
|
+
|
|
34
|
+
```css
|
|
35
|
+
/* a) No Tailwind in your app: one precompiled sheet (tokens + base + every utility the components use) */
|
|
36
|
+
@import '@craftzbay/ui/styles.css';
|
|
37
|
+
|
|
38
|
+
/* b) Your app already uses Tailwind v4: share the tokens and let Tailwind compile the library's classes */
|
|
39
|
+
@import 'tailwindcss';
|
|
40
|
+
@import 'tw-animate-css'; /* required on this path — overlays use animate-in / animate-out */
|
|
41
|
+
@import '@craftzbay/ui/theme.css';
|
|
42
|
+
@source "../node_modules/@craftzbay/ui/dist-lib";
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Path (b) needs `tw-animate-css` installed (`pnpm add -D tw-animate-css`; it is an optional peer). Path (a) already bundles it.
|
|
46
|
+
|
|
47
|
+
**3. Dark mode** — toggle the `dark` class on `<html>`; every token flips and `color-scheme` follows. Set it from a blocking script before first paint to avoid a flash (three states: light / dark / system). `data-theme` is not used.
|
|
48
|
+
|
|
49
|
+
**4. Providers** — `<Toaster />` once near the root if you use `toast()`; wrap the app in `<TooltipProvider>` if you use `Tooltip`. Nothing else is required.
|
|
50
|
+
|
|
21
51
|
```tsx
|
|
22
|
-
import { Button,
|
|
23
|
-
import '@craftzbay/ui/styles.css';
|
|
52
|
+
import { Button, Toaster, TooltipProvider, toast } from '@craftzbay/ui';
|
|
24
53
|
|
|
25
54
|
export function App() {
|
|
26
|
-
return
|
|
55
|
+
return (
|
|
56
|
+
<TooltipProvider>
|
|
57
|
+
<Button onClick={() => toast({ title: 'Saved' })}>Save</Button>
|
|
58
|
+
<Toaster />
|
|
59
|
+
</TooltipProvider>
|
|
60
|
+
);
|
|
27
61
|
}
|
|
28
62
|
```
|
|
29
63
|
|
|
30
|
-
|
|
64
|
+
**Next.js App Router** — since 0.10 every built module carries a `'use client'` banner, so `import { Button } from '@craftzbay/ui'` works directly inside Server Components without a local re-export file. Put the CSS import and fonts in `app/layout.tsx`. Note: the pure helpers (`formatDate`, `formatNumber`, `formatMNT`, `mnStrings`, `defaultStrings`) are also client-marked today — they still run fine in Server Components (the directive only affects the boundary), but they are not tree-shaken into a server-only chunk.
|
|
65
|
+
|
|
66
|
+
**Name-addressed icons** — `Icons.*` (curated, tree-shaken) ships in the main entry. The lazy `<Icon name="…">` + `iconNames` list lives in a separate entry so its ~1500-icon import map never enters your bundle unless asked for:
|
|
67
|
+
|
|
68
|
+
```tsx
|
|
69
|
+
import { Icon } from '@craftzbay/ui/icon';
|
|
70
|
+
<Icon name="calendar" className="size-4" />;
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The package is ESM-only, ships one module per component (`sideEffects` limited to CSS), so `import { Button }` pulls in about 8 KB gzipped — no calendar, drawer, or form dependencies.
|
|
74
|
+
|
|
75
|
+
## Localisation (Mongolian built in)
|
|
76
|
+
|
|
77
|
+
Every built-in string (close/dismiss labels, placeholders, "No results.", pagination summary, error-state copy…) is typed in `UiStrings` and read through `useStrings()`. Defaults are English; a full Mongolian set ships as `mnStrings`.
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
import { DesignSystemProvider, mnStrings } from '@craftzbay/ui';
|
|
81
|
+
<DesignSystemProvider strings={mnStrings}>…</DesignSystemProvider>; // whole library in Mongolian
|
|
82
|
+
<DesignSystemProvider strings={{ dialog: { close: 'Schließen' } }}>…</DesignSystemProvider>; // partial override, deep-merged over defaults
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Precedence is per-component props (`placeholder`, `labels`, `aria-label`) → nearest provider → `defaultStrings`. Templates use `{name}` placeholders, e.g. `pagination.showing: '{from}–{to} / {total}'`.
|
|
86
|
+
|
|
87
|
+
## Design rules
|
|
88
|
+
|
|
89
|
+
This library implements the [craftzbay design-research](https://github.com/craftzbay/design-research) guidelines
|
|
90
|
+
([rendered site](https://craftzbay.github.io/design-research/)) — colour, type, spacing, components, accessibility, tokens.
|
|
91
|
+
design-research is the source of truth (canonical numbers in [`14-defaults.md`](https://github.com/craftzbay/design-research/blob/main/14-defaults.md));
|
|
92
|
+
[`docs/PHILOSOPHY.md`](./docs/PHILOSOPHY.md) is the library-specific summary.
|
|
31
93
|
|
|
32
94
|
## Local development
|
|
33
95
|
|
|
@@ -46,8 +108,8 @@ Inside `packages/ui` itself, `pnpm build` produces the distributable bundle and
|
|
|
46
108
|
|
|
47
109
|
## Tech stack
|
|
48
110
|
|
|
49
|
-
- **Tailwind CSS v4** — tokens defined in `@theme` in `src/styles/globals.css`
|
|
50
|
-
- **React 18** + **TypeScript 5.7**
|
|
111
|
+
- **Tailwind CSS v4** — tokens defined in `@theme` in `src/styles/theme.css` (`globals.css` = tailwind + tw-animate-css + theme)
|
|
112
|
+
- **React 18 / 19** + **TypeScript 5.7**
|
|
51
113
|
- **Radix UI** primitives for accessibility-correct overlays
|
|
52
114
|
- **class-variance-authority** + `cn()` (`clsx` + `tailwind-merge`)
|
|
53
115
|
- **Lucide** icons (16 / 20px, 1.5 stroke)
|
|
@@ -58,12 +120,14 @@ Inside `packages/ui` itself, `pnpm build` produces the distributable bundle and
|
|
|
58
120
|
```
|
|
59
121
|
src/
|
|
60
122
|
├── styles/
|
|
61
|
-
│
|
|
123
|
+
│ ├── theme.css # @theme tokens, semantic vars, dark variant, base layer
|
|
124
|
+
│ └── globals.css # tailwindcss + tw-animate-css + theme.css
|
|
62
125
|
├── lib/
|
|
63
126
|
│ ├── utils.ts # cn() + uid()
|
|
64
|
-
│
|
|
127
|
+
│ ├── format.ts # formatDate / formatNumber / formatMNT
|
|
128
|
+
│ └── strings.ts # UiStrings, defaultStrings (+ strings.mn.ts)
|
|
65
129
|
├── components/
|
|
66
|
-
│ ├── ui/ # primitives —
|
|
130
|
+
│ ├── ui/ # primitives — 52 components
|
|
67
131
|
│ └── patterns/ # composed layouts
|
|
68
132
|
├── hooks/
|
|
69
133
|
│ ├── use-toast.ts
|
|
@@ -78,6 +142,7 @@ docs/
|
|
|
78
142
|
## Component index
|
|
79
143
|
|
|
80
144
|
### Inputs
|
|
145
|
+
|
|
81
146
|
- [Input](./src/components/ui/Input.tsx) — text / email / password / number / search with prefix, suffix, error
|
|
82
147
|
- [Textarea](./src/components/ui/Textarea.tsx) — auto-resize multi-line input
|
|
83
148
|
- [Select](./src/components/ui/Select.tsx) — single-choice menu (Radix)
|
|
@@ -91,11 +156,13 @@ docs/
|
|
|
91
156
|
- [Form primitives](./src/components/ui/Form.tsx) — react-hook-form bindings
|
|
92
157
|
|
|
93
158
|
### Buttons
|
|
159
|
+
|
|
94
160
|
- [Button](./src/components/ui/Button.tsx) — primary · secondary · outline · ghost · destructive · link
|
|
95
161
|
- [IconButton](./src/components/ui/IconButton.tsx) — square icon-only variant
|
|
96
162
|
- [Pagination](./src/components/ui/Pagination.tsx) — numbered + jumps + page-size
|
|
97
163
|
|
|
98
164
|
### Feedback
|
|
165
|
+
|
|
99
166
|
- [Alert](./src/components/ui/Alert.tsx) — inline banner, dismissible
|
|
100
167
|
- [Toast](./src/components/ui/Toast.tsx) + `useToast` hook
|
|
101
168
|
- [Spinner](./src/components/ui/Spinner.tsx) — accent / neutral / on-accent tones
|
|
@@ -105,6 +172,7 @@ docs/
|
|
|
105
172
|
- [ErrorState](./src/components/ui/ErrorState.tsx) — 404 / 500 / generic
|
|
106
173
|
|
|
107
174
|
### Navigation
|
|
175
|
+
|
|
108
176
|
- [TopNav](./src/components/ui/TopNav.tsx) + `TopNavLink`
|
|
109
177
|
- [Sidebar](./src/components/ui/Sidebar.tsx) + `SidebarSection` + `SidebarItem` + `SidebarGroup`
|
|
110
178
|
- [Breadcrumbs](./src/components/ui/Breadcrumbs.tsx) — with overflow ellipsis
|
|
@@ -112,12 +180,14 @@ docs/
|
|
|
112
180
|
- [Stepper](./src/components/ui/Stepper.tsx) — horizontal + vertical
|
|
113
181
|
|
|
114
182
|
### Layout
|
|
183
|
+
|
|
115
184
|
- [Card](./src/components/ui/Card.tsx) + `CardHeader/Title/Description/Content/Footer`
|
|
116
185
|
- [Separator](./src/components/ui/Separator.tsx)
|
|
117
186
|
- [ScrollArea](./src/components/ui/ScrollArea.tsx) — Radix-backed styled scroll
|
|
118
187
|
- [Accordion](./src/components/ui/Accordion.tsx) — single + multiple
|
|
119
188
|
|
|
120
189
|
### Overlays
|
|
190
|
+
|
|
121
191
|
- [Dialog](./src/components/ui/Dialog.tsx) + `ConfirmationDialog`
|
|
122
192
|
- [Sheet](./src/components/ui/Sheet.tsx) — left / right / top / bottom
|
|
123
193
|
- [Popover](./src/components/ui/Popover.tsx)
|
|
@@ -127,12 +197,14 @@ docs/
|
|
|
127
197
|
- [CommandPalette](./src/components/ui/CommandPalette.tsx) — ⌘K palette
|
|
128
198
|
|
|
129
199
|
### Data display
|
|
200
|
+
|
|
130
201
|
- [Table](./src/components/ui/Table.tsx) + `TableSortHeader`
|
|
131
202
|
- [DataGrid](./src/components/ui/DataGrid.tsx) — column visibility, filter, sortable
|
|
132
203
|
- [Badge](./src/components/ui/Badge.tsx) — subtle + outline, 6 tones
|
|
133
204
|
- [Avatar](./src/components/ui/Avatar.tsx) + `AvatarGroup`
|
|
134
205
|
|
|
135
206
|
### Typography
|
|
207
|
+
|
|
136
208
|
- [Kbd](./src/components/ui/Kbd.tsx) — keyboard shortcut indicator
|
|
137
209
|
|
|
138
210
|
## Blocks (page templates)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Test-only colour maths: CSS `hsl()` / `oklch()` / hex → sRGB, WCAG 2.x contrast. */
|
|
2
|
+
export type RGB = [number, number, number];
|
|
3
|
+
export declare function parseColor(input: string): RGB;
|
|
4
|
+
export declare function hslToRgb(h: number, s: number, l: number): RGB;
|
|
5
|
+
/** OKLCH → OKLab → linear sRGB → gamma sRGB (clipped to gamut). */
|
|
6
|
+
export declare function oklchToRgb(L: number, C: number, hDeg: number): RGB;
|
|
7
|
+
export declare function luminance([r, g, b]: RGB): number;
|
|
8
|
+
export declare function contrast(a: RGB, b: RGB): number;
|
|
9
|
+
export declare function toHex([r, g, b]: RGB): string;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare const THEME_PATH: any;
|
|
2
|
+
export type TokenMap = Record<string, string>;
|
|
3
|
+
export interface ParsedTheme {
|
|
4
|
+
/** `:root { … }` semantic block. */
|
|
5
|
+
light: TokenMap;
|
|
6
|
+
/** `.dark { … }` semantic block. */
|
|
7
|
+
dark: TokenMap;
|
|
8
|
+
/** `@theme { … }` raw primitives (fonts, text scale, radius, shadow, z, motion, palette). */
|
|
9
|
+
raw: TokenMap;
|
|
10
|
+
/** `[data-accent='x']` presets: light (`:root[...]`) and dark (`.dark[...]`) overrides. */
|
|
11
|
+
accents: Record<string, {
|
|
12
|
+
shared: TokenMap;
|
|
13
|
+
light: TokenMap;
|
|
14
|
+
dark: TokenMap;
|
|
15
|
+
}>;
|
|
16
|
+
}
|
|
17
|
+
export declare function parseDeclarations(body: string): TokenMap;
|
|
18
|
+
export declare function parseTheme(css?: any): ParsedTheme;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as t, jsxs as d } from "react/jsx-runtime";
|
|
3
|
+
import { forwardRef as a } from "react";
|
|
4
|
+
import * as n from "@radix-ui/react-accordion";
|
|
5
|
+
import { ChevronDown as m } from "../../icons/index.js";
|
|
6
|
+
import { cn as i } from "../../lib/utils.js";
|
|
7
|
+
const v = n.Root, f = a(function({ className: o, ...e }, r) {
|
|
8
|
+
return /* @__PURE__ */ t(
|
|
9
|
+
n.Item,
|
|
10
|
+
{
|
|
11
|
+
ref: r,
|
|
12
|
+
className: i("border-border border-b", o),
|
|
13
|
+
...e
|
|
14
|
+
}
|
|
15
|
+
);
|
|
16
|
+
});
|
|
17
|
+
f.displayName = "AccordionItem";
|
|
18
|
+
const l = a(function({ className: o, children: e, ...r }, c) {
|
|
19
|
+
return /* @__PURE__ */ t(n.Header, { className: "flex", children: /* @__PURE__ */ d(
|
|
20
|
+
n.Trigger,
|
|
21
|
+
{
|
|
22
|
+
ref: c,
|
|
23
|
+
className: i(
|
|
24
|
+
"text-foreground flex flex-1 items-center justify-between gap-2 py-4 text-left text-sm font-medium",
|
|
25
|
+
"transition-colors duration-[var(--duration-fast)] outline-none",
|
|
26
|
+
"hover:text-accent",
|
|
27
|
+
"focus-visible:ring-ring focus-visible:ring-offset-background focus-visible:ring-2 focus-visible:ring-offset-2",
|
|
28
|
+
"[&[data-state=open]>svg]:rotate-180",
|
|
29
|
+
o
|
|
30
|
+
),
|
|
31
|
+
...r,
|
|
32
|
+
children: [
|
|
33
|
+
e,
|
|
34
|
+
/* @__PURE__ */ t(
|
|
35
|
+
m,
|
|
36
|
+
{
|
|
37
|
+
className: "text-foreground-subtle size-4 shrink-0 transition-transform duration-[var(--duration-base)]",
|
|
38
|
+
"aria-hidden": !0
|
|
39
|
+
}
|
|
40
|
+
)
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
) });
|
|
44
|
+
});
|
|
45
|
+
l.displayName = "AccordionTrigger";
|
|
46
|
+
const u = a(function({ className: o, children: e, ...r }, c) {
|
|
47
|
+
return /* @__PURE__ */ t(
|
|
48
|
+
n.Content,
|
|
49
|
+
{
|
|
50
|
+
ref: c,
|
|
51
|
+
className: i(
|
|
52
|
+
"text-foreground-muted overflow-hidden text-sm",
|
|
53
|
+
// Deliberate motion exception: height animates (layout property). Radix only
|
|
54
|
+
// keeps Content mounted through CSS *animations*, so a grid-rows transition
|
|
55
|
+
// would cut the close. The panel is short and nothing below it reflows
|
|
56
|
+
// except by design; `prefers-reduced-motion` disables it globally.
|
|
57
|
+
"data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
|
|
58
|
+
),
|
|
59
|
+
...r,
|
|
60
|
+
children: /* @__PURE__ */ t("div", { className: i("pt-0 pb-4", o), children: e })
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
u.displayName = "AccordionContent";
|
|
65
|
+
export {
|
|
66
|
+
v as Accordion,
|
|
67
|
+
u as AccordionContent,
|
|
68
|
+
f as AccordionItem,
|
|
69
|
+
l as AccordionTrigger
|
|
70
|
+
};
|
|
71
|
+
//# sourceMappingURL=Accordion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Accordion.js","sources":["../../../src/components/ui/Accordion.tsx"],"sourcesContent":["'use client';\n\nimport { forwardRef, type ComponentPropsWithoutRef, type ElementRef } from 'react';\nimport * as AccordionPrimitive from '@radix-ui/react-accordion';\nimport { ChevronDown } from '@/icons';\nimport { cn } from '@/lib/utils';\n\n/**\n * Accessible disclosure list. Supports single (`type=\"single\"`) and multiple\n * (`type=\"multiple\"`) open behaviour — both come from Radix.\n *\n * @example FAQ\n * <Accordion type=\"single\" collapsible>\n * <AccordionItem value=\"q1\">\n * <AccordionTrigger>Can I cancel anytime?</AccordionTrigger>\n * <AccordionContent>Yes — usage stops at the end of the billing period.</AccordionContent>\n * </AccordionItem>\n * …\n * </Accordion>\n *\n * @do Use for FAQs, settings groups, and progressive-disclosure forms.\n * @dont Nest accordions inside accordions — the focus order becomes opaque.\n */\nexport const Accordion = AccordionPrimitive.Root;\n\nexport const AccordionItem = forwardRef<\n ElementRef<typeof AccordionPrimitive.Item>,\n ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>\n>(function AccordionItem({ className, ...props }, ref) {\n return (\n <AccordionPrimitive.Item\n ref={ref}\n className={cn('border-border border-b', className)}\n {...props}\n />\n );\n});\nAccordionItem.displayName = 'AccordionItem';\n\nexport const AccordionTrigger = forwardRef<\n ElementRef<typeof AccordionPrimitive.Trigger>,\n ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>\n>(function AccordionTrigger({ className, children, ...props }, ref) {\n return (\n <AccordionPrimitive.Header className=\"flex\">\n <AccordionPrimitive.Trigger\n ref={ref}\n className={cn(\n 'text-foreground flex flex-1 items-center justify-between gap-2 py-4 text-left text-sm font-medium',\n 'transition-colors duration-[var(--duration-fast)] outline-none',\n 'hover:text-accent',\n 'focus-visible:ring-ring focus-visible:ring-offset-background focus-visible:ring-2 focus-visible:ring-offset-2',\n '[&[data-state=open]>svg]:rotate-180',\n className,\n )}\n {...props}\n >\n {children}\n <ChevronDown\n className=\"text-foreground-subtle size-4 shrink-0 transition-transform duration-[var(--duration-base)]\"\n aria-hidden\n />\n </AccordionPrimitive.Trigger>\n </AccordionPrimitive.Header>\n );\n});\nAccordionTrigger.displayName = 'AccordionTrigger';\n\nexport const AccordionContent = forwardRef<\n ElementRef<typeof AccordionPrimitive.Content>,\n ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>\n>(function AccordionContent({ className, children, ...props }, ref) {\n return (\n <AccordionPrimitive.Content\n ref={ref}\n className={cn(\n 'text-foreground-muted overflow-hidden text-sm',\n // Deliberate motion exception: height animates (layout property). Radix only\n // keeps Content mounted through CSS *animations*, so a grid-rows transition\n // would cut the close. The panel is short and nothing below it reflows\n // except by design; `prefers-reduced-motion` disables it globally.\n 'data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down',\n )}\n {...props}\n >\n <div className={cn('pt-0 pb-4', className)}>{children}</div>\n </AccordionPrimitive.Content>\n );\n});\nAccordionContent.displayName = 'AccordionContent';\n"],"names":["Accordion","AccordionPrimitive","AccordionItem","forwardRef","className","props","ref","jsx","cn","AccordionTrigger","children","jsxs","ChevronDown","AccordionContent"],"mappings":";;;;;;AAuBO,MAAMA,IAAYC,EAAmB,MAE/BC,IAAgBC,EAG3B,SAAuB,EAAE,WAAAC,GAAW,GAAGC,EAAA,GAASC,GAAK;AACrD,SACE,gBAAAC;AAAA,IAACN,EAAmB;AAAA,IAAnB;AAAA,MACC,KAAAK;AAAA,MACA,WAAWE,EAAG,0BAA0BJ,CAAS;AAAA,MAChD,GAAGC;AAAA,IAAA;AAAA,EAAA;AAGV,CAAC;AACDH,EAAc,cAAc;AAErB,MAAMO,IAAmBN,EAG9B,SAA0B,EAAE,WAAAC,GAAW,UAAAM,GAAU,GAAGL,EAAA,GAASC,GAAK;AAClE,SACE,gBAAAC,EAACN,EAAmB,QAAnB,EAA0B,WAAU,QACnC,UAAA,gBAAAU;AAAA,IAACV,EAAmB;AAAA,IAAnB;AAAA,MACC,KAAAK;AAAA,MACA,WAAWE;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACAJ;AAAA,MAAA;AAAA,MAED,GAAGC;AAAA,MAEH,UAAA;AAAA,QAAAK;AAAA,QACD,gBAAAH;AAAA,UAACK;AAAA,UAAA;AAAA,YACC,WAAU;AAAA,YACV,eAAW;AAAA,UAAA;AAAA,QAAA;AAAA,MACb;AAAA,IAAA;AAAA,EAAA,GAEJ;AAEJ,CAAC;AACDH,EAAiB,cAAc;AAExB,MAAMI,IAAmBV,EAG9B,SAA0B,EAAE,WAAAC,GAAW,UAAAM,GAAU,GAAGL,EAAA,GAASC,GAAK;AAClE,SACE,gBAAAC;AAAA,IAACN,EAAmB;AAAA,IAAnB;AAAA,MACC,KAAAK;AAAA,MACA,WAAWE;AAAA,QACT;AAAA;AAAA;AAAA;AAAA;AAAA,QAKA;AAAA,MAAA;AAAA,MAED,GAAGH;AAAA,MAEJ,4BAAC,OAAA,EAAI,WAAWG,EAAG,aAAaJ,CAAS,GAAI,UAAAM,EAAA,CAAS;AAAA,IAAA;AAAA,EAAA;AAG5D,CAAC;AACDG,EAAiB,cAAc;"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { HTMLAttributes, ReactNode } from 'react';
|
|
2
|
-
import { VariantProps } from '../../lib/cva';
|
|
2
|
+
import { VariantProps } from '../../lib/cva.js';
|
|
3
3
|
declare const alert: (props?: ({
|
|
4
4
|
variant?: "default" | "success" | "warning" | "danger" | "info" | null | undefined;
|
|
5
5
|
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
@@ -10,8 +10,24 @@ export interface AlertProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'
|
|
|
10
10
|
dismissible?: boolean;
|
|
11
11
|
/** Called when the user dismisses. */
|
|
12
12
|
onDismiss?: () => void;
|
|
13
|
+
/**
|
|
14
|
+
* Controlled visibility. Omit for the default uncontrolled behaviour
|
|
15
|
+
* (visible until dismissed).
|
|
16
|
+
*/
|
|
17
|
+
open?: boolean;
|
|
18
|
+
/** Called with the next visibility when the user dismisses. */
|
|
19
|
+
onOpenChange?: (open: boolean) => void;
|
|
13
20
|
/** Override the variant's default icon. Pass `false` to suppress the icon. */
|
|
14
21
|
icon?: ReactNode | false;
|
|
22
|
+
/**
|
|
23
|
+
* Announce the alert to screen readers when it appears: `role="alert"` for
|
|
24
|
+
* `danger`, `role="status"` otherwise. Off by default — content that is on
|
|
25
|
+
* the page at load should not be announced as a live update.
|
|
26
|
+
* @default false
|
|
27
|
+
*/
|
|
28
|
+
live?: boolean;
|
|
29
|
+
/** Heading level of `title`. Default 3 — fits under a page h1 / section h2. */
|
|
30
|
+
headingLevel?: 2 | 3 | 4 | 5 | 6;
|
|
15
31
|
}
|
|
16
32
|
/**
|
|
17
33
|
* Inline banner for static page-level or section-level state. For transient
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs as l, jsx as e } from "react/jsx-runtime";
|
|
3
|
+
import { forwardRef as k, useState as w, createElement as z } from "react";
|
|
4
|
+
import { X as A, XCircle as j, AlertTriangle as y, CheckCircle2 as I, Info as S } from "../../icons/index.js";
|
|
5
|
+
import { cn as o } from "../../lib/utils.js";
|
|
6
|
+
import { useStrings as V } from "../../hooks/use-strings.js";
|
|
7
|
+
import { cva as X } from "class-variance-authority";
|
|
8
|
+
const E = X(["relative flex w-full gap-3 rounded-lg border p-4", "text-sm"], {
|
|
9
|
+
variants: {
|
|
10
|
+
variant: {
|
|
11
|
+
default: "border-border bg-background-subtle text-foreground",
|
|
12
|
+
info: "border-info-border-soft bg-info-soft text-info-text",
|
|
13
|
+
success: "border-success-border-soft bg-success-soft text-success-text",
|
|
14
|
+
warning: "border-warning-border-soft bg-warning-soft text-warning-text",
|
|
15
|
+
danger: "border-danger-border-soft bg-danger-soft text-danger-text"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
defaultVariants: { variant: "default" }
|
|
19
|
+
}), F = {
|
|
20
|
+
default: null,
|
|
21
|
+
info: /* @__PURE__ */ e(S, { className: "mt-0.5 size-4 shrink-0", "aria-hidden": !0 }),
|
|
22
|
+
success: /* @__PURE__ */ e(I, { className: "mt-0.5 size-4 shrink-0", "aria-hidden": !0 }),
|
|
23
|
+
warning: /* @__PURE__ */ e(y, { className: "mt-0.5 size-4 shrink-0", "aria-hidden": !0 }),
|
|
24
|
+
danger: /* @__PURE__ */ e(j, { className: "mt-0.5 size-4 shrink-0", "aria-hidden": !0 })
|
|
25
|
+
}, R = k(function({
|
|
26
|
+
className: d,
|
|
27
|
+
variant: r = "default",
|
|
28
|
+
title: i,
|
|
29
|
+
dismissible: c,
|
|
30
|
+
onDismiss: t,
|
|
31
|
+
open: n,
|
|
32
|
+
onOpenChange: s,
|
|
33
|
+
icon: a,
|
|
34
|
+
live: f = !1,
|
|
35
|
+
headingLevel: u = 3,
|
|
36
|
+
children: m,
|
|
37
|
+
...g
|
|
38
|
+
}, b) {
|
|
39
|
+
const x = V(), [h, v] = w(!0);
|
|
40
|
+
if (!(n ?? h)) return null;
|
|
41
|
+
const p = () => {
|
|
42
|
+
n === void 0 && v(!1), s == null || s(!1), t == null || t();
|
|
43
|
+
}, N = a === !1 ? null : a ?? F[r ?? "default"];
|
|
44
|
+
return /* @__PURE__ */ l(
|
|
45
|
+
"div",
|
|
46
|
+
{
|
|
47
|
+
ref: b,
|
|
48
|
+
role: f ? r === "danger" ? "alert" : "status" : void 0,
|
|
49
|
+
className: o(E({ variant: r }), d),
|
|
50
|
+
...g,
|
|
51
|
+
children: [
|
|
52
|
+
N,
|
|
53
|
+
/* @__PURE__ */ l("div", { className: "min-w-0 flex-1", children: [
|
|
54
|
+
i && z(`h${u}`, { className: "mb-1 leading-tight font-medium" }, i),
|
|
55
|
+
/* @__PURE__ */ e("div", { className: "text-sm leading-relaxed [&_p]:leading-relaxed", children: m })
|
|
56
|
+
] }),
|
|
57
|
+
c && /* @__PURE__ */ e(
|
|
58
|
+
"button",
|
|
59
|
+
{
|
|
60
|
+
type: "button",
|
|
61
|
+
"aria-label": x.alert.dismiss,
|
|
62
|
+
onClick: p,
|
|
63
|
+
className: o(
|
|
64
|
+
"inline-flex size-6 shrink-0 items-center justify-center rounded-md",
|
|
65
|
+
"outline-none hover:bg-current/10",
|
|
66
|
+
"focus-visible:ring-ring focus-visible:ring-offset-background focus-visible:ring-2 focus-visible:ring-offset-2"
|
|
67
|
+
),
|
|
68
|
+
children: /* @__PURE__ */ e(A, { className: "size-3.5", "aria-hidden": !0 })
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
);
|
|
74
|
+
});
|
|
75
|
+
R.displayName = "Alert";
|
|
76
|
+
export {
|
|
77
|
+
R as Alert
|
|
78
|
+
};
|
|
79
|
+
//# sourceMappingURL=Alert.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Alert.js","sources":["../../../src/components/ui/Alert.tsx"],"sourcesContent":["'use client';\n\nimport { createElement, forwardRef, useState, type HTMLAttributes, type ReactNode } from 'react';\nimport { AlertTriangle, CheckCircle2, Info, X, XCircle } from '@/icons';\nimport { cn } from '@/lib/utils';\nimport { useStrings } from '@/hooks/use-strings';\nimport { cva, type VariantProps } from '@/lib/cva';\n\nconst alert = cva(['relative flex w-full gap-3 rounded-lg border p-4', 'text-sm'], {\n variants: {\n variant: {\n default: 'border-border bg-background-subtle text-foreground',\n info: 'border-info-border-soft bg-info-soft text-info-text',\n success: 'border-success-border-soft bg-success-soft text-success-text',\n warning: 'border-warning-border-soft bg-warning-soft text-warning-text',\n danger: 'border-danger-border-soft bg-danger-soft text-danger-text',\n },\n },\n defaultVariants: { variant: 'default' },\n});\n\nconst iconForVariant = {\n default: null,\n info: <Info className=\"mt-0.5 size-4 shrink-0\" aria-hidden />,\n success: <CheckCircle2 className=\"mt-0.5 size-4 shrink-0\" aria-hidden />,\n warning: <AlertTriangle className=\"mt-0.5 size-4 shrink-0\" aria-hidden />,\n danger: <XCircle className=\"mt-0.5 size-4 shrink-0\" aria-hidden />,\n};\n\nexport interface AlertProps\n extends Omit<HTMLAttributes<HTMLDivElement>, 'title'>, VariantProps<typeof alert> {\n /** Heading. Renders bold above the description. */\n title?: ReactNode;\n /** Show a dismiss (×) button. */\n dismissible?: boolean;\n /** Called when the user dismisses. */\n onDismiss?: () => void;\n /**\n * Controlled visibility. Omit for the default uncontrolled behaviour\n * (visible until dismissed).\n */\n open?: boolean;\n /** Called with the next visibility when the user dismisses. */\n onOpenChange?: (open: boolean) => void;\n /** Override the variant's default icon. Pass `false` to suppress the icon. */\n icon?: ReactNode | false;\n /**\n * Announce the alert to screen readers when it appears: `role=\"alert\"` for\n * `danger`, `role=\"status\"` otherwise. Off by default — content that is on\n * the page at load should not be announced as a live update.\n * @default false\n */\n live?: boolean;\n /** Heading level of `title`. Default 3 — fits under a page h1 / section h2. */\n headingLevel?: 2 | 3 | 4 | 5 | 6;\n}\n\n/**\n * Inline banner for static page-level or section-level state. For transient\n * notifications use `Toast` instead.\n *\n * @example Inline error\n * <Alert variant=\"danger\" title=\"Payment failed\">\n * Your card was declined. Update your billing info to retry.\n * </Alert>\n *\n * @example Dismissible info\n * <Alert variant=\"info\" dismissible onDismiss={dismiss}>\n * We're improving search — give us feedback at #search-feedback.\n * </Alert>\n *\n * @do Use the closest semantic variant — `info` for neutral facts,\n * `warning` for \"watch out\", `danger` for \"this is broken\".\n * @dont Use Alert for one-time confirmations — that's Toast.\n */\nexport const Alert = forwardRef<HTMLDivElement, AlertProps>(function Alert(\n {\n className,\n variant = 'default',\n title,\n dismissible,\n onDismiss,\n open: openProp,\n onOpenChange,\n icon,\n live = false,\n headingLevel = 3,\n children,\n ...props\n },\n ref,\n) {\n const strings = useStrings();\n const [internalOpen, setInternalOpen] = useState(true);\n const open = openProp ?? internalOpen;\n if (!open) return null;\n\n const handleDismiss = () => {\n if (openProp === undefined) setInternalOpen(false);\n onOpenChange?.(false);\n onDismiss?.();\n };\n\n const renderedIcon = icon === false ? null : (icon ?? iconForVariant[variant ?? 'default']);\n\n return (\n <div\n ref={ref}\n // Only errors interrupt the screen reader; everything else is polite.\n role={live ? (variant === 'danger' ? 'alert' : 'status') : undefined}\n className={cn(alert({ variant }), className)}\n {...props}\n >\n {renderedIcon}\n <div className=\"min-w-0 flex-1\">\n {title &&\n createElement(`h${headingLevel}`, { className: 'mb-1 leading-tight font-medium' }, title)}\n <div className=\"text-sm leading-relaxed [&_p]:leading-relaxed\">{children}</div>\n </div>\n {dismissible && (\n <button\n type=\"button\"\n aria-label={strings.alert.dismiss}\n onClick={handleDismiss}\n className={cn(\n 'inline-flex size-6 shrink-0 items-center justify-center rounded-md',\n 'outline-none hover:bg-current/10',\n 'focus-visible:ring-ring focus-visible:ring-offset-background focus-visible:ring-2 focus-visible:ring-offset-2',\n )}\n >\n <X className=\"size-3.5\" aria-hidden />\n </button>\n )}\n </div>\n );\n});\nAlert.displayName = 'Alert';\n"],"names":["alert","cva","iconForVariant","jsx","Info","CheckCircle2","AlertTriangle","XCircle","Alert","forwardRef","className","variant","title","dismissible","onDismiss","openProp","onOpenChange","icon","live","headingLevel","children","props","ref","strings","useStrings","internalOpen","setInternalOpen","useState","handleDismiss","renderedIcon","jsxs","cn","createElement","X"],"mappings":";;;;;;;AAQA,MAAMA,IAAQC,EAAI,CAAC,oDAAoD,SAAS,GAAG;AAAA,EACjF,UAAU;AAAA,IACR,SAAS;AAAA,MACP,SAAS;AAAA,MACT,MAAM;AAAA,MACN,SAAS;AAAA,MACT,SAAS;AAAA,MACT,QAAQ;AAAA,IAAA;AAAA,EACV;AAAA,EAEF,iBAAiB,EAAE,SAAS,UAAA;AAC9B,CAAC,GAEKC,IAAiB;AAAA,EACrB,SAAS;AAAA,EACT,MAAM,gBAAAC,EAACC,GAAA,EAAK,WAAU,0BAAyB,eAAW,IAAC;AAAA,EAC3D,SAAS,gBAAAD,EAACE,GAAA,EAAa,WAAU,0BAAyB,eAAW,IAAC;AAAA,EACtE,SAAS,gBAAAF,EAACG,GAAA,EAAc,WAAU,0BAAyB,eAAW,IAAC;AAAA,EACvE,QAAQ,gBAAAH,EAACI,GAAA,EAAQ,WAAU,0BAAyB,eAAW,GAAA,CAAC;AAClE,GAgDaC,IAAQC,EAAuC,SAC1D;AAAA,EACE,WAAAC;AAAA,EACA,SAAAC,IAAU;AAAA,EACV,OAAAC;AAAA,EACA,aAAAC;AAAA,EACA,WAAAC;AAAA,EACA,MAAMC;AAAA,EACN,cAAAC;AAAA,EACA,MAAAC;AAAA,EACA,MAAAC,IAAO;AAAA,EACP,cAAAC,IAAe;AAAA,EACf,UAAAC;AAAA,EACA,GAAGC;AACL,GACAC,GACA;AACA,QAAMC,IAAUC,EAAA,GACV,CAACC,GAAcC,CAAe,IAAIC,EAAS,EAAI;AAErD,MAAI,EADSZ,KAAYU,GACd,QAAO;AAElB,QAAMG,IAAgB,MAAM;AAC1B,IAAIb,MAAa,UAAWW,EAAgB,EAAK,GACjDV,KAAA,QAAAA,EAAe,KACfF,KAAA,QAAAA;AAAA,EACF,GAEMe,IAAeZ,MAAS,KAAQ,OAAQA,KAAQf,EAAeS,KAAW,SAAS;AAEzF,SACE,gBAAAmB;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,KAAAR;AAAA,MAEA,MAAMJ,IAAQP,MAAY,WAAW,UAAU,WAAY;AAAA,MAC3D,WAAWoB,EAAG/B,EAAM,EAAE,SAAAW,EAAA,CAAS,GAAGD,CAAS;AAAA,MAC1C,GAAGW;AAAA,MAEH,UAAA;AAAA,QAAAQ;AAAA,QACD,gBAAAC,EAAC,OAAA,EAAI,WAAU,kBACZ,UAAA;AAAA,UAAAlB,KACCoB,EAAc,IAAIb,CAAY,IAAI,EAAE,WAAW,iCAAA,GAAoCP,CAAK;AAAA,UAC1F,gBAAAT,EAAC,OAAA,EAAI,WAAU,iDAAiD,UAAAiB,EAAA,CAAS;AAAA,QAAA,GAC3E;AAAA,QACCP,KACC,gBAAAV;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,MAAK;AAAA,YACL,cAAYoB,EAAQ,MAAM;AAAA,YAC1B,SAASK;AAAA,YACT,WAAWG;AAAA,cACT;AAAA,cACA;AAAA,cACA;AAAA,YAAA;AAAA,YAGF,UAAA,gBAAA5B,EAAC8B,GAAA,EAAE,WAAU,YAAW,eAAW,GAAA,CAAC;AAAA,UAAA;AAAA,QAAA;AAAA,MACtC;AAAA,IAAA;AAAA,EAAA;AAIR,CAAC;AACDzB,EAAM,cAAc;"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { ComponentPropsWithoutRef, HTMLAttributes, ReactNode } from 'react';
|
|
2
|
-
import { VariantProps } from '../../lib/cva';
|
|
2
|
+
import { VariantProps } from '../../lib/cva.js';
|
|
3
3
|
import * as AvatarPrimitive from '@radix-ui/react-avatar';
|
|
4
4
|
declare const sizeMap: {
|
|
5
|
-
readonly xs: "size-5 text-
|
|
5
|
+
readonly xs: "size-5 text-xs";
|
|
6
6
|
readonly sm: "size-6 text-xs";
|
|
7
7
|
readonly md: "size-8 text-xs";
|
|
8
8
|
readonly lg: "size-10 text-sm";
|
|
@@ -40,7 +40,7 @@ export declare const Avatar: import('react').ForwardRefExoticComponent<AvatarPro
|
|
|
40
40
|
export interface AvatarGroupProps extends HTMLAttributes<HTMLDivElement> {
|
|
41
41
|
/** Maximum visible avatars before showing a "+N" overflow chip. */
|
|
42
42
|
max?: number;
|
|
43
|
-
/** Avatar size applied to children + overflow. */
|
|
43
|
+
/** Avatar size applied to children (unless a child sets its own) + overflow. */
|
|
44
44
|
size?: keyof typeof sizeMap;
|
|
45
45
|
/** Children — Avatars. */
|
|
46
46
|
children: ReactNode;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs as o, jsx as i } from "react/jsx-runtime";
|
|
3
|
+
import { forwardRef as b, Children as N, isValidElement as h, cloneElement as y } from "react";
|
|
4
|
+
import * as g from "@radix-ui/react-avatar";
|
|
5
|
+
import { cn as l } from "../../lib/utils.js";
|
|
6
|
+
import { useStrings as p } from "../../hooks/use-strings.js";
|
|
7
|
+
import { formatString as v } from "../../lib/strings.js";
|
|
8
|
+
import { cva as A } from "class-variance-authority";
|
|
9
|
+
const x = {
|
|
10
|
+
xs: "size-5 text-xs",
|
|
11
|
+
sm: "size-6 text-xs",
|
|
12
|
+
md: "size-8 text-xs",
|
|
13
|
+
lg: "size-10 text-sm",
|
|
14
|
+
xl: "size-12 text-base"
|
|
15
|
+
}, j = A("relative inline-flex shrink-0", {
|
|
16
|
+
variants: { size: x },
|
|
17
|
+
defaultVariants: { size: "md" }
|
|
18
|
+
}), w = {
|
|
19
|
+
online: "bg-success",
|
|
20
|
+
busy: "bg-danger",
|
|
21
|
+
away: "bg-warning",
|
|
22
|
+
offline: "bg-foreground-subtle"
|
|
23
|
+
}, G = b(
|
|
24
|
+
function({ className: m, size: t, src: e, alt: c, fallback: u, status: r, ...d }, a) {
|
|
25
|
+
const s = p();
|
|
26
|
+
return /* @__PURE__ */ o("span", { className: l(j({ size: t }), m), children: [
|
|
27
|
+
/* @__PURE__ */ o(
|
|
28
|
+
g.Root,
|
|
29
|
+
{
|
|
30
|
+
ref: a,
|
|
31
|
+
className: "bg-background-muted text-foreground-muted block size-full overflow-hidden rounded-full",
|
|
32
|
+
...d,
|
|
33
|
+
children: [
|
|
34
|
+
e && /* @__PURE__ */ i(
|
|
35
|
+
g.Image,
|
|
36
|
+
{
|
|
37
|
+
src: e,
|
|
38
|
+
alt: c ?? "",
|
|
39
|
+
className: "aspect-square size-full object-cover"
|
|
40
|
+
}
|
|
41
|
+
),
|
|
42
|
+
/* @__PURE__ */ i(
|
|
43
|
+
g.Fallback,
|
|
44
|
+
{
|
|
45
|
+
delayMs: e ? 200 : 0,
|
|
46
|
+
className: "bg-background-muted flex size-full items-center justify-center font-medium uppercase",
|
|
47
|
+
children: u ?? "?"
|
|
48
|
+
}
|
|
49
|
+
)
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
),
|
|
53
|
+
r && /* @__PURE__ */ i(
|
|
54
|
+
"span",
|
|
55
|
+
{
|
|
56
|
+
role: "img",
|
|
57
|
+
"aria-label": v(s.avatar.status, { status: r }),
|
|
58
|
+
className: l(
|
|
59
|
+
"ring-background absolute right-0 bottom-0 block size-[28%] rounded-full ring-2",
|
|
60
|
+
w[r]
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
] });
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
G.displayName = "Avatar";
|
|
68
|
+
const C = b(function({ max: m = 4, size: t = "md", className: e, children: c, ...u }, r) {
|
|
69
|
+
const d = p(), a = N.toArray(c), s = a.slice(0, m), f = a.length - s.length;
|
|
70
|
+
return /* @__PURE__ */ o("div", { ref: r, className: l("flex items-center -space-x-2", e), ...u, children: [
|
|
71
|
+
s.map((n, k) => /* @__PURE__ */ i("div", { className: "ring-background rounded-full ring-2", children: h(n) && n.props.size === void 0 ? y(n, { size: t }) : n }, k)),
|
|
72
|
+
f > 0 && /* @__PURE__ */ o(
|
|
73
|
+
"div",
|
|
74
|
+
{
|
|
75
|
+
className: l(
|
|
76
|
+
"bg-background-muted text-foreground-muted ring-background inline-flex items-center justify-center rounded-full ring-2",
|
|
77
|
+
x[t],
|
|
78
|
+
"font-medium"
|
|
79
|
+
),
|
|
80
|
+
"aria-label": v(d.avatar.more, { n: f }),
|
|
81
|
+
children: [
|
|
82
|
+
"+",
|
|
83
|
+
f
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
)
|
|
87
|
+
] });
|
|
88
|
+
});
|
|
89
|
+
C.displayName = "AvatarGroup";
|
|
90
|
+
export {
|
|
91
|
+
G as Avatar,
|
|
92
|
+
C as AvatarGroup
|
|
93
|
+
};
|
|
94
|
+
//# sourceMappingURL=Avatar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Avatar.js","sources":["../../../src/components/ui/Avatar.tsx"],"sourcesContent":["'use client';\n\nimport {\n Children,\n cloneElement,\n forwardRef,\n isValidElement,\n type ComponentPropsWithoutRef,\n type ElementRef,\n type HTMLAttributes,\n type ReactNode,\n} from 'react';\nimport * as AvatarPrimitive from '@radix-ui/react-avatar';\nimport { cn } from '@/lib/utils';\nimport { useStrings } from '@/hooks/use-strings';\nimport { formatString } from '@/lib/strings';\nimport { cva, type VariantProps } from '@/lib/cva';\n\nconst sizeMap = {\n xs: 'size-5 text-xs',\n sm: 'size-6 text-xs',\n md: 'size-8 text-xs',\n lg: 'size-10 text-sm',\n xl: 'size-12 text-base',\n} as const;\n\n// The wrapper sets size — Root sits inside and clips image/fallback only.\nconst avatarWrapper = cva('relative inline-flex shrink-0', {\n variants: { size: sizeMap },\n defaultVariants: { size: 'md' },\n});\n\nexport interface AvatarProps\n extends\n ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>,\n VariantProps<typeof avatarWrapper> {\n /** Image URL. If absent or fails to load, fallback initials are shown. */\n src?: string;\n /** Alt text for the image; falls back to fallback when there's no `src`. */\n alt?: string;\n /** Initials shown when image is missing or loading. Pass at most 2 characters. */\n fallback?: string;\n /** Status dot rendered on the bottom-right. */\n status?: 'online' | 'busy' | 'away' | 'offline';\n}\n\nconst statusColour: Record<NonNullable<AvatarProps['status']>, string> = {\n online: 'bg-success',\n busy: 'bg-danger',\n away: 'bg-warning',\n offline: 'bg-foreground-subtle',\n};\n\n/**\n * Avatar with image, initials fallback, and optional status dot.\n *\n * @example\n * <Avatar src={user.photo} alt={user.name} fallback={getInitials(user.name)} status=\"online\" />\n *\n * @example AvatarGroup\n * <AvatarGroup max={3}>\n * {users.map(u => <Avatar key={u.id} src={u.photo} fallback={getInitials(u.name)} />)}\n * </AvatarGroup>\n *\n * @do Provide `alt` for image avatars and meaningful initials for the\n * fallback so screen readers announce something useful.\n * @dont Use the status dot without a tooltip explaining what it means.\n */\nexport const Avatar = forwardRef<ElementRef<typeof AvatarPrimitive.Root>, AvatarProps>(\n function Avatar({ className, size, src, alt, fallback, status, ...props }, ref) {\n const strings = useStrings();\n return (\n <span className={cn(avatarWrapper({ size }), className)}>\n <AvatarPrimitive.Root\n ref={ref}\n className=\"bg-background-muted text-foreground-muted block size-full overflow-hidden rounded-full\"\n {...props}\n >\n {src && (\n <AvatarPrimitive.Image\n src={src}\n alt={alt ?? ''}\n className=\"aspect-square size-full object-cover\"\n />\n )}\n <AvatarPrimitive.Fallback\n delayMs={src ? 200 : 0}\n className=\"bg-background-muted flex size-full items-center justify-center font-medium uppercase\"\n >\n {fallback ?? '?'}\n </AvatarPrimitive.Fallback>\n </AvatarPrimitive.Root>\n {status && (\n <span\n role=\"img\"\n aria-label={formatString(strings.avatar.status, { status })}\n className={cn(\n 'ring-background absolute right-0 bottom-0 block size-[28%] rounded-full ring-2',\n statusColour[status],\n )}\n />\n )}\n </span>\n );\n },\n);\nAvatar.displayName = 'Avatar';\n\nexport interface AvatarGroupProps extends HTMLAttributes<HTMLDivElement> {\n /** Maximum visible avatars before showing a \"+N\" overflow chip. */\n max?: number;\n /** Avatar size applied to children (unless a child sets its own) + overflow. */\n size?: keyof typeof sizeMap;\n /** Children — Avatars. */\n children: ReactNode;\n}\n\n/**\n * Overlapping avatars with overflow indicator. Pass plain `<Avatar>` children.\n */\nexport const AvatarGroup = forwardRef<HTMLDivElement, AvatarGroupProps>(function AvatarGroup(\n { max = 4, size = 'md', className, children, ...props },\n ref,\n) {\n const strings = useStrings();\n const items = Children.toArray(children);\n const visible = items.slice(0, max);\n const overflow = items.length - visible.length;\n\n return (\n <div ref={ref} className={cn('flex items-center -space-x-2', className)} {...props}>\n {visible.map((child, i) => (\n <div key={i} className=\"ring-background rounded-full ring-2\">\n {isValidElement<AvatarProps>(child) && child.props.size === undefined\n ? cloneElement(child, { size })\n : child}\n </div>\n ))}\n {overflow > 0 && (\n <div\n className={cn(\n 'bg-background-muted text-foreground-muted ring-background inline-flex items-center justify-center rounded-full ring-2',\n sizeMap[size],\n 'font-medium',\n )}\n aria-label={formatString(strings.avatar.more, { n: overflow })}\n >\n +{overflow}\n </div>\n )}\n </div>\n );\n});\nAvatarGroup.displayName = 'AvatarGroup';\n"],"names":["sizeMap","avatarWrapper","cva","statusColour","Avatar","forwardRef","className","size","src","alt","fallback","status","props","ref","strings","useStrings","jsxs","cn","AvatarPrimitive","jsx","formatString","AvatarGroup","max","children","items","Children","visible","overflow","child","i","isValidElement","cloneElement"],"mappings":";;;;;;;;AAkBA,MAAMA,IAAU;AAAA,EACd,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN,GAGMC,IAAgBC,EAAI,iCAAiC;AAAA,EACzD,UAAU,EAAE,MAAMF,EAAA;AAAA,EAClB,iBAAiB,EAAE,MAAM,KAAA;AAC3B,CAAC,GAgBKG,IAAmE;AAAA,EACvE,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,MAAM;AAAA,EACN,SAAS;AACX,GAiBaC,IAASC;AAAA,EACpB,SAAgB,EAAE,WAAAC,GAAW,MAAAC,GAAM,KAAAC,GAAK,KAAAC,GAAK,UAAAC,GAAU,QAAAC,GAAQ,GAAGC,EAAA,GAASC,GAAK;AAC9E,UAAMC,IAAUC,EAAA;AAChB,WACE,gBAAAC,EAAC,QAAA,EAAK,WAAWC,EAAGhB,EAAc,EAAE,MAAAM,EAAA,CAAM,GAAGD,CAAS,GACpD,UAAA;AAAA,MAAA,gBAAAU;AAAA,QAACE,EAAgB;AAAA,QAAhB;AAAA,UACC,KAAAL;AAAA,UACA,WAAU;AAAA,UACT,GAAGD;AAAA,UAEH,UAAA;AAAA,YAAAJ,KACC,gBAAAW;AAAA,cAACD,EAAgB;AAAA,cAAhB;AAAA,gBACC,KAAAV;AAAA,gBACA,KAAKC,KAAO;AAAA,gBACZ,WAAU;AAAA,cAAA;AAAA,YAAA;AAAA,YAGd,gBAAAU;AAAA,cAACD,EAAgB;AAAA,cAAhB;AAAA,gBACC,SAASV,IAAM,MAAM;AAAA,gBACrB,WAAU;AAAA,gBAET,UAAAE,KAAY;AAAA,cAAA;AAAA,YAAA;AAAA,UACf;AAAA,QAAA;AAAA,MAAA;AAAA,MAEDC,KACC,gBAAAQ;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,MAAK;AAAA,UACL,cAAYC,EAAaN,EAAQ,OAAO,QAAQ,EAAE,QAAAH,GAAQ;AAAA,UAC1D,WAAWM;AAAA,YACT;AAAA,YACAd,EAAaQ,CAAM;AAAA,UAAA;AAAA,QACrB;AAAA,MAAA;AAAA,IACF,GAEJ;AAAA,EAEJ;AACF;AACAP,EAAO,cAAc;AAcd,MAAMiB,IAAchB,EAA6C,SACtE,EAAE,KAAAiB,IAAM,GAAG,MAAAf,IAAO,MAAM,WAAAD,GAAW,UAAAiB,GAAU,GAAGX,EAAA,GAChDC,GACA;AACA,QAAMC,IAAUC,EAAA,GACVS,IAAQC,EAAS,QAAQF,CAAQ,GACjCG,IAAUF,EAAM,MAAM,GAAGF,CAAG,GAC5BK,IAAWH,EAAM,SAASE,EAAQ;AAExC,SACE,gBAAAV,EAAC,SAAI,KAAAH,GAAU,WAAWI,EAAG,gCAAgCX,CAAS,GAAI,GAAGM,GAC1E,UAAA;AAAA,IAAAc,EAAQ,IAAI,CAACE,GAAOC,wBAClB,OAAA,EAAY,WAAU,uCACpB,UAAAC,EAA4BF,CAAK,KAAKA,EAAM,MAAM,SAAS,SACxDG,EAAaH,GAAO,EAAE,MAAArB,GAAM,IAC5BqB,EAAA,GAHIC,CAIV,CACD;AAAA,IACAF,IAAW,KACV,gBAAAX;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAWC;AAAA,UACT;AAAA,UACAjB,EAAQO,CAAI;AAAA,UACZ;AAAA,QAAA;AAAA,QAEF,cAAYa,EAAaN,EAAQ,OAAO,MAAM,EAAE,GAAGa,GAAU;AAAA,QAC9D,UAAA;AAAA,UAAA;AAAA,UACGA;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EACJ,GAEJ;AAEJ,CAAC;AACDN,EAAY,cAAc;"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { HTMLAttributes } from 'react';
|
|
2
|
-
import { VariantProps } from '../../lib/cva';
|
|
1
|
+
import { HTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
import { VariantProps } from '../../lib/cva.js';
|
|
3
3
|
declare const badge: (props?: ({
|
|
4
4
|
variant?: "subtle" | "outline" | null | undefined;
|
|
5
5
|
tone?: "success" | "warning" | "danger" | "info" | "neutral" | "accent" | null | undefined;
|
|
@@ -7,6 +7,8 @@ declare const badge: (props?: ({
|
|
|
7
7
|
export interface BadgeProps extends HTMLAttributes<HTMLSpanElement>, VariantProps<typeof badge> {
|
|
8
8
|
/** Show a leading status dot in the same tone. */
|
|
9
9
|
dot?: boolean;
|
|
10
|
+
/** Leading icon (decorative — sized 12px, `aria-hidden`). Overrides `dot`. */
|
|
11
|
+
icon?: ReactNode;
|
|
10
12
|
}
|
|
11
13
|
/**
|
|
12
14
|
* Status pill. Use `subtle` for high-volume contexts (table cells, lists);
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs as u, jsx as n } from "react/jsx-runtime";
|
|
3
|
+
import { forwardRef as d } from "react";
|
|
4
|
+
import { cn as a } from "../../lib/utils.js";
|
|
5
|
+
import { cva as g } from "class-variance-authority";
|
|
6
|
+
const b = g(
|
|
7
|
+
[
|
|
8
|
+
"inline-flex items-center gap-1 rounded-full px-2 py-0.5",
|
|
9
|
+
"text-xs font-medium whitespace-nowrap"
|
|
10
|
+
],
|
|
11
|
+
{
|
|
12
|
+
variants: {
|
|
13
|
+
variant: {
|
|
14
|
+
subtle: "",
|
|
15
|
+
outline: "border bg-transparent"
|
|
16
|
+
},
|
|
17
|
+
tone: {
|
|
18
|
+
neutral: "",
|
|
19
|
+
accent: "",
|
|
20
|
+
success: "",
|
|
21
|
+
warning: "",
|
|
22
|
+
danger: "",
|
|
23
|
+
info: ""
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
compoundVariants: [
|
|
27
|
+
// Subtle — coloured fill + matching text.
|
|
28
|
+
{ variant: "subtle", tone: "neutral", class: "bg-background-muted text-foreground-muted" },
|
|
29
|
+
{ variant: "subtle", tone: "accent", class: "bg-accent-soft text-on-accent-soft" },
|
|
30
|
+
{ variant: "subtle", tone: "success", class: "bg-success-soft text-success-text" },
|
|
31
|
+
{ variant: "subtle", tone: "warning", class: "bg-warning-soft text-warning-text" },
|
|
32
|
+
{ variant: "subtle", tone: "danger", class: "bg-danger-soft text-danger-text" },
|
|
33
|
+
{ variant: "subtle", tone: "info", class: "bg-info-soft text-info-text" },
|
|
34
|
+
// Outline — coloured border + text, transparent fill.
|
|
35
|
+
{ variant: "outline", tone: "neutral", class: "border-border text-foreground-muted" },
|
|
36
|
+
{ variant: "outline", tone: "accent", class: "border-accent text-accent" },
|
|
37
|
+
{
|
|
38
|
+
variant: "outline",
|
|
39
|
+
tone: "success",
|
|
40
|
+
class: "border-success-border-soft text-success-text"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
variant: "outline",
|
|
44
|
+
tone: "warning",
|
|
45
|
+
class: "border-warning-border-soft text-warning-text"
|
|
46
|
+
},
|
|
47
|
+
{ variant: "outline", tone: "danger", class: "border-danger-border-soft text-danger-text" },
|
|
48
|
+
{ variant: "outline", tone: "info", class: "border-info-border-soft text-info-text" }
|
|
49
|
+
],
|
|
50
|
+
defaultVariants: { variant: "subtle", tone: "neutral" }
|
|
51
|
+
}
|
|
52
|
+
), f = {
|
|
53
|
+
neutral: "bg-foreground-subtle",
|
|
54
|
+
accent: "bg-accent",
|
|
55
|
+
success: "bg-success",
|
|
56
|
+
warning: "bg-warning",
|
|
57
|
+
danger: "bg-danger",
|
|
58
|
+
info: "bg-info"
|
|
59
|
+
}, x = d(function({ className: r, variant: s, tone: t = "neutral", dot: o, icon: e, children: i, ...c }, l) {
|
|
60
|
+
return /* @__PURE__ */ u("span", { ref: l, className: a(b({ variant: s, tone: t }), r), ...c, children: [
|
|
61
|
+
e && /* @__PURE__ */ n("span", { "aria-hidden": !0, className: "inline-flex shrink-0 [&_svg]:size-3", children: e }),
|
|
62
|
+
o && !e && /* @__PURE__ */ n(
|
|
63
|
+
"span",
|
|
64
|
+
{
|
|
65
|
+
"aria-hidden": !0,
|
|
66
|
+
className: a("inline-block size-1.5 rounded-full", f[t ?? "neutral"])
|
|
67
|
+
}
|
|
68
|
+
),
|
|
69
|
+
i
|
|
70
|
+
] });
|
|
71
|
+
});
|
|
72
|
+
x.displayName = "Badge";
|
|
73
|
+
export {
|
|
74
|
+
x as Badge
|
|
75
|
+
};
|
|
76
|
+
//# sourceMappingURL=Badge.js.map
|