@a2v2ai/uikit 0.0.1 → 0.0.3

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 (148) hide show
  1. package/Alert/Alert.stories.tsx +121 -0
  2. package/Alert/Alert.tsx +71 -0
  3. package/AlertDialog/AlertDialog.stories.tsx +665 -0
  4. package/AlertDialog/AlertDialog.tsx +241 -0
  5. package/Avatar/Avatar.stories.tsx +128 -0
  6. package/Avatar/Avatar.tsx +71 -0
  7. package/Badge/Badge.stories.tsx +76 -0
  8. package/Badge/Badge.tsx +39 -0
  9. package/Breadcrumb/Breadcrumb.stories.tsx +231 -0
  10. package/Breadcrumb/Breadcrumb.tsx +114 -0
  11. package/Button/Button.stories.tsx +684 -0
  12. package/Button/Button.tsx +90 -0
  13. package/Calendar/Calendar.stories.tsx +207 -0
  14. package/Calendar/Calendar.tsx +232 -0
  15. package/Card/Card.stories.tsx +136 -0
  16. package/Card/Card.tsx +96 -0
  17. package/ChatBubble/ChatBubble.stories.tsx +307 -0
  18. package/ChatBubble/ChatBubble.tsx +167 -0
  19. package/Checkbox/Checkbox.stories.tsx +137 -0
  20. package/Checkbox/Checkbox.tsx +53 -0
  21. package/Drawer/Drawer.stories.tsx +721 -0
  22. package/Drawer/Drawer.tsx +201 -0
  23. package/DropdownMenu/DropdownMenu.stories.tsx +251 -0
  24. package/DropdownMenu/DropdownMenu.tsx +199 -0
  25. package/ErrorMessage/ErrorMessage.stories.tsx +159 -0
  26. package/ErrorMessage/ErrorMessage.tsx +55 -0
  27. package/Flex/Flex.tsx +102 -0
  28. package/IconButton/IconButton.stories.tsx +566 -0
  29. package/IconButton/IconButton.tsx +95 -0
  30. package/Input/Input.stories.tsx +456 -0
  31. package/Input/Input.tsx +129 -0
  32. package/InputOTP/InputOTP.stories.tsx +246 -0
  33. package/InputOTP/InputOTP.tsx +127 -0
  34. package/Label/Label.stories.tsx +105 -0
  35. package/Label/Label.tsx +43 -0
  36. package/Loader/Loader.stories.tsx +170 -0
  37. package/Loader/Loader.tsx +62 -0
  38. package/Popover/Popover.stories.tsx +133 -0
  39. package/Popover/Popover.tsx +31 -0
  40. package/Progress/Progress.stories.tsx +146 -0
  41. package/Progress/Progress.tsx +67 -0
  42. package/README.md +12 -12
  43. package/RadioGroup/RadioGroup.stories.tsx +159 -0
  44. package/RadioGroup/RadioGroup.tsx +68 -0
  45. package/ScrollArea/ScrollArea.stories.tsx +136 -0
  46. package/ScrollArea/ScrollArea.tsx +46 -0
  47. package/Select/Select.stories.tsx +242 -0
  48. package/Select/Select.tsx +180 -0
  49. package/Separator/Separator.stories.tsx +110 -0
  50. package/Separator/Separator.tsx +29 -0
  51. package/Skeleton/Skeleton.stories.tsx +117 -0
  52. package/Skeleton/Skeleton.tsx +16 -0
  53. package/Spinner/Spinner.stories.tsx +210 -0
  54. package/Spinner/Spinner.tsx +78 -0
  55. package/Switch/Switch.stories.tsx +146 -0
  56. package/Switch/Switch.tsx +59 -0
  57. package/Tabs/Tabs.stories.tsx +197 -0
  58. package/Tabs/Tabs.tsx +74 -0
  59. package/Textarea/Textarea.stories.tsx +170 -0
  60. package/Textarea/Textarea.tsx +51 -0
  61. package/Toast/Toast.stories.tsx +285 -0
  62. package/Toast/Toast.tsx +59 -0
  63. package/Tooltip/Tooltip.stories.tsx +463 -0
  64. package/Tooltip/Tooltip.tsx +96 -0
  65. package/Typography/Typography.stories.tsx +235 -0
  66. package/Typography/Typography.tsx +171 -0
  67. package/helpers.ts +5 -0
  68. package/icons.ts +2 -0
  69. package/index.ts +136 -0
  70. package/lib/utils.ts +15 -0
  71. package/package.json +4 -1
  72. package/tsconfig.json +24 -0
  73. package/Alert/Alert.d.ts +0 -11
  74. package/Alert/Alert.js +0 -64
  75. package/AlertDialog/AlertDialog.d.ts +0 -35
  76. package/AlertDialog/AlertDialog.js +0 -121
  77. package/Avatar/Avatar.d.ts +0 -12
  78. package/Avatar/Avatar.js +0 -64
  79. package/Badge/Badge.d.ts +0 -9
  80. package/Badge/Badge.js +0 -26
  81. package/Breadcrumb/Breadcrumb.d.ts +0 -19
  82. package/Breadcrumb/Breadcrumb.js +0 -65
  83. package/Button/Button.d.ts +0 -14
  84. package/Button/Button.js +0 -75
  85. package/Calendar/Calendar.d.ts +0 -16
  86. package/Calendar/Calendar.js +0 -113
  87. package/Card/Card.d.ts +0 -14
  88. package/Card/Card.js +0 -70
  89. package/ChatBubble/ChatBubble.d.ts +0 -29
  90. package/ChatBubble/ChatBubble.js +0 -133
  91. package/Checkbox/Checkbox.d.ts +0 -10
  92. package/Checkbox/Checkbox.js +0 -57
  93. package/Dialog/Dialog.d.ts +0 -35
  94. package/Dialog/Dialog.js +0 -130
  95. package/Drawer/Drawer.d.ts +0 -31
  96. package/Drawer/Drawer.js +0 -69
  97. package/DropdownMenu/DropdownMenu.d.ts +0 -27
  98. package/DropdownMenu/DropdownMenu.js +0 -85
  99. package/ErrorMessage/ErrorMessage.d.ts +0 -27
  100. package/ErrorMessage/ErrorMessage.js +0 -15
  101. package/Flex/Flex.d.ts +0 -23
  102. package/Flex/Flex.js +0 -101
  103. package/IconButton/IconButton.d.ts +0 -17
  104. package/IconButton/IconButton.js +0 -85
  105. package/Input/Input.d.ts +0 -16
  106. package/Input/Input.js +0 -75
  107. package/InputOTP/InputOTP.d.ts +0 -18
  108. package/InputOTP/InputOTP.js +0 -85
  109. package/Label/Label.d.ts +0 -10
  110. package/Label/Label.js +0 -57
  111. package/Loader/Loader.d.ts +0 -18
  112. package/Loader/Loader.js +0 -67
  113. package/Popover/Popover.d.ts +0 -7
  114. package/Popover/Popover.js +0 -49
  115. package/Progress/Progress.d.ts +0 -13
  116. package/Progress/Progress.js +0 -71
  117. package/RadioGroup/RadioGroup.d.ts +0 -11
  118. package/RadioGroup/RadioGroup.js +0 -64
  119. package/ScrollArea/ScrollArea.d.ts +0 -5
  120. package/ScrollArea/ScrollArea.js +0 -48
  121. package/Select/Select.d.ts +0 -19
  122. package/Select/Select.js +0 -85
  123. package/Separator/Separator.d.ts +0 -4
  124. package/Separator/Separator.js +0 -43
  125. package/Skeleton/Skeleton.d.ts +0 -4
  126. package/Skeleton/Skeleton.js +0 -8
  127. package/Spinner/Spinner.d.ts +0 -15
  128. package/Spinner/Spinner.js +0 -68
  129. package/Switch/Switch.d.ts +0 -10
  130. package/Switch/Switch.js +0 -67
  131. package/Tabs/Tabs.d.ts +0 -13
  132. package/Tabs/Tabs.js +0 -64
  133. package/Textarea/Textarea.d.ts +0 -10
  134. package/Textarea/Textarea.js +0 -64
  135. package/Toast/Toast.d.ts +0 -10
  136. package/Toast/Toast.js +0 -29
  137. package/Tooltip/Tooltip.d.ts +0 -15
  138. package/Tooltip/Tooltip.js +0 -68
  139. package/Typography/Typography.d.ts +0 -15
  140. package/Typography/Typography.js +0 -125
  141. package/helpers.d.ts +0 -4
  142. package/helpers.js +0 -13
  143. package/icons.d.ts +0 -1
  144. package/icons.js +0 -18
  145. package/index.d.ts +0 -35
  146. package/index.js +0 -183
  147. package/lib/utils.d.ts +0 -3
  148. package/lib/utils.js +0 -18
@@ -0,0 +1,90 @@
1
+ import * as React from "react"
2
+ import { Slot } from "@radix-ui/react-slot"
3
+ import { cva } from "class-variance-authority"
4
+
5
+ import { cn } from "../lib/utils"
6
+
7
+ type ButtonVariant = "primary" | "secondary" | "outline" | "ghost" | "ghost-muted" | "destructive"
8
+ type ButtonSize = "mini" | "small" | "regular" | "large"
9
+ type ButtonRoundness = "default" | "round"
10
+
11
+ const buttonVariants = cva(
12
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap font-normal font-sans cursor-pointer transition-colors focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_#d4d4d4] disabled:pointer-events-none disabled:opacity-50 disabled:cursor-not-allowed [&_svg]:pointer-events-none [&_svg]:shrink-0",
13
+ {
14
+ variants: {
15
+ variant: {
16
+ primary:
17
+ "bg-main-950 text-main-50 hover:bg-main-800",
18
+ secondary:
19
+ "bg-main-100 text-main-950 hover:bg-main-200",
20
+ outline:
21
+ "border border-main-300 bg-transparent text-main-950 hover:bg-main-100",
22
+ ghost:
23
+ "bg-transparent text-main-600 hover:bg-main-100 hover:text-main-950",
24
+ "ghost-muted":
25
+ "bg-transparent text-main-400 hover:bg-main-100 hover:text-main-600",
26
+ destructive:
27
+ "bg-error-600 text-white hover:bg-error-700",
28
+ },
29
+ size: {
30
+ mini: "h-6 px-3 text-xs rounded-md [&_svg]:size-3",
31
+ small: "h-8 px-3 text-sm rounded-md [&_svg]:size-3.5",
32
+ regular: "h-9 px-4 text-sm rounded-lg [&_svg]:size-4",
33
+ large: "h-10 px-4 text-base rounded-lg [&_svg]:size-5",
34
+ },
35
+ roundness: {
36
+ default: "",
37
+ round: "!rounded-full",
38
+ },
39
+ },
40
+ defaultVariants: {
41
+ variant: "primary",
42
+ size: "regular",
43
+ roundness: "default",
44
+ },
45
+ }
46
+ )
47
+
48
+ export interface ButtonProps
49
+ extends React.ButtonHTMLAttributes<HTMLButtonElement> {
50
+ variant?: ButtonVariant
51
+ size?: ButtonSize
52
+ roundness?: ButtonRoundness
53
+ asChild?: boolean
54
+ leftIcon?: React.ReactNode
55
+ rightIcon?: React.ReactNode
56
+ }
57
+
58
+ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
59
+ (
60
+ {
61
+ className,
62
+ variant,
63
+ size,
64
+ roundness,
65
+ asChild = false,
66
+ leftIcon,
67
+ rightIcon,
68
+ children,
69
+ ...props
70
+ },
71
+ ref
72
+ ) => {
73
+ const Comp = asChild ? Slot : "button"
74
+ return (
75
+ <Comp
76
+ className={cn(buttonVariants({ variant, size, roundness, className }))}
77
+ ref={ref}
78
+ {...props}
79
+ >
80
+ {leftIcon && <span className="shrink-0">{leftIcon}</span>}
81
+ {children}
82
+ {rightIcon && <span className="shrink-0">{rightIcon}</span>}
83
+ </Comp>
84
+ )
85
+ }
86
+ )
87
+ Button.displayName = "Button"
88
+
89
+ export { Button, buttonVariants }
90
+ export type { ButtonVariant, ButtonSize, ButtonRoundness }
@@ -0,0 +1,207 @@
1
+ import type { Meta, StoryObj } from "@storybook/react"
2
+ import * as React from "react"
3
+ import type { DateRange } from "react-day-picker"
4
+ import { Calendar } from "./Calendar"
5
+ import { Flex } from "../Flex/Flex"
6
+ import { Typography } from "../Typography/Typography"
7
+
8
+ const meta: Meta<typeof Calendar> = {
9
+ title: "Components/Calendar",
10
+ component: Calendar,
11
+ parameters: {
12
+ layout: "centered",
13
+ },
14
+ tags: ["autodocs"],
15
+ argTypes: {
16
+ mode: {
17
+ control: "select",
18
+ options: ["single", "multiple", "range"],
19
+ description: "Selection mode for the calendar",
20
+ },
21
+ showOutsideDays: {
22
+ control: "boolean",
23
+ description: "Show days from adjacent months",
24
+ },
25
+ showWeekNumber: {
26
+ control: "boolean",
27
+ description: "Show week numbers",
28
+ },
29
+ captionLayout: {
30
+ control: "select",
31
+ options: ["label", "dropdown", "dropdown-months", "dropdown-years"],
32
+ description: "Layout of the caption (month/year selector)",
33
+ },
34
+ },
35
+ }
36
+
37
+ export default meta
38
+ type Story = StoryObj<typeof Calendar>
39
+
40
+ export const Default: Story = {
41
+ render: () => {
42
+ const [date, setDate] = React.useState<Date | undefined>(new Date())
43
+ return (
44
+ <Calendar
45
+ mode="single"
46
+ selected={date}
47
+ onSelect={setDate}
48
+ />
49
+ )
50
+ },
51
+ }
52
+
53
+ export const SingleSelection: Story = {
54
+ render: () => {
55
+ const [date, setDate] = React.useState<Date | undefined>(new Date())
56
+ return (
57
+ <Flex direction="column" gap={4} align="center">
58
+ <Calendar
59
+ mode="single"
60
+ selected={date}
61
+ onSelect={setDate}
62
+ />
63
+ <Typography variant="body2" color="main-600">
64
+ Selected: {date ? date.toLocaleDateString() : "None"}
65
+ </Typography>
66
+ </Flex>
67
+ )
68
+ },
69
+ }
70
+
71
+ export const MultipleSelection: Story = {
72
+ render: () => {
73
+ const [dates, setDates] = React.useState<Date[] | undefined>([])
74
+ return (
75
+ <Flex direction="column" gap={4} align="center">
76
+ <Calendar
77
+ mode="multiple"
78
+ selected={dates}
79
+ onSelect={setDates}
80
+ />
81
+ <Typography variant="body2" color="main-600">
82
+ Selected: {dates && dates.length > 0
83
+ ? dates.map(d => d.toLocaleDateString()).join(", ")
84
+ : "None"}
85
+ </Typography>
86
+ </Flex>
87
+ )
88
+ },
89
+ }
90
+
91
+ export const RangeSelection: Story = {
92
+ render: () => {
93
+ const [range, setRange] = React.useState<DateRange | undefined>()
94
+ return (
95
+ <Flex direction="column" gap={4} align="center">
96
+ <Calendar
97
+ mode="range"
98
+ selected={range}
99
+ onSelect={setRange}
100
+ numberOfMonths={2}
101
+ />
102
+ <Typography variant="body2" color="main-600">
103
+ {range?.from && range?.to
104
+ ? `${range.from.toLocaleDateString()} - ${range.to.toLocaleDateString()}`
105
+ : range?.from
106
+ ? `${range.from.toLocaleDateString()} - Select end date`
107
+ : "Select a date range"}
108
+ </Typography>
109
+ </Flex>
110
+ )
111
+ },
112
+ }
113
+
114
+ export const WithWeekNumbers: Story = {
115
+ render: () => {
116
+ const [date, setDate] = React.useState<Date | undefined>(new Date())
117
+ return (
118
+ <Calendar
119
+ mode="single"
120
+ selected={date}
121
+ onSelect={setDate}
122
+ showWeekNumber
123
+ />
124
+ )
125
+ },
126
+ }
127
+
128
+ export const WithDropdowns: Story = {
129
+ render: () => {
130
+ const [date, setDate] = React.useState<Date | undefined>(new Date())
131
+ return (
132
+ <Calendar
133
+ mode="single"
134
+ selected={date}
135
+ onSelect={setDate}
136
+ captionLayout="dropdown"
137
+ fromYear={2020}
138
+ toYear={2030}
139
+ />
140
+ )
141
+ },
142
+ }
143
+
144
+ export const DisabledDates: Story = {
145
+ render: () => {
146
+ const [date, setDate] = React.useState<Date | undefined>(new Date())
147
+
148
+ // Disable weekends
149
+ const disabledDays = [
150
+ { dayOfWeek: [0, 6] }, // Sunday and Saturday
151
+ ]
152
+
153
+ return (
154
+ <Flex direction="column" gap={4} align="center">
155
+ <Typography variant="body2" color="main-600">
156
+ Weekends are disabled
157
+ </Typography>
158
+ <Calendar
159
+ mode="single"
160
+ selected={date}
161
+ onSelect={setDate}
162
+ disabled={disabledDays}
163
+ />
164
+ </Flex>
165
+ )
166
+ },
167
+ }
168
+
169
+ export const LimitedDateRange: Story = {
170
+ render: () => {
171
+ const [date, setDate] = React.useState<Date | undefined>(new Date())
172
+
173
+ // Only allow dates in the next 30 days
174
+ const today = new Date()
175
+ const thirtyDaysFromNow = new Date()
176
+ thirtyDaysFromNow.setDate(today.getDate() + 30)
177
+
178
+ return (
179
+ <Flex direction="column" gap={4} align="center">
180
+ <Typography variant="body2" color="main-600">
181
+ Only next 30 days selectable
182
+ </Typography>
183
+ <Calendar
184
+ mode="single"
185
+ selected={date}
186
+ onSelect={setDate}
187
+ fromDate={today}
188
+ toDate={thirtyDaysFromNow}
189
+ />
190
+ </Flex>
191
+ )
192
+ },
193
+ }
194
+
195
+ export const TwoMonths: Story = {
196
+ render: () => {
197
+ const [date, setDate] = React.useState<Date | undefined>(new Date())
198
+ return (
199
+ <Calendar
200
+ mode="single"
201
+ selected={date}
202
+ onSelect={setDate}
203
+ numberOfMonths={2}
204
+ />
205
+ )
206
+ },
207
+ }
@@ -0,0 +1,232 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import {
5
+ ChevronDownIcon,
6
+ ChevronLeftIcon,
7
+ ChevronRightIcon,
8
+ } from "lucide-react"
9
+ import { DayButton, DayPicker, getDefaultClassNames } from "react-day-picker"
10
+
11
+ import { cn } from "../lib/utils"
12
+ import { Button, buttonVariants } from "../Button/Button"
13
+
14
+ export type CalendarProps = React.ComponentProps<typeof DayPicker> & {
15
+ /**
16
+ * Variant for navigation buttons
17
+ * @default "ghost"
18
+ */
19
+ buttonVariant?: React.ComponentProps<typeof Button>["variant"]
20
+ }
21
+
22
+ function Calendar({
23
+ className,
24
+ classNames,
25
+ showOutsideDays = true,
26
+ captionLayout = "label",
27
+ buttonVariant = "ghost",
28
+ formatters,
29
+ components,
30
+ ...props
31
+ }: CalendarProps) {
32
+ const defaultClassNames = getDefaultClassNames()
33
+
34
+ return (
35
+ <DayPicker
36
+ showOutsideDays={showOutsideDays}
37
+ className={cn(
38
+ "bg-white group/calendar p-3 rounded-lg border border-grey-200",
39
+ className
40
+ )}
41
+ captionLayout={captionLayout}
42
+ formatters={{
43
+ formatMonthDropdown: (date) =>
44
+ date.toLocaleString("default", { month: "short" }),
45
+ ...formatters,
46
+ }}
47
+ classNames={{
48
+ root: cn("w-fit", defaultClassNames.root),
49
+ months: cn(
50
+ "flex gap-4 flex-col md:flex-row relative",
51
+ defaultClassNames.months
52
+ ),
53
+ month: cn("flex flex-col w-full gap-4", defaultClassNames.month),
54
+ nav: cn(
55
+ "flex items-center gap-1 w-full absolute top-0 inset-x-0 justify-between",
56
+ defaultClassNames.nav
57
+ ),
58
+ button_previous: cn(
59
+ buttonVariants({ variant: buttonVariant, size: "small" }),
60
+ "size-8 aria-disabled:opacity-50 p-0 select-none",
61
+ defaultClassNames.button_previous
62
+ ),
63
+ button_next: cn(
64
+ buttonVariants({ variant: buttonVariant, size: "small" }),
65
+ "size-8 aria-disabled:opacity-50 p-0 select-none",
66
+ defaultClassNames.button_next
67
+ ),
68
+ month_caption: cn(
69
+ "flex items-center justify-center h-8 w-full px-8",
70
+ defaultClassNames.month_caption
71
+ ),
72
+ dropdowns: cn(
73
+ "w-full flex items-center text-sm font-medium justify-center h-8 gap-1.5",
74
+ defaultClassNames.dropdowns
75
+ ),
76
+ dropdown_root: cn(
77
+ "relative has-focus:border-grey-400 border border-grey-300 shadow-xs has-focus:ring-grey-300/50 has-focus:ring-[3px] rounded-md",
78
+ defaultClassNames.dropdown_root
79
+ ),
80
+ dropdown: cn(
81
+ "absolute bg-white inset-0 opacity-0",
82
+ defaultClassNames.dropdown
83
+ ),
84
+ caption_label: cn(
85
+ "select-none font-medium text-main-900",
86
+ captionLayout === "label"
87
+ ? "text-sm"
88
+ : "rounded-md pl-2 pr-1 flex items-center gap-1 text-sm h-8 [&>svg]:text-main-400 [&>svg]:size-3.5",
89
+ defaultClassNames.caption_label
90
+ ),
91
+ table: "w-full border-collapse",
92
+ weekdays: cn("flex", defaultClassNames.weekdays),
93
+ weekday: cn(
94
+ "text-main-500 rounded-md flex-1 font-normal text-[0.8rem] select-none w-8 h-8 flex items-center justify-center",
95
+ defaultClassNames.weekday
96
+ ),
97
+ week: cn("flex w-full mt-2", defaultClassNames.week),
98
+ week_number_header: cn(
99
+ "select-none w-8",
100
+ defaultClassNames.week_number_header
101
+ ),
102
+ week_number: cn(
103
+ "text-[0.8rem] select-none text-main-400",
104
+ defaultClassNames.week_number
105
+ ),
106
+ day: cn(
107
+ "relative w-8 h-8 p-0 text-center [&:last-child[data-selected=true]_button]:rounded-r-md group/day select-none",
108
+ props.showWeekNumber
109
+ ? "[&:nth-child(2)[data-selected=true]_button]:rounded-l-md"
110
+ : "[&:first-child[data-selected=true]_button]:rounded-l-md",
111
+ defaultClassNames.day
112
+ ),
113
+ range_start: cn(
114
+ "rounded-l-md bg-main-100",
115
+ defaultClassNames.range_start
116
+ ),
117
+ range_middle: cn("rounded-none", defaultClassNames.range_middle),
118
+ range_end: cn("rounded-r-md bg-main-100", defaultClassNames.range_end),
119
+ today: cn(
120
+ "bg-main-100 text-main-900 rounded-md data-[selected=true]:rounded-none",
121
+ defaultClassNames.today
122
+ ),
123
+ outside: cn(
124
+ "text-main-300 aria-selected:text-main-400",
125
+ defaultClassNames.outside
126
+ ),
127
+ disabled: cn(
128
+ "text-main-300 opacity-50",
129
+ defaultClassNames.disabled
130
+ ),
131
+ hidden: cn("invisible", defaultClassNames.hidden),
132
+ ...classNames,
133
+ }}
134
+ components={{
135
+ Root: ({ className, rootRef, ...props }) => {
136
+ return (
137
+ <div
138
+ data-slot="calendar"
139
+ ref={rootRef}
140
+ className={cn(className)}
141
+ {...props}
142
+ />
143
+ )
144
+ },
145
+ Chevron: ({ className, orientation, ...props }) => {
146
+ if (orientation === "left") {
147
+ return (
148
+ <ChevronLeftIcon className={cn("size-4", className)} {...props} />
149
+ )
150
+ }
151
+
152
+ if (orientation === "right") {
153
+ return (
154
+ <ChevronRightIcon
155
+ className={cn("size-4", className)}
156
+ {...props}
157
+ />
158
+ )
159
+ }
160
+
161
+ return (
162
+ <ChevronDownIcon className={cn("size-4", className)} {...props} />
163
+ )
164
+ },
165
+ DayButton: CalendarDayButton,
166
+ WeekNumber: ({ children, ...props }) => {
167
+ return (
168
+ <td {...props}>
169
+ <div className="flex size-8 items-center justify-center text-center">
170
+ {children}
171
+ </div>
172
+ </td>
173
+ )
174
+ },
175
+ ...components,
176
+ }}
177
+ {...props}
178
+ />
179
+ )
180
+ }
181
+
182
+ function CalendarDayButton({
183
+ className,
184
+ day,
185
+ modifiers,
186
+ ...props
187
+ }: React.ComponentProps<typeof DayButton>) {
188
+ const defaultClassNames = getDefaultClassNames()
189
+
190
+ const ref = React.useRef<HTMLButtonElement>(null)
191
+ React.useEffect(() => {
192
+ if (modifiers.focused) ref.current?.focus()
193
+ }, [modifiers.focused])
194
+
195
+ return (
196
+ <Button
197
+ ref={ref}
198
+ variant="ghost"
199
+ size="small"
200
+ data-day={day.date.toLocaleDateString()}
201
+ data-selected-single={
202
+ modifiers.selected &&
203
+ !modifiers.range_start &&
204
+ !modifiers.range_end &&
205
+ !modifiers.range_middle
206
+ }
207
+ data-range-start={modifiers.range_start}
208
+ data-range-end={modifiers.range_end}
209
+ data-range-middle={modifiers.range_middle}
210
+ className={cn(
211
+ "size-8 p-0 font-normal",
212
+ "data-[selected-single=true]:bg-main-900 data-[selected-single=true]:text-white",
213
+ "data-[range-middle=true]:bg-main-100 data-[range-middle=true]:text-main-900",
214
+ "data-[range-start=true]:bg-main-900 data-[range-start=true]:text-white",
215
+ "data-[range-end=true]:bg-main-900 data-[range-end=true]:text-white",
216
+ "group-data-[focused=true]/day:border-grey-400 group-data-[focused=true]/day:ring-grey-300/50",
217
+ "hover:bg-main-100 hover:text-main-900",
218
+ "group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[3px]",
219
+ "data-[range-end=true]:rounded-md data-[range-end=true]:rounded-r-md",
220
+ "data-[range-middle=true]:rounded-none",
221
+ "data-[range-start=true]:rounded-md data-[range-start=true]:rounded-l-md",
222
+ defaultClassNames.day,
223
+ className
224
+ )}
225
+ {...props}
226
+ />
227
+ )
228
+ }
229
+
230
+ Calendar.displayName = "Calendar"
231
+
232
+ export { Calendar, CalendarDayButton }
@@ -0,0 +1,136 @@
1
+ import type { Meta, StoryObj } from "@storybook/react"
2
+
3
+ import { Button } from "../Button/Button"
4
+ import { Input } from "../Input/Input"
5
+ import { Label } from "../Label/Label"
6
+ import { Flex } from "../Flex/Flex"
7
+ import { Typography } from "../Typography/Typography"
8
+ import { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } from "./Card"
9
+
10
+ const meta: Meta<typeof Card> = {
11
+ title: "Components/Card",
12
+ component: Card,
13
+ parameters: {
14
+ layout: "centered",
15
+ },
16
+ argTypes: {
17
+ variant: {
18
+ control: "select",
19
+ options: ["default", "outline"],
20
+ description: "The visual variant of the card",
21
+ },
22
+ },
23
+ tags: ["autodocs"],
24
+ }
25
+
26
+ export default meta
27
+ type Story = StoryObj<typeof meta>
28
+
29
+ export const Default: Story = {
30
+ render: () => (
31
+ <Card className="w-[350px]">
32
+ <CardHeader>
33
+ <CardTitle>Card Title</CardTitle>
34
+ <CardDescription>Card Description</CardDescription>
35
+ </CardHeader>
36
+ <CardContent>
37
+ <p>Card Content</p>
38
+ </CardContent>
39
+ <CardFooter>
40
+ <p>Card Footer</p>
41
+ </CardFooter>
42
+ </Card>
43
+ ),
44
+ }
45
+
46
+ export const Outline: Story = {
47
+ render: () => (
48
+ <Card variant="outline" className="w-[350px]">
49
+ <CardHeader>
50
+ <CardTitle>Outline Card</CardTitle>
51
+ <CardDescription>This card has no shadow</CardDescription>
52
+ </CardHeader>
53
+ <CardContent>
54
+ <p>Card content goes here.</p>
55
+ </CardContent>
56
+ </Card>
57
+ ),
58
+ }
59
+
60
+ export const WithForm: Story = {
61
+ render: () => (
62
+ <Card className="w-[350px]">
63
+ <CardHeader>
64
+ <CardTitle>Create project</CardTitle>
65
+ <CardDescription>Deploy your new project in one-click.</CardDescription>
66
+ </CardHeader>
67
+ <CardContent>
68
+ <form>
69
+ <Flex direction="column" gap={4}>
70
+ <Flex direction="column" gap={2}>
71
+ <Label htmlFor="name">Name</Label>
72
+ <Input id="name" placeholder="Name of your project" />
73
+ </Flex>
74
+ <Flex direction="column" gap={2}>
75
+ <Label htmlFor="framework">Framework</Label>
76
+ <Input id="framework" placeholder="Select a framework" />
77
+ </Flex>
78
+ </Flex>
79
+ </form>
80
+ </CardContent>
81
+ <CardFooter className="flex justify-between">
82
+ <Button variant="outline">Cancel</Button>
83
+ <Button>Deploy</Button>
84
+ </CardFooter>
85
+ </Card>
86
+ ),
87
+ }
88
+
89
+ export const SimpleCard: Story = {
90
+ render: () => (
91
+ <Card className="w-[350px] p-6">
92
+ <Flex direction="column" gap={2}>
93
+ <Typography variant="h4">Notifications</Typography>
94
+ <Typography variant="body2" color="grey-500">
95
+ You have 3 unread messages.
96
+ </Typography>
97
+ </Flex>
98
+ </Card>
99
+ ),
100
+ }
101
+
102
+ export const AllVariants: Story = {
103
+ render: () => (
104
+ <Flex direction="column" gap={6}>
105
+ <Typography variant="h4" className="text-white">Card Variants</Typography>
106
+
107
+ <Flex gap={4} wrap="wrap">
108
+ <Card className="w-[300px]">
109
+ <CardHeader>
110
+ <CardTitle>Default Card</CardTitle>
111
+ <CardDescription>With shadow</CardDescription>
112
+ </CardHeader>
113
+ <CardContent>
114
+ <p>This is the default card style with a subtle shadow.</p>
115
+ </CardContent>
116
+ <CardFooter>
117
+ <Button size="small">Action</Button>
118
+ </CardFooter>
119
+ </Card>
120
+
121
+ <Card variant="outline" className="w-[300px]">
122
+ <CardHeader>
123
+ <CardTitle>Outline Card</CardTitle>
124
+ <CardDescription>Without shadow</CardDescription>
125
+ </CardHeader>
126
+ <CardContent>
127
+ <p>This card has an outline border but no shadow.</p>
128
+ </CardContent>
129
+ <CardFooter>
130
+ <Button size="small" variant="outline">Action</Button>
131
+ </CardFooter>
132
+ </Card>
133
+ </Flex>
134
+ </Flex>
135
+ ),
136
+ }