@kinetixui/ui 0.1.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +30 -21
- package/package.json +12 -4
- package/src/components/button.tsx +19 -12
- package/src/components/context-menu.tsx +1 -1
- package/src/components/dropdown-menu.tsx +1 -1
- package/src/components/input-group.tsx +2 -1
- package/src/components/list.tsx +3 -1
- package/src/components/menubar.tsx +2 -2
- package/src/components/navigation-bar.tsx +1 -1
- package/src/components/select.tsx +1 -1
- package/src/components/toggle.tsx +1 -1
- package/src/components-behavior.test.tsx +74 -0
- package/src/components-smoke.test.tsx +47 -0
- package/src/stories/Accordion.stories.tsx +49 -0
- package/src/stories/Alert.stories.tsx +38 -0
- package/src/stories/AlertDialog.stories.tsx +35 -0
- package/src/stories/AspectRatio.stories.tsx +32 -0
- package/src/stories/AudioPlayer.stories.tsx +26 -0
- package/src/stories/Avatar.stories.tsx +23 -0
- package/src/stories/AvatarGroup.stories.tsx +37 -0
- package/src/stories/Badge.stories.tsx +35 -0
- package/src/stories/Breadcrumb.stories.tsx +36 -0
- package/src/stories/Calendar.stories.tsx +25 -0
- package/src/stories/Card.stories.tsx +33 -0
- package/src/stories/Carousel.stories.tsx +33 -0
- package/src/stories/Chart.stories.tsx +44 -0
- package/src/stories/Checkbox.stories.tsx +29 -0
- package/src/stories/CircularProgress.stories.tsx +42 -0
- package/src/stories/CodeBlock.stories.tsx +25 -0
- package/src/stories/Collapsible.stories.tsx +50 -0
- package/src/stories/Combobox.stories.tsx +32 -0
- package/src/stories/Command.stories.tsx +32 -0
- package/src/stories/ContextMenu.stories.tsx +31 -0
- package/src/stories/DataTable.stories.tsx +31 -0
- package/src/stories/DatePicker.stories.tsx +30 -0
- package/src/stories/Dialog.stories.tsx +38 -0
- package/src/stories/Drawer.stories.tsx +39 -0
- package/src/stories/DropdownMenu.stories.tsx +32 -0
- package/src/stories/Fab.stories.tsx +47 -0
- package/src/stories/Field.stories.tsx +35 -0
- package/src/stories/FileUpload.stories.tsx +36 -0
- package/src/stories/Footer.stories.tsx +39 -0
- package/src/stories/Form.stories.tsx +28 -0
- package/src/stories/HoverCard.stories.tsx +41 -0
- package/src/stories/Image.stories.tsx +35 -0
- package/src/stories/Inform.stories.tsx +39 -0
- package/src/stories/InputGroup.stories.tsx +36 -0
- package/src/stories/InputOTP.stories.tsx +27 -0
- package/src/stories/Label.stories.tsx +24 -0
- package/src/stories/List.stories.tsx +26 -0
- package/src/stories/Menubar.stories.tsx +38 -0
- package/src/stories/Metric.stories.tsx +32 -0
- package/src/stories/Modal.stories.tsx +31 -0
- package/src/stories/NavigationBar.stories.tsx +32 -0
- package/src/stories/NavigationMenu.stories.tsx +34 -0
- package/src/stories/NumberInput.stories.tsx +29 -0
- package/src/stories/Pagination.stories.tsx +30 -0
- package/src/stories/PasswordInput.stories.tsx +23 -0
- package/src/stories/Popover.stories.tsx +47 -0
- package/src/stories/Progress.stories.tsx +31 -0
- package/src/stories/Quote.stories.tsx +32 -0
- package/src/stories/RadioGroup.stories.tsx +45 -0
- package/src/stories/Rating.stories.tsx +35 -0
- package/src/stories/Resizable.stories.tsx +29 -0
- package/src/stories/ScrollArea.stories.tsx +25 -0
- package/src/stories/Select.stories.tsx +46 -0
- package/src/stories/Separator.stories.tsx +41 -0
- package/src/stories/Sheet.stories.tsx +31 -0
- package/src/stories/Sidebar.stories.tsx +25 -0
- package/src/stories/Skeleton.stories.tsx +27 -0
- package/src/stories/Slider.stories.tsx +24 -0
- package/src/stories/Sonner.stories.tsx +26 -0
- package/src/stories/Spinner.stories.tsx +33 -0
- package/src/stories/Stepper.stories.tsx +35 -0
- package/src/stories/Switch.stories.tsx +29 -0
- package/src/stories/TabBar.stories.tsx +35 -0
- package/src/stories/Table.stories.tsx +42 -0
- package/src/stories/TableOfContents.stories.tsx +31 -0
- package/src/stories/Tabs.stories.tsx +50 -0
- package/src/stories/Tag.stories.tsx +42 -0
- package/src/stories/Toggle.stories.tsx +34 -0
- package/src/stories/ToggleGroup.stories.tsx +37 -0
- package/src/stories/Tooltip.stories.tsx +42 -0
package/dist/index.js
CHANGED
|
@@ -27,13 +27,20 @@ var buttonVariants = cva(
|
|
|
27
27
|
variants: {
|
|
28
28
|
variant: {
|
|
29
29
|
// bg primary / text on-primary; hover+active step down the blue ramp; disabled -> border grey
|
|
30
|
-
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
// rest = --primary; hover/active dim it via opacity so it works in BOTH themes.
|
|
31
|
+
// (the old hover:bg-[--color-blue-600] was a hard-coded dark navy — on dark mode,
|
|
32
|
+
// where --primary is light, it inverted the button into the near-black background.)
|
|
33
|
+
Primary: "bg-primary text-primary-foreground hover:bg-primary/90 active:bg-primary/80 disabled:bg-border disabled:text-muted-foreground",
|
|
34
|
+
// rest = --secondary; hover/active promote to the --secondary-foreground fill with
|
|
35
|
+
// inverted text — theme-aware, unlike the previous hard-coded green primitives
|
|
36
|
+
Secondary: "bg-secondary text-secondary-foreground hover:bg-secondary-foreground hover:text-secondary active:bg-secondary-foreground/90 active:text-secondary focus-visible:bg-secondary disabled:bg-border disabled:text-muted-foreground",
|
|
37
|
+
// 1px outline; hover fills with accent + strengthens the edge to --ring so the
|
|
38
|
+
// state stays visible on the darkest surfaces (WCAG 2.2 SC 1.4.11); active pins primary
|
|
39
|
+
Outline: "border border-input bg-transparent text-foreground hover:border-ring hover:bg-accent active:border-primary active:bg-accent active:text-primary disabled:border-input disabled:text-muted-foreground disabled:bg-transparent",
|
|
35
40
|
Destructive: "bg-destructive text-destructive-foreground hover:brightness-95 active:brightness-90 disabled:bg-border disabled:text-muted-foreground",
|
|
36
|
-
|
|
41
|
+
// transparent rest; hover adds an inset --ring outline (the accent fill alone is
|
|
42
|
+
// <3:1 against the page on dark) plus the accent fill; active pins primary text
|
|
43
|
+
Ghost: "bg-transparent text-foreground hover:bg-accent hover:ring-1 hover:ring-inset hover:ring-ring active:bg-accent active:text-primary active:ring-0 disabled:text-muted-foreground disabled:bg-transparent",
|
|
37
44
|
Link: "bg-transparent text-primary underline-offset-4 hover:underline hover:text-foreground rounded-none px-0 disabled:text-muted-foreground disabled:no-underline"
|
|
38
45
|
},
|
|
39
46
|
size: {
|
|
@@ -63,13 +70,13 @@ var buttonVariants = cva(
|
|
|
63
70
|
},
|
|
64
71
|
compoundVariants: [
|
|
65
72
|
{ variant: "Destructive", class: "focus-visible:shadow-focus-destructive" },
|
|
66
|
-
{ variant: "Primary", state: "Hover", class: "bg-
|
|
67
|
-
{ variant: "Primary", state: "Active", class: "bg-
|
|
68
|
-
{ variant: "Secondary", state: "Hover", class: "bg-
|
|
69
|
-
{ variant: "Secondary", state: "Active", class: "bg-
|
|
70
|
-
{ variant: "Outline", state: "Hover", class: "bg-accent" },
|
|
73
|
+
{ variant: "Primary", state: "Hover", class: "bg-primary/90" },
|
|
74
|
+
{ variant: "Primary", state: "Active", class: "bg-primary/80" },
|
|
75
|
+
{ variant: "Secondary", state: "Hover", class: "bg-secondary-foreground text-secondary" },
|
|
76
|
+
{ variant: "Secondary", state: "Active", class: "bg-secondary-foreground/90 text-secondary" },
|
|
77
|
+
{ variant: "Outline", state: "Hover", class: "border-ring bg-accent" },
|
|
71
78
|
{ variant: "Outline", state: "Active", class: "border-primary bg-accent text-primary" },
|
|
72
|
-
{ variant: "Ghost", state: "Hover", class: "bg-accent" },
|
|
79
|
+
{ variant: "Ghost", state: "Hover", class: "bg-accent ring-1 ring-inset ring-ring" },
|
|
73
80
|
{ variant: "Ghost", state: "Active", class: "bg-accent text-primary" },
|
|
74
81
|
{ variant: "Link", state: "Hover", class: "underline text-foreground" },
|
|
75
82
|
{ variant: "Link", size: ["sm", "md", "lg"], class: "py-2 px-0 h-auto" }
|
|
@@ -596,7 +603,7 @@ var DropdownMenuPortal = DropdownMenuPrimitive.Portal;
|
|
|
596
603
|
var DropdownMenuSub = DropdownMenuPrimitive.Sub;
|
|
597
604
|
var DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
|
|
598
605
|
var contentCls = "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md font-sans data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95";
|
|
599
|
-
var itemCls = "relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50";
|
|
606
|
+
var itemCls = "relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground focus:ring-1 focus:ring-inset focus:ring-ring data-[disabled]:pointer-events-none data-[disabled]:opacity-50";
|
|
600
607
|
var DropdownMenuContent = React12.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx13(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ jsx13(DropdownMenuPrimitive.Content, { ref, sideOffset, className: cn(contentCls, className), ...props }) }));
|
|
601
608
|
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
|
|
602
609
|
var DropdownMenuItem = React12.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx13(DropdownMenuPrimitive.Item, { ref, className: cn(itemCls, inset && "pl-8", className), ...props }));
|
|
@@ -886,7 +893,7 @@ var SelectItem = React20.forwardRef(({ className, children, ...props }, ref) =>
|
|
|
886
893
|
ref,
|
|
887
894
|
className: cn(
|
|
888
895
|
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none",
|
|
889
|
-
"focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
896
|
+
"focus:bg-accent focus:text-accent-foreground focus:ring-1 focus:ring-inset focus:ring-ring data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
890
897
|
className
|
|
891
898
|
),
|
|
892
899
|
...props,
|
|
@@ -1262,7 +1269,7 @@ import * as TogglePrimitive from "@radix-ui/react-toggle";
|
|
|
1262
1269
|
import { cva as cva9 } from "class-variance-authority";
|
|
1263
1270
|
import { jsx as jsx32 } from "react/jsx-runtime";
|
|
1264
1271
|
var toggleVariants = cva9(
|
|
1265
|
-
"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium font-sans transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:size-4 [&_svg]:shrink-0",
|
|
1272
|
+
"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium font-sans transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground data-[state=on]:ring-1 data-[state=on]:ring-inset data-[state=on]:ring-ring [&_svg]:size-4 [&_svg]:shrink-0",
|
|
1266
1273
|
{
|
|
1267
1274
|
variants: {
|
|
1268
1275
|
variant: {
|
|
@@ -1729,7 +1736,7 @@ var ContextMenuGroup = ContextMenuPrimitive.Group;
|
|
|
1729
1736
|
var ContextMenuPortal = ContextMenuPrimitive.Portal;
|
|
1730
1737
|
var ContextMenuSub = ContextMenuPrimitive.Sub;
|
|
1731
1738
|
var ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup;
|
|
1732
|
-
var itemCls2 = "relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50";
|
|
1739
|
+
var itemCls2 = "relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground focus:ring-1 focus:ring-inset focus:ring-ring data-[disabled]:pointer-events-none data-[disabled]:opacity-50";
|
|
1733
1740
|
var contentCls2 = "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md font-sans data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95";
|
|
1734
1741
|
var ContextMenuSubTrigger = React34.forwardRef(({ className, inset, children, ...props }, ref) => /* @__PURE__ */ jsxs17(
|
|
1735
1742
|
ContextMenuPrimitive.SubTrigger,
|
|
@@ -2070,7 +2077,8 @@ var InputGroupButton = React39.forwardRef(({ className, type = "button", ...prop
|
|
|
2070
2077
|
type,
|
|
2071
2078
|
className: cn(
|
|
2072
2079
|
"flex h-full shrink-0 items-center gap-1.5 self-stretch border-l border-input bg-muted px-3 text-label-md text-foreground",
|
|
2073
|
-
|
|
2080
|
+
// base is bg-muted and muted==accent on dark, so the fill is a no-op there — the ring carries it
|
|
2081
|
+
"outline-none transition-colors hover:bg-accent hover:ring-1 hover:ring-inset hover:ring-ring focus-visible:bg-accent focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
|
|
2074
2082
|
"[&_svg]:size-4 [&_svg]:shrink-0",
|
|
2075
2083
|
className
|
|
2076
2084
|
),
|
|
@@ -2157,7 +2165,7 @@ var MenubarGroup = (props) => /* @__PURE__ */ jsx47(MenubarPrimitive.Group, { ..
|
|
|
2157
2165
|
var MenubarPortal = (props) => /* @__PURE__ */ jsx47(MenubarPrimitive.Portal, { ...props });
|
|
2158
2166
|
var MenubarSub = (props) => /* @__PURE__ */ jsx47(MenubarPrimitive.Sub, { ...props });
|
|
2159
2167
|
var MenubarRadioGroup = (props) => /* @__PURE__ */ jsx47(MenubarPrimitive.RadioGroup, { ...props });
|
|
2160
|
-
var itemCls3 = "relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50";
|
|
2168
|
+
var itemCls3 = "relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground focus:ring-1 focus:ring-inset focus:ring-ring data-[disabled]:pointer-events-none data-[disabled]:opacity-50";
|
|
2161
2169
|
var contentCls3 = "z-50 min-w-[12rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md font-sans data-[state=open]:animate-in data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95";
|
|
2162
2170
|
var Menubar = React42.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx47(
|
|
2163
2171
|
MenubarPrimitive.Root,
|
|
@@ -2173,7 +2181,7 @@ var MenubarTrigger = React42.forwardRef(({ className, ...props }, ref) => /* @__
|
|
|
2173
2181
|
{
|
|
2174
2182
|
ref,
|
|
2175
2183
|
className: cn(
|
|
2176
|
-
"flex cursor-default select-none items-center rounded-sm px-3 py-1 text-sm font-medium outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
|
|
2184
|
+
"flex cursor-default select-none items-center rounded-sm px-3 py-1 text-sm font-medium outline-none focus:bg-accent focus:text-accent-foreground focus:ring-1 focus:ring-inset focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-accent-foreground data-[state=open]:ring-1 data-[state=open]:ring-inset data-[state=open]:ring-ring",
|
|
2177
2185
|
className
|
|
2178
2186
|
),
|
|
2179
2187
|
...props
|
|
@@ -3021,7 +3029,8 @@ var ListItem = React51.forwardRef(
|
|
|
3021
3029
|
} : void 0,
|
|
3022
3030
|
className: cn(
|
|
3023
3031
|
"flex items-center gap-3 px-3 py-3 font-sans outline-none",
|
|
3024
|
-
|
|
3032
|
+
// ring keeps the highlighted row perceptible where the accent fill is <3:1 (dark)
|
|
3033
|
+
interactive && "cursor-pointer hover:bg-accent hover:ring-1 hover:ring-inset hover:ring-ring focus-visible:bg-accent focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring",
|
|
3025
3034
|
disabled && "pointer-events-none opacity-50",
|
|
3026
3035
|
className
|
|
3027
3036
|
),
|
|
@@ -3199,7 +3208,7 @@ var NavigationBar = React55.forwardRef(
|
|
|
3199
3208
|
type: "button",
|
|
3200
3209
|
onClick: onBack,
|
|
3201
3210
|
"aria-label": "Back",
|
|
3202
|
-
className: "flex size-9 items-center justify-center rounded-full text-foreground outline-none transition-colors hover:bg-accent focus-visible:bg-accent",
|
|
3211
|
+
className: "flex size-9 items-center justify-center rounded-full text-foreground outline-none transition-colors hover:bg-accent hover:ring-1 hover:ring-inset hover:ring-ring focus-visible:bg-accent focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring",
|
|
3203
3212
|
children: /* @__PURE__ */ jsx61(ChevronLeft3, { className: "size-5" })
|
|
3204
3213
|
}
|
|
3205
3214
|
)) }),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kinetixui/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "KinetixUI React components — CVA + Radix + Tailwind on the KinetixUI token contract. Also distributed via the kinetixui CLI as a component registry.",
|
|
6
6
|
"keywords": [
|
|
@@ -89,18 +89,26 @@
|
|
|
89
89
|
"react-dom": ">=18"
|
|
90
90
|
},
|
|
91
91
|
"devDependencies": {
|
|
92
|
-
"@kinetixui/tokens": "0.
|
|
92
|
+
"@kinetixui/tokens": "0.4.0",
|
|
93
|
+
"@testing-library/jest-dom": "^6.6.3",
|
|
94
|
+
"@testing-library/react": "^16.1.0",
|
|
95
|
+
"@testing-library/user-event": "^14.5.2",
|
|
93
96
|
"@types/react": "^18.3.17",
|
|
94
97
|
"@types/react-dom": "^18.3.5",
|
|
98
|
+
"@vitejs/plugin-react": "^4.3.4",
|
|
99
|
+
"jsdom": "^25.0.1",
|
|
95
100
|
"react": "^18.3.1",
|
|
96
101
|
"react-dom": "^18.3.1",
|
|
97
102
|
"tailwindcss": "^3.4.17",
|
|
98
103
|
"tsup": "^8.3.5",
|
|
99
|
-
"typescript": "^5.7.2"
|
|
104
|
+
"typescript": "^5.7.2",
|
|
105
|
+
"vitest": "^2.1.8"
|
|
100
106
|
},
|
|
101
107
|
"scripts": {
|
|
102
108
|
"build": "tsup src/index.ts --format esm --dts --external react --external react-dom",
|
|
103
109
|
"lint": "eslint src",
|
|
104
|
-
"typecheck": "tsc --noEmit"
|
|
110
|
+
"typecheck": "tsc --noEmit",
|
|
111
|
+
"test": "vitest run",
|
|
112
|
+
"test:watch": "vitest"
|
|
105
113
|
}
|
|
106
114
|
}
|
|
@@ -35,18 +35,25 @@ const buttonVariants = cva(
|
|
|
35
35
|
variants: {
|
|
36
36
|
variant: {
|
|
37
37
|
// bg primary / text on-primary; hover+active step down the blue ramp; disabled -> border grey
|
|
38
|
+
// rest = --primary; hover/active dim it via opacity so it works in BOTH themes.
|
|
39
|
+
// (the old hover:bg-[--color-blue-600] was a hard-coded dark navy — on dark mode,
|
|
40
|
+
// where --primary is light, it inverted the button into the near-black background.)
|
|
38
41
|
Primary:
|
|
39
|
-
"bg-primary text-primary-foreground hover:bg-
|
|
40
|
-
// rest =
|
|
42
|
+
"bg-primary text-primary-foreground hover:bg-primary/90 active:bg-primary/80 disabled:bg-border disabled:text-muted-foreground",
|
|
43
|
+
// rest = --secondary; hover/active promote to the --secondary-foreground fill with
|
|
44
|
+
// inverted text — theme-aware, unlike the previous hard-coded green primitives
|
|
41
45
|
Secondary:
|
|
42
|
-
"bg-secondary text-secondary-foreground hover:bg-
|
|
43
|
-
// 1px outline; hover fills with accent
|
|
46
|
+
"bg-secondary text-secondary-foreground hover:bg-secondary-foreground hover:text-secondary active:bg-secondary-foreground/90 active:text-secondary focus-visible:bg-secondary disabled:bg-border disabled:text-muted-foreground",
|
|
47
|
+
// 1px outline; hover fills with accent + strengthens the edge to --ring so the
|
|
48
|
+
// state stays visible on the darkest surfaces (WCAG 2.2 SC 1.4.11); active pins primary
|
|
44
49
|
Outline:
|
|
45
|
-
"border border-input bg-transparent text-foreground hover:bg-accent active:border-primary active:bg-accent active:text-primary disabled:border-input disabled:text-muted-foreground disabled:bg-transparent",
|
|
50
|
+
"border border-input bg-transparent text-foreground hover:border-ring hover:bg-accent active:border-primary active:bg-accent active:text-primary disabled:border-input disabled:text-muted-foreground disabled:bg-transparent",
|
|
46
51
|
Destructive:
|
|
47
52
|
"bg-destructive text-destructive-foreground hover:brightness-95 active:brightness-90 disabled:bg-border disabled:text-muted-foreground",
|
|
53
|
+
// transparent rest; hover adds an inset --ring outline (the accent fill alone is
|
|
54
|
+
// <3:1 against the page on dark) plus the accent fill; active pins primary text
|
|
48
55
|
Ghost:
|
|
49
|
-
"bg-transparent text-foreground hover:bg-accent active:bg-accent active:text-primary disabled:text-muted-foreground disabled:bg-transparent",
|
|
56
|
+
"bg-transparent text-foreground hover:bg-accent hover:ring-1 hover:ring-inset hover:ring-ring active:bg-accent active:text-primary active:ring-0 disabled:text-muted-foreground disabled:bg-transparent",
|
|
50
57
|
Link: "bg-transparent text-primary underline-offset-4 hover:underline hover:text-foreground rounded-none px-0 disabled:text-muted-foreground disabled:no-underline",
|
|
51
58
|
},
|
|
52
59
|
size: {
|
|
@@ -76,13 +83,13 @@ const buttonVariants = cva(
|
|
|
76
83
|
},
|
|
77
84
|
compoundVariants: [
|
|
78
85
|
{ variant: "Destructive", class: "focus-visible:shadow-focus-destructive" },
|
|
79
|
-
{ variant: "Primary", state: "Hover", class: "bg-
|
|
80
|
-
{ variant: "Primary", state: "Active", class: "bg-
|
|
81
|
-
{ variant: "Secondary", state: "Hover", class: "bg-
|
|
82
|
-
{ variant: "Secondary", state: "Active", class: "bg-
|
|
83
|
-
{ variant: "Outline", state: "Hover", class: "bg-accent" },
|
|
86
|
+
{ variant: "Primary", state: "Hover", class: "bg-primary/90" },
|
|
87
|
+
{ variant: "Primary", state: "Active", class: "bg-primary/80" },
|
|
88
|
+
{ variant: "Secondary", state: "Hover", class: "bg-secondary-foreground text-secondary" },
|
|
89
|
+
{ variant: "Secondary", state: "Active", class: "bg-secondary-foreground/90 text-secondary" },
|
|
90
|
+
{ variant: "Outline", state: "Hover", class: "border-ring bg-accent" },
|
|
84
91
|
{ variant: "Outline", state: "Active", class: "border-primary bg-accent text-primary" },
|
|
85
|
-
{ variant: "Ghost", state: "Hover", class: "bg-accent" },
|
|
92
|
+
{ variant: "Ghost", state: "Hover", class: "bg-accent ring-1 ring-inset ring-ring" },
|
|
86
93
|
{ variant: "Ghost", state: "Active", class: "bg-accent text-primary" },
|
|
87
94
|
{ variant: "Link", state: "Hover", class: "underline text-foreground" },
|
|
88
95
|
{ variant: "Link", size: ["sm", "md", "lg"], class: "py-2 px-0 h-auto" },
|
|
@@ -13,7 +13,7 @@ const ContextMenuSub = ContextMenuPrimitive.Sub;
|
|
|
13
13
|
const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup;
|
|
14
14
|
|
|
15
15
|
const itemCls =
|
|
16
|
-
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50";
|
|
16
|
+
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground focus:ring-1 focus:ring-inset focus:ring-ring data-[disabled]:pointer-events-none data-[disabled]:opacity-50";
|
|
17
17
|
const contentCls =
|
|
18
18
|
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md font-sans data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95";
|
|
19
19
|
|
|
@@ -15,7 +15,7 @@ const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
|
|
|
15
15
|
const contentCls =
|
|
16
16
|
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md font-sans data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95";
|
|
17
17
|
const itemCls =
|
|
18
|
-
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50";
|
|
18
|
+
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground focus:ring-1 focus:ring-inset focus:ring-ring data-[disabled]:pointer-events-none data-[disabled]:opacity-50";
|
|
19
19
|
|
|
20
20
|
const DropdownMenuContent = React.forwardRef<
|
|
21
21
|
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
|
|
@@ -84,7 +84,8 @@ const InputGroupButton = React.forwardRef<
|
|
|
84
84
|
type={type}
|
|
85
85
|
className={cn(
|
|
86
86
|
"flex h-full shrink-0 items-center gap-1.5 self-stretch border-l border-input bg-muted px-3 text-label-md text-foreground",
|
|
87
|
-
|
|
87
|
+
// base is bg-muted and muted==accent on dark, so the fill is a no-op there — the ring carries it
|
|
88
|
+
"outline-none transition-colors hover:bg-accent hover:ring-1 hover:ring-inset hover:ring-ring focus-visible:bg-accent focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
|
|
88
89
|
"[&_svg]:size-4 [&_svg]:shrink-0",
|
|
89
90
|
className,
|
|
90
91
|
)}
|
package/src/components/list.tsx
CHANGED
|
@@ -48,7 +48,9 @@ const ListItem = React.forwardRef<HTMLDivElement, ListItemProps>(
|
|
|
48
48
|
}
|
|
49
49
|
className={cn(
|
|
50
50
|
"flex items-center gap-3 px-3 py-3 font-sans outline-none",
|
|
51
|
-
|
|
51
|
+
// ring keeps the highlighted row perceptible where the accent fill is <3:1 (dark)
|
|
52
|
+
interactive &&
|
|
53
|
+
"cursor-pointer hover:bg-accent hover:ring-1 hover:ring-inset hover:ring-ring focus-visible:bg-accent focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring",
|
|
52
54
|
disabled && "pointer-events-none opacity-50",
|
|
53
55
|
className,
|
|
54
56
|
)}
|
|
@@ -22,7 +22,7 @@ const MenubarRadioGroup = (props: React.ComponentProps<typeof MenubarPrimitive.R
|
|
|
22
22
|
);
|
|
23
23
|
|
|
24
24
|
const itemCls =
|
|
25
|
-
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50";
|
|
25
|
+
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground focus:ring-1 focus:ring-inset focus:ring-ring data-[disabled]:pointer-events-none data-[disabled]:opacity-50";
|
|
26
26
|
const contentCls =
|
|
27
27
|
"z-50 min-w-[12rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md font-sans data-[state=open]:animate-in data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95";
|
|
28
28
|
|
|
@@ -45,7 +45,7 @@ const MenubarTrigger = React.forwardRef<
|
|
|
45
45
|
<MenubarPrimitive.Trigger
|
|
46
46
|
ref={ref}
|
|
47
47
|
className={cn(
|
|
48
|
-
"flex cursor-default select-none items-center rounded-sm px-3 py-1 text-sm font-medium outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
|
|
48
|
+
"flex cursor-default select-none items-center rounded-sm px-3 py-1 text-sm font-medium outline-none focus:bg-accent focus:text-accent-foreground focus:ring-1 focus:ring-inset focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-accent-foreground data-[state=open]:ring-1 data-[state=open]:ring-inset data-[state=open]:ring-ring",
|
|
49
49
|
className,
|
|
50
50
|
)}
|
|
51
51
|
{...props}
|
|
@@ -30,7 +30,7 @@ const NavigationBar = React.forwardRef<HTMLElement, NavigationBarProps>(
|
|
|
30
30
|
type="button"
|
|
31
31
|
onClick={onBack}
|
|
32
32
|
aria-label="Back"
|
|
33
|
-
className="flex size-9 items-center justify-center rounded-full text-foreground outline-none transition-colors hover:bg-accent focus-visible:bg-accent"
|
|
33
|
+
className="flex size-9 items-center justify-center rounded-full text-foreground outline-none transition-colors hover:bg-accent hover:ring-1 hover:ring-inset hover:ring-ring focus-visible:bg-accent focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring"
|
|
34
34
|
>
|
|
35
35
|
<ChevronLeft className="size-5" />
|
|
36
36
|
</button>
|
|
@@ -89,7 +89,7 @@ const SelectItem = React.forwardRef<
|
|
|
89
89
|
ref={ref}
|
|
90
90
|
className={cn(
|
|
91
91
|
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none",
|
|
92
|
-
"focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
92
|
+
"focus:bg-accent focus:text-accent-foreground focus:ring-1 focus:ring-inset focus:ring-ring data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
93
93
|
className,
|
|
94
94
|
)}
|
|
95
95
|
{...props}
|
|
@@ -6,7 +6,7 @@ import { cva, type VariantProps } from "class-variance-authority";
|
|
|
6
6
|
import { cn } from "../lib/utils";
|
|
7
7
|
|
|
8
8
|
const toggleVariants = cva(
|
|
9
|
-
"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium font-sans transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:size-4 [&_svg]:shrink-0",
|
|
9
|
+
"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium font-sans transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground data-[state=on]:ring-1 data-[state=on]:ring-inset data-[state=on]:ring-ring [&_svg]:size-4 [&_svg]:shrink-0",
|
|
10
10
|
{
|
|
11
11
|
variants: {
|
|
12
12
|
variant: {
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { render, screen } from "@testing-library/react";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
import { describe, expect, it, vi } from "vitest";
|
|
5
|
+
import { Badge } from "./components/badge";
|
|
6
|
+
import { Button } from "./components/button";
|
|
7
|
+
import { Input } from "./components/input";
|
|
8
|
+
import {
|
|
9
|
+
Accordion,
|
|
10
|
+
AccordionContent,
|
|
11
|
+
AccordionItem,
|
|
12
|
+
AccordionTrigger,
|
|
13
|
+
} from "./components/accordion";
|
|
14
|
+
|
|
15
|
+
describe("Button", () => {
|
|
16
|
+
it("renders the label and forwards clicks", async () => {
|
|
17
|
+
const onClick = vi.fn();
|
|
18
|
+
render(<Button onClick={onClick}>Save</Button>);
|
|
19
|
+
await userEvent.click(screen.getByRole("button", { name: "Save" }));
|
|
20
|
+
expect(onClick).toHaveBeenCalledOnce();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("maps the design-source variant prop to a class", () => {
|
|
24
|
+
render(<Button variant="Destructive">x</Button>);
|
|
25
|
+
expect(screen.getByRole("button")).toHaveClass("bg-destructive");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("renders as a child element with asChild", () => {
|
|
29
|
+
render(
|
|
30
|
+
<Button asChild>
|
|
31
|
+
<a href="/x">link</a>
|
|
32
|
+
</Button>,
|
|
33
|
+
);
|
|
34
|
+
expect(screen.getByRole("link", { name: "link" })).toHaveAttribute("href", "/x");
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe("Input", () => {
|
|
39
|
+
it("sets aria-invalid when state is Error", () => {
|
|
40
|
+
render(<Input state="Error" defaultValue="nope" />);
|
|
41
|
+
expect(screen.getByRole("textbox")).toHaveAttribute("aria-invalid", "true");
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("is not aria-invalid by default", () => {
|
|
45
|
+
render(<Input />);
|
|
46
|
+
expect(screen.getByRole("textbox")).not.toHaveAttribute("aria-invalid", "true");
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe("Badge", () => {
|
|
51
|
+
it("gives each variant a distinct class", () => {
|
|
52
|
+
const { rerender } = render(<Badge>tag</Badge>);
|
|
53
|
+
const def = screen.getByText("tag").className;
|
|
54
|
+
rerender(<Badge variant="secondary">tag</Badge>);
|
|
55
|
+
expect(screen.getByText("tag").className).not.toBe(def);
|
|
56
|
+
expect(screen.getByText("tag")).toHaveClass("text-secondary");
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe("Accordion", () => {
|
|
61
|
+
it("reveals content when a trigger is activated", async () => {
|
|
62
|
+
render(
|
|
63
|
+
<Accordion type="single" collapsible>
|
|
64
|
+
<AccordionItem value="a">
|
|
65
|
+
<AccordionTrigger>Question</AccordionTrigger>
|
|
66
|
+
<AccordionContent>Answer text</AccordionContent>
|
|
67
|
+
</AccordionItem>
|
|
68
|
+
</Accordion>,
|
|
69
|
+
);
|
|
70
|
+
expect(screen.queryByText("Answer text")).not.toBeInTheDocument();
|
|
71
|
+
await userEvent.click(screen.getByRole("button", { name: "Question" }));
|
|
72
|
+
expect(screen.getByText("Answer text")).toBeVisible();
|
|
73
|
+
});
|
|
74
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { render, cleanup } from "@testing-library/react";
|
|
3
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Every Storybook story is a real composition of a shipped component. Render
|
|
7
|
+
* each one in jsdom and assert it mounts without throwing — a cheap regression
|
|
8
|
+
* net across the whole library.
|
|
9
|
+
*/
|
|
10
|
+
const stories = import.meta.glob("./stories/*.stories.tsx", { eager: true }) as Record<
|
|
11
|
+
string,
|
|
12
|
+
Record<string, unknown>
|
|
13
|
+
>;
|
|
14
|
+
|
|
15
|
+
// Stories that can't render meaningfully in jsdom (need real layout / canvas).
|
|
16
|
+
const SKIP = new Set<string>([]);
|
|
17
|
+
|
|
18
|
+
afterEach(cleanup);
|
|
19
|
+
|
|
20
|
+
describe("component smoke", () => {
|
|
21
|
+
const entries = Object.entries(stories).sort(([a], [b]) => a.localeCompare(b));
|
|
22
|
+
expect(entries.length).toBeGreaterThan(60);
|
|
23
|
+
|
|
24
|
+
for (const [path, mod] of entries) {
|
|
25
|
+
const name = path.split("/").pop()!.replace(".stories.tsx", "");
|
|
26
|
+
const meta = mod.default as { component?: React.ComponentType<unknown>; args?: Record<string, unknown> };
|
|
27
|
+
|
|
28
|
+
const storyKey = ["Default", "Playground", "Variants", "States"].find(
|
|
29
|
+
(k) => mod[k] && typeof mod[k] === "object",
|
|
30
|
+
);
|
|
31
|
+
const story = storyKey ? (mod[storyKey] as { render?: (...a: unknown[]) => React.ReactNode; args?: Record<string, unknown> }) : undefined;
|
|
32
|
+
|
|
33
|
+
(SKIP.has(name) ? it.skip : it)(`${name} mounts`, () => {
|
|
34
|
+
const args = { ...meta?.args, ...story?.args };
|
|
35
|
+
const node =
|
|
36
|
+
typeof story?.render === "function"
|
|
37
|
+
? story.render(args, { args } as never)
|
|
38
|
+
: meta?.component
|
|
39
|
+
? React.createElement(meta.component, args)
|
|
40
|
+
: null;
|
|
41
|
+
|
|
42
|
+
expect(node, `${name}: no renderable story export`).not.toBeNull();
|
|
43
|
+
const { container } = render(node as React.ReactElement);
|
|
44
|
+
expect(container).toBeInTheDocument();
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/* AUTO-GENERATED by scripts/gen-stories.mjs — do not edit.
|
|
2
|
+
Source of truth: apps/web/src/registry/demos.tsx · controls: scripts/gen-stories.mjs CONTROLS */
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
5
|
+
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@kinetixui/ui";
|
|
6
|
+
|
|
7
|
+
const Demo = () => (
|
|
8
|
+
<Accordion type="single" collapsible className="w-full max-w-md">
|
|
9
|
+
<AccordionItem value="a">
|
|
10
|
+
<AccordionTrigger>Is it accessible?</AccordionTrigger>
|
|
11
|
+
<AccordionContent>Yes. It follows the WAI-ARIA design pattern.</AccordionContent>
|
|
12
|
+
</AccordionItem>
|
|
13
|
+
<AccordionItem value="b">
|
|
14
|
+
<AccordionTrigger>Is it themed?</AccordionTrigger>
|
|
15
|
+
<AccordionContent>Yes — entirely from the KinetixUI token contract.</AccordionContent>
|
|
16
|
+
</AccordionItem>
|
|
17
|
+
</Accordion>
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
const meta = {
|
|
21
|
+
title: "Data Display/Accordion",
|
|
22
|
+
component: Accordion,
|
|
23
|
+
args: { type: "single", collapsible: true },
|
|
24
|
+
argTypes: {
|
|
25
|
+
type: { control: "inline-radio", options: ["single", "multiple"] },
|
|
26
|
+
collapsible: { control: "boolean" },
|
|
27
|
+
},
|
|
28
|
+
parameters: {
|
|
29
|
+
layout: "centered",
|
|
30
|
+
docs: { source: { code: `<Accordion type="single" collapsible>\n <AccordionItem value="a">\n <AccordionTrigger>Is it accessible?</AccordionTrigger>\n <AccordionContent>Yes. It follows the WAI-ARIA design pattern.</AccordionContent>\n </AccordionItem>\n</Accordion>`, language: "tsx" } },
|
|
31
|
+
},
|
|
32
|
+
} satisfies Meta;
|
|
33
|
+
|
|
34
|
+
export default meta;
|
|
35
|
+
|
|
36
|
+
export const Playground: StoryObj<typeof meta> = { render: (args) => (
|
|
37
|
+
<Accordion {...args} className="w-full max-w-md">
|
|
38
|
+
<AccordionItem value="a">
|
|
39
|
+
<AccordionTrigger>Is it accessible?</AccordionTrigger>
|
|
40
|
+
<AccordionContent>Yes. It follows the WAI-ARIA design pattern.</AccordionContent>
|
|
41
|
+
</AccordionItem>
|
|
42
|
+
<AccordionItem value="b">
|
|
43
|
+
<AccordionTrigger>Is it themed?</AccordionTrigger>
|
|
44
|
+
<AccordionContent>Yes — entirely from the KinetixUI token contract.</AccordionContent>
|
|
45
|
+
</AccordionItem>
|
|
46
|
+
</Accordion>
|
|
47
|
+
) };
|
|
48
|
+
|
|
49
|
+
export const Default: StoryObj<typeof meta> = { render: () => <Demo /> };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/* AUTO-GENERATED by scripts/gen-stories.mjs — do not edit.
|
|
2
|
+
Source of truth: apps/web/src/registry/demos.tsx · controls: scripts/gen-stories.mjs CONTROLS */
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
5
|
+
import { Alert, AlertDescription, AlertTitle } from "@kinetixui/ui";
|
|
6
|
+
import { Terminal } from "lucide-react";
|
|
7
|
+
|
|
8
|
+
const Demo = () => (
|
|
9
|
+
<Alert className="max-w-md">
|
|
10
|
+
<Terminal className="size-4" />
|
|
11
|
+
<AlertTitle>Heads up!</AlertTitle>
|
|
12
|
+
<AlertDescription>You can add components to your app using the CLI.</AlertDescription>
|
|
13
|
+
</Alert>
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const meta = {
|
|
17
|
+
title: "Feedback/Alert",
|
|
18
|
+
component: Alert,
|
|
19
|
+
args: { variant: "default" },
|
|
20
|
+
argTypes: {
|
|
21
|
+
variant: { control: "inline-radio", options: ["default", "destructive", "success", "warning", "info"] },
|
|
22
|
+
},
|
|
23
|
+
parameters: {
|
|
24
|
+
layout: "centered",
|
|
25
|
+
docs: { source: { code: `<Alert>\n <Terminal className="size-4" />\n <AlertTitle>Heads up!</AlertTitle>\n <AlertDescription>You can add components to your app using the CLI.</AlertDescription>\n</Alert>`, language: "tsx" } },
|
|
26
|
+
},
|
|
27
|
+
} satisfies Meta;
|
|
28
|
+
|
|
29
|
+
export default meta;
|
|
30
|
+
|
|
31
|
+
export const Playground: StoryObj<typeof meta> = { render: (args) => (
|
|
32
|
+
<Alert {...args} className="max-w-md">
|
|
33
|
+
<AlertTitle>Heads up!</AlertTitle>
|
|
34
|
+
<AlertDescription>You can add components to your app using the CLI.</AlertDescription>
|
|
35
|
+
</Alert>
|
|
36
|
+
) };
|
|
37
|
+
|
|
38
|
+
export const Default: StoryObj<typeof meta> = { render: () => <Demo /> };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/* AUTO-GENERATED by scripts/gen-stories.mjs — do not edit.
|
|
2
|
+
Source of truth: apps/web/src/registry/demos.tsx */
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
5
|
+
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, Button } from "@kinetixui/ui";
|
|
6
|
+
|
|
7
|
+
const Demo = () => (
|
|
8
|
+
<AlertDialog>
|
|
9
|
+
<AlertDialogTrigger asChild>
|
|
10
|
+
<Button variant="Outline">Delete account</Button>
|
|
11
|
+
</AlertDialogTrigger>
|
|
12
|
+
<AlertDialogContent>
|
|
13
|
+
<AlertDialogHeader>
|
|
14
|
+
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
|
|
15
|
+
<AlertDialogDescription>This action cannot be undone.</AlertDialogDescription>
|
|
16
|
+
</AlertDialogHeader>
|
|
17
|
+
<AlertDialogFooter>
|
|
18
|
+
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
|
19
|
+
<AlertDialogAction>Continue</AlertDialogAction>
|
|
20
|
+
</AlertDialogFooter>
|
|
21
|
+
</AlertDialogContent>
|
|
22
|
+
</AlertDialog>
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const meta = {
|
|
26
|
+
title: "Overlays/AlertDialog",
|
|
27
|
+
parameters: {
|
|
28
|
+
layout: "centered",
|
|
29
|
+
docs: { source: { code: `<AlertDialog>\n <AlertDialogTrigger asChild><Button variant="Outline">Delete account</Button></AlertDialogTrigger>\n <AlertDialogContent>…</AlertDialogContent>\n</AlertDialog>`, language: "tsx" } },
|
|
30
|
+
},
|
|
31
|
+
} satisfies Meta;
|
|
32
|
+
|
|
33
|
+
export default meta;
|
|
34
|
+
|
|
35
|
+
export const Default: StoryObj<typeof meta> = { render: () => <Demo /> };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/* AUTO-GENERATED by scripts/gen-stories.mjs — do not edit.
|
|
2
|
+
Source of truth: apps/web/src/registry/demos.tsx · controls: scripts/gen-stories.mjs CONTROLS */
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
5
|
+
import { AspectRatio } from "@kinetixui/ui";
|
|
6
|
+
|
|
7
|
+
const Demo = () => (
|
|
8
|
+
<div className="w-[280px]">
|
|
9
|
+
<AspectRatio ratio={16 / 9} className="rounded-md bg-muted" />
|
|
10
|
+
</div>
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
const meta = {
|
|
14
|
+
title: "Foundations/AspectRatio",
|
|
15
|
+
component: AspectRatio,
|
|
16
|
+
args: { ratio: 1.7778 },
|
|
17
|
+
argTypes: { ratio: { control: { type: "number", step: 0.05 } } },
|
|
18
|
+
parameters: {
|
|
19
|
+
layout: "centered",
|
|
20
|
+
docs: { source: { code: `<AspectRatio ratio={16 / 9} className="bg-muted rounded-md" />`, language: "tsx" } },
|
|
21
|
+
},
|
|
22
|
+
} satisfies Meta;
|
|
23
|
+
|
|
24
|
+
export default meta;
|
|
25
|
+
|
|
26
|
+
export const Playground: StoryObj<typeof meta> = { render: (args) => (
|
|
27
|
+
<div className="w-64">
|
|
28
|
+
<AspectRatio {...args} className="rounded-md bg-muted" />
|
|
29
|
+
</div>
|
|
30
|
+
) };
|
|
31
|
+
|
|
32
|
+
export const Default: StoryObj<typeof meta> = { render: () => <Demo /> };
|