@craftzbay/ui 0.10.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 +14 -8
- 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 +18 -7
- package/dist-lib/components/ui/Accordion.js.map +1 -1
- package/dist-lib/components/ui/Alert.d.ts +16 -0
- package/dist-lib/components/ui/Alert.js +47 -34
- package/dist-lib/components/ui/Alert.js.map +1 -1
- package/dist-lib/components/ui/Avatar.d.ts +1 -1
- package/dist-lib/components/ui/Avatar.js +45 -47
- package/dist-lib/components/ui/Avatar.js.map +1 -1
- package/dist-lib/components/ui/Badge.d.ts +3 -1
- package/dist-lib/components/ui/Badge.js +24 -15
- package/dist-lib/components/ui/Badge.js.map +1 -1
- package/dist-lib/components/ui/Breadcrumbs.d.ts +2 -0
- package/dist-lib/components/ui/Breadcrumbs.js +46 -28
- package/dist-lib/components/ui/Breadcrumbs.js.map +1 -1
- package/dist-lib/components/ui/Button.d.ts +5 -3
- package/dist-lib/components/ui/Button.js +59 -45
- package/dist-lib/components/ui/Button.js.map +1 -1
- package/dist-lib/components/ui/Calendar.d.ts +1 -1
- package/dist-lib/components/ui/Calendar.js +87 -59
- package/dist-lib/components/ui/Calendar.js.map +1 -1
- package/dist-lib/components/ui/Card.d.ts +2 -0
- package/dist-lib/components/ui/Card.js +48 -67
- package/dist-lib/components/ui/Card.js.map +1 -1
- package/dist-lib/components/ui/Carousel.d.ts +6 -6
- package/dist-lib/components/ui/Carousel.js +133 -102
- package/dist-lib/components/ui/Carousel.js.map +1 -1
- package/dist-lib/components/ui/Chart.d.ts +49 -10
- package/dist-lib/components/ui/Chart.js +415 -181
- package/dist-lib/components/ui/Chart.js.map +1 -1
- package/dist-lib/components/ui/Checkbox.js +42 -28
- package/dist-lib/components/ui/Checkbox.js.map +1 -1
- package/dist-lib/components/ui/Combobox.d.ts +2 -0
- package/dist-lib/components/ui/Combobox.js +142 -111
- package/dist-lib/components/ui/Combobox.js.map +1 -1
- package/dist-lib/components/ui/CommandPalette.d.ts +6 -3
- package/dist-lib/components/ui/CommandPalette.js +92 -87
- package/dist-lib/components/ui/CommandPalette.js.map +1 -1
- package/dist-lib/components/ui/ContextMenu.d.ts +7 -2
- package/dist-lib/components/ui/ContextMenu.js +77 -62
- package/dist-lib/components/ui/ContextMenu.js.map +1 -1
- package/dist-lib/components/ui/DataGrid.d.ts +17 -4
- package/dist-lib/components/ui/DataGrid.js +65 -50
- package/dist-lib/components/ui/DataGrid.js.map +1 -1
- package/dist-lib/components/ui/DatePicker.js +118 -99
- package/dist-lib/components/ui/DatePicker.js.map +1 -1
- package/dist-lib/components/ui/DesignSystemProvider.d.ts +94 -40
- package/dist-lib/components/ui/DesignSystemProvider.js +111 -55
- package/dist-lib/components/ui/DesignSystemProvider.js.map +1 -1
- package/dist-lib/components/ui/Dialog.d.ts +18 -3
- package/dist-lib/components/ui/Dialog.js +105 -80
- package/dist-lib/components/ui/Dialog.js.map +1 -1
- package/dist-lib/components/ui/Drawer.d.ts +17 -2
- package/dist-lib/components/ui/Drawer.js +89 -55
- package/dist-lib/components/ui/Drawer.js.map +1 -1
- package/dist-lib/components/ui/DropdownMenu.js +14 -9
- package/dist-lib/components/ui/DropdownMenu.js.map +1 -1
- package/dist-lib/components/ui/EmptyState.d.ts +2 -0
- package/dist-lib/components/ui/EmptyState.js +30 -16
- package/dist-lib/components/ui/EmptyState.js.map +1 -1
- package/dist-lib/components/ui/ErrorState.d.ts +13 -2
- package/dist-lib/components/ui/ErrorState.js +44 -23
- package/dist-lib/components/ui/ErrorState.js.map +1 -1
- package/dist-lib/components/ui/FileUpload.js +59 -56
- package/dist-lib/components/ui/FileUpload.js.map +1 -1
- package/dist-lib/components/ui/Form.d.ts +15 -2
- package/dist-lib/components/ui/Form.js +97 -71
- package/dist-lib/components/ui/Form.js.map +1 -1
- package/dist-lib/components/ui/Icon.d.ts +1 -1
- package/dist-lib/components/ui/Icon.js +20 -14
- package/dist-lib/components/ui/Icon.js.map +1 -1
- package/dist-lib/components/ui/IconButton.d.ts +2 -2
- package/dist-lib/components/ui/IconButton.js +28 -20
- package/dist-lib/components/ui/IconButton.js.map +1 -1
- package/dist-lib/components/ui/Input.d.ts +3 -1
- package/dist-lib/components/ui/Input.js +92 -78
- package/dist-lib/components/ui/Input.js.map +1 -1
- package/dist-lib/components/ui/Kbd.js +1 -1
- package/dist-lib/components/ui/Kbd.js.map +1 -1
- package/dist-lib/components/ui/MultiSelect.js +121 -107
- package/dist-lib/components/ui/MultiSelect.js.map +1 -1
- package/dist-lib/components/ui/Pagination.js +67 -62
- package/dist-lib/components/ui/Pagination.js.map +1 -1
- package/dist-lib/components/ui/Popover.js +14 -14
- package/dist-lib/components/ui/Popover.js.map +1 -1
- package/dist-lib/components/ui/Progress.d.ts +3 -3
- package/dist-lib/components/ui/Progress.js +77 -72
- package/dist-lib/components/ui/Progress.js.map +1 -1
- package/dist-lib/components/ui/RadioGroup.d.ts +1 -1
- package/dist-lib/components/ui/RadioGroup.js +69 -52
- package/dist-lib/components/ui/RadioGroup.js.map +1 -1
- 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 +43 -27
- package/dist-lib/components/ui/ScrollArea.js.map +1 -1
- package/dist-lib/components/ui/Select.d.ts +6 -1
- package/dist-lib/components/ui/Select.js +79 -71
- package/dist-lib/components/ui/Select.js.map +1 -1
- package/dist-lib/components/ui/Separator.js +1 -1
- package/dist-lib/components/ui/Separator.js.map +1 -1
- package/dist-lib/components/ui/Sheet.d.ts +9 -0
- package/dist-lib/components/ui/Sheet.js +69 -46
- package/dist-lib/components/ui/Sheet.js.map +1 -1
- package/dist-lib/components/ui/Sidebar.d.ts +12 -9
- package/dist-lib/components/ui/Sidebar.js +152 -109
- package/dist-lib/components/ui/Sidebar.js.map +1 -1
- package/dist-lib/components/ui/Skeleton.d.ts +13 -2
- package/dist-lib/components/ui/Skeleton.js +15 -14
- package/dist-lib/components/ui/Skeleton.js.map +1 -1
- package/dist-lib/components/ui/Slider.js +41 -29
- package/dist-lib/components/ui/Slider.js.map +1 -1
- package/dist-lib/components/ui/Snackbar.js +23 -22
- package/dist-lib/components/ui/Snackbar.js.map +1 -1
- package/dist-lib/components/ui/Spinner.js +18 -6
- package/dist-lib/components/ui/Spinner.js.map +1 -1
- package/dist-lib/components/ui/Stepper.d.ts +6 -0
- package/dist-lib/components/ui/Stepper.js +71 -48
- package/dist-lib/components/ui/Stepper.js.map +1 -1
- package/dist-lib/components/ui/Switch.js +46 -43
- package/dist-lib/components/ui/Switch.js.map +1 -1
- package/dist-lib/components/ui/Table.d.ts +33 -5
- package/dist-lib/components/ui/Table.js +141 -119
- package/dist-lib/components/ui/Table.js.map +1 -1
- package/dist-lib/components/ui/Tabs.js +50 -48
- package/dist-lib/components/ui/Tabs.js.map +1 -1
- package/dist-lib/components/ui/TagInput.js +45 -45
- package/dist-lib/components/ui/TagInput.js.map +1 -1
- package/dist-lib/components/ui/Textarea.js +65 -54
- package/dist-lib/components/ui/Textarea.js.map +1 -1
- package/dist-lib/components/ui/Timeline.js +39 -35
- package/dist-lib/components/ui/Timeline.js.map +1 -1
- package/dist-lib/components/ui/Toast.js +44 -57
- package/dist-lib/components/ui/Toast.js.map +1 -1
- package/dist-lib/components/ui/Tooltip.d.ts +4 -1
- package/dist-lib/components/ui/Tooltip.js +24 -22
- package/dist-lib/components/ui/Tooltip.js.map +1 -1
- package/dist-lib/components/ui/TopNav.d.ts +7 -3
- package/dist-lib/components/ui/TopNav.js +35 -25
- package/dist-lib/components/ui/TopNav.js.map +1 -1
- package/dist-lib/components/ui/Tree.d.ts +15 -10
- package/dist-lib/components/ui/Tree.js +82 -79
- package/dist-lib/components/ui/Tree.js.map +1 -1
- 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 +2 -1
- package/dist-lib/hooks/use-field-ids.js +9 -9
- package/dist-lib/hooks/use-field-ids.js.map +1 -1
- package/dist-lib/hooks/use-media-query.d.ts +3 -1
- package/dist-lib/hooks/use-media-query.js +16 -12
- package/dist-lib/hooks/use-media-query.js.map +1 -1
- 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-toast.d.ts +7 -2
- package/dist-lib/hooks/use-toast.js +61 -30
- package/dist-lib/hooks/use-toast.js.map +1 -1
- package/dist-lib/icons/index.d.ts +9 -0
- package/dist-lib/icons/index.js +148 -130
- package/dist-lib/icons/index.js.map +1 -1
- package/dist-lib/illustrations/index.d.ts +1 -0
- package/dist-lib/illustrations/index.js +15 -6
- package/dist-lib/illustrations/index.js.map +1 -1
- package/dist-lib/index.d.ts +6 -0
- package/dist-lib/index.js +280 -260
- 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 +62 -1
- package/dist-lib/lib/strings.js +75 -19
- package/dist-lib/lib/strings.js.map +1 -1
- package/dist-lib/lib/strings.mn.js +50 -5
- package/dist-lib/lib/strings.mn.js.map +1 -1
- package/dist-lib/styles.css +1 -1
- package/dist-lib/theme.css +141 -78
- package/package.json +10 -3
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ primitives — Button through DataGrid — plus composed patterns
|
|
|
18
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. Add to `<head>`:
|
|
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
22
|
|
|
23
23
|
```html
|
|
24
24
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
@@ -37,11 +37,14 @@ pnpm add @craftzbay/ui # peers: react ^18 || ^19, react-dom ^18 || ^1
|
|
|
37
37
|
|
|
38
38
|
/* b) Your app already uses Tailwind v4: share the tokens and let Tailwind compile the library's classes */
|
|
39
39
|
@import 'tailwindcss';
|
|
40
|
+
@import 'tw-animate-css'; /* required on this path — overlays use animate-in / animate-out */
|
|
40
41
|
@import '@craftzbay/ui/theme.css';
|
|
41
42
|
@source "../node_modules/@craftzbay/ui/dist-lib";
|
|
42
43
|
```
|
|
43
44
|
|
|
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.
|
|
45
48
|
|
|
46
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.
|
|
47
50
|
|
|
@@ -58,7 +61,7 @@ export function App() {
|
|
|
58
61
|
}
|
|
59
62
|
```
|
|
60
63
|
|
|
61
|
-
**Next.js App Router** —
|
|
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.
|
|
62
65
|
|
|
63
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:
|
|
64
67
|
|
|
@@ -85,7 +88,8 @@ Precedence is per-component props (`placeholder`, `labels`, `aria-label`) → ne
|
|
|
85
88
|
|
|
86
89
|
This library implements the [craftzbay design-research](https://github.com/craftzbay/design-research) guidelines
|
|
87
90
|
([rendered site](https://craftzbay.github.io/design-research/)) — colour, type, spacing, components, accessibility, tokens.
|
|
88
|
-
|
|
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.
|
|
89
93
|
|
|
90
94
|
## Local development
|
|
91
95
|
|
|
@@ -104,7 +108,7 @@ Inside `packages/ui` itself, `pnpm build` produces the distributable bundle and
|
|
|
104
108
|
|
|
105
109
|
## Tech stack
|
|
106
110
|
|
|
107
|
-
- **Tailwind CSS v4** — tokens defined in `@theme` in `src/styles/globals.css`
|
|
111
|
+
- **Tailwind CSS v4** — tokens defined in `@theme` in `src/styles/theme.css` (`globals.css` = tailwind + tw-animate-css + theme)
|
|
108
112
|
- **React 18 / 19** + **TypeScript 5.7**
|
|
109
113
|
- **Radix UI** primitives for accessibility-correct overlays
|
|
110
114
|
- **class-variance-authority** + `cn()` (`clsx` + `tailwind-merge`)
|
|
@@ -116,12 +120,14 @@ Inside `packages/ui` itself, `pnpm build` produces the distributable bundle and
|
|
|
116
120
|
```
|
|
117
121
|
src/
|
|
118
122
|
├── styles/
|
|
119
|
-
│
|
|
123
|
+
│ ├── theme.css # @theme tokens, semantic vars, dark variant, base layer
|
|
124
|
+
│ └── globals.css # tailwindcss + tw-animate-css + theme.css
|
|
120
125
|
├── lib/
|
|
121
126
|
│ ├── utils.ts # cn() + uid()
|
|
122
|
-
│
|
|
127
|
+
│ ├── format.ts # formatDate / formatNumber / formatMNT
|
|
128
|
+
│ └── strings.ts # UiStrings, defaultStrings (+ strings.mn.ts)
|
|
123
129
|
├── components/
|
|
124
|
-
│ ├── ui/ # primitives —
|
|
130
|
+
│ ├── ui/ # primitives — 52 components
|
|
125
131
|
│ └── patterns/ # composed layouts
|
|
126
132
|
├── hooks/
|
|
127
133
|
│ ├── use-toast.ts
|
|
@@ -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;
|
|
@@ -5,7 +5,14 @@ import * as n from "@radix-ui/react-accordion";
|
|
|
5
5
|
import { ChevronDown as m } from "../../icons/index.js";
|
|
6
6
|
import { cn as i } from "../../lib/utils.js";
|
|
7
7
|
const v = n.Root, f = a(function({ className: o, ...e }, r) {
|
|
8
|
-
return /* @__PURE__ */ t(
|
|
8
|
+
return /* @__PURE__ */ t(
|
|
9
|
+
n.Item,
|
|
10
|
+
{
|
|
11
|
+
ref: r,
|
|
12
|
+
className: i("border-border border-b", o),
|
|
13
|
+
...e
|
|
14
|
+
}
|
|
15
|
+
);
|
|
9
16
|
});
|
|
10
17
|
f.displayName = "AccordionItem";
|
|
11
18
|
const l = a(function({ className: o, children: e, ...r }, c) {
|
|
@@ -14,10 +21,10 @@ const l = a(function({ className: o, children: e, ...r }, c) {
|
|
|
14
21
|
{
|
|
15
22
|
ref: c,
|
|
16
23
|
className: i(
|
|
17
|
-
"flex flex-1 items-center justify-between gap-2 py-4 text-left text-sm font-medium
|
|
18
|
-
"
|
|
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",
|
|
19
26
|
"hover:text-accent",
|
|
20
|
-
"focus-visible:ring-
|
|
27
|
+
"focus-visible:ring-ring focus-visible:ring-offset-background focus-visible:ring-2 focus-visible:ring-offset-2",
|
|
21
28
|
"[&[data-state=open]>svg]:rotate-180",
|
|
22
29
|
o
|
|
23
30
|
),
|
|
@@ -27,7 +34,7 @@ const l = a(function({ className: o, children: e, ...r }, c) {
|
|
|
27
34
|
/* @__PURE__ */ t(
|
|
28
35
|
m,
|
|
29
36
|
{
|
|
30
|
-
className: "size-4 shrink-0
|
|
37
|
+
className: "text-foreground-subtle size-4 shrink-0 transition-transform duration-[var(--duration-base)]",
|
|
31
38
|
"aria-hidden": !0
|
|
32
39
|
}
|
|
33
40
|
)
|
|
@@ -42,11 +49,15 @@ const u = a(function({ className: o, children: e, ...r }, c) {
|
|
|
42
49
|
{
|
|
43
50
|
ref: c,
|
|
44
51
|
className: i(
|
|
45
|
-
"overflow-hidden text-sm
|
|
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.
|
|
46
57
|
"data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
|
|
47
58
|
),
|
|
48
59
|
...r,
|
|
49
|
-
children: /* @__PURE__ */ t("div", { className: i("pb-4
|
|
60
|
+
children: /* @__PURE__ */ t("div", { className: i("pt-0 pb-4", o), children: e })
|
|
50
61
|
}
|
|
51
62
|
);
|
|
52
63
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Accordion.js","sources":["../../../src/components/ui/Accordion.tsx"],"sourcesContent":["'use client';\n\nimport {
|
|
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;"}
|
|
@@ -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
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsxs as
|
|
3
|
-
import { forwardRef as
|
|
4
|
-
import { X as
|
|
5
|
-
import { cn as
|
|
6
|
-
import { useStrings as
|
|
7
|
-
import { cva as
|
|
8
|
-
const
|
|
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
9
|
variants: {
|
|
10
10
|
variant: {
|
|
11
11
|
default: "border-border bg-background-subtle text-foreground",
|
|
@@ -16,51 +16,64 @@ const j = C(["relative flex w-full gap-3 rounded-lg border p-4", "text-sm"], {
|
|
|
16
16
|
}
|
|
17
17
|
},
|
|
18
18
|
defaultVariants: { variant: "default" }
|
|
19
|
-
}),
|
|
19
|
+
}), F = {
|
|
20
20
|
default: null,
|
|
21
|
-
info: /* @__PURE__ */ e(
|
|
22
|
-
success: /* @__PURE__ */ e(
|
|
23
|
-
warning: /* @__PURE__ */ e(
|
|
24
|
-
danger: /* @__PURE__ */ e(
|
|
25
|
-
},
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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(
|
|
32
45
|
"div",
|
|
33
46
|
{
|
|
34
|
-
ref:
|
|
35
|
-
role: r === "danger" ? "alert" : "status",
|
|
36
|
-
className:
|
|
37
|
-
...
|
|
47
|
+
ref: b,
|
|
48
|
+
role: f ? r === "danger" ? "alert" : "status" : void 0,
|
|
49
|
+
className: o(E({ variant: r }), d),
|
|
50
|
+
...g,
|
|
38
51
|
children: [
|
|
39
|
-
|
|
40
|
-
/* @__PURE__ */
|
|
41
|
-
|
|
42
|
-
/* @__PURE__ */ e("div", { className: "text-sm leading-relaxed [&_p]:leading-relaxed", 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 })
|
|
43
56
|
] }),
|
|
44
|
-
|
|
57
|
+
c && /* @__PURE__ */ e(
|
|
45
58
|
"button",
|
|
46
59
|
{
|
|
47
60
|
type: "button",
|
|
48
|
-
"aria-label":
|
|
49
|
-
onClick:
|
|
50
|
-
className:
|
|
61
|
+
"aria-label": x.alert.dismiss,
|
|
62
|
+
onClick: p,
|
|
63
|
+
className: o(
|
|
51
64
|
"inline-flex size-6 shrink-0 items-center justify-center rounded-md",
|
|
52
65
|
"outline-none hover:bg-current/10",
|
|
53
66
|
"focus-visible:ring-ring focus-visible:ring-offset-background focus-visible:ring-2 focus-visible:ring-offset-2"
|
|
54
67
|
),
|
|
55
|
-
children: /* @__PURE__ */ e(
|
|
68
|
+
children: /* @__PURE__ */ e(A, { className: "size-3.5", "aria-hidden": !0 })
|
|
56
69
|
}
|
|
57
70
|
)
|
|
58
71
|
]
|
|
59
72
|
}
|
|
60
73
|
);
|
|
61
74
|
});
|
|
62
|
-
|
|
75
|
+
R.displayName = "Alert";
|
|
63
76
|
export {
|
|
64
|
-
|
|
77
|
+
R as Alert
|
|
65
78
|
};
|
|
66
79
|
//# sourceMappingURL=Alert.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Alert.js","sources":["../../../src/components/ui/Alert.tsx"],"sourcesContent":["'use client';\n\nimport { 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 /** Override the variant's default icon. Pass `false` to suppress the icon. */\n icon?: ReactNode | false;\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 {
|
|
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;"}
|
|
@@ -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;
|
|
@@ -1,96 +1,94 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsxs as
|
|
3
|
-
import { forwardRef as
|
|
4
|
-
import * as
|
|
5
|
-
import { cn as
|
|
6
|
-
import { useStrings as
|
|
7
|
-
import { formatString as
|
|
8
|
-
import { cva as
|
|
9
|
-
const
|
|
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
10
|
xs: "size-5 text-xs",
|
|
11
11
|
sm: "size-6 text-xs",
|
|
12
12
|
md: "size-8 text-xs",
|
|
13
13
|
lg: "size-10 text-sm",
|
|
14
14
|
xl: "size-12 text-base"
|
|
15
|
-
},
|
|
16
|
-
variants: { size:
|
|
15
|
+
}, j = A("relative inline-flex shrink-0", {
|
|
16
|
+
variants: { size: x },
|
|
17
17
|
defaultVariants: { size: "md" }
|
|
18
|
-
}),
|
|
18
|
+
}), w = {
|
|
19
19
|
online: "bg-success",
|
|
20
20
|
busy: "bg-danger",
|
|
21
21
|
away: "bg-warning",
|
|
22
22
|
offline: "bg-foreground-subtle"
|
|
23
|
-
},
|
|
24
|
-
function({ className:
|
|
25
|
-
const s =
|
|
26
|
-
return /* @__PURE__ */
|
|
27
|
-
/* @__PURE__ */
|
|
28
|
-
|
|
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
29
|
{
|
|
30
|
-
ref:
|
|
31
|
-
className:
|
|
32
|
-
|
|
33
|
-
l
|
|
34
|
-
),
|
|
35
|
-
...u,
|
|
30
|
+
ref: a,
|
|
31
|
+
className: "bg-background-muted text-foreground-muted block size-full overflow-hidden rounded-full",
|
|
32
|
+
...d,
|
|
36
33
|
children: [
|
|
37
34
|
e && /* @__PURE__ */ i(
|
|
38
|
-
|
|
35
|
+
g.Image,
|
|
39
36
|
{
|
|
40
37
|
src: e,
|
|
41
|
-
alt:
|
|
38
|
+
alt: c ?? "",
|
|
42
39
|
className: "aspect-square size-full object-cover"
|
|
43
40
|
}
|
|
44
41
|
),
|
|
45
42
|
/* @__PURE__ */ i(
|
|
46
|
-
|
|
43
|
+
g.Fallback,
|
|
47
44
|
{
|
|
48
45
|
delayMs: e ? 200 : 0,
|
|
49
|
-
className: "flex size-full items-center justify-center
|
|
50
|
-
children:
|
|
46
|
+
className: "bg-background-muted flex size-full items-center justify-center font-medium uppercase",
|
|
47
|
+
children: u ?? "?"
|
|
51
48
|
}
|
|
52
49
|
)
|
|
53
50
|
]
|
|
54
51
|
}
|
|
55
52
|
),
|
|
56
|
-
|
|
53
|
+
r && /* @__PURE__ */ i(
|
|
57
54
|
"span",
|
|
58
55
|
{
|
|
59
|
-
"
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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]
|
|
63
61
|
)
|
|
64
62
|
}
|
|
65
63
|
)
|
|
66
64
|
] });
|
|
67
65
|
}
|
|
68
66
|
);
|
|
69
|
-
|
|
70
|
-
const
|
|
71
|
-
const
|
|
72
|
-
return /* @__PURE__ */
|
|
73
|
-
s.map((
|
|
74
|
-
|
|
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(
|
|
75
73
|
"div",
|
|
76
74
|
{
|
|
77
|
-
className:
|
|
78
|
-
"inline-flex items-center justify-center rounded-full
|
|
79
|
-
|
|
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],
|
|
80
78
|
"font-medium"
|
|
81
79
|
),
|
|
82
|
-
"aria-label":
|
|
80
|
+
"aria-label": v(d.avatar.more, { n: f }),
|
|
83
81
|
children: [
|
|
84
82
|
"+",
|
|
85
|
-
|
|
83
|
+
f
|
|
86
84
|
]
|
|
87
85
|
}
|
|
88
86
|
)
|
|
89
87
|
] });
|
|
90
88
|
});
|
|
91
|
-
|
|
89
|
+
C.displayName = "AvatarGroup";
|
|
92
90
|
export {
|
|
93
|
-
|
|
94
|
-
|
|
91
|
+
G as Avatar,
|
|
92
|
+
C as AvatarGroup
|
|
95
93
|
};
|
|
96
94
|
//# sourceMappingURL=Avatar.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Avatar.js","sources":["../../../src/components/ui/Avatar.tsx"],"sourcesContent":["'use client';\n\nimport {\n forwardRef,\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
|
|
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,4 +1,4 @@
|
|
|
1
|
-
import { HTMLAttributes } from 'react';
|
|
1
|
+
import { HTMLAttributes, ReactNode } from 'react';
|
|
2
2
|
import { VariantProps } from '../../lib/cva.js';
|
|
3
3
|
declare const badge: (props?: ({
|
|
4
4
|
variant?: "subtle" | "outline" | 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);
|