@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.
Files changed (118) hide show
  1. package/dist/{chunk-2CUFS5LF.js → chunk-2BMJRXQX.js} +1 -1
  2. package/dist/chunk-HLQ2IRE7.js +279 -0
  3. package/dist/chunk-IQASCH2V.js +25 -0
  4. package/dist/{chunk-UXAYXRFN.js → chunk-QJ54Z7TC.js} +15 -1
  5. package/dist/{chunk-X6GATUI2.js → chunk-UISA4CIG.js} +1 -0
  6. package/dist/components/alert-dialog.js +1 -1
  7. package/dist/components/alert.d.ts +1 -1
  8. package/dist/components/badge.d.ts +1 -1
  9. package/dist/components/button.d.ts +1 -1
  10. package/dist/components/button.js +1 -1
  11. package/dist/components/calendar.d.ts +9 -7
  12. package/dist/components/calendar.js +153 -39
  13. package/dist/components/card.d.ts +2 -1
  14. package/dist/components/card.js +1 -1
  15. package/dist/components/carousel.js +1 -1
  16. package/dist/components/checkbox.js +3 -25
  17. package/dist/components/command.d.ts +13 -13
  18. package/dist/components/dropdown-menu.js +1 -1
  19. package/dist/components/empty-state.js +1 -1
  20. package/dist/components/haptic-button.js +1 -1
  21. package/dist/components/link-button.js +1 -1
  22. package/dist/components/multi-select-combobox.js +1 -1
  23. package/dist/components/multi-select-menu.d.ts +26 -0
  24. package/dist/components/multi-select-menu.js +71 -0
  25. package/dist/components/pagination.js +1 -1
  26. package/dist/components/phone-frame.d.ts +17 -0
  27. package/dist/components/phone-frame.js +41 -0
  28. package/dist/components/place-about-tab.d.ts +17 -0
  29. package/dist/components/place-about-tab.js +6 -0
  30. package/dist/components/place-card.d.ts +17 -0
  31. package/dist/components/place-card.js +145 -0
  32. package/dist/components/place-details-mobile.d.ts +18 -0
  33. package/dist/components/place-details-mobile.js +187 -0
  34. package/dist/components/place-types.d.ts +145 -0
  35. package/dist/components/place-types.js +1 -0
  36. package/dist/components/search-bar.js +1 -1
  37. package/dist/components/searchable-select.js +1 -1
  38. package/dist/components/skeleton-card.js +1 -1
  39. package/dist/components/skeleton.js +1 -1
  40. package/dist/components/sliding-panels.js +1 -1
  41. package/dist/components/sonner.js +11 -2
  42. package/dist/components/tabs.js +1 -1
  43. package/dist/components/text.d.ts +3 -3
  44. package/dist/components/toaster.js +1 -1
  45. package/package.json +28 -9
  46. package/src/components/accordion.stories.tsx +51 -0
  47. package/src/components/alert-dialog.stories.tsx +45 -0
  48. package/src/components/alert.stories.tsx +85 -0
  49. package/src/components/aspect-ratio.stories.tsx +47 -0
  50. package/src/components/avatar.stories.tsx +66 -0
  51. package/src/components/badge.stories.tsx +60 -0
  52. package/src/components/breadcrumb.stories.tsx +63 -0
  53. package/src/components/button.stories.tsx +115 -0
  54. package/src/components/button.tsx +1 -1
  55. package/src/components/calendar.stories.tsx +47 -0
  56. package/src/components/calendar.tsx +160 -40
  57. package/src/components/card.stories.tsx +72 -0
  58. package/src/components/card.tsx +13 -1
  59. package/src/components/carousel.stories.tsx +62 -0
  60. package/src/components/checkbox.stories.tsx +54 -0
  61. package/src/components/checkbox.tsx +1 -1
  62. package/src/components/collapsible.stories.tsx +34 -0
  63. package/src/components/command.stories.tsx +64 -0
  64. package/src/components/currency-input.stories.tsx +50 -0
  65. package/src/components/dialog.stories.tsx +75 -0
  66. package/src/components/drawer.stories.tsx +47 -0
  67. package/src/components/dropdown-menu.stories.tsx +64 -0
  68. package/src/components/dropdown-menu.tsx +1 -1
  69. package/src/components/empty-state.stories.tsx +67 -0
  70. package/src/components/form.stories.tsx +73 -0
  71. package/src/components/haptic-button.stories.tsx +41 -0
  72. package/src/components/input-otp.stories.tsx +42 -0
  73. package/src/components/input.stories.tsx +41 -0
  74. package/src/components/label.stories.tsx +35 -0
  75. package/src/components/markdown-renderer.stories.tsx +45 -0
  76. package/src/components/multi-select-combobox.stories.tsx +78 -0
  77. package/src/components/multi-select-menu.tsx +96 -0
  78. package/src/components/pagination.stories.tsx +48 -0
  79. package/src/components/phone-frame.stories.tsx +51 -0
  80. package/src/components/phone-frame.test.tsx +82 -0
  81. package/src/components/phone-frame.tsx +59 -0
  82. package/src/components/place-about-tab.stories.tsx +164 -0
  83. package/src/components/place-about-tab.test.tsx +325 -0
  84. package/src/components/place-about-tab.tsx +474 -0
  85. package/src/components/place-card.stories.tsx +120 -0
  86. package/src/components/place-card.test.tsx +187 -0
  87. package/src/components/place-card.tsx +187 -0
  88. package/src/components/place-details-mobile.tsx +251 -0
  89. package/src/components/place-types.ts +127 -0
  90. package/src/components/popover.stories.tsx +42 -0
  91. package/src/components/progress.stories.tsx +35 -0
  92. package/src/components/radio-group.stories.tsx +46 -0
  93. package/src/components/rating.stories.tsx +45 -0
  94. package/src/components/scroll-area.stories.tsx +48 -0
  95. package/src/components/search-bar.stories.tsx +92 -0
  96. package/src/components/searchable-select.stories.tsx +55 -0
  97. package/src/components/select.stories.tsx +92 -0
  98. package/src/components/separator.stories.tsx +33 -0
  99. package/src/components/sheet.stories.tsx +95 -0
  100. package/src/components/skeleton-card.stories.tsx +53 -0
  101. package/src/components/skeleton.stories.tsx +42 -0
  102. package/src/components/skeleton.tsx +1 -0
  103. package/src/components/slider.stories.tsx +27 -0
  104. package/src/components/sliding-panels.stories.tsx +68 -0
  105. package/src/components/sonner.tsx +17 -2
  106. package/src/components/sticky-container.stories.tsx +33 -0
  107. package/src/components/switch.stories.tsx +35 -0
  108. package/src/components/table.stories.tsx +60 -0
  109. package/src/components/tabs.stories.tsx +53 -0
  110. package/src/components/tabs.tsx +1 -1
  111. package/src/components/text.stories.tsx +82 -0
  112. package/src/components/textarea.stories.tsx +35 -0
  113. package/src/components/toast.stories.tsx +63 -0
  114. package/src/components/toggle-group.stories.tsx +60 -0
  115. package/src/components/toggle.stories.tsx +48 -0
  116. package/src/components/tooltip.stories.tsx +71 -0
  117. package/src/globals.css +21 -2
  118. package/src/test/setup.ts +9 -0
@@ -1,60 +1,180 @@
1
1
  "use client";
2
2
 
3
- import type * as React from "react";
4
- import { ChevronLeft, ChevronRight } from "lucide-react";
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
- export type CalendarProps = React.ComponentProps<typeof DayPicker>;
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("p-3", className)}
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
- months: "flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0",
18
- month: "space-y-4",
19
- caption: "flex justify-center pt-1 relative items-center",
20
- caption_label: "text-sm font-medium",
21
- nav: "space-x-1 flex items-center",
22
- nav_button: cn(
23
- buttonVariants({ variant: "outline" }),
24
- "h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100 glass-hover"
25
- ),
26
- nav_button_previous: "absolute left-1",
27
- nav_button_next: "absolute right-1",
28
- table: "w-full border-collapse space-y-1",
29
- head_row: "flex",
30
- head_cell: "text-muted-foreground rounded-md w-9 font-normal text-[0.8rem]",
31
- row: "flex w-full mt-2",
32
- cell: "h-9 w-9 text-center text-sm p-0 relative [&:has([aria-selected].day-range-end)]:rounded-r-md [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected])]:bg-accent first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md focus-within:relative focus-within:z-20",
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
- buttonVariants({ variant: "ghost" }),
35
- "h-9 w-9 p-0 font-normal aria-selected:opacity-100 glass-hover"
36
- ),
37
- day_range_end: "day-range-end",
38
- day_selected:
39
- "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground glass-glow",
40
- day_today: "bg-accent text-accent-foreground",
41
- day_outside:
42
- "day-outside text-muted-foreground opacity-50 aria-selected:bg-accent/50 aria-selected:text-muted-foreground aria-selected:opacity-30",
43
- day_disabled: "text-muted-foreground opacity-50",
44
- day_range_middle: "aria-selected:bg-accent aria-selected:text-accent-foreground",
45
- day_hidden: "invisible",
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
- Chevron: (props) => {
50
- if (props.orientation === "left") return <ChevronLeft className="h-4 w-4" />;
51
- return <ChevronRight className="h-4 w-4" />;
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
- export { Calendar };
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
+ };
@@ -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 glass transition-all duration-300 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 hover:border-accent/50 hover:shadow-md",
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
+ };