@robomous/ui-core 0.1.0 → 0.2.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 +19 -7
- package/dist/{primitives → components}/alert.js +1 -1
- package/dist/{primitives → components}/badge.js +1 -1
- package/dist/{primitives → components}/button.d.ts +1 -1
- package/dist/{primitives → components}/button.js +4 -1
- package/dist/{primitives → components}/card.d.ts +1 -1
- package/dist/{primitives → components}/card.js +2 -2
- package/dist/{primitives → components}/combobox.js +4 -4
- package/dist/{primitives → components}/dialog.js +1 -1
- package/dist/{primitives → components}/dropdown-menu.js +12 -7
- package/dist/{primitives → components}/field.js +2 -4
- package/dist/{primitives → components}/input-group.js +1 -1
- package/dist/{primitives → components}/input.js +1 -1
- package/dist/{primitives → components}/label.js +1 -1
- package/dist/components/progress.js +7 -0
- package/dist/{primitives → components}/select.d.ts +3 -1
- package/dist/{primitives → components}/select.js +7 -4
- package/dist/{primitives → components}/separator.js +1 -1
- package/dist/{primitives → components}/sheet.js +1 -1
- package/dist/{primitives → components}/skeleton.js +1 -1
- package/dist/{primitives → components}/sonner.js +17 -12
- package/dist/{primitives → components}/table.d.ts +1 -1
- package/dist/{primitives → components}/table.js +3 -3
- package/dist/{primitives → components}/tabs.js +1 -1
- package/dist/{primitives → components}/textarea.js +1 -1
- package/dist/{primitives → components}/tooltip.js +1 -1
- package/dist/gates/index.d.ts +28 -0
- package/dist/gates/index.js +142 -0
- package/dist/index.d.ts +31 -32
- package/dist/index.js +32 -33
- package/dist/{tokens.d.ts → theme/tokens.d.ts} +5 -5
- package/dist/{tokens.js → theme/tokens.js} +12 -12
- package/package.json +23 -12
- package/{shadcn → src/components}/alert.tsx +14 -21
- package/src/{primitives → components}/badge.tsx +15 -21
- package/src/{primitives → components}/button.tsx +13 -10
- package/{shadcn → src/components}/card.tsx +16 -31
- package/src/{primitives → components}/combobox.test.tsx +15 -2
- package/src/{primitives → components}/combobox.tsx +45 -74
- package/src/{primitives/primitives.test.tsx → components/components.test.tsx} +106 -69
- package/src/{primitives → components}/dialog.tsx +31 -54
- package/src/{primitives → components}/dropdown-menu.tsx +51 -65
- package/src/{primitives → components}/field.tsx +48 -62
- package/src/{primitives → components}/input-group.tsx +37 -49
- package/{shadcn → src/components}/input.tsx +5 -5
- package/{shadcn → src/components}/label.tsx +7 -10
- package/src/{primitives → components}/progress.tsx +7 -6
- package/{shadcn → src/components}/select.tsx +40 -40
- package/{shadcn → src/components}/separator.tsx +7 -7
- package/src/{primitives → components}/sheet.tsx +27 -44
- package/{shadcn → src/components}/skeleton.tsx +3 -3
- package/src/{primitives → components}/sonner.test.tsx +12 -4
- package/src/components/sonner.tsx +67 -0
- package/{shadcn → src/components}/table.tsx +17 -44
- package/{shadcn → src/components}/tabs.tsx +16 -26
- package/{shadcn → src/components}/textarea.tsx +5 -5
- package/{shadcn → src/components}/tooltip.tsx +12 -16
- package/src/gates/design.test.ts +188 -0
- package/src/gates/index.test.ts +11 -0
- package/src/gates/index.ts +165 -0
- package/src/gates/tokens.test.ts +215 -0
- package/src/harness.test.tsx +1 -1
- package/src/index.ts +63 -33
- package/src/theme/imports.test.ts +160 -0
- package/src/{statusTone.test.ts → theme/statusTone.test.ts} +5 -1
- package/src/{styles.css → theme/styles.css} +45 -31
- package/src/theme/tailwind.css +646 -0
- package/src/{tokens.test.ts → theme/tokens.test.ts} +30 -23
- package/src/{tokens.ts → theme/tokens.ts} +12 -12
- package/components.json +0 -13
- package/dist/lib/button.d.ts +0 -1
- package/dist/lib/button.js +0 -4
- package/dist/lib/cn.d.ts +0 -18
- package/dist/lib/cn.js +0 -21
- package/dist/lib/menu.d.ts +0 -1
- package/dist/lib/menu.js +0 -16
- package/dist/lib/progress.d.ts +0 -3
- package/dist/lib/progress.js +0 -6
- package/dist/lib/select.d.ts +0 -1
- package/dist/lib/select.js +0 -3
- package/dist/primitives/progress.js +0 -7
- package/gates/canonical.test.mjs +0 -71
- package/gates/extensions.test.mjs +0 -373
- package/gates/index.d.mts +0 -51
- package/gates/index.mjs +0 -514
- package/gates/tokens.test.mjs +0 -276
- package/shadcn/README.md +0 -13
- package/shadcn/badge.tsx +0 -49
- package/shadcn/button.tsx +0 -67
- package/shadcn/combobox.tsx +0 -299
- package/shadcn/dialog.tsx +0 -166
- package/shadcn/dropdown-menu.tsx +0 -267
- package/shadcn/field.tsx +0 -236
- package/shadcn/input-group.tsx +0 -154
- package/shadcn/progress.tsx +0 -29
- package/shadcn/sheet.tsx +0 -145
- package/shadcn/sonner.tsx +0 -49
- package/src/lib/button.ts +0 -4
- package/src/lib/cn.test.ts +0 -33
- package/src/lib/cn.ts +0 -23
- package/src/lib/menu.ts +0 -16
- package/src/lib/progress.ts +0 -6
- package/src/lib/select.ts +0 -4
- package/src/primitives/alert.tsx +0 -76
- package/src/primitives/card.tsx +0 -103
- package/src/primitives/input.tsx +0 -19
- package/src/primitives/label.tsx +0 -22
- package/src/primitives/select.tsx +0 -190
- package/src/primitives/separator.tsx +0 -28
- package/src/primitives/skeleton.tsx +0 -13
- package/src/primitives/sonner.tsx +0 -65
- package/src/primitives/table.tsx +0 -114
- package/src/primitives/tabs.tsx +0 -88
- package/src/primitives/textarea.tsx +0 -18
- package/src/primitives/tooltip.tsx +0 -57
- /package/dist/{primitives → components}/alert.d.ts +0 -0
- /package/dist/{primitives → components}/badge.d.ts +0 -0
- /package/dist/{primitives → components}/combobox.d.ts +0 -0
- /package/dist/{primitives → components}/dialog.d.ts +0 -0
- /package/dist/{primitives → components}/dropdown-menu.d.ts +0 -0
- /package/dist/{primitives → components}/field.d.ts +0 -0
- /package/dist/{primitives → components}/input-group.d.ts +0 -0
- /package/dist/{primitives → components}/input.d.ts +0 -0
- /package/dist/{primitives → components}/label.d.ts +0 -0
- /package/dist/{primitives → components}/progress.d.ts +0 -0
- /package/dist/{primitives → components}/separator.d.ts +0 -0
- /package/dist/{primitives → components}/sheet.d.ts +0 -0
- /package/dist/{primitives → components}/skeleton.d.ts +0 -0
- /package/dist/{primitives → components}/sonner.d.ts +0 -0
- /package/dist/{primitives → components}/tabs.d.ts +0 -0
- /package/dist/{primitives → components}/textarea.d.ts +0 -0
- /package/dist/{primitives → components}/tooltip.d.ts +0 -0
- /package/dist/{statusTone.d.ts → theme/statusTone.d.ts} +0 -0
- /package/dist/{statusTone.js → theme/statusTone.js} +0 -0
- /package/src/{statusTone.ts → theme/statusTone.ts} +0 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @robomous/ui-core/gates — the pure helpers and token facts that keep the
|
|
3
|
+
* design system honest, published so every consumer repo can run the same
|
|
4
|
+
* gates over its own sources with its own extensions registry.
|
|
5
|
+
*
|
|
6
|
+
* Eight exports: four scanners over one file's text, three readers for a
|
|
7
|
+
* stylesheet's blocks and declarations, and the token names read off the
|
|
8
|
+
* stylesheet that ships. DESIGN.md names the rule each one holds.
|
|
9
|
+
*/
|
|
10
|
+
import { readFileSync } from "node:fs";
|
|
11
|
+
import path from "node:path";
|
|
12
|
+
import { fileURLToPath } from "node:url";
|
|
13
|
+
const PKG = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
14
|
+
const COMMENT = /^\s*(?:\/\/|\/\*|\*|#)/;
|
|
15
|
+
// ---- status palette discipline ----
|
|
16
|
+
/**
|
|
17
|
+
* The status palette's one Tailwind family: emerald/amber/sky, across every
|
|
18
|
+
* prefix that can carry a colour. `Badge` and `statusTone.ts` are its one
|
|
19
|
+
* home — see `statusTone.ts`'s own docstring — so a third place naming the
|
|
20
|
+
* family is a fork of the palette, not a use of it.
|
|
21
|
+
*/
|
|
22
|
+
const STATUS_PALETTE = /\b(?:bg|text|border|ring|fill|stroke|from|to|via|outline|decoration|shadow)-(?:emerald|amber|sky)-\d/;
|
|
23
|
+
/** Every `file:line` in `text` painting with the status palette, outside a comment. */
|
|
24
|
+
export function statusPaletteIn(file, text) {
|
|
25
|
+
return text
|
|
26
|
+
.split("\n")
|
|
27
|
+
.map((line, index) => ({ line, at: index + 1 }))
|
|
28
|
+
.filter(({ line }) => !COMMENT.test(line) && STATUS_PALETTE.test(line))
|
|
29
|
+
.map(({ line, at }) => `${file}:${at}: ${line.trim()}`);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* A colour family that competes with the status palette for the same job —
|
|
33
|
+
* "a warning", "a success" — and so could stand in for it undetected. Unlike
|
|
34
|
+
* the palette itself these have no allowed home anywhere in a consumer.
|
|
35
|
+
*/
|
|
36
|
+
const COMPETING_PALETTE = /\b(?:bg|text|border)-(?:green|lime|teal|yellow|orange|blue|cyan|red)-\d/;
|
|
37
|
+
/** Every `file:line` in `text` reaching for a colour family that competes with the status palette. */
|
|
38
|
+
export function competingStatusPaletteIn(file, text) {
|
|
39
|
+
return text
|
|
40
|
+
.split("\n")
|
|
41
|
+
.map((line, index) => ({ line, at: index + 1 }))
|
|
42
|
+
.filter(({ line }) => !COMMENT.test(line) && COMPETING_PALETTE.test(line))
|
|
43
|
+
.map(({ line, at }) => `${file}:${at}: ${line.trim()}`);
|
|
44
|
+
}
|
|
45
|
+
// ---- colour discipline (from tests/scripts/design_tokens.test.mjs) ----
|
|
46
|
+
/**
|
|
47
|
+
* A Tailwind arbitrary value whose content is a colour.
|
|
48
|
+
*
|
|
49
|
+
* Assembled from fragments so this file does not match itself. The `-` before
|
|
50
|
+
* the bracket is what makes it a *utility* rather than an array index or a
|
|
51
|
+
* TypeScript tuple type.
|
|
52
|
+
*/
|
|
53
|
+
const HEX = ["#", "[0-9a-fA-F]{3,8}"].join("");
|
|
54
|
+
const LITERAL = String.raw `(?:${HEX}|rgba?\(|hsla?\(|oklch\()`;
|
|
55
|
+
// A raw colour right inside the bracket…
|
|
56
|
+
const ARBITRARY_COLOUR = new RegExp(String.raw `-\[\s*(?:${LITERAL}|var\(\s*--)`);
|
|
57
|
+
// The one colour-mix the preset writes mixes tokens only:
|
|
58
|
+
// `color-mix(in_oklch,var(--secondary),var(--foreground)_5%)`. Any other
|
|
59
|
+
// argument shape — a literal, a named colour, a bare number — is a colour.
|
|
60
|
+
const TOKEN_MIX = String.raw `color-mix\(in_[a-z0-9-]+(?:,var\(--[a-z0-9-]+\)(?:_\d+(?:\.\d+)?%)?)+\)`;
|
|
61
|
+
const BRACKET_MIX = new RegExp(String.raw `-\[\s*color-mix\([^\]]*\]`);
|
|
62
|
+
const ALLOWED_MIX = new RegExp(String.raw `-\[\s*${TOKEN_MIX}\]`);
|
|
63
|
+
/** Every `file:line` in `text` that puts a colour inside a Tailwind class. */
|
|
64
|
+
export function colouredClassesIn(file, text) {
|
|
65
|
+
return text
|
|
66
|
+
.split("\n")
|
|
67
|
+
.map((line, index) => ({ line, at: index + 1 }))
|
|
68
|
+
.filter(({ line }) => !COMMENT.test(line) &&
|
|
69
|
+
(ARBITRARY_COLOUR.test(line) || (BRACKET_MIX.test(line) && !ALLOWED_MIX.test(line))))
|
|
70
|
+
.map(({ line, at }) => `${file}:${at}: ${line.trim()}`);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* `DESIGN.md` "Where the brand is": the brand is identity, not a functional-UI
|
|
74
|
+
* colour — the wordmark and the styleguide swatch that shows it off, nothing
|
|
75
|
+
* a person acts on. This is not a headcount: the gate does not exist to hold
|
|
76
|
+
* a count of sites, it exists so brand can never migrate onto a control (a
|
|
77
|
+
* button, a progress fill, anything with a function) instead of staying the
|
|
78
|
+
* one place it is allowed to just be seen. A pure function over one file's
|
|
79
|
+
* text, so the gate is provable with fabricated input, and `COMMENT` keeps
|
|
80
|
+
* the styles.css line that *states* the rule from counting as a usage of it.
|
|
81
|
+
*/
|
|
82
|
+
const BRAND_UTILITY = /\b(?:bg|text|border|ring|fill|stroke)-brand\b/;
|
|
83
|
+
/** Every line in `text` that paints with the brand colour. */
|
|
84
|
+
export function brandUsagesIn(file, text) {
|
|
85
|
+
return text
|
|
86
|
+
.split("\n")
|
|
87
|
+
.map((line, index) => ({ line, at: index + 1 }))
|
|
88
|
+
.filter(({ line }) => !COMMENT.test(line) && BRAND_UTILITY.test(line))
|
|
89
|
+
.map(({ line, at }) => ({ file, at, text: line.trim() }));
|
|
90
|
+
}
|
|
91
|
+
// ---- stylesheet parsing (from src/theme/tokens.test.ts, translated TS→JS) ----
|
|
92
|
+
/** Whitespace is presentation; a value that wraps is the same value — used internally by `rawDeclarations`. */
|
|
93
|
+
function normalize(value) {
|
|
94
|
+
return value.replace(/\s+/g, " ").trim();
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* The `{ … }` body belonging to the block whose header (e.g. `:root {` or
|
|
98
|
+
* `.dark {`) appears first in the file, matched by brace depth rather than by
|
|
99
|
+
* the next `\n}` so a nested `calc(...)` or `color-mix(...)` cannot fool it.
|
|
100
|
+
*/
|
|
101
|
+
export function blockBody(css, header) {
|
|
102
|
+
const headerAt = css.indexOf(header);
|
|
103
|
+
if (headerAt === -1)
|
|
104
|
+
throw new Error(`stylesheet has no ${JSON.stringify(header)} block`);
|
|
105
|
+
const braceAt = css.indexOf("{", headerAt);
|
|
106
|
+
let depth = 1;
|
|
107
|
+
let i = braceAt + 1;
|
|
108
|
+
while (depth > 0 && i < css.length) {
|
|
109
|
+
if (css[i] === "{")
|
|
110
|
+
depth++;
|
|
111
|
+
else if (css[i] === "}")
|
|
112
|
+
depth--;
|
|
113
|
+
i++;
|
|
114
|
+
}
|
|
115
|
+
return css.slice(braceAt + 1, i - 1);
|
|
116
|
+
}
|
|
117
|
+
/** Every `--name: value;` declaration in a block, keyed WITH the leading `--`. */
|
|
118
|
+
export function rawDeclarations(block) {
|
|
119
|
+
const map = new Map();
|
|
120
|
+
for (const match of block.matchAll(/(--[\w-]+):\s*([^;]+);/g)) {
|
|
121
|
+
map.set(match[1], normalize(match[2]));
|
|
122
|
+
}
|
|
123
|
+
return map;
|
|
124
|
+
}
|
|
125
|
+
/** The same, keyed WITHOUT the leading `--` — the shape `LIGHT_THEME` uses. */
|
|
126
|
+
export function declarations(block) {
|
|
127
|
+
const map = new Map();
|
|
128
|
+
for (const [name, value] of rawDeclarations(block)) {
|
|
129
|
+
map.set(name.slice(2), value);
|
|
130
|
+
}
|
|
131
|
+
return map;
|
|
132
|
+
}
|
|
133
|
+
// ---- foundation facts ----
|
|
134
|
+
/**
|
|
135
|
+
* The foundation token names, read off the shipped stylesheet's `:root` so
|
|
136
|
+
* they can never drift from what actually runs. `radius` is a geometry
|
|
137
|
+
* setting, not a colour token, and is excluded — it matches LIGHT_THEME's keys.
|
|
138
|
+
*/
|
|
139
|
+
export function foundationTokenNames() {
|
|
140
|
+
const css = readFileSync(path.join(PKG, "src/theme/styles.css"), "utf8");
|
|
141
|
+
return [...declarations(blockBody(css, ":root {")).keys()].filter((n) => n !== "radius");
|
|
142
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `@robomous/ui-core` — the Robomous design system.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Twenty-one React components this package owns outright, the design tokens
|
|
5
|
+
* they resolve through, and the status-tone vocabulary. A consumer imports
|
|
6
|
+
* exactly:
|
|
7
7
|
*
|
|
8
8
|
* ```ts
|
|
9
9
|
* import "@robomous/ui-core/styles.css"; // once, in the app's entry
|
|
@@ -11,34 +11,33 @@
|
|
|
11
11
|
* ```
|
|
12
12
|
*
|
|
13
13
|
* The public surface is listed explicitly rather than `export *`, so what this
|
|
14
|
-
* package promises stays auditable. The
|
|
15
|
-
*
|
|
14
|
+
* package promises stays auditable. The scanners behind this repository's own
|
|
15
|
+
* design rules are published too, for a consumer to run over its own sources:
|
|
16
|
+
* `import { ... } from "@robomous/ui-core/gates"`. DESIGN.md names each rule
|
|
17
|
+
* and the gate that holds it.
|
|
16
18
|
*/
|
|
17
|
-
export { cssVar, DARK_THEME, LIGHT_THEME, THEME } from "./tokens.js";
|
|
18
|
-
export {
|
|
19
|
-
export {
|
|
20
|
-
export {
|
|
21
|
-
export {
|
|
22
|
-
export {
|
|
23
|
-
export {
|
|
24
|
-
export {
|
|
25
|
-
export {
|
|
26
|
-
export {
|
|
27
|
-
export {
|
|
28
|
-
export {
|
|
29
|
-
export {
|
|
30
|
-
export {
|
|
31
|
-
export {
|
|
32
|
-
export {
|
|
33
|
-
export {
|
|
34
|
-
export {
|
|
35
|
-
export {
|
|
36
|
-
export {
|
|
37
|
-
export {
|
|
38
|
-
export {
|
|
39
|
-
export {
|
|
40
|
-
export { Progress } from "./primitives/progress.js";
|
|
41
|
-
export { Skeleton } from "./primitives/skeleton.js";
|
|
42
|
-
export { Toaster } from "./primitives/sonner.js";
|
|
19
|
+
export { cssVar, DARK_THEME, LIGHT_THEME, THEME } from "./theme/tokens.js";
|
|
20
|
+
export { cn } from "cn";
|
|
21
|
+
export { STATUS_INK, TONE_BORDER, TONE_FILL, type StatusTone } from "./theme/statusTone.js";
|
|
22
|
+
export { Button, buttonVariants } from "./components/button.js";
|
|
23
|
+
export { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, } from "./components/card.js";
|
|
24
|
+
export { Input } from "./components/input.js";
|
|
25
|
+
export { Textarea } from "./components/textarea.js";
|
|
26
|
+
export { Label } from "./components/label.js";
|
|
27
|
+
export { Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTitle, } from "./components/field.js";
|
|
28
|
+
export { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, } from "./components/input-group.js";
|
|
29
|
+
export { Combobox, ComboboxChip, ComboboxChips, ComboboxChipsInput, ComboboxCollection, ComboboxContent, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxLabel, ComboboxList, ComboboxSeparator, ComboboxTrigger, ComboboxValue, useComboboxAnchor, } from "./components/combobox.js";
|
|
30
|
+
export { Badge, badgeVariants } from "./components/badge.js";
|
|
31
|
+
export { Alert, AlertAction, AlertDescription, AlertTitle } from "./components/alert.js";
|
|
32
|
+
export { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, } from "./components/dialog.js";
|
|
33
|
+
export { Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, } from "./components/sheet.js";
|
|
34
|
+
export { Tabs, TabsContent, TabsList, tabsListVariants, TabsTrigger } from "./components/tabs.js";
|
|
35
|
+
export { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, } from "./components/select.js";
|
|
36
|
+
export { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, } from "./components/dropdown-menu.js";
|
|
37
|
+
export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./components/tooltip.js";
|
|
38
|
+
export { Progress } from "./components/progress.js";
|
|
39
|
+
export { Separator } from "./components/separator.js";
|
|
40
|
+
export { Skeleton } from "./components/skeleton.js";
|
|
41
|
+
export { Toaster } from "./components/sonner.js";
|
|
43
42
|
export { toast } from "sonner";
|
|
44
|
-
export { Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, } from "./
|
|
43
|
+
export { Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, } from "./components/table.js";
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `@robomous/ui-core` — the Robomous design system.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Twenty-one React components this package owns outright, the design tokens
|
|
5
|
+
* they resolve through, and the status-tone vocabulary. A consumer imports
|
|
6
|
+
* exactly:
|
|
7
7
|
*
|
|
8
8
|
* ```ts
|
|
9
9
|
* import "@robomous/ui-core/styles.css"; // once, in the app's entry
|
|
@@ -11,37 +11,36 @@
|
|
|
11
11
|
* ```
|
|
12
12
|
*
|
|
13
13
|
* The public surface is listed explicitly rather than `export *`, so what this
|
|
14
|
-
* package promises stays auditable. The
|
|
15
|
-
*
|
|
14
|
+
* package promises stays auditable. The scanners behind this repository's own
|
|
15
|
+
* design rules are published too, for a consumer to run over its own sources:
|
|
16
|
+
* `import { ... } from "@robomous/ui-core/gates"`. DESIGN.md names each rule
|
|
17
|
+
* and the gate that holds it.
|
|
16
18
|
*/
|
|
17
19
|
// The design tokens, and their prose contract in DESIGN.md.
|
|
18
|
-
export { cssVar, DARK_THEME, LIGHT_THEME, THEME } from "./tokens.js";
|
|
19
|
-
export {
|
|
20
|
-
export { cn } from "./lib/cn.js";
|
|
21
|
-
export { menuSurface } from "./lib/menu.js";
|
|
22
|
-
export { twoLineTrigger } from "./lib/select.js";
|
|
23
|
-
export { progressAria } from "./lib/progress.js";
|
|
20
|
+
export { cssVar, DARK_THEME, LIGHT_THEME, THEME } from "./theme/tokens.js";
|
|
21
|
+
export { cn } from "cn";
|
|
24
22
|
// The one home for status colour outside the Badge.
|
|
25
|
-
export { STATUS_INK, TONE_BORDER, TONE_FILL } from "./statusTone.js";
|
|
26
|
-
//
|
|
27
|
-
export { Button, buttonVariants } from "./
|
|
28
|
-
export { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, } from "./
|
|
29
|
-
export { Input } from "./
|
|
30
|
-
export { Textarea } from "./
|
|
31
|
-
export { Label } from "./
|
|
32
|
-
export { Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTitle, } from "./
|
|
33
|
-
export { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, } from "./
|
|
34
|
-
export { Combobox, ComboboxChip, ComboboxChips, ComboboxChipsInput, ComboboxCollection, ComboboxContent, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxLabel, ComboboxList, ComboboxSeparator, ComboboxTrigger, ComboboxValue, useComboboxAnchor, } from "./
|
|
35
|
-
export { Badge, badgeVariants } from "./
|
|
36
|
-
export { Alert, AlertAction, AlertDescription, AlertTitle } from "./
|
|
37
|
-
export { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger } from "./
|
|
38
|
-
export { Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger } from "./
|
|
39
|
-
export { Tabs, TabsContent, TabsList, tabsListVariants, TabsTrigger } from "./
|
|
40
|
-
export { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue } from "./
|
|
41
|
-
export { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, } from "./
|
|
42
|
-
export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./
|
|
43
|
-
export { Progress } from "./
|
|
44
|
-
export {
|
|
45
|
-
export {
|
|
23
|
+
export { STATUS_INK, TONE_BORDER, TONE_FILL } from "./theme/statusTone.js";
|
|
24
|
+
// The components — Radix and Base UI behaviour, iconed with lucide.
|
|
25
|
+
export { Button, buttonVariants } from "./components/button.js";
|
|
26
|
+
export { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, } from "./components/card.js";
|
|
27
|
+
export { Input } from "./components/input.js";
|
|
28
|
+
export { Textarea } from "./components/textarea.js";
|
|
29
|
+
export { Label } from "./components/label.js";
|
|
30
|
+
export { Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTitle, } from "./components/field.js";
|
|
31
|
+
export { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, } from "./components/input-group.js";
|
|
32
|
+
export { Combobox, ComboboxChip, ComboboxChips, ComboboxChipsInput, ComboboxCollection, ComboboxContent, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxLabel, ComboboxList, ComboboxSeparator, ComboboxTrigger, ComboboxValue, useComboboxAnchor, } from "./components/combobox.js";
|
|
33
|
+
export { Badge, badgeVariants } from "./components/badge.js";
|
|
34
|
+
export { Alert, AlertAction, AlertDescription, AlertTitle } from "./components/alert.js";
|
|
35
|
+
export { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, } from "./components/dialog.js";
|
|
36
|
+
export { Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, } from "./components/sheet.js";
|
|
37
|
+
export { Tabs, TabsContent, TabsList, tabsListVariants, TabsTrigger } from "./components/tabs.js";
|
|
38
|
+
export { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, } from "./components/select.js";
|
|
39
|
+
export { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, } from "./components/dropdown-menu.js";
|
|
40
|
+
export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./components/tooltip.js";
|
|
41
|
+
export { Progress } from "./components/progress.js";
|
|
42
|
+
export { Separator } from "./components/separator.js";
|
|
43
|
+
export { Skeleton } from "./components/skeleton.js";
|
|
44
|
+
export { Toaster } from "./components/sonner.js";
|
|
46
45
|
export { toast } from "sonner";
|
|
47
|
-
export { Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, } from "./
|
|
46
|
+
export { Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, } from "./components/table.js";
|
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
* which parses the stylesheet and asserts the two agree, declaration for
|
|
9
9
|
* declaration.
|
|
10
10
|
*
|
|
11
|
-
* `LIGHT_THEME`/`DARK_THEME`
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* consumer
|
|
11
|
+
* `LIGHT_THEME`/`DARK_THEME` carry the semantic vocabulary on a neutral base
|
|
12
|
+
* colour and chart palette, plus one name of this package's own: `brand`,
|
|
13
|
+
* Robomous orange, identity only. A consumer's own vocabulary lives in the
|
|
14
|
+
* consumer's stylesheet and token module, never here — DESIGN.md,
|
|
15
|
+
* *Per-consumer extensions*.
|
|
16
16
|
*/
|
|
17
17
|
export declare const LIGHT_THEME: Readonly<Record<string, string>>;
|
|
18
18
|
export declare const DARK_THEME: Readonly<Record<string, string>>;
|
|
@@ -8,23 +8,23 @@
|
|
|
8
8
|
* which parses the stylesheet and asserts the two agree, declaration for
|
|
9
9
|
* declaration.
|
|
10
10
|
*
|
|
11
|
-
* `LIGHT_THEME`/`DARK_THEME`
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* consumer
|
|
11
|
+
* `LIGHT_THEME`/`DARK_THEME` carry the semantic vocabulary on a neutral base
|
|
12
|
+
* colour and chart palette, plus one name of this package's own: `brand`,
|
|
13
|
+
* Robomous orange, identity only. A consumer's own vocabulary lives in the
|
|
14
|
+
* consumer's stylesheet and token module, never here — DESIGN.md,
|
|
15
|
+
* *Per-consumer extensions*.
|
|
16
16
|
*/
|
|
17
17
|
export const LIGHT_THEME = Object.freeze({
|
|
18
18
|
background: "oklch(1 0 0)",
|
|
19
|
-
foreground: "oklch(0.
|
|
19
|
+
foreground: "oklch(0.2 0 0)",
|
|
20
20
|
card: "oklch(1 0 0)",
|
|
21
21
|
"card-foreground": "oklch(0.145 0 0)",
|
|
22
22
|
popover: "oklch(1 0 0)",
|
|
23
23
|
"popover-foreground": "oklch(0.145 0 0)",
|
|
24
|
-
primary: "oklch(0.
|
|
24
|
+
primary: "oklch(0.2 0 0)",
|
|
25
25
|
"primary-foreground": "oklch(0.985 0 0)",
|
|
26
26
|
secondary: "oklch(0.97 0 0)",
|
|
27
|
-
"secondary-foreground": "oklch(0.
|
|
27
|
+
"secondary-foreground": "oklch(0.269 0 0)",
|
|
28
28
|
muted: "oklch(0.97 0 0)",
|
|
29
29
|
"muted-foreground": "oklch(0.556 0 0)",
|
|
30
30
|
accent: "oklch(0.97 0 0)",
|
|
@@ -46,9 +46,9 @@ export const LIGHT_THEME = Object.freeze({
|
|
|
46
46
|
"sidebar-accent-foreground": "oklch(0.205 0 0)",
|
|
47
47
|
"sidebar-border": "oklch(0.922 0 0)",
|
|
48
48
|
"sidebar-ring": "oklch(0.708 0 0)",
|
|
49
|
-
// Robomous
|
|
50
|
-
// — never a functional-UI colour.
|
|
51
|
-
brand: "oklch(0.
|
|
49
|
+
// Robomous orange (#F5580B). Identity only — the wordmark and its styleguide
|
|
50
|
+
// swatch — never a functional-UI colour.
|
|
51
|
+
brand: "oklch(0.663 0.205 39.9)",
|
|
52
52
|
});
|
|
53
53
|
export const DARK_THEME = Object.freeze({
|
|
54
54
|
background: "oklch(0.145 0 0)",
|
|
@@ -82,7 +82,7 @@ export const DARK_THEME = Object.freeze({
|
|
|
82
82
|
"sidebar-accent-foreground": "oklch(0.985 0 0)",
|
|
83
83
|
"sidebar-border": "oklch(1 0 0 / 10%)",
|
|
84
84
|
"sidebar-ring": "oklch(0.556 0 0)",
|
|
85
|
-
brand: "oklch(0.
|
|
85
|
+
brand: "oklch(0.663 0.205 39.9)",
|
|
86
86
|
});
|
|
87
87
|
/** The two provenance facts a colour string alone cannot carry. */
|
|
88
88
|
export const THEME = {
|
package/package.json
CHANGED
|
@@ -1,36 +1,46 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@robomous/ui-core",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Robomous design system:
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Robomous design system: twenty-one owned React components, the design tokens they resolve through, and the gates that hold the rules behind them.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
|
-
"repository": {
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/Robomous/ui-core.git"
|
|
9
|
+
},
|
|
7
10
|
"type": "module",
|
|
8
11
|
"main": "./dist/index.js",
|
|
9
12
|
"types": "./dist/index.d.ts",
|
|
10
13
|
"exports": {
|
|
11
|
-
".": {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"import": "./dist/index.js"
|
|
17
|
+
},
|
|
18
|
+
"./styles.css": "./src/theme/styles.css",
|
|
19
|
+
"./gates": {
|
|
20
|
+
"types": "./dist/gates/index.d.ts",
|
|
21
|
+
"import": "./dist/gates/index.js"
|
|
22
|
+
}
|
|
14
23
|
},
|
|
15
|
-
"files": [
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"src"
|
|
27
|
+
],
|
|
16
28
|
"scripts": {
|
|
17
29
|
"build": "tsc -p tsconfig.build.json",
|
|
18
30
|
"test": "vitest run",
|
|
19
|
-
"test:gates": "node --test gates/*.test.mjs",
|
|
20
31
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
21
32
|
"lint": "eslint src && pnpm run typecheck",
|
|
22
|
-
"
|
|
33
|
+
"format": "prettier --write .",
|
|
34
|
+
"format:check": "prettier --check ."
|
|
23
35
|
},
|
|
24
36
|
"dependencies": {
|
|
25
37
|
"@base-ui/react": "^1.7.0",
|
|
26
38
|
"@fontsource-variable/geist": "^5.3.0",
|
|
27
39
|
"class-variance-authority": "^0.7.1",
|
|
28
|
-
"
|
|
40
|
+
"cn": "^0.2.4",
|
|
29
41
|
"lucide-react": "^1.32.0",
|
|
30
42
|
"radix-ui": "^1.6.7",
|
|
31
|
-
"shadcn": "^4.19.0",
|
|
32
43
|
"sonner": "^2.0.8",
|
|
33
|
-
"tailwind-merge": "^3.6.0",
|
|
34
44
|
"tw-animate-css": "^1.4.0"
|
|
35
45
|
},
|
|
36
46
|
"peerDependencies": {
|
|
@@ -50,6 +60,7 @@
|
|
|
50
60
|
"eslint": "^10.9.0",
|
|
51
61
|
"eslint-plugin-react-hooks": "^7.1.1",
|
|
52
62
|
"jsdom": "^30.0.1",
|
|
63
|
+
"prettier": "^3.9.6",
|
|
53
64
|
"react": "^19.2.8",
|
|
54
65
|
"react-dom": "^19.2.8",
|
|
55
66
|
"tailwindcss": "^4.3.3",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
3
|
|
|
4
|
-
import { cn } from "
|
|
4
|
+
import { cn } from "cn";
|
|
5
5
|
|
|
6
6
|
const alertVariants = cva(
|
|
7
7
|
"group/alert relative grid w-full gap-0.5 rounded-lg border px-2.5 py-2 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4",
|
|
@@ -16,8 +16,8 @@ const alertVariants = cva(
|
|
|
16
16
|
defaultVariants: {
|
|
17
17
|
variant: "default",
|
|
18
18
|
},
|
|
19
|
-
}
|
|
20
|
-
)
|
|
19
|
+
},
|
|
20
|
+
);
|
|
21
21
|
|
|
22
22
|
function Alert({
|
|
23
23
|
className,
|
|
@@ -31,7 +31,7 @@ function Alert({
|
|
|
31
31
|
className={cn(alertVariants({ variant }), className)}
|
|
32
32
|
{...props}
|
|
33
33
|
/>
|
|
34
|
-
)
|
|
34
|
+
);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
|
|
@@ -40,37 +40,30 @@ function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
|
|
|
40
40
|
data-slot="alert-title"
|
|
41
41
|
className={cn(
|
|
42
42
|
"font-medium group-has-[>svg]/alert:col-start-2 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground",
|
|
43
|
-
className
|
|
43
|
+
className,
|
|
44
44
|
)}
|
|
45
45
|
{...props}
|
|
46
46
|
/>
|
|
47
|
-
)
|
|
47
|
+
);
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
function AlertDescription({
|
|
51
|
-
className,
|
|
52
|
-
...props
|
|
53
|
-
}: React.ComponentProps<"div">) {
|
|
50
|
+
function AlertDescription({ className, ...props }: React.ComponentProps<"div">) {
|
|
54
51
|
return (
|
|
55
52
|
<div
|
|
56
53
|
data-slot="alert-description"
|
|
57
54
|
className={cn(
|
|
58
55
|
"text-sm text-balance text-muted-foreground md:text-pretty [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4",
|
|
59
|
-
className
|
|
56
|
+
className,
|
|
60
57
|
)}
|
|
61
58
|
{...props}
|
|
62
59
|
/>
|
|
63
|
-
)
|
|
60
|
+
);
|
|
64
61
|
}
|
|
65
62
|
|
|
66
63
|
function AlertAction({ className, ...props }: React.ComponentProps<"div">) {
|
|
67
64
|
return (
|
|
68
|
-
<div
|
|
69
|
-
|
|
70
|
-
className={cn("absolute top-2 right-2", className)}
|
|
71
|
-
{...props}
|
|
72
|
-
/>
|
|
73
|
-
)
|
|
65
|
+
<div data-slot="alert-action" className={cn("absolute top-2 right-2", className)} {...props} />
|
|
66
|
+
);
|
|
74
67
|
}
|
|
75
68
|
|
|
76
|
-
export { Alert, AlertTitle, AlertDescription, AlertAction }
|
|
69
|
+
export { Alert, AlertTitle, AlertDescription, AlertAction };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
-
import { Slot } from "radix-ui"
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
+
import { Slot } from "radix-ui";
|
|
4
4
|
|
|
5
|
-
import { cn } from "
|
|
5
|
+
import { cn } from "cn";
|
|
6
6
|
|
|
7
7
|
const badgeVariants = cva(
|
|
8
8
|
"group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!",
|
|
@@ -10,39 +10,33 @@ const badgeVariants = cva(
|
|
|
10
10
|
variants: {
|
|
11
11
|
variant: {
|
|
12
12
|
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
|
|
13
|
-
secondary:
|
|
14
|
-
"bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
|
|
13
|
+
secondary: "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
|
|
15
14
|
destructive:
|
|
16
15
|
"bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20",
|
|
17
16
|
success:
|
|
18
17
|
"bg-emerald-500/10 text-emerald-700 focus-visible:ring-emerald-500/20 dark:bg-emerald-400/10 dark:text-emerald-400 dark:focus-visible:ring-emerald-400/40 [a]:hover:bg-emerald-500/20 dark:[a]:hover:bg-emerald-400/20",
|
|
19
18
|
warning:
|
|
20
19
|
"bg-amber-500/10 text-amber-700 focus-visible:ring-amber-500/20 dark:bg-amber-400/10 dark:text-amber-400 dark:focus-visible:ring-amber-400/40 [a]:hover:bg-amber-500/20 dark:[a]:hover:bg-amber-400/20",
|
|
21
|
-
info:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
outline:
|
|
26
|
-
"border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
|
|
27
|
-
ghost:
|
|
28
|
-
"hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
|
|
20
|
+
info: "bg-sky-500/10 text-sky-700 focus-visible:ring-sky-500/20 dark:bg-sky-400/10 dark:text-sky-400 dark:focus-visible:ring-sky-400/40 [a]:hover:bg-sky-500/20 dark:[a]:hover:bg-sky-400/20",
|
|
21
|
+
quiet: "bg-muted text-muted-foreground [a]:hover:bg-muted/80",
|
|
22
|
+
outline: "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
|
|
23
|
+
ghost: "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
|
|
29
24
|
link: "text-primary underline-offset-4 hover:underline",
|
|
30
25
|
},
|
|
31
26
|
},
|
|
32
27
|
defaultVariants: {
|
|
33
28
|
variant: "default",
|
|
34
29
|
},
|
|
35
|
-
}
|
|
36
|
-
)
|
|
30
|
+
},
|
|
31
|
+
);
|
|
37
32
|
|
|
38
33
|
function Badge({
|
|
39
34
|
className,
|
|
40
35
|
variant = "default",
|
|
41
36
|
asChild = false,
|
|
42
37
|
...props
|
|
43
|
-
}: React.ComponentProps<"span"> &
|
|
44
|
-
|
|
45
|
-
const Comp = asChild ? Slot.Root : "span"
|
|
38
|
+
}: React.ComponentProps<"span"> & VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
|
|
39
|
+
const Comp = asChild ? Slot.Root : "span";
|
|
46
40
|
|
|
47
41
|
return (
|
|
48
42
|
<Comp
|
|
@@ -51,7 +45,7 @@ function Badge({
|
|
|
51
45
|
className={cn(badgeVariants({ variant }), className)}
|
|
52
46
|
{...props}
|
|
53
47
|
/>
|
|
54
|
-
)
|
|
48
|
+
);
|
|
55
49
|
}
|
|
56
50
|
|
|
57
|
-
export { Badge, badgeVariants }
|
|
51
|
+
export { Badge, badgeVariants };
|