@bearmenu/ui 0.1.1 → 0.5.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/{chunk-2CUFS5LF.js → chunk-2BMJRXQX.js} +1 -1
- package/dist/chunk-HLQ2IRE7.js +279 -0
- package/dist/chunk-IQASCH2V.js +25 -0
- package/dist/{chunk-UXAYXRFN.js → chunk-QJ54Z7TC.js} +15 -1
- package/dist/{chunk-X6GATUI2.js → chunk-UISA4CIG.js} +1 -0
- package/dist/components/alert-dialog.js +1 -1
- package/dist/components/button.js +1 -1
- package/dist/components/calendar.d.ts +9 -7
- package/dist/components/calendar.js +153 -39
- package/dist/components/card.d.ts +2 -1
- package/dist/components/card.js +1 -1
- package/dist/components/carousel.js +1 -1
- package/dist/components/checkbox.js +3 -25
- package/dist/components/dropdown-menu.js +1 -1
- package/dist/components/empty-state.js +1 -1
- package/dist/components/haptic-button.js +1 -1
- package/dist/components/link-button.js +1 -1
- package/dist/components/multi-select-combobox.js +1 -1
- package/dist/components/multi-select-menu.d.ts +26 -0
- package/dist/components/multi-select-menu.js +71 -0
- package/dist/components/pagination.js +1 -1
- package/dist/components/phone-frame.d.ts +17 -0
- package/dist/components/phone-frame.js +41 -0
- package/dist/components/place-about-tab.d.ts +17 -0
- package/dist/components/place-about-tab.js +6 -0
- package/dist/components/place-card.d.ts +17 -0
- package/dist/components/place-card.js +145 -0
- package/dist/components/place-details-mobile.d.ts +18 -0
- package/dist/components/place-details-mobile.js +187 -0
- package/dist/components/place-types.d.ts +145 -0
- package/dist/components/place-types.js +1 -0
- package/dist/components/search-bar.js +1 -1
- package/dist/components/searchable-select.js +1 -1
- package/dist/components/skeleton-card.js +1 -1
- package/dist/components/skeleton.js +1 -1
- package/dist/components/sliding-panels.js +1 -1
- package/dist/components/sonner.js +11 -2
- package/dist/components/tabs.js +1 -1
- package/dist/components/toaster.js +1 -1
- package/package.json +28 -9
- package/src/components/accordion.stories.tsx +51 -0
- package/src/components/alert-dialog.stories.tsx +45 -0
- package/src/components/alert.stories.tsx +85 -0
- package/src/components/aspect-ratio.stories.tsx +47 -0
- package/src/components/avatar.stories.tsx +66 -0
- package/src/components/badge.stories.tsx +60 -0
- package/src/components/breadcrumb.stories.tsx +63 -0
- package/src/components/button.stories.tsx +115 -0
- package/src/components/button.tsx +1 -1
- package/src/components/calendar.stories.tsx +47 -0
- package/src/components/calendar.tsx +160 -40
- package/src/components/card.stories.tsx +72 -0
- package/src/components/card.tsx +13 -1
- package/src/components/carousel.stories.tsx +62 -0
- package/src/components/checkbox.stories.tsx +54 -0
- package/src/components/checkbox.tsx +1 -1
- package/src/components/collapsible.stories.tsx +34 -0
- package/src/components/command.stories.tsx +64 -0
- package/src/components/currency-input.stories.tsx +50 -0
- package/src/components/dialog.stories.tsx +75 -0
- package/src/components/drawer.stories.tsx +47 -0
- package/src/components/dropdown-menu.stories.tsx +64 -0
- package/src/components/dropdown-menu.tsx +1 -1
- package/src/components/empty-state.stories.tsx +67 -0
- package/src/components/form.stories.tsx +73 -0
- package/src/components/haptic-button.stories.tsx +41 -0
- package/src/components/input-otp.stories.tsx +42 -0
- package/src/components/input.stories.tsx +41 -0
- package/src/components/label.stories.tsx +35 -0
- package/src/components/markdown-renderer.stories.tsx +45 -0
- package/src/components/multi-select-combobox.stories.tsx +78 -0
- package/src/components/multi-select-menu.tsx +96 -0
- package/src/components/pagination.stories.tsx +48 -0
- package/src/components/phone-frame.stories.tsx +51 -0
- package/src/components/phone-frame.test.tsx +82 -0
- package/src/components/phone-frame.tsx +59 -0
- package/src/components/place-about-tab.stories.tsx +164 -0
- package/src/components/place-about-tab.test.tsx +325 -0
- package/src/components/place-about-tab.tsx +474 -0
- package/src/components/place-card.stories.tsx +120 -0
- package/src/components/place-card.test.tsx +187 -0
- package/src/components/place-card.tsx +187 -0
- package/src/components/place-details-mobile.tsx +251 -0
- package/src/components/place-types.ts +127 -0
- package/src/components/popover.stories.tsx +42 -0
- package/src/components/progress.stories.tsx +35 -0
- package/src/components/radio-group.stories.tsx +46 -0
- package/src/components/rating.stories.tsx +45 -0
- package/src/components/scroll-area.stories.tsx +48 -0
- package/src/components/search-bar.stories.tsx +92 -0
- package/src/components/searchable-select.stories.tsx +55 -0
- package/src/components/select.stories.tsx +92 -0
- package/src/components/separator.stories.tsx +33 -0
- package/src/components/sheet.stories.tsx +95 -0
- package/src/components/skeleton-card.stories.tsx +53 -0
- package/src/components/skeleton.stories.tsx +42 -0
- package/src/components/skeleton.tsx +1 -0
- package/src/components/slider.stories.tsx +27 -0
- package/src/components/sliding-panels.stories.tsx +68 -0
- package/src/components/sonner.tsx +17 -2
- package/src/components/sticky-container.stories.tsx +33 -0
- package/src/components/switch.stories.tsx +35 -0
- package/src/components/table.stories.tsx +60 -0
- package/src/components/tabs.stories.tsx +53 -0
- package/src/components/tabs.tsx +1 -1
- package/src/components/text.stories.tsx +82 -0
- package/src/components/textarea.stories.tsx +35 -0
- package/src/components/toast.stories.tsx +63 -0
- package/src/components/toggle-group.stories.tsx +60 -0
- package/src/components/toggle.stories.tsx +48 -0
- package/src/components/tooltip.stories.tsx +71 -0
- package/src/globals.css +14 -2
- package/src/test/setup.ts +9 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { Checkbox } from "./checkbox";
|
|
3
|
+
import { Label } from "./label";
|
|
4
|
+
|
|
5
|
+
const meta = {
|
|
6
|
+
title: "Components/Checkbox",
|
|
7
|
+
component: Checkbox,
|
|
8
|
+
tags: ["autodocs"],
|
|
9
|
+
} satisfies Meta<typeof Checkbox>;
|
|
10
|
+
|
|
11
|
+
export default meta;
|
|
12
|
+
type Story = StoryObj<typeof meta>;
|
|
13
|
+
|
|
14
|
+
export const Default: Story = {};
|
|
15
|
+
|
|
16
|
+
export const Checked: Story = {
|
|
17
|
+
args: { defaultChecked: true },
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const Disabled: Story = {
|
|
21
|
+
args: { disabled: true },
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const DisabledChecked: Story = {
|
|
25
|
+
args: { disabled: true, defaultChecked: true },
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const WithLabel: Story = {
|
|
29
|
+
render: () => (
|
|
30
|
+
<div className="flex items-center gap-2">
|
|
31
|
+
<Checkbox id="terms" />
|
|
32
|
+
<Label htmlFor="terms">I agree to the terms of service</Label>
|
|
33
|
+
</div>
|
|
34
|
+
),
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const Multiple: Story = {
|
|
38
|
+
render: () => (
|
|
39
|
+
<div className="flex flex-col gap-3">
|
|
40
|
+
<div className="flex items-center gap-2">
|
|
41
|
+
<Checkbox id="opt1" defaultChecked />
|
|
42
|
+
<Label htmlFor="opt1">Email notifications</Label>
|
|
43
|
+
</div>
|
|
44
|
+
<div className="flex items-center gap-2">
|
|
45
|
+
<Checkbox id="opt2" />
|
|
46
|
+
<Label htmlFor="opt2">SMS notifications</Label>
|
|
47
|
+
</div>
|
|
48
|
+
<div className="flex items-center gap-2">
|
|
49
|
+
<Checkbox id="opt3" disabled />
|
|
50
|
+
<Label htmlFor="opt3">Push notifications (coming soon)</Label>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
),
|
|
54
|
+
};
|
|
@@ -12,7 +12,7 @@ const Checkbox = React.forwardRef<
|
|
|
12
12
|
<CheckboxPrimitive.Root
|
|
13
13
|
ref={ref}
|
|
14
14
|
className={cn(
|
|
15
|
-
"peer h-5 w-5 shrink-0 rounded-md border border-border
|
|
15
|
+
"peer h-5 w-5 shrink-0 rounded-md border border-border bg-background transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=checked]:border-primary data-[state=checked]:shadow-lg data-[state=checked]:shadow-primary/20 data-[state=indeterminate]:bg-primary data-[state=indeterminate]:text-primary-foreground data-[state=indeterminate]:border-primary data-[state=indeterminate]:shadow-lg data-[state=indeterminate]:shadow-primary/20 hover:border-primary/40 hover:shadow-md",
|
|
16
16
|
className
|
|
17
17
|
)}
|
|
18
18
|
{...props}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "./collapsible";
|
|
3
|
+
import { Button } from "./button";
|
|
4
|
+
import { ChevronsUpDown } from "lucide-react";
|
|
5
|
+
|
|
6
|
+
const meta = {
|
|
7
|
+
title: "Components/Collapsible",
|
|
8
|
+
component: Collapsible,
|
|
9
|
+
tags: ["autodocs"],
|
|
10
|
+
} satisfies Meta<typeof Collapsible>;
|
|
11
|
+
|
|
12
|
+
export default meta;
|
|
13
|
+
type Story = StoryObj<typeof meta>;
|
|
14
|
+
|
|
15
|
+
export const Default: Story = {
|
|
16
|
+
render: () => (
|
|
17
|
+
<Collapsible className="w-[350px] space-y-2">
|
|
18
|
+
<div className="flex items-center justify-between space-x-4 px-4">
|
|
19
|
+
<h4 className="text-sm font-semibold">@peduarte starred 3 repositories</h4>
|
|
20
|
+
<CollapsibleTrigger asChild>
|
|
21
|
+
<Button variant="ghost" size="sm" className="w-9 p-0">
|
|
22
|
+
<ChevronsUpDown className="h-4 w-4" />
|
|
23
|
+
<span className="sr-only">Toggle</span>
|
|
24
|
+
</Button>
|
|
25
|
+
</CollapsibleTrigger>
|
|
26
|
+
</div>
|
|
27
|
+
<div className="rounded-md border px-4 py-3 text-sm">@radix-ui/primitives</div>
|
|
28
|
+
<CollapsibleContent className="space-y-2">
|
|
29
|
+
<div className="rounded-md border px-4 py-3 text-sm">@radix-ui/colors</div>
|
|
30
|
+
<div className="rounded-md border px-4 py-3 text-sm">@stitches/react</div>
|
|
31
|
+
</CollapsibleContent>
|
|
32
|
+
</Collapsible>
|
|
33
|
+
),
|
|
34
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import {
|
|
3
|
+
Command,
|
|
4
|
+
CommandEmpty,
|
|
5
|
+
CommandGroup,
|
|
6
|
+
CommandInput,
|
|
7
|
+
CommandItem,
|
|
8
|
+
CommandList,
|
|
9
|
+
CommandSeparator,
|
|
10
|
+
CommandShortcut,
|
|
11
|
+
} from "./command";
|
|
12
|
+
import { Calculator, Calendar, CreditCard, Settings, Smile, User } from "lucide-react";
|
|
13
|
+
|
|
14
|
+
const meta = {
|
|
15
|
+
title: "Components/Command",
|
|
16
|
+
component: Command,
|
|
17
|
+
tags: ["autodocs"],
|
|
18
|
+
} satisfies Meta<typeof Command>;
|
|
19
|
+
|
|
20
|
+
export default meta;
|
|
21
|
+
type Story = StoryObj<typeof meta>;
|
|
22
|
+
|
|
23
|
+
export const Default: Story = {
|
|
24
|
+
render: () => (
|
|
25
|
+
<Command className="rounded-lg border shadow-md w-[400px]">
|
|
26
|
+
<CommandInput placeholder="Type a command or search..." />
|
|
27
|
+
<CommandList>
|
|
28
|
+
<CommandEmpty>No results found.</CommandEmpty>
|
|
29
|
+
<CommandGroup heading="Suggestions">
|
|
30
|
+
<CommandItem>
|
|
31
|
+
<Calendar className="mr-2 h-4 w-4" />
|
|
32
|
+
<span>Calendar</span>
|
|
33
|
+
</CommandItem>
|
|
34
|
+
<CommandItem>
|
|
35
|
+
<Smile className="mr-2 h-4 w-4" />
|
|
36
|
+
<span>Search Emoji</span>
|
|
37
|
+
</CommandItem>
|
|
38
|
+
<CommandItem>
|
|
39
|
+
<Calculator className="mr-2 h-4 w-4" />
|
|
40
|
+
<span>Calculator</span>
|
|
41
|
+
</CommandItem>
|
|
42
|
+
</CommandGroup>
|
|
43
|
+
<CommandSeparator />
|
|
44
|
+
<CommandGroup heading="Settings">
|
|
45
|
+
<CommandItem>
|
|
46
|
+
<User className="mr-2 h-4 w-4" />
|
|
47
|
+
<span>Profile</span>
|
|
48
|
+
<CommandShortcut>⌘P</CommandShortcut>
|
|
49
|
+
</CommandItem>
|
|
50
|
+
<CommandItem>
|
|
51
|
+
<CreditCard className="mr-2 h-4 w-4" />
|
|
52
|
+
<span>Billing</span>
|
|
53
|
+
<CommandShortcut>⌘B</CommandShortcut>
|
|
54
|
+
</CommandItem>
|
|
55
|
+
<CommandItem>
|
|
56
|
+
<Settings className="mr-2 h-4 w-4" />
|
|
57
|
+
<span>Settings</span>
|
|
58
|
+
<CommandShortcut>⌘S</CommandShortcut>
|
|
59
|
+
</CommandItem>
|
|
60
|
+
</CommandGroup>
|
|
61
|
+
</CommandList>
|
|
62
|
+
</Command>
|
|
63
|
+
),
|
|
64
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
3
|
+
import { CurrencyInput } from "./currency-input";
|
|
4
|
+
import { Label } from "./label";
|
|
5
|
+
|
|
6
|
+
const meta = {
|
|
7
|
+
title: "Components/CurrencyInput",
|
|
8
|
+
component: CurrencyInput,
|
|
9
|
+
tags: ["autodocs"],
|
|
10
|
+
} satisfies Meta<typeof CurrencyInput>;
|
|
11
|
+
|
|
12
|
+
export default meta;
|
|
13
|
+
type Story = StoryObj<typeof meta>;
|
|
14
|
+
|
|
15
|
+
function CurrencyInputDemo() {
|
|
16
|
+
const [value, setValue] = useState<number | null>(null);
|
|
17
|
+
return (
|
|
18
|
+
<div className="grid w-full max-w-sm gap-1.5">
|
|
19
|
+
<Label>Amount</Label>
|
|
20
|
+
<CurrencyInput value={value} onChange={setValue} placeholder="Enter amount" />
|
|
21
|
+
<p className="text-sm text-muted-foreground">Value: {value ?? "null"}</p>
|
|
22
|
+
</div>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const Default: Story = {
|
|
27
|
+
render: () => <CurrencyInputDemo />,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
function CurrencyInputWithConstraints() {
|
|
31
|
+
const [value, setValue] = useState<number | null>(50);
|
|
32
|
+
return (
|
|
33
|
+
<div className="grid w-full max-w-sm gap-1.5">
|
|
34
|
+
<Label>Price (min: 0, max: 1000)</Label>
|
|
35
|
+
<CurrencyInput value={value} onChange={setValue} min={0} max={1000} />
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const WithConstraints: Story = {
|
|
41
|
+
render: () => <CurrencyInputWithConstraints />,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const Disabled: Story = {
|
|
45
|
+
render: () => <CurrencyInput value={99.99} onChange={() => {}} disabled />,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const WithError: Story = {
|
|
49
|
+
render: () => <CurrencyInput value={null} onChange={() => {}} error="Amount is required" />,
|
|
50
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import {
|
|
3
|
+
Dialog,
|
|
4
|
+
DialogContent,
|
|
5
|
+
DialogDescription,
|
|
6
|
+
DialogFooter,
|
|
7
|
+
DialogHeader,
|
|
8
|
+
DialogTitle,
|
|
9
|
+
DialogTrigger,
|
|
10
|
+
} from "./dialog";
|
|
11
|
+
import { Button } from "./button";
|
|
12
|
+
import { Input } from "./input";
|
|
13
|
+
import { Label } from "./label";
|
|
14
|
+
|
|
15
|
+
const meta = {
|
|
16
|
+
title: "Components/Dialog",
|
|
17
|
+
component: Dialog,
|
|
18
|
+
tags: ["autodocs"],
|
|
19
|
+
} satisfies Meta<typeof Dialog>;
|
|
20
|
+
|
|
21
|
+
export default meta;
|
|
22
|
+
type Story = StoryObj<typeof meta>;
|
|
23
|
+
|
|
24
|
+
export const Default: Story = {
|
|
25
|
+
render: () => (
|
|
26
|
+
<Dialog>
|
|
27
|
+
<DialogTrigger asChild>
|
|
28
|
+
<Button variant="outline">Open Dialog</Button>
|
|
29
|
+
</DialogTrigger>
|
|
30
|
+
<DialogContent>
|
|
31
|
+
<DialogHeader>
|
|
32
|
+
<DialogTitle>Are you sure?</DialogTitle>
|
|
33
|
+
<DialogDescription>
|
|
34
|
+
This action cannot be undone. This will permanently delete your account.
|
|
35
|
+
</DialogDescription>
|
|
36
|
+
</DialogHeader>
|
|
37
|
+
<DialogFooter>
|
|
38
|
+
<Button variant="outline">Cancel</Button>
|
|
39
|
+
<Button variant="destructive">Delete</Button>
|
|
40
|
+
</DialogFooter>
|
|
41
|
+
</DialogContent>
|
|
42
|
+
</Dialog>
|
|
43
|
+
),
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const WithForm: Story = {
|
|
47
|
+
render: () => (
|
|
48
|
+
<Dialog>
|
|
49
|
+
<DialogTrigger asChild>
|
|
50
|
+
<Button>Edit Profile</Button>
|
|
51
|
+
</DialogTrigger>
|
|
52
|
+
<DialogContent>
|
|
53
|
+
<DialogHeader>
|
|
54
|
+
<DialogTitle>Edit profile</DialogTitle>
|
|
55
|
+
<DialogDescription>
|
|
56
|
+
Make changes to your profile here. Click save when done.
|
|
57
|
+
</DialogDescription>
|
|
58
|
+
</DialogHeader>
|
|
59
|
+
<div className="flex flex-col gap-4 py-4">
|
|
60
|
+
<div className="grid gap-1.5">
|
|
61
|
+
<Label htmlFor="name">Name</Label>
|
|
62
|
+
<Input id="name" defaultValue="John Doe" />
|
|
63
|
+
</div>
|
|
64
|
+
<div className="grid gap-1.5">
|
|
65
|
+
<Label htmlFor="username">Username</Label>
|
|
66
|
+
<Input id="username" defaultValue="@johndoe" />
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
<DialogFooter>
|
|
70
|
+
<Button>Save changes</Button>
|
|
71
|
+
</DialogFooter>
|
|
72
|
+
</DialogContent>
|
|
73
|
+
</Dialog>
|
|
74
|
+
),
|
|
75
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import {
|
|
3
|
+
Drawer,
|
|
4
|
+
DrawerClose,
|
|
5
|
+
DrawerContent,
|
|
6
|
+
DrawerDescription,
|
|
7
|
+
DrawerFooter,
|
|
8
|
+
DrawerHeader,
|
|
9
|
+
DrawerTitle,
|
|
10
|
+
DrawerTrigger,
|
|
11
|
+
} from "./drawer";
|
|
12
|
+
import { Button } from "./button";
|
|
13
|
+
|
|
14
|
+
const meta = {
|
|
15
|
+
title: "Components/Drawer",
|
|
16
|
+
component: Drawer,
|
|
17
|
+
tags: ["autodocs"],
|
|
18
|
+
} satisfies Meta<typeof Drawer>;
|
|
19
|
+
|
|
20
|
+
export default meta;
|
|
21
|
+
type Story = StoryObj<typeof meta>;
|
|
22
|
+
|
|
23
|
+
export const Default: Story = {
|
|
24
|
+
render: () => (
|
|
25
|
+
<Drawer>
|
|
26
|
+
<DrawerTrigger asChild>
|
|
27
|
+
<Button variant="outline">Open Drawer</Button>
|
|
28
|
+
</DrawerTrigger>
|
|
29
|
+
<DrawerContent>
|
|
30
|
+
<DrawerHeader>
|
|
31
|
+
<DrawerTitle>Move Goal</DrawerTitle>
|
|
32
|
+
<DrawerDescription>Set your daily activity goal.</DrawerDescription>
|
|
33
|
+
</DrawerHeader>
|
|
34
|
+
<div className="p-4">
|
|
35
|
+
<p className="text-center text-4xl font-bold">350</p>
|
|
36
|
+
<p className="text-center text-muted-foreground">calories/day</p>
|
|
37
|
+
</div>
|
|
38
|
+
<DrawerFooter>
|
|
39
|
+
<Button>Submit</Button>
|
|
40
|
+
<DrawerClose asChild>
|
|
41
|
+
<Button variant="outline">Cancel</Button>
|
|
42
|
+
</DrawerClose>
|
|
43
|
+
</DrawerFooter>
|
|
44
|
+
</DrawerContent>
|
|
45
|
+
</Drawer>
|
|
46
|
+
),
|
|
47
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import {
|
|
3
|
+
DropdownMenu,
|
|
4
|
+
DropdownMenuContent,
|
|
5
|
+
DropdownMenuGroup,
|
|
6
|
+
DropdownMenuItem,
|
|
7
|
+
DropdownMenuLabel,
|
|
8
|
+
DropdownMenuSeparator,
|
|
9
|
+
DropdownMenuShortcut,
|
|
10
|
+
DropdownMenuTrigger,
|
|
11
|
+
} from "./dropdown-menu";
|
|
12
|
+
import { Button } from "./button";
|
|
13
|
+
import { LogOut, Settings, User, CreditCard, Keyboard } from "lucide-react";
|
|
14
|
+
|
|
15
|
+
const meta = {
|
|
16
|
+
title: "Components/DropdownMenu",
|
|
17
|
+
component: DropdownMenu,
|
|
18
|
+
tags: ["autodocs"],
|
|
19
|
+
} satisfies Meta<typeof DropdownMenu>;
|
|
20
|
+
|
|
21
|
+
export default meta;
|
|
22
|
+
type Story = StoryObj<typeof meta>;
|
|
23
|
+
|
|
24
|
+
export const Default: Story = {
|
|
25
|
+
render: () => (
|
|
26
|
+
<DropdownMenu>
|
|
27
|
+
<DropdownMenuTrigger asChild>
|
|
28
|
+
<Button variant="outline">Open Menu</Button>
|
|
29
|
+
</DropdownMenuTrigger>
|
|
30
|
+
<DropdownMenuContent className="w-56">
|
|
31
|
+
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
|
32
|
+
<DropdownMenuSeparator />
|
|
33
|
+
<DropdownMenuGroup>
|
|
34
|
+
<DropdownMenuItem>
|
|
35
|
+
<User className="mr-2 h-4 w-4" />
|
|
36
|
+
<span>Profile</span>
|
|
37
|
+
<DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
|
|
38
|
+
</DropdownMenuItem>
|
|
39
|
+
<DropdownMenuItem>
|
|
40
|
+
<CreditCard className="mr-2 h-4 w-4" />
|
|
41
|
+
<span>Billing</span>
|
|
42
|
+
<DropdownMenuShortcut>⌘B</DropdownMenuShortcut>
|
|
43
|
+
</DropdownMenuItem>
|
|
44
|
+
<DropdownMenuItem>
|
|
45
|
+
<Settings className="mr-2 h-4 w-4" />
|
|
46
|
+
<span>Settings</span>
|
|
47
|
+
<DropdownMenuShortcut>⌘S</DropdownMenuShortcut>
|
|
48
|
+
</DropdownMenuItem>
|
|
49
|
+
<DropdownMenuItem>
|
|
50
|
+
<Keyboard className="mr-2 h-4 w-4" />
|
|
51
|
+
<span>Keyboard shortcuts</span>
|
|
52
|
+
<DropdownMenuShortcut>⌘K</DropdownMenuShortcut>
|
|
53
|
+
</DropdownMenuItem>
|
|
54
|
+
</DropdownMenuGroup>
|
|
55
|
+
<DropdownMenuSeparator />
|
|
56
|
+
<DropdownMenuItem variant="destructive">
|
|
57
|
+
<LogOut className="mr-2 h-4 w-4" />
|
|
58
|
+
<span>Log out</span>
|
|
59
|
+
<DropdownMenuShortcut>⇧⌘Q</DropdownMenuShortcut>
|
|
60
|
+
</DropdownMenuItem>
|
|
61
|
+
</DropdownMenuContent>
|
|
62
|
+
</DropdownMenu>
|
|
63
|
+
),
|
|
64
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { EmptyState, SearchEmptyState, ErrorEmptyState } from "./empty-state";
|
|
3
|
+
|
|
4
|
+
const meta = {
|
|
5
|
+
title: "Components/EmptyState",
|
|
6
|
+
component: EmptyState,
|
|
7
|
+
tags: ["autodocs"],
|
|
8
|
+
argTypes: {
|
|
9
|
+
variant: {
|
|
10
|
+
control: "select",
|
|
11
|
+
options: ["default", "search", "error"],
|
|
12
|
+
},
|
|
13
|
+
size: {
|
|
14
|
+
control: "select",
|
|
15
|
+
options: ["sm", "md", "lg"],
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
} satisfies Meta<typeof EmptyState>;
|
|
19
|
+
|
|
20
|
+
export default meta;
|
|
21
|
+
type Story = StoryObj<typeof meta>;
|
|
22
|
+
|
|
23
|
+
export const Default: Story = {
|
|
24
|
+
args: {
|
|
25
|
+
title: "No items yet",
|
|
26
|
+
description: "Get started by creating your first item.",
|
|
27
|
+
action: { label: "Create item", onClick: () => {} },
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const Search: Story = {
|
|
32
|
+
render: () => (
|
|
33
|
+
<SearchEmptyState
|
|
34
|
+
title="No results found"
|
|
35
|
+
description="Try adjusting your search or filters to find what you're looking for."
|
|
36
|
+
action={{ label: "Clear filters", onClick: () => {} }}
|
|
37
|
+
/>
|
|
38
|
+
),
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const Error: Story = {
|
|
42
|
+
render: () => (
|
|
43
|
+
<ErrorEmptyState
|
|
44
|
+
title="Something went wrong"
|
|
45
|
+
description="We couldn't load the data. Please try again."
|
|
46
|
+
action={{ label: "Retry", onClick: () => {} }}
|
|
47
|
+
secondaryAction={{ label: "Go back", onClick: () => {} }}
|
|
48
|
+
/>
|
|
49
|
+
),
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const Small: Story = {
|
|
53
|
+
args: {
|
|
54
|
+
title: "No data",
|
|
55
|
+
description: "Nothing to show here.",
|
|
56
|
+
size: "sm",
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const Large: Story = {
|
|
61
|
+
args: {
|
|
62
|
+
title: "Welcome!",
|
|
63
|
+
description: "This is your dashboard. Start by adding some content.",
|
|
64
|
+
size: "lg",
|
|
65
|
+
action: { label: "Get started", onClick: () => {} },
|
|
66
|
+
},
|
|
67
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { useForm } from "react-hook-form";
|
|
3
|
+
import {
|
|
4
|
+
Form,
|
|
5
|
+
FormControl,
|
|
6
|
+
FormDescription,
|
|
7
|
+
FormField,
|
|
8
|
+
FormItem,
|
|
9
|
+
FormLabel,
|
|
10
|
+
FormMessage,
|
|
11
|
+
} from "./form";
|
|
12
|
+
import { Input } from "./input";
|
|
13
|
+
import { Button } from "./button";
|
|
14
|
+
|
|
15
|
+
const meta = {
|
|
16
|
+
title: "Components/Form",
|
|
17
|
+
component: Form,
|
|
18
|
+
tags: ["autodocs"],
|
|
19
|
+
} satisfies Meta<typeof Form>;
|
|
20
|
+
|
|
21
|
+
export default meta;
|
|
22
|
+
type Story = StoryObj<typeof meta>;
|
|
23
|
+
|
|
24
|
+
function FormDemo() {
|
|
25
|
+
const form = useForm({
|
|
26
|
+
defaultValues: {
|
|
27
|
+
username: "",
|
|
28
|
+
email: "",
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<Form {...form}>
|
|
34
|
+
<form onSubmit={form.handleSubmit(() => {})} className="space-y-6 w-[400px]">
|
|
35
|
+
<FormField
|
|
36
|
+
control={form.control}
|
|
37
|
+
name="username"
|
|
38
|
+
rules={{ required: "Username is required" }}
|
|
39
|
+
render={({ field }) => (
|
|
40
|
+
<FormItem>
|
|
41
|
+
<FormLabel>Username</FormLabel>
|
|
42
|
+
<FormControl>
|
|
43
|
+
<Input placeholder="johndoe" {...field} />
|
|
44
|
+
</FormControl>
|
|
45
|
+
<FormDescription>This is your public display name.</FormDescription>
|
|
46
|
+
<FormMessage />
|
|
47
|
+
</FormItem>
|
|
48
|
+
)}
|
|
49
|
+
/>
|
|
50
|
+
<FormField
|
|
51
|
+
control={form.control}
|
|
52
|
+
name="email"
|
|
53
|
+
rules={{ required: "Email is required" }}
|
|
54
|
+
render={({ field }) => (
|
|
55
|
+
<FormItem>
|
|
56
|
+
<FormLabel>Email</FormLabel>
|
|
57
|
+
<FormControl>
|
|
58
|
+
<Input type="email" placeholder="john@example.com" {...field} />
|
|
59
|
+
</FormControl>
|
|
60
|
+
<FormDescription>We'll never share your email.</FormDescription>
|
|
61
|
+
<FormMessage />
|
|
62
|
+
</FormItem>
|
|
63
|
+
)}
|
|
64
|
+
/>
|
|
65
|
+
<Button type="submit">Submit</Button>
|
|
66
|
+
</form>
|
|
67
|
+
</Form>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const Default: Story = {
|
|
72
|
+
render: () => <FormDemo />,
|
|
73
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { HapticButton } from "./haptic-button";
|
|
3
|
+
|
|
4
|
+
const meta = {
|
|
5
|
+
title: "Components/HapticButton",
|
|
6
|
+
component: HapticButton,
|
|
7
|
+
tags: ["autodocs"],
|
|
8
|
+
} satisfies Meta<typeof HapticButton>;
|
|
9
|
+
|
|
10
|
+
export default meta;
|
|
11
|
+
type Story = StoryObj<typeof meta>;
|
|
12
|
+
|
|
13
|
+
export const Light: Story = {
|
|
14
|
+
render: () => <HapticButton hapticStyle="light">Light Haptic</HapticButton>,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const Medium: Story = {
|
|
18
|
+
render: () => (
|
|
19
|
+
<HapticButton hapticStyle="medium" variant="secondary">
|
|
20
|
+
Medium Haptic
|
|
21
|
+
</HapticButton>
|
|
22
|
+
),
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const Heavy: Story = {
|
|
26
|
+
render: () => (
|
|
27
|
+
<HapticButton hapticStyle="heavy" variant="destructive">
|
|
28
|
+
Heavy Haptic
|
|
29
|
+
</HapticButton>
|
|
30
|
+
),
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const AllStyles: Story = {
|
|
34
|
+
render: () => (
|
|
35
|
+
<div className="flex gap-3">
|
|
36
|
+
<HapticButton hapticStyle="light">Light</HapticButton>
|
|
37
|
+
<HapticButton hapticStyle="medium" variant="secondary">Medium</HapticButton>
|
|
38
|
+
<HapticButton hapticStyle="heavy" variant="outline">Heavy</HapticButton>
|
|
39
|
+
</div>
|
|
40
|
+
),
|
|
41
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator } from "./input-otp";
|
|
3
|
+
|
|
4
|
+
const meta = {
|
|
5
|
+
title: "Components/InputOTP",
|
|
6
|
+
component: InputOTP,
|
|
7
|
+
tags: ["autodocs"],
|
|
8
|
+
} satisfies Meta<typeof InputOTP>;
|
|
9
|
+
|
|
10
|
+
export default meta;
|
|
11
|
+
type Story = StoryObj<typeof meta>;
|
|
12
|
+
|
|
13
|
+
export const Default: Story = {
|
|
14
|
+
render: () => (
|
|
15
|
+
<InputOTP maxLength={6}>
|
|
16
|
+
<InputOTPGroup>
|
|
17
|
+
<InputOTPSlot index={0} />
|
|
18
|
+
<InputOTPSlot index={1} />
|
|
19
|
+
<InputOTPSlot index={2} />
|
|
20
|
+
</InputOTPGroup>
|
|
21
|
+
<InputOTPSeparator />
|
|
22
|
+
<InputOTPGroup>
|
|
23
|
+
<InputOTPSlot index={3} />
|
|
24
|
+
<InputOTPSlot index={4} />
|
|
25
|
+
<InputOTPSlot index={5} />
|
|
26
|
+
</InputOTPGroup>
|
|
27
|
+
</InputOTP>
|
|
28
|
+
),
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const FourDigits: Story = {
|
|
32
|
+
render: () => (
|
|
33
|
+
<InputOTP maxLength={4}>
|
|
34
|
+
<InputOTPGroup>
|
|
35
|
+
<InputOTPSlot index={0} />
|
|
36
|
+
<InputOTPSlot index={1} />
|
|
37
|
+
<InputOTPSlot index={2} />
|
|
38
|
+
<InputOTPSlot index={3} />
|
|
39
|
+
</InputOTPGroup>
|
|
40
|
+
</InputOTP>
|
|
41
|
+
),
|
|
42
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { Input } from "./input";
|
|
3
|
+
import { Label } from "./label";
|
|
4
|
+
|
|
5
|
+
const meta = {
|
|
6
|
+
title: "Components/Input",
|
|
7
|
+
component: Input,
|
|
8
|
+
tags: ["autodocs"],
|
|
9
|
+
} satisfies Meta<typeof Input>;
|
|
10
|
+
|
|
11
|
+
export default meta;
|
|
12
|
+
type Story = StoryObj<typeof meta>;
|
|
13
|
+
|
|
14
|
+
export const Default: Story = {
|
|
15
|
+
args: { placeholder: "Enter text..." },
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const WithLabel: Story = {
|
|
19
|
+
render: () => (
|
|
20
|
+
<div className="grid w-full max-w-sm gap-1.5">
|
|
21
|
+
<Label htmlFor="email">Email</Label>
|
|
22
|
+
<Input type="email" id="email" placeholder="name@example.com" />
|
|
23
|
+
</div>
|
|
24
|
+
),
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const Password: Story = {
|
|
28
|
+
args: { type: "password", placeholder: "Enter password..." },
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const Disabled: Story = {
|
|
32
|
+
args: { placeholder: "Disabled", disabled: true },
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const WithValue: Story = {
|
|
36
|
+
args: { defaultValue: "hello@bearmenu.com", type: "email" },
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const File: Story = {
|
|
40
|
+
args: { type: "file" },
|
|
41
|
+
};
|