@alpic-ai/ui 0.0.0-dev.4a35dc7 → 0.0.0-dev.6953a69
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/components/accordion-card.d.mts +1 -1
- package/dist/components/accordion.d.mts +1 -1
- package/dist/components/alert.d.mts +1 -1
- package/dist/components/attachment-tile.mjs +1 -1
- package/dist/components/avatar.d.mts +1 -1
- package/dist/components/breadcrumb.d.mts +1 -1
- package/dist/components/button.d.mts +1 -1
- package/dist/components/card.d.mts +1 -1
- package/dist/components/checkbox.d.mts +1 -1
- package/dist/components/collapsible.d.mts +1 -1
- package/dist/components/combobox.d.mts +1 -1
- package/dist/components/combobox.mjs +2 -2
- package/dist/components/command.d.mts +1 -1
- package/dist/components/copyable.d.mts +1 -1
- package/dist/components/copyable.mjs +1 -1
- package/dist/components/description-list.d.mts +1 -1
- package/dist/components/dialog.d.mts +1 -1
- package/dist/components/dialog.mjs +2 -2
- package/dist/components/dropdown-menu.d.mts +1 -1
- package/dist/components/form.d.mts +119 -0
- package/dist/components/form.mjs +192 -0
- package/dist/components/input-group.d.mts +1 -1
- package/dist/components/input.d.mts +3 -1
- package/dist/components/input.mjs +20 -11
- package/dist/components/label.d.mts +1 -1
- package/dist/components/pagination.d.mts +1 -1
- package/dist/components/popover.d.mts +1 -1
- package/dist/components/radio-group.d.mts +1 -1
- package/dist/components/scroll-area.d.mts +1 -1
- package/dist/components/select-trigger-variants.mjs +1 -0
- package/dist/components/select.d.mts +2 -10
- package/dist/components/select.mjs +8 -28
- package/dist/components/separator.d.mts +1 -1
- package/dist/components/sheet.d.mts +1 -1
- package/dist/components/sidebar.d.mts +1 -1
- package/dist/components/sidebar.mjs +8 -8
- package/dist/components/sonner.d.mts +1 -1
- package/dist/components/switch.d.mts +1 -1
- package/dist/components/table.d.mts +1 -1
- package/dist/components/tabs.d.mts +1 -1
- package/dist/components/tabs.mjs +1 -1
- package/dist/components/textarea.d.mts +3 -1
- package/dist/components/textarea.mjs +20 -11
- package/dist/components/toggle-group.d.mts +1 -1
- package/dist/components/toggle-group.mjs +1 -1
- package/dist/components/tooltip-icon-button.mjs +1 -1
- package/dist/components/tooltip.d.mts +1 -1
- package/package.json +10 -11
- package/src/components/combobox.tsx +1 -1
- package/src/components/dialog.tsx +2 -2
- package/src/components/form.tsx +343 -0
- package/src/components/input.tsx +12 -0
- package/src/components/select-trigger-variants.ts +1 -0
- package/src/components/select.tsx +2 -35
- package/src/components/sidebar.tsx +8 -10
- package/src/components/textarea.tsx +12 -1
- package/src/stories/accordion-card.stories.tsx +53 -0
- package/src/stories/accordion.stories.tsx +65 -0
- package/src/stories/alert.stories.tsx +58 -0
- package/src/stories/attachment-tile.stories.tsx +37 -0
- package/src/stories/avatar.stories.tsx +54 -0
- package/src/stories/badge.stories.tsx +50 -0
- package/src/stories/breadcrumb.stories.tsx +107 -0
- package/src/stories/button.stories.tsx +342 -0
- package/src/stories/card.stories.tsx +89 -0
- package/src/stories/checkbox.stories.tsx +56 -0
- package/src/stories/collapsible.stories.tsx +69 -0
- package/src/stories/combobox.stories.tsx +214 -0
- package/src/stories/command.stories.tsx +95 -0
- package/src/stories/copyable.stories.tsx +29 -0
- package/src/stories/description-list.stories.tsx +71 -0
- package/src/stories/dialog.stories.tsx +135 -0
- package/src/stories/dropdown-menu.stories.tsx +191 -0
- package/src/stories/form.stories.tsx +91 -0
- package/src/stories/input-group.stories.tsx +63 -0
- package/src/stories/input.stories.tsx +72 -0
- package/src/stories/label.stories.tsx +26 -0
- package/src/stories/ladle.css +3 -0
- package/src/stories/pagination.stories.tsx +35 -0
- package/src/stories/popover.stories.tsx +34 -0
- package/src/stories/radio-group.stories.tsx +59 -0
- package/src/stories/scroll-area.stories.tsx +43 -0
- package/src/stories/select.stories.tsx +95 -0
- package/src/stories/separator.stories.tsx +36 -0
- package/src/stories/sheet.stories.tsx +76 -0
- package/src/stories/sidebar.stories.tsx +255 -0
- package/src/stories/skeleton.stories.tsx +47 -0
- package/src/stories/sonner.stories.tsx +91 -0
- package/src/stories/spinner.stories.tsx +66 -0
- package/src/stories/status-dot.stories.tsx +27 -0
- package/src/stories/switch.stories.tsx +46 -0
- package/src/stories/table.stories.tsx +242 -0
- package/src/stories/tabs.stories.tsx +169 -0
- package/src/stories/tag.stories.tsx +82 -0
- package/src/stories/textarea.stories.tsx +60 -0
- package/src/stories/toggle-group.stories.tsx +142 -0
- package/src/stories/tooltip-icon-button.stories.tsx +59 -0
- package/src/stories/tooltip.stories.tsx +54 -0
- package/README.md +0 -33
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { Story } from "@ladle/react";
|
|
2
|
+
import { Mail, RotateCcw } from "lucide-react";
|
|
3
|
+
import { ActionToast, Toaster, toast } from "../components/sonner";
|
|
4
|
+
|
|
5
|
+
export const AllVariants: Story = () => (
|
|
6
|
+
<div className="flex min-h-80 flex-col gap-6">
|
|
7
|
+
<div className="flex flex-col gap-1.5">
|
|
8
|
+
<p className="type-text-xs font-medium text-subtle-foreground">Standard toasts — click to trigger</p>
|
|
9
|
+
<div className="flex flex-wrap gap-2">
|
|
10
|
+
<button
|
|
11
|
+
type="button"
|
|
12
|
+
className="h-8 rounded-md border border-border bg-background px-3 type-text-sm font-medium text-foreground [@media(hover:hover)]:hover:bg-background-hover"
|
|
13
|
+
onClick={() => toast.success("Changes saved", { description: "Your project settings have been updated." })}
|
|
14
|
+
>
|
|
15
|
+
Success
|
|
16
|
+
</button>
|
|
17
|
+
<button
|
|
18
|
+
type="button"
|
|
19
|
+
className="h-8 rounded-md border border-border bg-background px-3 type-text-sm font-medium text-foreground [@media(hover:hover)]:hover:bg-background-hover"
|
|
20
|
+
onClick={() => toast.error("Deployment failed", { description: "Build exited with code 1. Check the logs." })}
|
|
21
|
+
>
|
|
22
|
+
Error
|
|
23
|
+
</button>
|
|
24
|
+
<button
|
|
25
|
+
type="button"
|
|
26
|
+
className="h-8 rounded-md border border-border bg-background px-3 type-text-sm font-medium text-foreground [@media(hover:hover)]:hover:bg-background-hover"
|
|
27
|
+
onClick={() =>
|
|
28
|
+
toast.warning("Rate limit approaching", { description: "You've used 90% of your monthly quota." })
|
|
29
|
+
}
|
|
30
|
+
>
|
|
31
|
+
Warning
|
|
32
|
+
</button>
|
|
33
|
+
<button
|
|
34
|
+
type="button"
|
|
35
|
+
className="h-8 rounded-md border border-border bg-background px-3 type-text-sm font-medium text-foreground [@media(hover:hover)]:hover:bg-background-hover"
|
|
36
|
+
onClick={() =>
|
|
37
|
+
toast.info("New version available", { description: "v2.4.0 includes performance improvements." })
|
|
38
|
+
}
|
|
39
|
+
>
|
|
40
|
+
Info
|
|
41
|
+
</button>
|
|
42
|
+
<button
|
|
43
|
+
type="button"
|
|
44
|
+
className="h-8 rounded-md border border-border bg-background px-3 type-text-sm font-medium text-foreground [@media(hover:hover)]:hover:bg-background-hover"
|
|
45
|
+
onClick={() => toast.loading("Deploying...")}
|
|
46
|
+
>
|
|
47
|
+
Loading
|
|
48
|
+
</button>
|
|
49
|
+
<button
|
|
50
|
+
type="button"
|
|
51
|
+
className="h-8 rounded-md border border-border bg-background px-3 type-text-sm font-medium text-foreground [@media(hover:hover)]:hover:bg-background-hover"
|
|
52
|
+
onClick={() => toast("File uploaded", { closeButton: true })}
|
|
53
|
+
>
|
|
54
|
+
With Close
|
|
55
|
+
</button>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
<div className="flex flex-col gap-1.5">
|
|
60
|
+
<p className="type-text-xs font-medium text-subtle-foreground">ActionToast</p>
|
|
61
|
+
<div className="flex max-w-sm flex-col gap-4">
|
|
62
|
+
<div className="rounded-lg border border-border bg-background p-4 shadow-md">
|
|
63
|
+
<ActionToast
|
|
64
|
+
toastId="story-1"
|
|
65
|
+
title="Environment variables added"
|
|
66
|
+
description="A new deployment is needed for changes to take effect."
|
|
67
|
+
confirm={{ label: "Redeploy", icon: <RotateCcw />, onClick: () => {} }}
|
|
68
|
+
/>
|
|
69
|
+
</div>
|
|
70
|
+
<div className="rounded-lg border border-border bg-background p-4 shadow-md">
|
|
71
|
+
<ActionToast
|
|
72
|
+
toastId="story-2"
|
|
73
|
+
title="Redeploying..."
|
|
74
|
+
description="A new deployment is needed for changes to take effect."
|
|
75
|
+
confirm={{ label: "Redeploy", icon: <RotateCcw />, onClick: () => {}, loading: true }}
|
|
76
|
+
/>
|
|
77
|
+
</div>
|
|
78
|
+
<div className="rounded-lg border border-border bg-background p-4 shadow-md">
|
|
79
|
+
<ActionToast
|
|
80
|
+
toastId="story-3"
|
|
81
|
+
title="Stay in the loop!"
|
|
82
|
+
description="We ship new features regularly. Get a quick update when something useful drops."
|
|
83
|
+
confirm={{ label: "Subscribe", icon: <Mail />, onClick: () => {} }}
|
|
84
|
+
/>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
|
|
89
|
+
<Toaster />
|
|
90
|
+
</div>
|
|
91
|
+
);
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { Story } from "@ladle/react";
|
|
2
|
+
|
|
3
|
+
import { Spinner, SpinnerBlock } from "../components/spinner";
|
|
4
|
+
|
|
5
|
+
export const AllVariants: Story = () => (
|
|
6
|
+
<div className="flex flex-col gap-6">
|
|
7
|
+
<div className="flex flex-col gap-1.5">
|
|
8
|
+
<p className="type-text-xs font-medium text-subtle-foreground">Sizes</p>
|
|
9
|
+
<div className="flex items-center gap-4">
|
|
10
|
+
<div className="flex flex-col items-center gap-1.5">
|
|
11
|
+
<Spinner size="sm" />
|
|
12
|
+
<p className="type-text-xs text-muted-foreground">sm</p>
|
|
13
|
+
</div>
|
|
14
|
+
<div className="flex flex-col items-center gap-1.5">
|
|
15
|
+
<Spinner size="md" />
|
|
16
|
+
<p className="type-text-xs text-muted-foreground">md</p>
|
|
17
|
+
</div>
|
|
18
|
+
<div className="flex flex-col items-center gap-1.5">
|
|
19
|
+
<Spinner size="lg" />
|
|
20
|
+
<p className="type-text-xs text-muted-foreground">lg</p>
|
|
21
|
+
</div>
|
|
22
|
+
<div className="flex flex-col items-center gap-1.5">
|
|
23
|
+
<Spinner size="xl" />
|
|
24
|
+
<p className="type-text-xs text-muted-foreground">xl</p>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<div className="flex flex-col gap-1.5">
|
|
30
|
+
<p className="type-text-xs font-medium text-subtle-foreground">Variants</p>
|
|
31
|
+
<div className="flex items-center gap-4">
|
|
32
|
+
<div className="flex flex-col items-center gap-1.5">
|
|
33
|
+
<Spinner size="lg" variant="primary" />
|
|
34
|
+
<p className="type-text-xs text-muted-foreground">primary</p>
|
|
35
|
+
</div>
|
|
36
|
+
<div className="flex flex-col items-center gap-1.5">
|
|
37
|
+
<Spinner size="lg" variant="secondary" />
|
|
38
|
+
<p className="type-text-xs text-muted-foreground">secondary</p>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<div className="flex flex-col gap-1.5">
|
|
44
|
+
<p className="type-text-xs font-medium text-subtle-foreground">SpinnerBlock</p>
|
|
45
|
+
<div className="rounded-md border border-border">
|
|
46
|
+
<SpinnerBlock variant="secondary" size="lg" />
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<div className="flex flex-col gap-1.5">
|
|
51
|
+
<p className="type-text-xs font-medium text-subtle-foreground">SpinnerBlock with label</p>
|
|
52
|
+
<div className="rounded-md border border-border">
|
|
53
|
+
<SpinnerBlock variant="secondary" withLabel />
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<div className="flex flex-col gap-1.5">
|
|
58
|
+
<p className="type-text-xs font-medium text-subtle-foreground">
|
|
59
|
+
SpinnerBlock fullscreen (uses min-h-screen — shown inline for preview)
|
|
60
|
+
</p>
|
|
61
|
+
<div className="flex h-48 items-center justify-center rounded-md border border-border">
|
|
62
|
+
<Spinner size="xl" />
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Story } from "@ladle/react";
|
|
2
|
+
import { StatusDot } from "../components/status-dot";
|
|
3
|
+
|
|
4
|
+
export const AllVariants: Story = () => (
|
|
5
|
+
<div className="flex flex-col gap-6">
|
|
6
|
+
<div className="flex flex-col gap-1.5">
|
|
7
|
+
<p className="type-text-xs font-medium text-subtle-foreground">Success</p>
|
|
8
|
+
<StatusDot variant="success" />
|
|
9
|
+
</div>
|
|
10
|
+
<div className="flex flex-col gap-1.5">
|
|
11
|
+
<p className="type-text-xs font-medium text-subtle-foreground">Destructive</p>
|
|
12
|
+
<StatusDot variant="destructive" />
|
|
13
|
+
</div>
|
|
14
|
+
<div className="flex flex-col gap-1.5">
|
|
15
|
+
<p className="type-text-xs font-medium text-subtle-foreground">Warning</p>
|
|
16
|
+
<StatusDot variant="warning" />
|
|
17
|
+
</div>
|
|
18
|
+
<div className="flex flex-col gap-1.5">
|
|
19
|
+
<p className="type-text-xs font-medium text-subtle-foreground">Muted</p>
|
|
20
|
+
<StatusDot variant="muted" />
|
|
21
|
+
</div>
|
|
22
|
+
<div className="flex flex-col gap-1.5">
|
|
23
|
+
<p className="type-text-xs font-medium text-subtle-foreground">Warning + Pulse</p>
|
|
24
|
+
<StatusDot variant="warning" pulse />
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
);
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Story } from "@ladle/react";
|
|
2
|
+
import { Label } from "../components/label";
|
|
3
|
+
import { Switch } from "../components/switch";
|
|
4
|
+
|
|
5
|
+
export const AllVariants: Story = () => (
|
|
6
|
+
<div className="flex flex-col gap-6">
|
|
7
|
+
<div className="flex flex-col gap-1.5">
|
|
8
|
+
<p className="type-text-xs font-medium text-subtle-foreground">Off (default)</p>
|
|
9
|
+
<Switch />
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
<div className="flex flex-col gap-1.5">
|
|
13
|
+
<p className="type-text-xs font-medium text-subtle-foreground">On (defaultChecked)</p>
|
|
14
|
+
<Switch defaultChecked />
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
<div className="flex flex-col gap-1.5">
|
|
18
|
+
<p className="type-text-xs font-medium text-subtle-foreground">Disabled (off)</p>
|
|
19
|
+
<Switch disabled />
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<div className="flex flex-col gap-1.5">
|
|
23
|
+
<p className="type-text-xs font-medium text-subtle-foreground">Disabled (on)</p>
|
|
24
|
+
<Switch disabled defaultChecked />
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<div className="flex flex-col gap-1.5">
|
|
28
|
+
<p className="type-text-xs font-medium text-subtle-foreground">With label</p>
|
|
29
|
+
<div className="flex items-center gap-2">
|
|
30
|
+
<Switch id="with-label" />
|
|
31
|
+
<Label htmlFor="with-label">Remember me</Label>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<div className="flex flex-col gap-1.5">
|
|
36
|
+
<p className="type-text-xs font-medium text-subtle-foreground">With label and description</p>
|
|
37
|
+
<div className="flex gap-2">
|
|
38
|
+
<Switch id="with-description" className="mt-0.5" />
|
|
39
|
+
<div className="flex flex-col">
|
|
40
|
+
<Label htmlFor="with-description">Remember me</Label>
|
|
41
|
+
<p className="type-text-sm text-subtle-foreground">Save my login details for next time.</p>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
);
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import type { Story } from "@ladle/react";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { AvatarLabelGroup } from "../components/avatar";
|
|
4
|
+
import { Badge } from "../components/badge";
|
|
5
|
+
import { Checkbox } from "../components/checkbox";
|
|
6
|
+
import { Pagination } from "../components/pagination";
|
|
7
|
+
import {
|
|
8
|
+
Table,
|
|
9
|
+
TableBody,
|
|
10
|
+
TableCaption,
|
|
11
|
+
TableCell,
|
|
12
|
+
TableFooter,
|
|
13
|
+
TableHead,
|
|
14
|
+
TableHeader,
|
|
15
|
+
TableRow,
|
|
16
|
+
} from "../components/table";
|
|
17
|
+
|
|
18
|
+
const DEMO_AVATAR = "https://i.pravatar.cc/150";
|
|
19
|
+
|
|
20
|
+
const USERS = [
|
|
21
|
+
{
|
|
22
|
+
id: "1",
|
|
23
|
+
name: "Alice Carter",
|
|
24
|
+
handle: "@alice",
|
|
25
|
+
role: "Product Designer",
|
|
26
|
+
email: "alice.carter@example.com",
|
|
27
|
+
status: "Active",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: "2",
|
|
31
|
+
name: "Bob Miller",
|
|
32
|
+
handle: "@bob",
|
|
33
|
+
role: "Product Manager",
|
|
34
|
+
email: "bob.miller@example.com",
|
|
35
|
+
status: "Active",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: "3",
|
|
39
|
+
name: "Charlie Davis",
|
|
40
|
+
handle: "@charlie",
|
|
41
|
+
role: "Frontend Developer",
|
|
42
|
+
email: "charlie.davis@example.com",
|
|
43
|
+
status: "Active",
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
id: "4",
|
|
47
|
+
name: "Diana Moore",
|
|
48
|
+
handle: "@diana",
|
|
49
|
+
role: "Backend Developer",
|
|
50
|
+
email: "diana.moore@example.com",
|
|
51
|
+
status: "Active",
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: "5",
|
|
55
|
+
name: "Ethan Taylor",
|
|
56
|
+
handle: "@ethan",
|
|
57
|
+
role: "Fullstack Developer",
|
|
58
|
+
email: "ethan.taylor@example.com",
|
|
59
|
+
status: "Active",
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: "6",
|
|
63
|
+
name: "Fiona Zhang",
|
|
64
|
+
handle: "@fiona",
|
|
65
|
+
role: "DevOps Engineer",
|
|
66
|
+
email: "fiona.zhang@example.com",
|
|
67
|
+
status: "Active",
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
id: "7",
|
|
71
|
+
name: "George Kim",
|
|
72
|
+
handle: "@george",
|
|
73
|
+
role: "QA Engineer",
|
|
74
|
+
email: "george.kim@example.com",
|
|
75
|
+
status: "Active",
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: "8",
|
|
79
|
+
name: "Hannah Lee",
|
|
80
|
+
handle: "@hannah",
|
|
81
|
+
role: "Data Scientist",
|
|
82
|
+
email: "hannah.lee@example.com",
|
|
83
|
+
status: "Inactive",
|
|
84
|
+
},
|
|
85
|
+
] as const;
|
|
86
|
+
|
|
87
|
+
const PAGE_SIZE = 3;
|
|
88
|
+
|
|
89
|
+
export const AllVariants: Story = () => (
|
|
90
|
+
<div className="flex flex-col gap-8 p-6 bg-background max-w-4xl">
|
|
91
|
+
{/* Text-only table */}
|
|
92
|
+
<div className="flex flex-col gap-1.5">
|
|
93
|
+
<p className="type-text-xs font-medium text-subtle-foreground">Text cells</p>
|
|
94
|
+
<Table>
|
|
95
|
+
<TableHeader>
|
|
96
|
+
<TableRow>
|
|
97
|
+
<TableHead>Name</TableHead>
|
|
98
|
+
<TableHead>Role</TableHead>
|
|
99
|
+
<TableHead>Email</TableHead>
|
|
100
|
+
<TableHead>Status</TableHead>
|
|
101
|
+
</TableRow>
|
|
102
|
+
</TableHeader>
|
|
103
|
+
<TableBody>
|
|
104
|
+
{USERS.map((user) => (
|
|
105
|
+
<TableRow key={user.id}>
|
|
106
|
+
<TableCell className="font-medium text-foreground">{user.name}</TableCell>
|
|
107
|
+
<TableCell className="text-subtle-foreground">{user.role}</TableCell>
|
|
108
|
+
<TableCell className="text-subtle-foreground">{user.email}</TableCell>
|
|
109
|
+
<TableCell>
|
|
110
|
+
<Badge variant="success">{user.status}</Badge>
|
|
111
|
+
</TableCell>
|
|
112
|
+
</TableRow>
|
|
113
|
+
))}
|
|
114
|
+
</TableBody>
|
|
115
|
+
</Table>
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
{/* Avatar cells + checkbox */}
|
|
119
|
+
<div className="flex flex-col gap-1.5">
|
|
120
|
+
<p className="type-text-xs font-medium text-subtle-foreground">Avatar cells + checkbox</p>
|
|
121
|
+
<Table>
|
|
122
|
+
<TableHeader>
|
|
123
|
+
<TableRow>
|
|
124
|
+
<TableHead>
|
|
125
|
+
<Checkbox role="checkbox" aria-label="Select all" />
|
|
126
|
+
</TableHead>
|
|
127
|
+
<TableHead>Name</TableHead>
|
|
128
|
+
<TableHead>Role</TableHead>
|
|
129
|
+
<TableHead>Status</TableHead>
|
|
130
|
+
</TableRow>
|
|
131
|
+
</TableHeader>
|
|
132
|
+
<TableBody>
|
|
133
|
+
{USERS.map((user) => (
|
|
134
|
+
<TableRow key={user.id}>
|
|
135
|
+
<TableCell>
|
|
136
|
+
<Checkbox role="checkbox" aria-label={`Select ${user.name}`} />
|
|
137
|
+
</TableCell>
|
|
138
|
+
<TableCell>
|
|
139
|
+
<AvatarLabelGroup size="md" src={DEMO_AVATAR} name={user.name} label={user.handle} />
|
|
140
|
+
</TableCell>
|
|
141
|
+
<TableCell className="text-subtle-foreground">{user.role}</TableCell>
|
|
142
|
+
<TableCell>
|
|
143
|
+
<Badge variant="success">{user.status}</Badge>
|
|
144
|
+
</TableCell>
|
|
145
|
+
</TableRow>
|
|
146
|
+
))}
|
|
147
|
+
</TableBody>
|
|
148
|
+
</Table>
|
|
149
|
+
</div>
|
|
150
|
+
|
|
151
|
+
{/* With footer */}
|
|
152
|
+
<div className="flex flex-col gap-1.5">
|
|
153
|
+
<p className="type-text-xs font-medium text-subtle-foreground">With caption + footer</p>
|
|
154
|
+
<Table>
|
|
155
|
+
<TableCaption>Team members — 5 total</TableCaption>
|
|
156
|
+
<TableHeader>
|
|
157
|
+
<TableRow>
|
|
158
|
+
<TableHead>Name</TableHead>
|
|
159
|
+
<TableHead>Role</TableHead>
|
|
160
|
+
<TableHead>Email</TableHead>
|
|
161
|
+
</TableRow>
|
|
162
|
+
</TableHeader>
|
|
163
|
+
<TableBody>
|
|
164
|
+
{USERS.slice(0, 3).map((user) => (
|
|
165
|
+
<TableRow key={user.id}>
|
|
166
|
+
<TableCell className="font-medium text-foreground">{user.name}</TableCell>
|
|
167
|
+
<TableCell className="text-subtle-foreground">{user.role}</TableCell>
|
|
168
|
+
<TableCell className="text-subtle-foreground">{user.email}</TableCell>
|
|
169
|
+
</TableRow>
|
|
170
|
+
))}
|
|
171
|
+
</TableBody>
|
|
172
|
+
<TableFooter>
|
|
173
|
+
<TableRow>
|
|
174
|
+
<TableCell colSpan={2}>Total</TableCell>
|
|
175
|
+
<TableCell>3 members</TableCell>
|
|
176
|
+
</TableRow>
|
|
177
|
+
</TableFooter>
|
|
178
|
+
</Table>
|
|
179
|
+
</div>
|
|
180
|
+
|
|
181
|
+
{/* Selected row state */}
|
|
182
|
+
<div className="flex flex-col gap-1.5">
|
|
183
|
+
<p className="type-text-xs font-medium text-subtle-foreground">Selected row state</p>
|
|
184
|
+
<Table>
|
|
185
|
+
<TableHeader>
|
|
186
|
+
<TableRow>
|
|
187
|
+
<TableHead>Name</TableHead>
|
|
188
|
+
<TableHead>Role</TableHead>
|
|
189
|
+
<TableHead>Email</TableHead>
|
|
190
|
+
</TableRow>
|
|
191
|
+
</TableHeader>
|
|
192
|
+
<TableBody>
|
|
193
|
+
{USERS.slice(0, 3).map((user, i) => (
|
|
194
|
+
<TableRow key={user.id} data-state={i === 1 ? "selected" : undefined}>
|
|
195
|
+
<TableCell className="font-medium text-foreground">{user.name}</TableCell>
|
|
196
|
+
<TableCell className="text-subtle-foreground">{user.role}</TableCell>
|
|
197
|
+
<TableCell className="text-subtle-foreground">{user.email}</TableCell>
|
|
198
|
+
</TableRow>
|
|
199
|
+
))}
|
|
200
|
+
</TableBody>
|
|
201
|
+
</Table>
|
|
202
|
+
</div>
|
|
203
|
+
|
|
204
|
+
{/* Paginated table */}
|
|
205
|
+
<PaginatedTableExample />
|
|
206
|
+
</div>
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
function PaginatedTableExample() {
|
|
210
|
+
const [page, setPage] = useState(1);
|
|
211
|
+
const totalPages = Math.ceil(USERS.length / PAGE_SIZE);
|
|
212
|
+
const paginatedUsers = USERS.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE);
|
|
213
|
+
|
|
214
|
+
return (
|
|
215
|
+
<div className="flex flex-col gap-4">
|
|
216
|
+
<p className="type-text-xs font-medium text-subtle-foreground">With pagination</p>
|
|
217
|
+
<Pagination currentPage={page} totalPages={totalPages} onPageChange={setPage} />
|
|
218
|
+
<Table>
|
|
219
|
+
<TableHeader>
|
|
220
|
+
<TableRow>
|
|
221
|
+
<TableHead>Name</TableHead>
|
|
222
|
+
<TableHead>Role</TableHead>
|
|
223
|
+
<TableHead>Email</TableHead>
|
|
224
|
+
<TableHead>Status</TableHead>
|
|
225
|
+
</TableRow>
|
|
226
|
+
</TableHeader>
|
|
227
|
+
<TableBody>
|
|
228
|
+
{paginatedUsers.map((user) => (
|
|
229
|
+
<TableRow key={user.id}>
|
|
230
|
+
<TableCell className="font-medium text-foreground">{user.name}</TableCell>
|
|
231
|
+
<TableCell className="text-subtle-foreground">{user.role}</TableCell>
|
|
232
|
+
<TableCell className="text-subtle-foreground">{user.email}</TableCell>
|
|
233
|
+
<TableCell>
|
|
234
|
+
<Badge variant={user.status === "Active" ? "success" : "secondary"}>{user.status}</Badge>
|
|
235
|
+
</TableCell>
|
|
236
|
+
</TableRow>
|
|
237
|
+
))}
|
|
238
|
+
</TableBody>
|
|
239
|
+
</Table>
|
|
240
|
+
</div>
|
|
241
|
+
);
|
|
242
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import type { Story } from "@ladle/react";
|
|
2
|
+
import { ChartBar, Layers, PackageOpen, Presentation, Rocket, Search, Settings } from "lucide-react";
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
|
|
5
|
+
import { Tabs, TabsContent, TabsList, TabsNav, TabsNavList, TabsNavTrigger, TabsTrigger } from "../components/tabs";
|
|
6
|
+
|
|
7
|
+
const SECTION_HEADER = "type-text-xs font-medium text-muted-foreground uppercase tracking-wide pt-4";
|
|
8
|
+
|
|
9
|
+
const navTabs = [
|
|
10
|
+
{ id: "overview", label: "Overview", icon: Presentation },
|
|
11
|
+
{ id: "deployments", label: "Deployments", icon: Rocket },
|
|
12
|
+
{ id: "analytics", label: "Analytics", icon: ChartBar },
|
|
13
|
+
{ id: "logs", label: "Logs", icon: Search },
|
|
14
|
+
{ id: "environments", label: "Environments", icon: Layers },
|
|
15
|
+
{ id: "distribution", label: "Distribution", icon: PackageOpen },
|
|
16
|
+
{ id: "settings", label: "Settings", icon: Settings },
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
const sideNavTabs = [
|
|
20
|
+
{ id: "general", label: "General" },
|
|
21
|
+
{ id: "authentication", label: "Authentication" },
|
|
22
|
+
{ id: "notifications", label: "Notifications" },
|
|
23
|
+
{ id: "billing", label: "Billing" },
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
function useHashRoute(fallback: string) {
|
|
27
|
+
const [active, setActive] = useState(() => window.location.hash.slice(1) || fallback);
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
const onChange = () => setActive(window.location.hash.slice(1) || fallback);
|
|
31
|
+
window.addEventListener("hashchange", onChange);
|
|
32
|
+
return () => window.removeEventListener("hashchange", onChange);
|
|
33
|
+
}, [fallback]);
|
|
34
|
+
|
|
35
|
+
return active;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const AllVariants: Story = () => {
|
|
39
|
+
const activeNav = useHashRoute("overview");
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<div className="flex flex-col gap-10 p-8">
|
|
43
|
+
{/* ─── Navigation tabs ─────────────────────────────────────── */}
|
|
44
|
+
|
|
45
|
+
{/* Horizontal + line (default) */}
|
|
46
|
+
<div>
|
|
47
|
+
<p className={SECTION_HEADER}>Nav — horizontal, variant="line" (default)</p>
|
|
48
|
+
<div className="mt-4">
|
|
49
|
+
<TabsNav aria-label="Project" orientation="horizontal" variant="line">
|
|
50
|
+
<TabsNavList>
|
|
51
|
+
{navTabs.map(({ id, label, icon: Icon }) => (
|
|
52
|
+
<TabsNavTrigger key={id} href={`#${id}`} active={activeNav === id}>
|
|
53
|
+
<Icon className="h-4 w-4" />
|
|
54
|
+
{label}
|
|
55
|
+
</TabsNavTrigger>
|
|
56
|
+
))}
|
|
57
|
+
</TabsNavList>
|
|
58
|
+
</TabsNav>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
{/* Horizontal + pill */}
|
|
63
|
+
<div>
|
|
64
|
+
<p className={SECTION_HEADER}>Nav — horizontal, variant="pill"</p>
|
|
65
|
+
<div className="mt-4">
|
|
66
|
+
<TabsNav aria-label="Project" orientation="horizontal" variant="pill">
|
|
67
|
+
<TabsNavList>
|
|
68
|
+
{navTabs.slice(0, 4).map(({ id, label, icon: Icon }) => (
|
|
69
|
+
<TabsNavTrigger key={id} href={`#${id}`} active={activeNav === id}>
|
|
70
|
+
<Icon className="h-4 w-4" />
|
|
71
|
+
{label}
|
|
72
|
+
</TabsNavTrigger>
|
|
73
|
+
))}
|
|
74
|
+
</TabsNavList>
|
|
75
|
+
</TabsNav>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
{/* Vertical + pill (default) */}
|
|
80
|
+
<div>
|
|
81
|
+
<p className={SECTION_HEADER}>Nav — vertical, variant="pill" (default)</p>
|
|
82
|
+
<div className="mt-4 flex gap-6">
|
|
83
|
+
<aside className="shrink-0 basis-40">
|
|
84
|
+
<TabsNav aria-label="Settings" orientation="vertical" variant="pill">
|
|
85
|
+
<TabsNavList>
|
|
86
|
+
{sideNavTabs.map(({ id, label }) => (
|
|
87
|
+
<TabsNavTrigger key={id} href={`#${id}`} active={activeNav === id}>
|
|
88
|
+
{label}
|
|
89
|
+
</TabsNavTrigger>
|
|
90
|
+
))}
|
|
91
|
+
</TabsNavList>
|
|
92
|
+
</TabsNav>
|
|
93
|
+
</aside>
|
|
94
|
+
<div className="flex-1 rounded-md border p-4">
|
|
95
|
+
<p className="type-text-sm text-muted-foreground">Content for "{activeNav}"</p>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
|
|
100
|
+
{/* ─── Tabs (state-based) ────────────────────────────── */}
|
|
101
|
+
|
|
102
|
+
{/* Tabs — line */}
|
|
103
|
+
<div>
|
|
104
|
+
<p className={SECTION_HEADER}>Tabs — line variant</p>
|
|
105
|
+
<Tabs defaultValue="all" className="mt-4">
|
|
106
|
+
<TabsList variant="line">
|
|
107
|
+
<TabsTrigger value="all">All</TabsTrigger>
|
|
108
|
+
<TabsTrigger value="tool">Tool</TabsTrigger>
|
|
109
|
+
<TabsTrigger value="resource">Resource</TabsTrigger>
|
|
110
|
+
<TabsTrigger value="prompt">Prompt</TabsTrigger>
|
|
111
|
+
</TabsList>
|
|
112
|
+
<TabsContent value="all">All content</TabsContent>
|
|
113
|
+
<TabsContent value="tool">Tool content</TabsContent>
|
|
114
|
+
<TabsContent value="resource">Resource content</TabsContent>
|
|
115
|
+
<TabsContent value="prompt">Prompt content</TabsContent>
|
|
116
|
+
</Tabs>
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
{/* Tabs — default (horizontal) */}
|
|
120
|
+
<div>
|
|
121
|
+
<p className={SECTION_HEADER}>Tabs — default variant (horizontal)</p>
|
|
122
|
+
<Tabs defaultValue="all" className="mt-4">
|
|
123
|
+
<TabsList>
|
|
124
|
+
<TabsTrigger value="all">All</TabsTrigger>
|
|
125
|
+
<TabsTrigger value="tool">Tool</TabsTrigger>
|
|
126
|
+
<TabsTrigger value="resource">Resource</TabsTrigger>
|
|
127
|
+
<TabsTrigger value="prompt">Prompt</TabsTrigger>
|
|
128
|
+
</TabsList>
|
|
129
|
+
<TabsContent value="all">All content</TabsContent>
|
|
130
|
+
<TabsContent value="tool">Tool content</TabsContent>
|
|
131
|
+
<TabsContent value="resource">Resource content</TabsContent>
|
|
132
|
+
<TabsContent value="prompt">Prompt content</TabsContent>
|
|
133
|
+
</Tabs>
|
|
134
|
+
</div>
|
|
135
|
+
|
|
136
|
+
{/* Tabs — default (vertical) */}
|
|
137
|
+
<div>
|
|
138
|
+
<p className={SECTION_HEADER}>Tabs — default variant (vertical)</p>
|
|
139
|
+
<Tabs defaultValue="overview" orientation="vertical" className="mt-4">
|
|
140
|
+
<TabsList>
|
|
141
|
+
<TabsTrigger value="overview">Overview</TabsTrigger>
|
|
142
|
+
<TabsTrigger value="analytics">Analytics</TabsTrigger>
|
|
143
|
+
<TabsTrigger value="settings">Settings</TabsTrigger>
|
|
144
|
+
</TabsList>
|
|
145
|
+
<TabsContent value="overview">Overview content</TabsContent>
|
|
146
|
+
<TabsContent value="analytics">Analytics content</TabsContent>
|
|
147
|
+
<TabsContent value="settings">Settings content</TabsContent>
|
|
148
|
+
</Tabs>
|
|
149
|
+
</div>
|
|
150
|
+
|
|
151
|
+
{/* Tabs — disabled */}
|
|
152
|
+
<div>
|
|
153
|
+
<p className={SECTION_HEADER}>Tabs — disabled trigger</p>
|
|
154
|
+
<Tabs defaultValue="one" className="mt-4">
|
|
155
|
+
<TabsList>
|
|
156
|
+
<TabsTrigger value="one">One</TabsTrigger>
|
|
157
|
+
<TabsTrigger value="two" disabled>
|
|
158
|
+
Two (disabled)
|
|
159
|
+
</TabsTrigger>
|
|
160
|
+
<TabsTrigger value="three">Three</TabsTrigger>
|
|
161
|
+
</TabsList>
|
|
162
|
+
<TabsContent value="one">One content</TabsContent>
|
|
163
|
+
<TabsContent value="two">Two content</TabsContent>
|
|
164
|
+
<TabsContent value="three">Three content</TabsContent>
|
|
165
|
+
</Tabs>
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
168
|
+
);
|
|
169
|
+
};
|