@bearmenu/ui 0.1.1 → 0.5.1
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/alert.d.ts +1 -1
- package/dist/components/badge.d.ts +1 -1
- package/dist/components/button.d.ts +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/command.d.ts +13 -13
- 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/text.d.ts +3 -3
- 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 +21 -2
- package/src/test/setup.ts +9 -0
|
@@ -1,60 +1,180 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import { DayPicker } from "react-day-picker";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
|
|
5
|
+
import { DayPicker, getDefaultClassNames, type DayButton } from "react-day-picker";
|
|
6
|
+
|
|
6
7
|
import { cn } from "../lib/utils";
|
|
7
|
-
import { buttonVariants } from "./button";
|
|
8
|
+
import { Button, buttonVariants } from "./button";
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
function Calendar({
|
|
11
|
+
className,
|
|
12
|
+
classNames,
|
|
13
|
+
showOutsideDays = true,
|
|
14
|
+
captionLayout = "label",
|
|
15
|
+
buttonVariant = "ghost",
|
|
16
|
+
formatters,
|
|
17
|
+
components,
|
|
18
|
+
...props
|
|
19
|
+
}: React.ComponentProps<typeof DayPicker> & {
|
|
20
|
+
buttonVariant?: React.ComponentProps<typeof Button>["variant"];
|
|
21
|
+
}) {
|
|
22
|
+
const defaultClassNames = getDefaultClassNames();
|
|
10
23
|
|
|
11
|
-
function Calendar({ className, classNames, showOutsideDays = true, ...props }: CalendarProps) {
|
|
12
24
|
return (
|
|
13
25
|
<DayPicker
|
|
14
26
|
showOutsideDays={showOutsideDays}
|
|
15
|
-
className={cn(
|
|
27
|
+
className={cn(
|
|
28
|
+
"group/calendar bg-background p-3 [--cell-size:--spacing(8)] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent",
|
|
29
|
+
String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
|
|
30
|
+
String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
|
|
31
|
+
className
|
|
32
|
+
)}
|
|
33
|
+
captionLayout={captionLayout}
|
|
34
|
+
formatters={{
|
|
35
|
+
formatMonthDropdown: (date) => date.toLocaleString("default", { month: "short" }),
|
|
36
|
+
...formatters,
|
|
37
|
+
}}
|
|
16
38
|
classNames={{
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
39
|
+
root: cn("w-fit", defaultClassNames.root),
|
|
40
|
+
months: cn("relative flex flex-col gap-4 md:flex-row", defaultClassNames.months),
|
|
41
|
+
month: cn("flex w-full flex-col gap-4", defaultClassNames.month),
|
|
42
|
+
nav: cn(
|
|
43
|
+
"absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1",
|
|
44
|
+
defaultClassNames.nav
|
|
45
|
+
),
|
|
46
|
+
button_previous: cn(
|
|
47
|
+
buttonVariants({ variant: buttonVariant }),
|
|
48
|
+
"size-(--cell-size) p-0 select-none aria-disabled:opacity-50",
|
|
49
|
+
defaultClassNames.button_previous
|
|
50
|
+
),
|
|
51
|
+
button_next: cn(
|
|
52
|
+
buttonVariants({ variant: buttonVariant }),
|
|
53
|
+
"size-(--cell-size) p-0 select-none aria-disabled:opacity-50",
|
|
54
|
+
defaultClassNames.button_next
|
|
55
|
+
),
|
|
56
|
+
month_caption: cn(
|
|
57
|
+
"flex h-(--cell-size) w-full items-center justify-center px-(--cell-size)",
|
|
58
|
+
defaultClassNames.month_caption
|
|
59
|
+
),
|
|
60
|
+
dropdowns: cn(
|
|
61
|
+
"flex h-(--cell-size) w-full items-center justify-center gap-1.5 text-sm font-medium",
|
|
62
|
+
defaultClassNames.dropdowns
|
|
63
|
+
),
|
|
64
|
+
dropdown_root: cn(
|
|
65
|
+
"relative rounded-md border border-input shadow-xs has-focus:border-ring has-focus:ring-[3px] has-focus:ring-ring/50",
|
|
66
|
+
defaultClassNames.dropdown_root
|
|
67
|
+
),
|
|
68
|
+
dropdown: cn("absolute inset-0 bg-popover opacity-0", defaultClassNames.dropdown),
|
|
69
|
+
caption_label: cn(
|
|
70
|
+
"font-medium select-none",
|
|
71
|
+
captionLayout === "label"
|
|
72
|
+
? "text-sm"
|
|
73
|
+
: "flex h-8 items-center gap-1 rounded-md pr-1 pl-2 text-sm [&>svg]:size-3.5 [&>svg]:text-muted-foreground",
|
|
74
|
+
defaultClassNames.caption_label
|
|
75
|
+
),
|
|
76
|
+
table: "w-full border-collapse",
|
|
77
|
+
weekdays: cn("flex", defaultClassNames.weekdays),
|
|
78
|
+
weekday: cn(
|
|
79
|
+
"flex-1 rounded-md text-[0.8rem] font-normal text-muted-foreground select-none",
|
|
80
|
+
defaultClassNames.weekday
|
|
81
|
+
),
|
|
82
|
+
week: cn("mt-2 flex w-full", defaultClassNames.week),
|
|
83
|
+
week_number_header: cn("w-(--cell-size) select-none", defaultClassNames.week_number_header),
|
|
84
|
+
week_number: cn(
|
|
85
|
+
"text-[0.8rem] text-muted-foreground select-none",
|
|
86
|
+
defaultClassNames.week_number
|
|
87
|
+
),
|
|
33
88
|
day: cn(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
89
|
+
"group/day relative aspect-square h-full w-full p-0 text-center select-none [&:last-child[data-selected=true]_button]:rounded-r-md",
|
|
90
|
+
props.showWeekNumber
|
|
91
|
+
? "[&:nth-child(2)[data-selected=true]_button]:rounded-l-md"
|
|
92
|
+
: "[&:first-child[data-selected=true]_button]:rounded-l-md",
|
|
93
|
+
defaultClassNames.day
|
|
94
|
+
),
|
|
95
|
+
range_start: cn("rounded-l-md bg-accent", defaultClassNames.range_start),
|
|
96
|
+
range_middle: cn("rounded-none", defaultClassNames.range_middle),
|
|
97
|
+
range_end: cn("rounded-r-md bg-accent", defaultClassNames.range_end),
|
|
98
|
+
today: cn(
|
|
99
|
+
"rounded-md bg-accent text-accent-foreground data-[selected=true]:rounded-none",
|
|
100
|
+
defaultClassNames.today
|
|
101
|
+
),
|
|
102
|
+
outside: cn(
|
|
103
|
+
"text-muted-foreground aria-selected:text-muted-foreground",
|
|
104
|
+
defaultClassNames.outside
|
|
105
|
+
),
|
|
106
|
+
disabled: cn("text-muted-foreground opacity-50", defaultClassNames.disabled),
|
|
107
|
+
hidden: cn("invisible", defaultClassNames.hidden),
|
|
46
108
|
...classNames,
|
|
47
109
|
}}
|
|
48
110
|
components={{
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
111
|
+
Root: ({ className, rootRef, ...props }) => {
|
|
112
|
+
return <div data-slot="calendar" ref={rootRef} className={cn(className)} {...props} />;
|
|
113
|
+
},
|
|
114
|
+
Chevron: ({ className, orientation, ...props }) => {
|
|
115
|
+
if (orientation === "left") {
|
|
116
|
+
return <ChevronLeftIcon className={cn("size-4", className)} {...props} />;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (orientation === "right") {
|
|
120
|
+
return <ChevronRightIcon className={cn("size-4", className)} {...props} />;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return <ChevronDownIcon className={cn("size-4", className)} {...props} />;
|
|
52
124
|
},
|
|
125
|
+
DayButton: CalendarDayButton,
|
|
126
|
+
WeekNumber: ({ children, ...props }) => {
|
|
127
|
+
return (
|
|
128
|
+
<td {...props}>
|
|
129
|
+
<div className="flex size-(--cell-size) items-center justify-center text-center">
|
|
130
|
+
{children}
|
|
131
|
+
</div>
|
|
132
|
+
</td>
|
|
133
|
+
);
|
|
134
|
+
},
|
|
135
|
+
...components,
|
|
53
136
|
}}
|
|
54
137
|
{...props}
|
|
55
138
|
/>
|
|
56
139
|
);
|
|
57
140
|
}
|
|
58
|
-
Calendar.displayName = "Calendar";
|
|
59
141
|
|
|
60
|
-
|
|
142
|
+
function CalendarDayButton({
|
|
143
|
+
className,
|
|
144
|
+
day,
|
|
145
|
+
modifiers,
|
|
146
|
+
...props
|
|
147
|
+
}: React.ComponentProps<typeof DayButton>) {
|
|
148
|
+
const defaultClassNames = getDefaultClassNames();
|
|
149
|
+
|
|
150
|
+
const ref = React.useRef<HTMLButtonElement>(null);
|
|
151
|
+
React.useEffect(() => {
|
|
152
|
+
if (modifiers.focused) ref.current?.focus();
|
|
153
|
+
}, [modifiers.focused]);
|
|
154
|
+
|
|
155
|
+
return (
|
|
156
|
+
<Button
|
|
157
|
+
ref={ref}
|
|
158
|
+
variant="ghost"
|
|
159
|
+
size="icon"
|
|
160
|
+
data-day={day.date.toLocaleDateString()}
|
|
161
|
+
data-selected-single={
|
|
162
|
+
modifiers.selected &&
|
|
163
|
+
!modifiers.range_start &&
|
|
164
|
+
!modifiers.range_end &&
|
|
165
|
+
!modifiers.range_middle
|
|
166
|
+
}
|
|
167
|
+
data-range-start={modifiers.range_start}
|
|
168
|
+
data-range-end={modifiers.range_end}
|
|
169
|
+
data-range-middle={modifiers.range_middle}
|
|
170
|
+
className={cn(
|
|
171
|
+
"group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-ring/50 data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground dark:hover:text-accent-foreground flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[3px] data-[range-end=true]:rounded-md data-[range-end=true]:rounded-r-md data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-md data-[range-start=true]:rounded-l-md [&>span]:text-xs [&>span]:opacity-70",
|
|
172
|
+
defaultClassNames.day,
|
|
173
|
+
className
|
|
174
|
+
)}
|
|
175
|
+
{...props}
|
|
176
|
+
/>
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export { Calendar, CalendarDayButton };
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from "./card";
|
|
3
|
+
import { Button } from "./button";
|
|
4
|
+
import { Input } from "./input";
|
|
5
|
+
import { Label } from "./label";
|
|
6
|
+
|
|
7
|
+
const meta = {
|
|
8
|
+
title: "Components/Card",
|
|
9
|
+
component: Card,
|
|
10
|
+
tags: ["autodocs"],
|
|
11
|
+
argTypes: {
|
|
12
|
+
variant: {
|
|
13
|
+
control: "select",
|
|
14
|
+
options: ["glass", "surface"],
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
} satisfies Meta<typeof Card>;
|
|
18
|
+
|
|
19
|
+
export default meta;
|
|
20
|
+
type Story = StoryObj<typeof meta>;
|
|
21
|
+
|
|
22
|
+
export const Glass: Story = {
|
|
23
|
+
render: () => (
|
|
24
|
+
<Card variant="glass" className="w-[350px]">
|
|
25
|
+
<CardHeader>
|
|
26
|
+
<CardTitle>Glass Card</CardTitle>
|
|
27
|
+
<CardDescription>A card with glass morphism effect.</CardDescription>
|
|
28
|
+
</CardHeader>
|
|
29
|
+
<CardContent>
|
|
30
|
+
<p className="text-sm text-muted-foreground">Card content goes here.</p>
|
|
31
|
+
</CardContent>
|
|
32
|
+
</Card>
|
|
33
|
+
),
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const Surface: Story = {
|
|
37
|
+
render: () => (
|
|
38
|
+
<Card variant="surface" className="w-[350px]">
|
|
39
|
+
<CardHeader>
|
|
40
|
+
<CardTitle>Surface Card</CardTitle>
|
|
41
|
+
<CardDescription>A card with solid surface background.</CardDescription>
|
|
42
|
+
</CardHeader>
|
|
43
|
+
<CardContent>
|
|
44
|
+
<p className="text-sm text-muted-foreground">Card content goes here.</p>
|
|
45
|
+
</CardContent>
|
|
46
|
+
</Card>
|
|
47
|
+
),
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export const WithForm: Story = {
|
|
51
|
+
render: () => (
|
|
52
|
+
<Card className="w-[350px]">
|
|
53
|
+
<CardHeader>
|
|
54
|
+
<CardTitle>Create account</CardTitle>
|
|
55
|
+
<CardDescription>Enter your details below.</CardDescription>
|
|
56
|
+
</CardHeader>
|
|
57
|
+
<CardContent className="flex flex-col gap-4">
|
|
58
|
+
<div className="grid gap-1.5">
|
|
59
|
+
<Label htmlFor="name">Name</Label>
|
|
60
|
+
<Input id="name" placeholder="Your name" />
|
|
61
|
+
</div>
|
|
62
|
+
<div className="grid gap-1.5">
|
|
63
|
+
<Label htmlFor="email">Email</Label>
|
|
64
|
+
<Input id="email" type="email" placeholder="name@example.com" />
|
|
65
|
+
</div>
|
|
66
|
+
</CardContent>
|
|
67
|
+
<CardFooter>
|
|
68
|
+
<Button className="w-full">Sign up</Button>
|
|
69
|
+
</CardFooter>
|
|
70
|
+
</Card>
|
|
71
|
+
),
|
|
72
|
+
};
|
package/src/components/card.tsx
CHANGED
|
@@ -57,6 +57,18 @@ const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDi
|
|
|
57
57
|
);
|
|
58
58
|
CardContent.displayName = "CardContent";
|
|
59
59
|
|
|
60
|
+
const CardAction = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
61
|
+
({ className, ...props }, ref) => (
|
|
62
|
+
<div
|
|
63
|
+
ref={ref}
|
|
64
|
+
data-slot="card-action"
|
|
65
|
+
className={cn("col-start-2 row-span-2 row-start-1 self-start justify-self-end", className)}
|
|
66
|
+
{...props}
|
|
67
|
+
/>
|
|
68
|
+
)
|
|
69
|
+
);
|
|
70
|
+
CardAction.displayName = "CardAction";
|
|
71
|
+
|
|
60
72
|
const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
61
73
|
({ className, ...props }, ref) => (
|
|
62
74
|
<div ref={ref} className={cn("flex items-center p-6 pt-0", className)} {...props} />
|
|
@@ -64,4 +76,4 @@ const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDiv
|
|
|
64
76
|
);
|
|
65
77
|
CardFooter.displayName = "CardFooter";
|
|
66
78
|
|
|
67
|
-
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };
|
|
79
|
+
export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import {
|
|
3
|
+
Carousel,
|
|
4
|
+
CarouselContent,
|
|
5
|
+
CarouselItem,
|
|
6
|
+
CarouselNext,
|
|
7
|
+
CarouselPrevious,
|
|
8
|
+
} from "./carousel";
|
|
9
|
+
import { Card } from "./card";
|
|
10
|
+
|
|
11
|
+
const meta = {
|
|
12
|
+
title: "Components/Carousel",
|
|
13
|
+
component: Carousel,
|
|
14
|
+
tags: ["autodocs"],
|
|
15
|
+
} satisfies Meta<typeof Carousel>;
|
|
16
|
+
|
|
17
|
+
export default meta;
|
|
18
|
+
type Story = StoryObj<typeof meta>;
|
|
19
|
+
|
|
20
|
+
export const Default: Story = {
|
|
21
|
+
render: () => (
|
|
22
|
+
<div className="w-full max-w-xs mx-auto">
|
|
23
|
+
<Carousel>
|
|
24
|
+
<CarouselContent>
|
|
25
|
+
{Array.from({ length: 5 }).map((_, i) => (
|
|
26
|
+
<CarouselItem key={i}>
|
|
27
|
+
<Card className="p-6">
|
|
28
|
+
<div className="flex aspect-square items-center justify-center text-3xl font-semibold">
|
|
29
|
+
{i + 1}
|
|
30
|
+
</div>
|
|
31
|
+
</Card>
|
|
32
|
+
</CarouselItem>
|
|
33
|
+
))}
|
|
34
|
+
</CarouselContent>
|
|
35
|
+
<CarouselPrevious />
|
|
36
|
+
<CarouselNext />
|
|
37
|
+
</Carousel>
|
|
38
|
+
</div>
|
|
39
|
+
),
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const ThreePerView: Story = {
|
|
43
|
+
render: () => (
|
|
44
|
+
<div className="w-full max-w-sm mx-auto">
|
|
45
|
+
<Carousel opts={{ align: "start" }}>
|
|
46
|
+
<CarouselContent className="-ml-2">
|
|
47
|
+
{Array.from({ length: 8 }).map((_, i) => (
|
|
48
|
+
<CarouselItem key={i} className="pl-2 basis-1/3">
|
|
49
|
+
<Card className="p-4">
|
|
50
|
+
<div className="flex aspect-square items-center justify-center text-lg font-semibold">
|
|
51
|
+
{i + 1}
|
|
52
|
+
</div>
|
|
53
|
+
</Card>
|
|
54
|
+
</CarouselItem>
|
|
55
|
+
))}
|
|
56
|
+
</CarouselContent>
|
|
57
|
+
<CarouselPrevious />
|
|
58
|
+
<CarouselNext />
|
|
59
|
+
</Carousel>
|
|
60
|
+
</div>
|
|
61
|
+
),
|
|
62
|
+
};
|
|
@@ -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
|
+
};
|