@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.
Files changed (113) 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/button.js +1 -1
  8. package/dist/components/calendar.d.ts +9 -7
  9. package/dist/components/calendar.js +153 -39
  10. package/dist/components/card.d.ts +2 -1
  11. package/dist/components/card.js +1 -1
  12. package/dist/components/carousel.js +1 -1
  13. package/dist/components/checkbox.js +3 -25
  14. package/dist/components/dropdown-menu.js +1 -1
  15. package/dist/components/empty-state.js +1 -1
  16. package/dist/components/haptic-button.js +1 -1
  17. package/dist/components/link-button.js +1 -1
  18. package/dist/components/multi-select-combobox.js +1 -1
  19. package/dist/components/multi-select-menu.d.ts +26 -0
  20. package/dist/components/multi-select-menu.js +71 -0
  21. package/dist/components/pagination.js +1 -1
  22. package/dist/components/phone-frame.d.ts +17 -0
  23. package/dist/components/phone-frame.js +41 -0
  24. package/dist/components/place-about-tab.d.ts +17 -0
  25. package/dist/components/place-about-tab.js +6 -0
  26. package/dist/components/place-card.d.ts +17 -0
  27. package/dist/components/place-card.js +145 -0
  28. package/dist/components/place-details-mobile.d.ts +18 -0
  29. package/dist/components/place-details-mobile.js +187 -0
  30. package/dist/components/place-types.d.ts +145 -0
  31. package/dist/components/place-types.js +1 -0
  32. package/dist/components/search-bar.js +1 -1
  33. package/dist/components/searchable-select.js +1 -1
  34. package/dist/components/skeleton-card.js +1 -1
  35. package/dist/components/skeleton.js +1 -1
  36. package/dist/components/sliding-panels.js +1 -1
  37. package/dist/components/sonner.js +11 -2
  38. package/dist/components/tabs.js +1 -1
  39. package/dist/components/toaster.js +1 -1
  40. package/package.json +28 -9
  41. package/src/components/accordion.stories.tsx +51 -0
  42. package/src/components/alert-dialog.stories.tsx +45 -0
  43. package/src/components/alert.stories.tsx +85 -0
  44. package/src/components/aspect-ratio.stories.tsx +47 -0
  45. package/src/components/avatar.stories.tsx +66 -0
  46. package/src/components/badge.stories.tsx +60 -0
  47. package/src/components/breadcrumb.stories.tsx +63 -0
  48. package/src/components/button.stories.tsx +115 -0
  49. package/src/components/button.tsx +1 -1
  50. package/src/components/calendar.stories.tsx +47 -0
  51. package/src/components/calendar.tsx +160 -40
  52. package/src/components/card.stories.tsx +72 -0
  53. package/src/components/card.tsx +13 -1
  54. package/src/components/carousel.stories.tsx +62 -0
  55. package/src/components/checkbox.stories.tsx +54 -0
  56. package/src/components/checkbox.tsx +1 -1
  57. package/src/components/collapsible.stories.tsx +34 -0
  58. package/src/components/command.stories.tsx +64 -0
  59. package/src/components/currency-input.stories.tsx +50 -0
  60. package/src/components/dialog.stories.tsx +75 -0
  61. package/src/components/drawer.stories.tsx +47 -0
  62. package/src/components/dropdown-menu.stories.tsx +64 -0
  63. package/src/components/dropdown-menu.tsx +1 -1
  64. package/src/components/empty-state.stories.tsx +67 -0
  65. package/src/components/form.stories.tsx +73 -0
  66. package/src/components/haptic-button.stories.tsx +41 -0
  67. package/src/components/input-otp.stories.tsx +42 -0
  68. package/src/components/input.stories.tsx +41 -0
  69. package/src/components/label.stories.tsx +35 -0
  70. package/src/components/markdown-renderer.stories.tsx +45 -0
  71. package/src/components/multi-select-combobox.stories.tsx +78 -0
  72. package/src/components/multi-select-menu.tsx +96 -0
  73. package/src/components/pagination.stories.tsx +48 -0
  74. package/src/components/phone-frame.stories.tsx +51 -0
  75. package/src/components/phone-frame.test.tsx +82 -0
  76. package/src/components/phone-frame.tsx +59 -0
  77. package/src/components/place-about-tab.stories.tsx +164 -0
  78. package/src/components/place-about-tab.test.tsx +325 -0
  79. package/src/components/place-about-tab.tsx +474 -0
  80. package/src/components/place-card.stories.tsx +120 -0
  81. package/src/components/place-card.test.tsx +187 -0
  82. package/src/components/place-card.tsx +187 -0
  83. package/src/components/place-details-mobile.tsx +251 -0
  84. package/src/components/place-types.ts +127 -0
  85. package/src/components/popover.stories.tsx +42 -0
  86. package/src/components/progress.stories.tsx +35 -0
  87. package/src/components/radio-group.stories.tsx +46 -0
  88. package/src/components/rating.stories.tsx +45 -0
  89. package/src/components/scroll-area.stories.tsx +48 -0
  90. package/src/components/search-bar.stories.tsx +92 -0
  91. package/src/components/searchable-select.stories.tsx +55 -0
  92. package/src/components/select.stories.tsx +92 -0
  93. package/src/components/separator.stories.tsx +33 -0
  94. package/src/components/sheet.stories.tsx +95 -0
  95. package/src/components/skeleton-card.stories.tsx +53 -0
  96. package/src/components/skeleton.stories.tsx +42 -0
  97. package/src/components/skeleton.tsx +1 -0
  98. package/src/components/slider.stories.tsx +27 -0
  99. package/src/components/sliding-panels.stories.tsx +68 -0
  100. package/src/components/sonner.tsx +17 -2
  101. package/src/components/sticky-container.stories.tsx +33 -0
  102. package/src/components/switch.stories.tsx +35 -0
  103. package/src/components/table.stories.tsx +60 -0
  104. package/src/components/tabs.stories.tsx +53 -0
  105. package/src/components/tabs.tsx +1 -1
  106. package/src/components/text.stories.tsx +82 -0
  107. package/src/components/textarea.stories.tsx +35 -0
  108. package/src/components/toast.stories.tsx +63 -0
  109. package/src/components/toggle-group.stories.tsx +60 -0
  110. package/src/components/toggle.stories.tsx +48 -0
  111. package/src/components/tooltip.stories.tsx +71 -0
  112. package/src/globals.css +14 -2
  113. package/src/test/setup.ts +9 -0
@@ -0,0 +1,60 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { Badge } from "./badge";
3
+
4
+ const meta = {
5
+ title: "Components/Badge",
6
+ component: Badge,
7
+ tags: ["autodocs"],
8
+ argTypes: {
9
+ variant: {
10
+ control: "select",
11
+ options: ["default", "secondary", "destructive", "outline", "overlay", "glass", "success", "warning"],
12
+ },
13
+ },
14
+ } satisfies Meta<typeof Badge>;
15
+
16
+ export default meta;
17
+ type Story = StoryObj<typeof meta>;
18
+
19
+ export const Default: Story = {
20
+ args: { children: "Badge" },
21
+ };
22
+
23
+ export const Secondary: Story = {
24
+ args: { children: "Secondary", variant: "secondary" },
25
+ };
26
+
27
+ export const Destructive: Story = {
28
+ args: { children: "Destructive", variant: "destructive" },
29
+ };
30
+
31
+ export const Outline: Story = {
32
+ args: { children: "Outline", variant: "outline" },
33
+ };
34
+
35
+ export const Glass: Story = {
36
+ args: { children: "Glass", variant: "glass" },
37
+ };
38
+
39
+ export const Success: Story = {
40
+ args: { children: "Success", variant: "success" },
41
+ };
42
+
43
+ export const Warning: Story = {
44
+ args: { children: "Warning", variant: "warning" },
45
+ };
46
+
47
+ export const AllVariants: Story = {
48
+ render: () => (
49
+ <div className="flex flex-wrap items-center gap-3">
50
+ <Badge variant="default">Default</Badge>
51
+ <Badge variant="secondary">Secondary</Badge>
52
+ <Badge variant="destructive">Destructive</Badge>
53
+ <Badge variant="outline">Outline</Badge>
54
+ <Badge variant="overlay">Overlay</Badge>
55
+ <Badge variant="glass">Glass</Badge>
56
+ <Badge variant="success">Success</Badge>
57
+ <Badge variant="warning">Warning</Badge>
58
+ </div>
59
+ ),
60
+ };
@@ -0,0 +1,63 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import {
3
+ Breadcrumb,
4
+ BreadcrumbEllipsis,
5
+ BreadcrumbItem,
6
+ BreadcrumbLink,
7
+ BreadcrumbList,
8
+ BreadcrumbPage,
9
+ BreadcrumbSeparator,
10
+ } from "./breadcrumb";
11
+
12
+ const meta = {
13
+ title: "Components/Breadcrumb",
14
+ component: Breadcrumb,
15
+ tags: ["autodocs"],
16
+ } satisfies Meta<typeof Breadcrumb>;
17
+
18
+ export default meta;
19
+ type Story = StoryObj<typeof meta>;
20
+
21
+ export const Default: Story = {
22
+ render: () => (
23
+ <Breadcrumb>
24
+ <BreadcrumbList>
25
+ <BreadcrumbItem>
26
+ <BreadcrumbLink href="#">Home</BreadcrumbLink>
27
+ </BreadcrumbItem>
28
+ <BreadcrumbSeparator />
29
+ <BreadcrumbItem>
30
+ <BreadcrumbLink href="#">Products</BreadcrumbLink>
31
+ </BreadcrumbItem>
32
+ <BreadcrumbSeparator />
33
+ <BreadcrumbItem>
34
+ <BreadcrumbPage>Current Page</BreadcrumbPage>
35
+ </BreadcrumbItem>
36
+ </BreadcrumbList>
37
+ </Breadcrumb>
38
+ ),
39
+ };
40
+
41
+ export const WithEllipsis: Story = {
42
+ render: () => (
43
+ <Breadcrumb>
44
+ <BreadcrumbList>
45
+ <BreadcrumbItem>
46
+ <BreadcrumbLink href="#">Home</BreadcrumbLink>
47
+ </BreadcrumbItem>
48
+ <BreadcrumbSeparator />
49
+ <BreadcrumbItem>
50
+ <BreadcrumbEllipsis />
51
+ </BreadcrumbItem>
52
+ <BreadcrumbSeparator />
53
+ <BreadcrumbItem>
54
+ <BreadcrumbLink href="#">Category</BreadcrumbLink>
55
+ </BreadcrumbItem>
56
+ <BreadcrumbSeparator />
57
+ <BreadcrumbItem>
58
+ <BreadcrumbPage>Product</BreadcrumbPage>
59
+ </BreadcrumbItem>
60
+ </BreadcrumbList>
61
+ </Breadcrumb>
62
+ ),
63
+ };
@@ -0,0 +1,115 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { Button } from "./button";
3
+ import { Heart, Mail, Plus } from "lucide-react";
4
+
5
+ const meta = {
6
+ title: "Components/Button",
7
+ component: Button,
8
+ tags: ["autodocs"],
9
+ argTypes: {
10
+ variant: {
11
+ control: "select",
12
+ options: [
13
+ "default",
14
+ "destructive",
15
+ "outline",
16
+ "secondary",
17
+ "ghost",
18
+ "link",
19
+ "glass",
20
+ "brown",
21
+ "antagonist",
22
+ ],
23
+ },
24
+ size: {
25
+ control: "select",
26
+ options: ["default", "sm", "lg", "icon"],
27
+ },
28
+ },
29
+ } satisfies Meta<typeof Button>;
30
+
31
+ export default meta;
32
+ type Story = StoryObj<typeof meta>;
33
+
34
+ export const Default: Story = {
35
+ args: { children: "Button" },
36
+ };
37
+
38
+ export const Destructive: Story = {
39
+ args: { children: "Delete", variant: "destructive" },
40
+ };
41
+
42
+ export const Outline: Story = {
43
+ args: { children: "Outline", variant: "outline" },
44
+ };
45
+
46
+ export const Secondary: Story = {
47
+ args: { children: "Secondary", variant: "secondary" },
48
+ };
49
+
50
+ export const Ghost: Story = {
51
+ args: { children: "Ghost", variant: "ghost" },
52
+ };
53
+
54
+ export const Link: Story = {
55
+ args: { children: "Link", variant: "link" },
56
+ };
57
+
58
+ export const Glass: Story = {
59
+ args: { children: "Glass", variant: "glass" },
60
+ };
61
+
62
+ export const Brown: Story = {
63
+ args: { children: "Brown", variant: "brown" },
64
+ };
65
+
66
+ export const Antagonist: Story = {
67
+ args: { children: "Antagonist", variant: "antagonist" },
68
+ };
69
+
70
+ export const Small: Story = {
71
+ args: { children: "Small", size: "sm" },
72
+ };
73
+
74
+ export const Large: Story = {
75
+ args: { children: "Large", size: "lg" },
76
+ };
77
+
78
+ export const Icon: Story = {
79
+ args: { size: "icon", children: <Plus className="h-4 w-4" /> },
80
+ };
81
+
82
+ export const WithIcon: Story = {
83
+ args: { children: <><Mail className="h-4 w-4" /> Send Email</> },
84
+ };
85
+
86
+ export const Disabled: Story = {
87
+ args: { children: "Disabled", disabled: true },
88
+ };
89
+
90
+ export const AllVariants: Story = {
91
+ render: () => (
92
+ <div className="flex flex-wrap items-center gap-4">
93
+ <Button variant="default">Default</Button>
94
+ <Button variant="destructive">Destructive</Button>
95
+ <Button variant="outline">Outline</Button>
96
+ <Button variant="secondary">Secondary</Button>
97
+ <Button variant="ghost">Ghost</Button>
98
+ <Button variant="link">Link</Button>
99
+ <Button variant="glass">Glass</Button>
100
+ <Button variant="brown">Brown</Button>
101
+ <Button variant="antagonist">Antagonist</Button>
102
+ </div>
103
+ ),
104
+ };
105
+
106
+ export const AllSizes: Story = {
107
+ render: () => (
108
+ <div className="flex items-center gap-4">
109
+ <Button size="sm">Small</Button>
110
+ <Button size="default">Default</Button>
111
+ <Button size="lg">Large</Button>
112
+ <Button size="icon"><Heart className="h-4 w-4" /></Button>
113
+ </div>
114
+ ),
115
+ };
@@ -14,7 +14,7 @@ const buttonVariants = cva(
14
14
  destructive:
15
15
  "bg-destructive text-destructive-foreground shadow-lg hover:bg-destructive/90 hover:shadow-destructive/20 backdrop-blur-sm border border-destructive/20",
16
16
  outline:
17
- "border border-border bg-card/70 backdrop-blur-md hover:bg-accent hover:text-accent-foreground hover:border-accent-foreground/20 hover:shadow-lg glass-hover",
17
+ "border border-border bg-sidebar backdrop-blur-md hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:border-sidebar-accent-foreground/20 hover:shadow-lg",
18
18
  secondary:
19
19
  "bg-secondary text-secondary-foreground shadow-lg hover:bg-secondary/90 hover:shadow-secondary/20 backdrop-blur-sm border border-secondary/20",
20
20
  ghost: "hover:bg-foreground/10 hover:text-foreground transition-colors duration-200",
@@ -0,0 +1,47 @@
1
+ import { useState } from "react";
2
+ import type { Meta, StoryObj } from "@storybook/react";
3
+ import { Calendar } from "./calendar";
4
+
5
+ const meta = {
6
+ title: "Components/Calendar",
7
+ component: Calendar,
8
+ tags: ["autodocs"],
9
+ } satisfies Meta<typeof Calendar>;
10
+
11
+ export default meta;
12
+ type Story = StoryObj<typeof meta>;
13
+
14
+ function CalendarDemo() {
15
+ const [date, setDate] = useState<Date | undefined>(new Date());
16
+ return (
17
+ <Calendar
18
+ mode="single"
19
+ selected={date}
20
+ onSelect={setDate}
21
+ className="rounded-md border"
22
+ />
23
+ );
24
+ }
25
+
26
+ export const Default: Story = {
27
+ render: () => <CalendarDemo />,
28
+ };
29
+
30
+ function CalendarRangeDemo() {
31
+ const [range, setRange] = useState<{ from: Date; to?: Date }>({
32
+ from: new Date(),
33
+ });
34
+ return (
35
+ <Calendar
36
+ mode="range"
37
+ selected={range}
38
+ onSelect={(r) => r && setRange(r as { from: Date; to?: Date })}
39
+ numberOfMonths={2}
40
+ className="rounded-md border"
41
+ />
42
+ );
43
+ }
44
+
45
+ export const Range: Story = {
46
+ render: () => <CalendarRangeDemo />,
47
+ };
@@ -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
+ };