@kinetixui/ui 0.23.3 → 0.24.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 +134 -0
- package/dist/index.d.ts +212 -31
- package/dist/index.js +1466 -1118
- package/dist/kx-src-hash.json +3 -0
- package/package.json +7 -7
- package/src/components/accordion.tsx +1 -1
- package/src/components/alert-dialog.tsx +7 -5
- package/src/components/avatar.tsx +4 -1
- package/src/components/banner.tsx +9 -3
- package/src/components/breadcrumb.tsx +1 -1
- package/src/components/button.tsx +1 -1
- package/src/components/card.tsx +77 -6
- package/src/components/checkbox.tsx +42 -8
- package/src/components/collapsible.tsx +41 -1
- package/src/components/command.tsx +2 -2
- package/src/components/context-menu.tsx +21 -13
- package/src/components/dialog.tsx +5 -4
- package/src/components/direction-provider.tsx +85 -1
- package/src/components/drawer.tsx +62 -18
- package/src/components/dropdown-menu.tsx +12 -4
- package/src/components/file-upload.tsx +1 -1
- package/src/components/hover-card.tsx +1 -1
- package/src/components/inform.tsx +9 -3
- package/src/components/input-group.tsx +27 -5
- package/src/components/input-otp.tsx +48 -5
- package/src/components/input.tsx +20 -3
- package/src/components/json-viewer.tsx +1 -1
- package/src/components/menubar.tsx +21 -15
- package/src/components/modal.tsx +3 -2
- package/src/components/multi-select.tsx +14 -5
- package/src/components/native-select.tsx +10 -2
- package/src/components/navigation-bar.tsx +1 -1
- package/src/components/navigation-menu.tsx +6 -6
- package/src/components/number-input.tsx +28 -10
- package/src/components/pagination.tsx +2 -2
- package/src/components/popover.tsx +29 -5
- package/src/components/radio-group.tsx +23 -8
- package/src/components/segmented-control.tsx +20 -4
- package/src/components/select.tsx +25 -8
- package/src/components/sheet.tsx +4 -3
- package/src/components/spinner.tsx +1 -1
- package/src/components/switch.tsx +31 -8
- package/src/components/table.tsx +121 -8
- package/src/components/tabs.tsx +29 -4
- package/src/components/tag.tsx +1 -1
- package/src/components/textarea.tsx +20 -3
- package/src/components/tooltip.tsx +14 -4
- package/src/components/tour.tsx +22 -7
- package/src/components/tree-view.tsx +1 -1
- package/src/components-behavior.test.tsx +74 -0
- package/src/components-direction-portal.test.tsx +482 -0
- package/src/components-form.test.tsx +435 -0
- package/src/components-icons.test.tsx +197 -0
- package/src/components-keyboard.test.tsx +542 -2
- package/src/components-overlay.test.tsx +381 -0
- package/src/components-rtl.test.tsx +258 -0
- package/src/components-selection.test.tsx +207 -0
- package/src/components-table-scroll.test.tsx +363 -0
- package/src/index.ts +2 -0
- package/src/navigation-menu-focus.test.tsx +35 -0
- package/src/stories/Card.stories.tsx +81 -1
- package/src/stories/Checkbox.stories.tsx +23 -1
- package/src/stories/ContextMenu.stories.tsx +1 -1
- package/src/stories/Input.stories.tsx +34 -0
- package/src/stories/InputGroup.stories.tsx +96 -2
- package/src/stories/InputOTP.stories.tsx +24 -1
- package/src/stories/MultiSelect.stories.tsx +25 -1
- package/src/stories/NativeSelect.stories.tsx +24 -1
- package/src/stories/NumberInput.stories.tsx +20 -1
- package/src/stories/Progress.stories.tsx +1 -1
- package/src/stories/RadioGroup.stories.tsx +33 -1
- package/src/stories/SegmentedControl.stories.tsx +21 -1
- package/src/stories/Select.stories.tsx +28 -1
- package/src/stories/Slider.stories.tsx +1 -1
- package/src/stories/Switch.stories.tsx +37 -1
- package/src/stories/Table.stories.tsx +35 -1
- package/src/stories/Tabs.stories.tsx +27 -2
- package/src/stories/Textarea.stories.tsx +34 -0
- package/src/usage/icons.tsx +60 -0
- package/tailwind.config.ts +118 -22
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Source of truth: apps/web/src/registry/demos.tsx */
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
5
|
-
import { Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Input } from "@kinetixui/ui";
|
|
5
|
+
import { Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Input, Label, Switch } from "@kinetixui/ui";
|
|
6
6
|
|
|
7
7
|
const Demo = () => (
|
|
8
8
|
<Card className="w-[320px]">
|
|
@@ -31,3 +31,83 @@ const meta = {
|
|
|
31
31
|
export default meta;
|
|
32
32
|
|
|
33
33
|
export const Default: StoryObj<typeof meta> = { render: () => <Demo /> };
|
|
34
|
+
|
|
35
|
+
export const Grouped: StoryObj<typeof meta> = { render: () => (
|
|
36
|
+
<section aria-labelledby="card-grouped-heading" className="w-[36rem] max-w-full rounded-xl bg-surface-grouped p-4 sm:p-6">
|
|
37
|
+
<h2 id="card-grouped-heading" className="mb-4 text-title-md text-foreground">Workspace settings</h2>
|
|
38
|
+
<div className="grid gap-4">
|
|
39
|
+
<Card>
|
|
40
|
+
<CardHeader>
|
|
41
|
+
<CardTitle>Notifications</CardTitle>
|
|
42
|
+
<CardDescription>Choose what reaches your inbox.</CardDescription>
|
|
43
|
+
</CardHeader>
|
|
44
|
+
<CardContent>
|
|
45
|
+
<div className="flex items-center justify-between gap-4">
|
|
46
|
+
<Label htmlFor="card-grouped-digest">Weekly digest</Label>
|
|
47
|
+
<Switch id="card-grouped-digest" defaultChecked />
|
|
48
|
+
</div>
|
|
49
|
+
</CardContent>
|
|
50
|
+
</Card>
|
|
51
|
+
<Card>
|
|
52
|
+
<CardHeader>
|
|
53
|
+
<CardTitle>Danger zone</CardTitle>
|
|
54
|
+
<CardDescription>Archiving hides the workspace from every member.</CardDescription>
|
|
55
|
+
</CardHeader>
|
|
56
|
+
<CardFooter>
|
|
57
|
+
<Button variant="Outline" size="sm">Archive workspace</Button>
|
|
58
|
+
</CardFooter>
|
|
59
|
+
</Card>
|
|
60
|
+
</div>
|
|
61
|
+
</section>
|
|
62
|
+
) };
|
|
63
|
+
|
|
64
|
+
export const Dense: StoryObj<typeof meta> = { render: () => (
|
|
65
|
+
<div className="grid w-[40rem] max-w-full gap-3 sm:grid-cols-3">
|
|
66
|
+
{[
|
|
67
|
+
["Revenue", "$45,231", "+12.5% this month"],
|
|
68
|
+
["Active users", "2,420", "+8.1% this month"],
|
|
69
|
+
["Churn", "1.2%", "−0.3% this month"],
|
|
70
|
+
].map(([label, value, change]) => (
|
|
71
|
+
<Card key={label}>
|
|
72
|
+
<CardContent className="flex flex-col gap-1 p-4">
|
|
73
|
+
<span className="text-body-sm text-muted-foreground">{label}</span>
|
|
74
|
+
<span className="text-headline-sm font-medium tabular-nums">{value}</span>
|
|
75
|
+
<span className="text-body-sm text-muted-foreground">{change}</span>
|
|
76
|
+
</CardContent>
|
|
77
|
+
</Card>
|
|
78
|
+
))}
|
|
79
|
+
</div>
|
|
80
|
+
) };
|
|
81
|
+
|
|
82
|
+
export const Interactive: StoryObj<typeof meta> = { render: () => (
|
|
83
|
+
<div className="grid w-[40rem] max-w-full gap-4 sm:grid-cols-2">
|
|
84
|
+
<Card asChild>
|
|
85
|
+
<a href="#reports-q3">
|
|
86
|
+
<CardHeader>
|
|
87
|
+
<CardTitle>Q3 report</CardTitle>
|
|
88
|
+
<CardDescription>Revenue, churn and cohort retention.</CardDescription>
|
|
89
|
+
</CardHeader>
|
|
90
|
+
</a>
|
|
91
|
+
</Card>
|
|
92
|
+
<Card asChild>
|
|
93
|
+
<a href="#reports-q4" aria-current="page">
|
|
94
|
+
<CardHeader>
|
|
95
|
+
<CardTitle>Q4 report</CardTitle>
|
|
96
|
+
<CardDescription>The report you are viewing.</CardDescription>
|
|
97
|
+
</CardHeader>
|
|
98
|
+
</a>
|
|
99
|
+
</Card>
|
|
100
|
+
<Card asChild>
|
|
101
|
+
<button type="button" aria-pressed="true" className="p-6">
|
|
102
|
+
<span className="block font-semibold leading-none tracking-tight">Daily backups</span>
|
|
103
|
+
<span className="mt-1.5 block text-sm text-muted-foreground">Included in this plan.</span>
|
|
104
|
+
</button>
|
|
105
|
+
</Card>
|
|
106
|
+
<Card asChild>
|
|
107
|
+
<button type="button" aria-pressed="false" className="p-6">
|
|
108
|
+
<span className="block font-semibold leading-none tracking-tight">Audit log</span>
|
|
109
|
+
<span className="mt-1.5 block text-sm text-muted-foreground">Add to this plan.</span>
|
|
110
|
+
</button>
|
|
111
|
+
</Card>
|
|
112
|
+
</div>
|
|
113
|
+
) };
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Source of truth: apps/web/src/registry/demos.tsx · controls: scripts/gen-stories.mjs CONTROLS */
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
5
|
-
import { Checkbox, Label } from "@kinetixui/ui";
|
|
5
|
+
import { Card, CardContent, Checkbox, Label, Select } from "@kinetixui/ui";
|
|
6
6
|
|
|
7
7
|
const Demo = () => (
|
|
8
8
|
<div className="flex items-center gap-2">
|
|
@@ -27,3 +27,25 @@ export default meta;
|
|
|
27
27
|
export const Playground: StoryObj<typeof meta> = { render: (args) => <Checkbox aria-label="Accept terms" {...args} /> };
|
|
28
28
|
|
|
29
29
|
export const Default: StoryObj<typeof meta> = { render: () => <Demo /> };
|
|
30
|
+
|
|
31
|
+
export const States: StoryObj<typeof meta> = { render: () => (
|
|
32
|
+
<Card className="w-[22rem] max-w-full">
|
|
33
|
+
<CardContent className="grid gap-4 p-6">
|
|
34
|
+
{([
|
|
35
|
+
["unchecked", "Email me about product updates", {}],
|
|
36
|
+
["checked", "Email me about security alerts", { defaultChecked: true }],
|
|
37
|
+
["mixed", "Select all regions", { checked: "indeterminate" }],
|
|
38
|
+
["invalid", "I accept the terms of service", { "aria-invalid": true, "aria-describedby": "cb-terms-error" }],
|
|
39
|
+
["invalid-checked", "I accept the data processing terms", { "aria-invalid": true, defaultChecked: true }],
|
|
40
|
+
["disabled", "Archive automatically (admins only)", { disabled: true }],
|
|
41
|
+
["disabled-checked", "Keep an audit log (always on)", { disabled: true, defaultChecked: true }],
|
|
42
|
+
] as const).map(([key, label, props]) => (
|
|
43
|
+
<div key={key} className="flex items-center gap-3">
|
|
44
|
+
<Checkbox id={`cb-${key}`} data-kx-case={key} {...props} />
|
|
45
|
+
<Label htmlFor={`cb-${key}`}>{label}</Label>
|
|
46
|
+
</div>
|
|
47
|
+
))}
|
|
48
|
+
<p id="cb-terms-error" className="text-body-sm text-destructive">Accept the terms to continue.</p>
|
|
49
|
+
</CardContent>
|
|
50
|
+
</Card>
|
|
51
|
+
) };
|
|
@@ -6,7 +6,7 @@ import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuSeparator,
|
|
|
6
6
|
|
|
7
7
|
const Demo = () => (
|
|
8
8
|
<ContextMenu>
|
|
9
|
-
<ContextMenuTrigger className="flex h-32 w-64 items-center justify-center rounded-md border border-dashed text-sm">
|
|
9
|
+
<ContextMenuTrigger className="flex h-32 w-full max-w-64 items-center justify-center rounded-md border border-dashed text-sm">
|
|
10
10
|
Right-click here
|
|
11
11
|
</ContextMenuTrigger>
|
|
12
12
|
<ContextMenuContent className="w-52">
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
2
|
import { Input } from "../components/input";
|
|
3
|
+
import { Card, CardContent } from "../components/card";
|
|
4
|
+
import { Label } from "../components/label";
|
|
3
5
|
|
|
4
6
|
const STATES = ["Default", "Focus", "Error", "Disabled"] as const;
|
|
5
7
|
|
|
@@ -48,6 +50,38 @@ export const States: Story = {
|
|
|
48
50
|
};
|
|
49
51
|
|
|
50
52
|
/** reproduces the full Figma field: label + control + helper, colour tracks state */
|
|
53
|
+
/**
|
|
54
|
+
* The text-entry state contract (TOKENS.md, "Text entry and navigation") as a real form produces it: empty
|
|
55
|
+
* with a placeholder, filled, invalid with its error text, read-only and disabled, side by side in a raised
|
|
56
|
+
* Card. Real states, not the `state` prop's pinned ones — `check:entry-visual` drives hover and focus on
|
|
57
|
+
* these with a real pointer and keyboard, and both axe passes read them.
|
|
58
|
+
*/
|
|
59
|
+
export const EntryStates: Story = {
|
|
60
|
+
render: () => (
|
|
61
|
+
<Card className="w-[22rem] max-w-full">
|
|
62
|
+
<CardContent className="grid gap-5 p-6">
|
|
63
|
+
{([
|
|
64
|
+
["rest", "Display name", { placeholder: "e.g. Ada Lovelace" }],
|
|
65
|
+
["filled", "Email", { type: "email", defaultValue: "ada@example.com" }],
|
|
66
|
+
["invalid", "Username", { defaultValue: "ada lovelace", "aria-invalid": true, "aria-describedby": "in-invalid-error" }],
|
|
67
|
+
["readonly", "Account ID", { readOnly: true, defaultValue: "acct_7Q2X9" }],
|
|
68
|
+
["disabled", "Organisation", { disabled: true, defaultValue: "Managed by your admin" }],
|
|
69
|
+
] as const).map(([key, label, props]) => (
|
|
70
|
+
<div key={key} className="grid gap-2">
|
|
71
|
+
<Label htmlFor={`in-${key}`}>{label}</Label>
|
|
72
|
+
<Input id={`in-${key}`} data-kx-case={key} {...props} />
|
|
73
|
+
{key === "invalid" ? (
|
|
74
|
+
<p id="in-invalid-error" className="text-body-sm text-destructive">
|
|
75
|
+
Use letters, numbers and dashes only.
|
|
76
|
+
</p>
|
|
77
|
+
) : null}
|
|
78
|
+
</div>
|
|
79
|
+
))}
|
|
80
|
+
</CardContent>
|
|
81
|
+
</Card>
|
|
82
|
+
),
|
|
83
|
+
};
|
|
84
|
+
|
|
51
85
|
export const WithFieldChrome: Story = {
|
|
52
86
|
render: (args) => (
|
|
53
87
|
<div className="flex flex-col gap-6">
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Source of truth: apps/web/src/registry/demos.tsx */
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
5
|
-
import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText } from "@kinetixui/ui";
|
|
5
|
+
import { Card, CardContent, CardHeader, CardTitle, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputOTP, InputOTPGroup, InputOTPSlot, Label, MultiSelect, NumberInput, PasswordInput } from "@kinetixui/ui";
|
|
6
6
|
import { Search } from "lucide-react";
|
|
7
7
|
|
|
8
8
|
const Demo = () => (
|
|
@@ -26,7 +26,7 @@ const Demo = () => (
|
|
|
26
26
|
const meta = {
|
|
27
27
|
title: "Form Inputs/InputGroup",
|
|
28
28
|
parameters: {
|
|
29
|
-
layout: "
|
|
29
|
+
layout: "padded",
|
|
30
30
|
docs: { source: { code: "<InputGroup>\\n <InputGroupText>https://</InputGroupText>\\n <InputGroupInput placeholder=\"kinetixui.com\" />\\n</InputGroup>\\n\\n<InputGroup>\\n <InputGroupAddon align=\"start\">$</InputGroupAddon>\\n <InputGroupInput placeholder=\"0.00\" />\\n <InputGroupText>USD</InputGroupText>\\n</InputGroup>", language: "tsx" } },
|
|
31
31
|
},
|
|
32
32
|
} satisfies Meta;
|
|
@@ -34,3 +34,97 @@ const meta = {
|
|
|
34
34
|
export default meta;
|
|
35
35
|
|
|
36
36
|
export const Default: StoryObj<typeof meta> = { render: () => <Demo /> };
|
|
37
|
+
|
|
38
|
+
export const EntryStates: StoryObj<typeof meta> = { render: () => (
|
|
39
|
+
<Card className="w-[22rem] max-w-full">
|
|
40
|
+
<CardContent className="grid gap-5 p-6">
|
|
41
|
+
{([
|
|
42
|
+
["rest", "Website", {}],
|
|
43
|
+
["filled", "Docs site", { defaultValue: "docs.kinetixui.com" }],
|
|
44
|
+
["invalid", "Status page", { defaultValue: "status page", "aria-invalid": true, "aria-describedby": "ig-invalid-error" }],
|
|
45
|
+
["readonly", "Workspace URL", { defaultValue: "acme.kinetixui.com", readOnly: true }],
|
|
46
|
+
["disabled", "Custom domain (Pro)", { defaultValue: "app.acme.com", disabled: true }],
|
|
47
|
+
] as const).map(([key, label, props]) => (
|
|
48
|
+
<div key={key} className="grid gap-2">
|
|
49
|
+
<Label htmlFor={`ig-${key}`}>{label}</Label>
|
|
50
|
+
<InputGroup data-kx-case={key}>
|
|
51
|
+
<InputGroupText>https://</InputGroupText>
|
|
52
|
+
<InputGroupInput id={`ig-${key}`} placeholder="example.com" {...props} />
|
|
53
|
+
</InputGroup>
|
|
54
|
+
{key === "invalid" ? <p id="ig-invalid-error" className="text-body-sm text-destructive">Use a domain, like status.example.com.</p> : null}
|
|
55
|
+
</div>
|
|
56
|
+
))}
|
|
57
|
+
<div className="grid gap-2">
|
|
58
|
+
<Label htmlFor="ig-button">Invite link</Label>
|
|
59
|
+
<InputGroup data-kx-case="with-button">
|
|
60
|
+
<InputGroupInput id="ig-button" defaultValue="kx.link/j/7Q2X9" readOnly />
|
|
61
|
+
<InputGroupButton data-kx-part="button">Copy</InputGroupButton>
|
|
62
|
+
</InputGroup>
|
|
63
|
+
</div>
|
|
64
|
+
</CardContent>
|
|
65
|
+
</Card>
|
|
66
|
+
) };
|
|
67
|
+
|
|
68
|
+
export const Compositions: StoryObj<typeof meta> = { render: () => (
|
|
69
|
+
<div className="grid w-[40rem] max-w-[calc(100vw-4rem)] gap-6">
|
|
70
|
+
<Card data-kx-composition="account">
|
|
71
|
+
<CardHeader><CardTitle>Create your account</CardTitle></CardHeader>
|
|
72
|
+
<CardContent className="grid gap-4">
|
|
73
|
+
<div className="grid gap-2">
|
|
74
|
+
<Label htmlFor="cmp-site">Website</Label>
|
|
75
|
+
<InputGroup>
|
|
76
|
+
<InputGroupText>https://</InputGroupText>
|
|
77
|
+
<InputGroupInput id="cmp-site" placeholder="example.com" />
|
|
78
|
+
</InputGroup>
|
|
79
|
+
</div>
|
|
80
|
+
<div className="grid gap-2">
|
|
81
|
+
<Label htmlFor="cmp-password">Password</Label>
|
|
82
|
+
<PasswordInput id="cmp-password" />
|
|
83
|
+
</div>
|
|
84
|
+
<div className="grid gap-2">
|
|
85
|
+
<Label htmlFor="cmp-code">Verification code</Label>
|
|
86
|
+
<InputOTP id="cmp-code" maxLength={6} aria-describedby="cmp-code-hint">
|
|
87
|
+
<InputOTPGroup>
|
|
88
|
+
{[0, 1, 2, 3, 4, 5].map((i) => <InputOTPSlot key={i} index={i} />)}
|
|
89
|
+
</InputOTPGroup>
|
|
90
|
+
</InputOTP>
|
|
91
|
+
<p id="cmp-code-hint" className="text-body-sm text-muted-foreground">We sent six digits to ada@example.com.</p>
|
|
92
|
+
</div>
|
|
93
|
+
</CardContent>
|
|
94
|
+
</Card>
|
|
95
|
+
<div data-kx-composition="filter" role="search" aria-label="Filter orders" className="flex flex-wrap items-end gap-3 rounded-md bg-surface-grouped p-3">
|
|
96
|
+
<div className="grid min-w-[12rem] flex-1 gap-1.5">
|
|
97
|
+
<Label htmlFor="cmp-search">Search</Label>
|
|
98
|
+
<InputGroup>
|
|
99
|
+
<InputGroupAddon><Search aria-hidden="true" /></InputGroupAddon>
|
|
100
|
+
<InputGroupInput id="cmp-search" placeholder="Order, customer or SKU" />
|
|
101
|
+
</InputGroup>
|
|
102
|
+
</div>
|
|
103
|
+
<div className="grid min-w-[12rem] flex-1 gap-1.5">
|
|
104
|
+
<Label id="cmp-status-label">Status</Label>
|
|
105
|
+
<MultiSelect aria-labelledby="cmp-status-label" defaultValue={["paid", "shipped"]} options={[{ value: "paid", label: "Paid" }, { value: "shipped", label: "Shipped" }, { value: "refunded", label: "Refunded" }]} />
|
|
106
|
+
</div>
|
|
107
|
+
<div className="grid gap-1.5">
|
|
108
|
+
<Label id="cmp-min-label">Min. items</Label>
|
|
109
|
+
<NumberInput aria-labelledby="cmp-min-label" defaultValue={1} min={0} className="w-32" />
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
<Card data-kx-composition="settings">
|
|
113
|
+
<CardHeader><CardTitle>Workspace limits</CardTitle></CardHeader>
|
|
114
|
+
<CardContent className="grid gap-4 sm:grid-cols-2">
|
|
115
|
+
<div className="grid gap-2">
|
|
116
|
+
<Label id="cmp-seats-label">Seats</Label>
|
|
117
|
+
<NumberInput aria-labelledby="cmp-seats-label" defaultValue={12} min={1} max={500} />
|
|
118
|
+
</div>
|
|
119
|
+
<div className="grid gap-2">
|
|
120
|
+
<Label htmlFor="cmp-domain">Sign-in domain</Label>
|
|
121
|
+
<InputGroup>
|
|
122
|
+
<InputGroupText>@</InputGroupText>
|
|
123
|
+
<InputGroupInput id="cmp-domain" defaultValue="acme.com" aria-invalid aria-describedby="cmp-domain-error" />
|
|
124
|
+
</InputGroup>
|
|
125
|
+
<p id="cmp-domain-error" className="text-body-sm text-destructive">Verify this domain before limiting sign-in to it.</p>
|
|
126
|
+
</div>
|
|
127
|
+
</CardContent>
|
|
128
|
+
</Card>
|
|
129
|
+
</div>
|
|
130
|
+
) };
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Source of truth: apps/web/src/registry/demos.tsx */
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
5
|
-
import { InputOTP, InputOTPGroup, InputOTPSlot } from "@kinetixui/ui";
|
|
5
|
+
import { Card, CardContent, InputOTP, InputOTPGroup, InputOTPSlot, Label } from "@kinetixui/ui";
|
|
6
6
|
|
|
7
7
|
const Demo = () => (
|
|
8
8
|
<InputOTP maxLength={6} aria-label="One-time code">
|
|
@@ -25,3 +25,26 @@ const meta = {
|
|
|
25
25
|
export default meta;
|
|
26
26
|
|
|
27
27
|
export const Default: StoryObj<typeof meta> = { render: () => <Demo /> };
|
|
28
|
+
|
|
29
|
+
export const EntryStates: StoryObj<typeof meta> = { render: () => (
|
|
30
|
+
<Card className="w-[22rem] max-w-full">
|
|
31
|
+
<CardContent className="grid gap-5 p-6">
|
|
32
|
+
{([
|
|
33
|
+
["rest", "Verification code", {}],
|
|
34
|
+
["filled", "Backup code", { defaultValue: "482913" }],
|
|
35
|
+
["invalid", "Authenticator code", { defaultValue: "000000", "aria-invalid": true, "aria-describedby": "otp-invalid-error" }],
|
|
36
|
+
["disabled", "Recovery code (used)", { defaultValue: "771204", disabled: true }],
|
|
37
|
+
] as const).map(([key, label, props]) => (
|
|
38
|
+
<div key={key} className="grid gap-2">
|
|
39
|
+
<Label htmlFor={`otp-${key}`}>{label}</Label>
|
|
40
|
+
<InputOTP id={`otp-${key}`} data-kx-case={key} maxLength={6} {...props}>
|
|
41
|
+
<InputOTPGroup>
|
|
42
|
+
{[0, 1, 2, 3, 4, 5].map((i) => <InputOTPSlot key={i} index={i} />)}
|
|
43
|
+
</InputOTPGroup>
|
|
44
|
+
</InputOTP>
|
|
45
|
+
{key === "invalid" ? <p id="otp-invalid-error" className="text-body-sm text-destructive">That code has expired. Request a new one.</p> : null}
|
|
46
|
+
</div>
|
|
47
|
+
))}
|
|
48
|
+
</CardContent>
|
|
49
|
+
</Card>
|
|
50
|
+
) };
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Source of truth: apps/web/src/registry/demos.tsx */
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
5
|
-
import { MultiSelect, Select } from "@kinetixui/ui";
|
|
5
|
+
import { Card, CardContent, Label, MultiSelect, Select } from "@kinetixui/ui";
|
|
6
6
|
|
|
7
7
|
const Demo = () => {
|
|
8
8
|
const [value, setValue] = React.useState(["react", "swiftui"]);
|
|
@@ -34,3 +34,27 @@ const meta = {
|
|
|
34
34
|
export default meta;
|
|
35
35
|
|
|
36
36
|
export const Default: StoryObj<typeof meta> = { render: () => <Demo /> };
|
|
37
|
+
|
|
38
|
+
export const EntryStates: StoryObj<typeof meta> = { render: () => (
|
|
39
|
+
<Card className="w-[22rem] max-w-full">
|
|
40
|
+
<CardContent className="grid gap-5 p-6">
|
|
41
|
+
{([
|
|
42
|
+
["rest", "Labels", {}],
|
|
43
|
+
["filled", "Teams", { defaultValue: ["design", "web"] }],
|
|
44
|
+
["invalid", "Reviewers", { "aria-invalid": true, "aria-describedby": "ms-invalid-error" }],
|
|
45
|
+
] as const).map(([key, label, props]) => (
|
|
46
|
+
<div key={key} className="grid gap-2">
|
|
47
|
+
<Label id={`ms-${key}-label`}>{label}</Label>
|
|
48
|
+
<MultiSelect
|
|
49
|
+
data-kx-case={key}
|
|
50
|
+
aria-labelledby={`ms-${key}-label`}
|
|
51
|
+
placeholder="Choose some"
|
|
52
|
+
options={[{ value: "design", label: "Design" }, { value: "web", label: "Web" }, { value: "mobile", label: "Mobile" }]}
|
|
53
|
+
{...props}
|
|
54
|
+
/>
|
|
55
|
+
{key === "invalid" ? <p id="ms-invalid-error" className="text-body-sm text-destructive">Pick at least one reviewer.</p> : null}
|
|
56
|
+
</div>
|
|
57
|
+
))}
|
|
58
|
+
</CardContent>
|
|
59
|
+
</Card>
|
|
60
|
+
) };
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Source of truth: apps/web/src/registry/demos.tsx */
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
5
|
-
import { NativeSelect, NativeSelectOptGroup, NativeSelectOption, Select } from "@kinetixui/ui";
|
|
5
|
+
import { Card, CardContent, Label, NativeSelect, NativeSelectOptGroup, NativeSelectOption, Select } from "@kinetixui/ui";
|
|
6
6
|
|
|
7
7
|
const Demo = () => (
|
|
8
8
|
<NativeSelect aria-label="Fruit" className="max-w-xs" defaultValue="">
|
|
@@ -31,3 +31,26 @@ const meta = {
|
|
|
31
31
|
export default meta;
|
|
32
32
|
|
|
33
33
|
export const Default: StoryObj<typeof meta> = { render: () => <Demo /> };
|
|
34
|
+
|
|
35
|
+
export const EntryStates: StoryObj<typeof meta> = { render: () => (
|
|
36
|
+
<Card className="w-[22rem] max-w-full">
|
|
37
|
+
<CardContent className="grid gap-5 p-6">
|
|
38
|
+
{([
|
|
39
|
+
["rest", "Country", { defaultValue: "" }],
|
|
40
|
+
["filled", "Time zone", { defaultValue: "utc" }],
|
|
41
|
+
["invalid", "Plan", { defaultValue: "", "aria-invalid": true, "aria-describedby": "nsel-invalid-error" }],
|
|
42
|
+
["disabled", "Data region (locked)", { defaultValue: "utc", disabled: true }],
|
|
43
|
+
] as const).map(([key, label, props]) => (
|
|
44
|
+
<div key={key} className="grid gap-2">
|
|
45
|
+
<Label htmlFor={`nsel-${key}`}>{label}</Label>
|
|
46
|
+
<NativeSelect id={`nsel-${key}`} data-kx-case={key} {...props}>
|
|
47
|
+
<NativeSelectOption value="" disabled>Choose one</NativeSelectOption>
|
|
48
|
+
<NativeSelectOption value="utc">UTC</NativeSelectOption>
|
|
49
|
+
<NativeSelectOption value="cet">Central European Time</NativeSelectOption>
|
|
50
|
+
</NativeSelect>
|
|
51
|
+
{key === "invalid" ? <p id="nsel-invalid-error" className="text-body-sm text-destructive">Choose a plan to continue.</p> : null}
|
|
52
|
+
</div>
|
|
53
|
+
))}
|
|
54
|
+
</CardContent>
|
|
55
|
+
</Card>
|
|
56
|
+
) };
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Source of truth: apps/web/src/registry/demos.tsx · controls: scripts/gen-stories.mjs CONTROLS */
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
5
|
-
import { NumberInput } from "@kinetixui/ui";
|
|
5
|
+
import { Card, CardContent, Label, NumberInput } from "@kinetixui/ui";
|
|
6
6
|
|
|
7
7
|
const Demo = () => {
|
|
8
8
|
const [v, setV] = React.useState(2);
|
|
@@ -27,3 +27,22 @@ export default meta;
|
|
|
27
27
|
export const Playground: StoryObj<typeof meta> = { render: (args) => <NumberInput aria-label="Quantity" {...args} className="w-32" /> };
|
|
28
28
|
|
|
29
29
|
export const Default: StoryObj<typeof meta> = { render: () => <Demo /> };
|
|
30
|
+
|
|
31
|
+
export const EntryStates: StoryObj<typeof meta> = { render: () => (
|
|
32
|
+
<Card className="w-[22rem] max-w-full">
|
|
33
|
+
<CardContent className="grid gap-5 p-6">
|
|
34
|
+
{([
|
|
35
|
+
["rest", "Seats", { defaultValue: 4, min: 1 }],
|
|
36
|
+
["invalid", "Guests", { defaultValue: 12, "aria-invalid": true, "aria-describedby": "ni-invalid-error" }],
|
|
37
|
+
["readonly", "Plan seats", { defaultValue: 25, readOnly: true }],
|
|
38
|
+
["disabled", "Admins (locked)", { defaultValue: 2, disabled: true }],
|
|
39
|
+
] as const).map(([key, label, props]) => (
|
|
40
|
+
<div key={key} className="grid gap-2">
|
|
41
|
+
<Label id={`ni-${key}-label`}>{label}</Label>
|
|
42
|
+
<NumberInput data-kx-case={key} aria-labelledby={`ni-${key}-label`} {...props} />
|
|
43
|
+
{key === "invalid" ? <p id="ni-invalid-error" className="text-body-sm text-destructive">This room holds 10 people at most.</p> : null}
|
|
44
|
+
</div>
|
|
45
|
+
))}
|
|
46
|
+
</CardContent>
|
|
47
|
+
</Card>
|
|
48
|
+
) };
|
|
@@ -19,7 +19,7 @@ const meta = {
|
|
|
19
19
|
args: { value: 66 },
|
|
20
20
|
argTypes: { value: { control: { type: "range", min: 0, max: 100 } } },
|
|
21
21
|
parameters: {
|
|
22
|
-
layout: "
|
|
22
|
+
layout: "padded",
|
|
23
23
|
docs: { source: { code: "const [value, setValue] = React.useState(13)\\n// ...\\n<Progress value={value} aria-label=\\\"Upload progress\\\" />", language: "tsx" } },
|
|
24
24
|
},
|
|
25
25
|
} satisfies Meta;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Source of truth: apps/web/src/registry/demos.tsx · controls: scripts/gen-stories.mjs CONTROLS */
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
5
|
-
import { Label, RadioGroup, RadioGroupItem } from "@kinetixui/ui";
|
|
5
|
+
import { Card, CardContent, Label, RadioGroup, RadioGroupItem } from "@kinetixui/ui";
|
|
6
6
|
|
|
7
7
|
const Demo = () => (
|
|
8
8
|
<RadioGroup defaultValue="comfortable">
|
|
@@ -43,3 +43,35 @@ export const Playground: StoryObj<typeof meta> = { render: (args) => (
|
|
|
43
43
|
) };
|
|
44
44
|
|
|
45
45
|
export const Default: StoryObj<typeof meta> = { render: () => <Demo /> };
|
|
46
|
+
|
|
47
|
+
export const States: StoryObj<typeof meta> = { render: () => (
|
|
48
|
+
<Card className="w-[22rem] max-w-full">
|
|
49
|
+
<CardContent className="grid gap-6 p-6">
|
|
50
|
+
<RadioGroup defaultValue="weekly" aria-label="Digest frequency">
|
|
51
|
+
{([["daily", "Daily"], ["weekly", "Weekly"], ["never", "Never"]] as const).map(([v, label]) => (
|
|
52
|
+
<div key={v} className="flex items-center gap-3">
|
|
53
|
+
<RadioGroupItem value={v} id={`rg-${v}`} data-kx-case={v === "weekly" ? "checked" : v === "daily" ? "unchecked" : undefined} />
|
|
54
|
+
<Label htmlFor={`rg-${v}`}>{label}</Label>
|
|
55
|
+
</div>
|
|
56
|
+
))}
|
|
57
|
+
</RadioGroup>
|
|
58
|
+
<RadioGroup aria-label="Billing plan" aria-describedby="rg-plan-error">
|
|
59
|
+
{([["starter", "Starter"], ["team", "Team"]] as const).map(([v, label]) => (
|
|
60
|
+
<div key={v} className="flex items-center gap-3">
|
|
61
|
+
<RadioGroupItem value={v} id={`rg-${v}`} aria-invalid data-kx-case={v === "starter" ? "invalid" : undefined} />
|
|
62
|
+
<Label htmlFor={`rg-${v}`}>{label}</Label>
|
|
63
|
+
</div>
|
|
64
|
+
))}
|
|
65
|
+
<p id="rg-plan-error" className="text-body-sm text-destructive">Choose a plan to continue.</p>
|
|
66
|
+
</RadioGroup>
|
|
67
|
+
<RadioGroup defaultValue="eu" disabled aria-label="Data region (locked)">
|
|
68
|
+
{([["eu", "EU"], ["us", "US"]] as const).map(([v, label]) => (
|
|
69
|
+
<div key={v} className="flex items-center gap-3">
|
|
70
|
+
<RadioGroupItem value={v} id={`rg-${v}`} data-kx-case={v === "us" ? "disabled" : "disabled-checked"} />
|
|
71
|
+
<Label htmlFor={`rg-${v}`}>{label}</Label>
|
|
72
|
+
</div>
|
|
73
|
+
))}
|
|
74
|
+
</RadioGroup>
|
|
75
|
+
</CardContent>
|
|
76
|
+
</Card>
|
|
77
|
+
) };
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Source of truth: apps/web/src/registry/demos.tsx */
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
5
|
-
import { List, SegmentedControl, SegmentedControlItem } from "@kinetixui/ui";
|
|
5
|
+
import { Card, CardContent, List, SegmentedControl, SegmentedControlItem } from "@kinetixui/ui";
|
|
6
6
|
|
|
7
7
|
const Demo = () => (
|
|
8
8
|
<SegmentedControl defaultValue="grid" className="w-full max-w-xs">
|
|
@@ -23,3 +23,23 @@ const meta = {
|
|
|
23
23
|
export default meta;
|
|
24
24
|
|
|
25
25
|
export const Default: StoryObj<typeof meta> = { render: () => <Demo /> };
|
|
26
|
+
|
|
27
|
+
export const States: StoryObj<typeof meta> = { render: () => (
|
|
28
|
+
<div className="grid w-[24rem] max-w-full gap-6">
|
|
29
|
+
<SegmentedControl defaultValue="week" aria-label="Range" data-kx-case="page">
|
|
30
|
+
<SegmentedControlItem value="day">Day</SegmentedControlItem>
|
|
31
|
+
<SegmentedControlItem value="week">Week</SegmentedControlItem>
|
|
32
|
+
<SegmentedControlItem value="month">Month</SegmentedControlItem>
|
|
33
|
+
<SegmentedControlItem value="year" disabled>Year</SegmentedControlItem>
|
|
34
|
+
</SegmentedControl>
|
|
35
|
+
<Card>
|
|
36
|
+
<CardContent className="p-4">
|
|
37
|
+
<SegmentedControl defaultValue="grid" aria-label="Layout" data-kx-case="card" className="w-full">
|
|
38
|
+
<SegmentedControlItem value="list">List</SegmentedControlItem>
|
|
39
|
+
<SegmentedControlItem value="grid">Grid</SegmentedControlItem>
|
|
40
|
+
<SegmentedControlItem value="board">Board</SegmentedControlItem>
|
|
41
|
+
</SegmentedControl>
|
|
42
|
+
</CardContent>
|
|
43
|
+
</Card>
|
|
44
|
+
</div>
|
|
45
|
+
) };
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Source of truth: apps/web/src/registry/demos.tsx · controls: scripts/gen-stories.mjs CONTROLS */
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
5
|
-
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@kinetixui/ui";
|
|
5
|
+
import { Card, CardContent, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@kinetixui/ui";
|
|
6
6
|
|
|
7
7
|
const Demo = () => (
|
|
8
8
|
<Select>
|
|
@@ -44,3 +44,30 @@ export const Playground: StoryObj<typeof meta> = { render: (args) => (
|
|
|
44
44
|
) };
|
|
45
45
|
|
|
46
46
|
export const Default: StoryObj<typeof meta> = { render: () => <Demo /> };
|
|
47
|
+
|
|
48
|
+
export const EntryStates: StoryObj<typeof meta> = { render: () => (
|
|
49
|
+
<Card className="w-[22rem] max-w-full">
|
|
50
|
+
<CardContent className="grid gap-5 p-6">
|
|
51
|
+
{([
|
|
52
|
+
["rest", "Country", {}, {}],
|
|
53
|
+
["filled", "Time zone", { defaultValue: "utc" }, {}],
|
|
54
|
+
["invalid", "Plan", {}, { "aria-invalid": true, "aria-describedby": "sel-invalid-error" }],
|
|
55
|
+
["disabled", "Data region (locked)", { defaultValue: "utc", disabled: true }, {}],
|
|
56
|
+
] as const).map(([key, label, root, trigger]) => (
|
|
57
|
+
<div key={key} className="grid gap-2">
|
|
58
|
+
<Label htmlFor={`sel-${key}`}>{label}</Label>
|
|
59
|
+
<Select {...root}>
|
|
60
|
+
<SelectTrigger id={`sel-${key}`} data-kx-case={key} {...trigger}>
|
|
61
|
+
<SelectValue placeholder="Choose one" />
|
|
62
|
+
</SelectTrigger>
|
|
63
|
+
<SelectContent>
|
|
64
|
+
<SelectItem value="utc">UTC</SelectItem>
|
|
65
|
+
<SelectItem value="cet">Central European Time</SelectItem>
|
|
66
|
+
</SelectContent>
|
|
67
|
+
</Select>
|
|
68
|
+
{key === "invalid" ? <p id="sel-invalid-error" className="text-body-sm text-destructive">Choose a plan to continue.</p> : null}
|
|
69
|
+
</div>
|
|
70
|
+
))}
|
|
71
|
+
</CardContent>
|
|
72
|
+
</Card>
|
|
73
|
+
) };
|
|
@@ -12,7 +12,7 @@ const meta = {
|
|
|
12
12
|
args: { disabled: false },
|
|
13
13
|
argTypes: { disabled: { control: "boolean" } },
|
|
14
14
|
parameters: {
|
|
15
|
-
layout: "
|
|
15
|
+
layout: "padded",
|
|
16
16
|
docs: { source: { code: "<Slider aria-label=\"Volume\" defaultValue={[50]} max={100} step={1} />", language: "tsx" } },
|
|
17
17
|
},
|
|
18
18
|
} satisfies Meta;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Source of truth: apps/web/src/registry/demos.tsx · controls: scripts/gen-stories.mjs CONTROLS */
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
5
|
-
import { Label, Switch } from "@kinetixui/ui";
|
|
5
|
+
import { Card, CardContent, Label, Switch } from "@kinetixui/ui";
|
|
6
6
|
|
|
7
7
|
const Demo = () => (
|
|
8
8
|
<div className="flex items-center gap-2">
|
|
@@ -27,3 +27,39 @@ export default meta;
|
|
|
27
27
|
export const Playground: StoryObj<typeof meta> = { render: (args) => <Switch aria-label="Airplane mode" {...args} /> };
|
|
28
28
|
|
|
29
29
|
export const Default: StoryObj<typeof meta> = { render: () => <Demo /> };
|
|
30
|
+
|
|
31
|
+
export const States: StoryObj<typeof meta> = { render: () => (
|
|
32
|
+
<Card className="w-[22rem] max-w-full">
|
|
33
|
+
<CardContent className="grid gap-4 p-6">
|
|
34
|
+
{([
|
|
35
|
+
["off", "Push notifications", {}],
|
|
36
|
+
["on", "Weekly digest", { defaultChecked: true }],
|
|
37
|
+
["disabled", "SMS alerts (add a phone first)", { disabled: true }],
|
|
38
|
+
["disabled-on", "Security emails (required)", { disabled: true, defaultChecked: true }],
|
|
39
|
+
] as const).map(([key, label, props]) => (
|
|
40
|
+
<div key={key} className="flex items-center justify-between gap-4">
|
|
41
|
+
<Label htmlFor={`sw-${key}`}>{label}</Label>
|
|
42
|
+
<Switch id={`sw-${key}`} data-kx-case={key} {...props} />
|
|
43
|
+
</div>
|
|
44
|
+
))}
|
|
45
|
+
</CardContent>
|
|
46
|
+
</Card>
|
|
47
|
+
) };
|
|
48
|
+
|
|
49
|
+
export const Direction: StoryObj<typeof meta> = { render: () => (
|
|
50
|
+
<div className="grid w-[22rem] max-w-full gap-4">
|
|
51
|
+
{([
|
|
52
|
+
["inherit", undefined, "Follows the page"],
|
|
53
|
+
["ltr", "ltr", "Left-to-right section"],
|
|
54
|
+
["rtl", "rtl", "Right-to-left section"],
|
|
55
|
+
] as const).map(([key, dir, label]) => (
|
|
56
|
+
<div key={key} dir={dir} data-kx-dir={key} className="flex items-center justify-between gap-4">
|
|
57
|
+
<Label htmlFor={`sw-dir-${key}`}>{label}</Label>
|
|
58
|
+
<div className="flex items-center gap-3">
|
|
59
|
+
<Switch id={`sw-dir-${key}`} data-kx-case={`${key}-off`} />
|
|
60
|
+
<Switch aria-label={`${label}, on`} data-kx-case={`${key}-on`} defaultChecked />
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
))}
|
|
64
|
+
</div>
|
|
65
|
+
) };
|