@a2v2ai/uikit 0.0.37 → 0.0.38

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 (182) 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 +107 -0
  13. package/Calendar/Calendar.stories.tsx +291 -0
  14. package/Calendar/Calendar.tsx +246 -0
  15. package/Card/Card.stories.tsx +136 -0
  16. package/Card/Card.tsx +96 -0
  17. package/Carousel/Carousel.stories.tsx +256 -0
  18. package/Carousel/Carousel.tsx +301 -0
  19. package/ChatBubble/ChatBubble.stories.tsx +339 -0
  20. package/ChatBubble/ChatBubble.tsx +179 -0
  21. package/Checkbox/Checkbox.stories.tsx +137 -0
  22. package/Checkbox/Checkbox.tsx +53 -0
  23. package/DataTable/DataTable.stories.tsx +400 -0
  24. package/DataTable/DataTable.tsx +207 -0
  25. package/Drawer/Drawer.stories.tsx +721 -0
  26. package/Drawer/Drawer.tsx +201 -0
  27. package/DropdownMenu/DropdownMenu.stories.tsx +251 -0
  28. package/DropdownMenu/DropdownMenu.tsx +199 -0
  29. package/ErrorMessage/ErrorMessage.stories.tsx +159 -0
  30. package/ErrorMessage/ErrorMessage.tsx +55 -0
  31. package/Flex/Flex.stories.tsx +390 -0
  32. package/Flex/Flex.tsx +102 -0
  33. package/IconButton/IconButton.stories.tsx +566 -0
  34. package/IconButton/IconButton.tsx +95 -0
  35. package/Input/Input.stories.tsx +566 -0
  36. package/Input/Input.tsx +168 -0
  37. package/InputOTP/InputOTP.stories.tsx +246 -0
  38. package/InputOTP/InputOTP.tsx +127 -0
  39. package/Label/Label.stories.tsx +110 -0
  40. package/Label/Label.tsx +44 -0
  41. package/Loader/Loader.stories.tsx +170 -0
  42. package/Loader/Loader.tsx +62 -0
  43. package/Menubar/Menubar.stories.tsx +382 -0
  44. package/Menubar/Menubar.tsx +274 -0
  45. package/Menubar/index.ts +18 -0
  46. package/Pagination/Pagination.stories.tsx +196 -0
  47. package/Pagination/Pagination.tsx +122 -0
  48. package/Popover/Popover.stories.tsx +133 -0
  49. package/Popover/Popover.tsx +31 -0
  50. package/Progress/Progress.stories.tsx +146 -0
  51. package/Progress/Progress.tsx +67 -0
  52. package/RadioGroup/RadioGroup.stories.tsx +159 -0
  53. package/RadioGroup/RadioGroup.tsx +68 -0
  54. package/ScrollArea/ScrollArea.stories.tsx +136 -0
  55. package/ScrollArea/ScrollArea.tsx +46 -0
  56. package/Select/Select.stories.tsx +378 -0
  57. package/Select/Select.tsx +230 -0
  58. package/Separator/Separator.stories.tsx +110 -0
  59. package/Separator/Separator.tsx +29 -0
  60. package/Sidebar/Sidebar.stories.tsx +340 -0
  61. package/Sidebar/Sidebar.tsx +414 -0
  62. package/Sidebar/index.ts +28 -0
  63. package/Skeleton/Skeleton.stories.tsx +117 -0
  64. package/Skeleton/Skeleton.tsx +16 -0
  65. package/Slider/Slider.stories.tsx +216 -0
  66. package/Slider/Slider.tsx +29 -0
  67. package/Spinner/Spinner.stories.tsx +210 -0
  68. package/Spinner/Spinner.tsx +78 -0
  69. package/Switch/Switch.stories.tsx +146 -0
  70. package/Switch/Switch.tsx +59 -0
  71. package/Table/Table.stories.tsx +510 -0
  72. package/Table/Table.tsx +114 -0
  73. package/Tabs/Tabs.stories.tsx +197 -0
  74. package/Tabs/Tabs.tsx +74 -0
  75. package/Textarea/Textarea.stories.tsx +187 -0
  76. package/Textarea/Textarea.tsx +73 -0
  77. package/Toast/Toast.stories.tsx +285 -0
  78. package/Toast/Toast.tsx +59 -0
  79. package/Tooltip/Tooltip.stories.tsx +463 -0
  80. package/Tooltip/Tooltip.tsx +96 -0
  81. package/Typography/Typography.stories.tsx +425 -0
  82. package/Typography/Typography.tsx +106 -0
  83. package/helpers.ts +5 -0
  84. package/{icons.js → icons.ts} +1 -1
  85. package/index.ts +217 -0
  86. package/lib/typography-types.ts +223 -0
  87. package/lib/utils.ts +15 -0
  88. package/package.json +5 -2
  89. package/tsconfig.json +22 -0
  90. package/Alert/Alert.d.ts +0 -13
  91. package/Alert/Alert.js +0 -25
  92. package/AlertDialog/AlertDialog.d.ts +0 -43
  93. package/AlertDialog/AlertDialog.js +0 -71
  94. package/Avatar/Avatar.d.ts +0 -14
  95. package/Avatar/Avatar.js +0 -25
  96. package/Badge/Badge.d.ts +0 -11
  97. package/Badge/Badge.js +0 -23
  98. package/Breadcrumb/Breadcrumb.d.ts +0 -19
  99. package/Breadcrumb/Breadcrumb.js +0 -23
  100. package/Button/Button.d.ts +0 -23
  101. package/Button/Button.js +0 -52
  102. package/Calendar/Calendar.d.ts +0 -20
  103. package/Calendar/Calendar.js +0 -78
  104. package/Card/Card.d.ts +0 -16
  105. package/Card/Card.js +0 -28
  106. package/Carousel/Carousel.d.ts +0 -37
  107. package/Carousel/Carousel.js +0 -132
  108. package/ChatBubble/ChatBubble.d.ts +0 -33
  109. package/ChatBubble/ChatBubble.js +0 -107
  110. package/Checkbox/Checkbox.d.ts +0 -12
  111. package/Checkbox/Checkbox.js +0 -20
  112. package/DataTable/DataTable.d.ts +0 -35
  113. package/DataTable/DataTable.js +0 -51
  114. package/Drawer/Drawer.d.ts +0 -33
  115. package/Drawer/Drawer.js +0 -55
  116. package/DropdownMenu/DropdownMenu.d.ts +0 -27
  117. package/DropdownMenu/DropdownMenu.js +0 -35
  118. package/ErrorMessage/ErrorMessage.d.ts +0 -27
  119. package/ErrorMessage/ErrorMessage.js +0 -14
  120. package/Flex/Flex.d.ts +0 -31
  121. package/Flex/Flex.js +0 -64
  122. package/IconButton/IconButton.d.ts +0 -23
  123. package/IconButton/IconButton.js +0 -48
  124. package/Input/Input.d.ts +0 -27
  125. package/Input/Input.js +0 -42
  126. package/InputOTP/InputOTP.d.ts +0 -20
  127. package/InputOTP/InputOTP.js +0 -44
  128. package/Label/Label.d.ts +0 -13
  129. package/Label/Label.js +0 -19
  130. package/Loader/Loader.d.ts +0 -21
  131. package/Loader/Loader.js +0 -30
  132. package/Menubar/Menubar.d.ts +0 -26
  133. package/Menubar/Menubar.js +0 -54
  134. package/Menubar/index.d.ts +0 -1
  135. package/Menubar/index.js +0 -1
  136. package/Pagination/Pagination.d.ts +0 -35
  137. package/Pagination/Pagination.js +0 -37
  138. package/Popover/Popover.d.ts +0 -7
  139. package/Popover/Popover.js +0 -10
  140. package/Progress/Progress.d.ts +0 -17
  141. package/Progress/Progress.js +0 -33
  142. package/RadioGroup/RadioGroup.d.ts +0 -13
  143. package/RadioGroup/RadioGroup.js +0 -26
  144. package/ScrollArea/ScrollArea.d.ts +0 -5
  145. package/ScrollArea/ScrollArea.js +0 -11
  146. package/Select/Select.d.ts +0 -29
  147. package/Select/Select.js +0 -50
  148. package/Separator/Separator.d.ts +0 -4
  149. package/Separator/Separator.js +0 -7
  150. package/Sidebar/Sidebar.d.ts +0 -48
  151. package/Sidebar/Sidebar.js +0 -116
  152. package/Sidebar/index.d.ts +0 -2
  153. package/Sidebar/index.js +0 -1
  154. package/Skeleton/Skeleton.d.ts +0 -4
  155. package/Skeleton/Skeleton.js +0 -7
  156. package/Slider/Slider.d.ts +0 -6
  157. package/Slider/Slider.js +0 -7
  158. package/Spinner/Spinner.d.ts +0 -19
  159. package/Spinner/Spinner.js +0 -31
  160. package/Switch/Switch.d.ts +0 -12
  161. package/Switch/Switch.js +0 -30
  162. package/Table/Table.d.ts +0 -10
  163. package/Table/Table.js +0 -20
  164. package/Tabs/Tabs.d.ts +0 -15
  165. package/Tabs/Tabs.js +0 -24
  166. package/Textarea/Textarea.d.ts +0 -19
  167. package/Textarea/Textarea.js +0 -31
  168. package/Toast/Toast.d.ts +0 -12
  169. package/Toast/Toast.js +0 -25
  170. package/Tooltip/Tooltip.d.ts +0 -17
  171. package/Tooltip/Tooltip.js +0 -29
  172. package/Typography/Typography.d.ts +0 -20
  173. package/Typography/Typography.js +0 -43
  174. package/helpers.d.ts +0 -4
  175. package/helpers.js +0 -5
  176. package/icons.d.ts +0 -1
  177. package/index.d.ts +0 -42
  178. package/index.js +0 -45
  179. package/lib/typography-types.d.ts +0 -4
  180. package/lib/typography-types.js +0 -90
  181. package/lib/utils.d.ts +0 -3
  182. package/lib/utils.js +0 -14
@@ -0,0 +1,107 @@
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
+ type ButtonTextVariant = "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "subtitle1" | "subtitle2" | "body1" | "body2" | "body3" | "caption"
11
+
12
+ const buttonVariants = cva(
13
+ "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",
14
+ {
15
+ variants: {
16
+ variant: {
17
+ primary:
18
+ "bg-accent-600 text-white hover:bg-accent-700",
19
+ secondary:
20
+ "bg-main-100 text-main-950 hover:bg-main-200",
21
+ outline:
22
+ "border border-main-300 bg-transparent text-main-950 hover:bg-main-100",
23
+ ghost:
24
+ "bg-transparent text-main-600 hover:bg-main-100 hover:text-main-950",
25
+ "ghost-muted":
26
+ "bg-transparent text-main-400 hover:bg-main-100 hover:text-main-600",
27
+ destructive:
28
+ "bg-error-600 text-white hover:bg-error-700",
29
+ },
30
+ size: {
31
+ mini: "h-6 px-3 text-xs rounded-md [&_svg]:size-3",
32
+ small: "h-8 px-3 text-sm rounded-md [&_svg]:size-3.5",
33
+ regular: "h-9 px-4 text-sm rounded-lg [&_svg]:size-4",
34
+ large: "h-10 px-4 text-base rounded-lg [&_svg]:size-5",
35
+ },
36
+ roundness: {
37
+ default: "",
38
+ round: "!rounded-full",
39
+ },
40
+ textVariant: {
41
+ h1: "text-[64px] font-extrabold",
42
+ h2: "text-[48px] font-bold",
43
+ h3: "text-[32px] font-bold",
44
+ h4: "text-[24px] font-bold",
45
+ h5: "text-[20px] font-bold",
46
+ h6: "text-[20px] font-semibold",
47
+ subtitle1: "text-[20px] font-semibold",
48
+ subtitle2: "text-[18px] font-semibold",
49
+ body1: "text-[16px] font-normal",
50
+ body2: "text-[14px] font-normal",
51
+ body3: "text-[12px] font-normal",
52
+ caption: "text-[12px] font-light",
53
+ },
54
+ },
55
+ defaultVariants: {
56
+ variant: "primary",
57
+ size: "regular",
58
+ roundness: "default",
59
+ },
60
+ }
61
+ )
62
+
63
+ export interface ButtonProps
64
+ extends React.ButtonHTMLAttributes<HTMLButtonElement> {
65
+ variant?: ButtonVariant
66
+ size?: ButtonSize
67
+ roundness?: ButtonRoundness
68
+ textVariant?: ButtonTextVariant
69
+ asChild?: boolean
70
+ leftIcon?: React.ReactNode
71
+ rightIcon?: React.ReactNode
72
+ }
73
+
74
+ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
75
+ (
76
+ {
77
+ className,
78
+ variant,
79
+ size,
80
+ roundness,
81
+ textVariant,
82
+ asChild = false,
83
+ leftIcon,
84
+ rightIcon,
85
+ children,
86
+ ...props
87
+ },
88
+ ref
89
+ ) => {
90
+ const Comp = asChild ? Slot : "button"
91
+ return (
92
+ <Comp
93
+ className={cn(buttonVariants({ variant, size, roundness, textVariant, className }))}
94
+ ref={ref}
95
+ {...props}
96
+ >
97
+ {leftIcon && <span className="shrink-0">{leftIcon}</span>}
98
+ {children}
99
+ {rightIcon && <span className="shrink-0">{rightIcon}</span>}
100
+ </Comp>
101
+ )
102
+ }
103
+ )
104
+ Button.displayName = "Button"
105
+
106
+ export { Button, buttonVariants }
107
+ export type { ButtonVariant, ButtonSize, ButtonRoundness, ButtonTextVariant }
@@ -0,0 +1,291 @@
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
+ import { Button } from "../Button/Button"
8
+
9
+ const meta: Meta<typeof Calendar> = {
10
+ title: "Components/Calendar",
11
+ component: Calendar,
12
+ parameters: {
13
+ layout: "centered",
14
+ },
15
+ tags: ["autodocs"],
16
+ argTypes: {
17
+ mode: {
18
+ control: "select",
19
+ options: ["single", "multiple", "range"],
20
+ description: "Selection mode for the calendar",
21
+ },
22
+ showOutsideDays: {
23
+ control: "boolean",
24
+ description: "Show days from adjacent months",
25
+ },
26
+ showWeekNumber: {
27
+ control: "boolean",
28
+ description: "Show week numbers",
29
+ },
30
+ captionLayout: {
31
+ control: "select",
32
+ options: ["label", "dropdown", "dropdown-months", "dropdown-years"],
33
+ description: "Layout of the caption (month/year selector)",
34
+ },
35
+ },
36
+ }
37
+
38
+ export default meta
39
+ type Story = StoryObj<typeof Calendar>
40
+
41
+ export const Default: Story = {
42
+ render: () => {
43
+ const [date, setDate] = React.useState<Date | undefined>(new Date())
44
+ return (
45
+ <Calendar
46
+ mode="single"
47
+ selected={date}
48
+ onSelect={setDate}
49
+ />
50
+ )
51
+ },
52
+ }
53
+
54
+ export const SingleSelection: Story = {
55
+ render: () => {
56
+ const [date, setDate] = React.useState<Date | undefined>(new Date())
57
+ return (
58
+ <Flex gap={4} align="center">
59
+ <Calendar
60
+ mode="single"
61
+ selected={date}
62
+ onSelect={setDate}
63
+ />
64
+ <Typography variant="body2" color="main-600">
65
+ Selected: {date ? date.toLocaleDateString() : "None"}
66
+ </Typography>
67
+ </Flex>
68
+ )
69
+ },
70
+ }
71
+
72
+ export const MultipleSelection: Story = {
73
+ render: () => {
74
+ const [dates, setDates] = React.useState<Date[] | undefined>([])
75
+ return (
76
+ <Flex gap={4} align="center">
77
+ <Calendar
78
+ mode="multiple"
79
+ selected={dates}
80
+ onSelect={setDates}
81
+ />
82
+ <Typography variant="body2" color="main-600">
83
+ Selected: {dates && dates.length > 0
84
+ ? dates.map(d => d.toLocaleDateString()).join(", ")
85
+ : "None"}
86
+ </Typography>
87
+ </Flex>
88
+ )
89
+ },
90
+ }
91
+
92
+ export const RangeSelection: Story = {
93
+ render: () => {
94
+ const [range, setRange] = React.useState<DateRange | undefined>()
95
+ return (
96
+ <Flex gap={4} align="center">
97
+ <Calendar
98
+ mode="range"
99
+ selected={range}
100
+ onSelect={setRange}
101
+ numberOfMonths={2}
102
+ />
103
+ <Typography variant="body2" color="main-600">
104
+ {range?.from && range?.to
105
+ ? `${range.from.toLocaleDateString()} - ${range.to.toLocaleDateString()}`
106
+ : range?.from
107
+ ? `${range.from.toLocaleDateString()} - Select end date`
108
+ : "Select a date range"}
109
+ </Typography>
110
+ </Flex>
111
+ )
112
+ },
113
+ }
114
+
115
+ export const WithWeekNumbers: Story = {
116
+ render: () => {
117
+ const [date, setDate] = React.useState<Date | undefined>(new Date())
118
+ return (
119
+ <Calendar
120
+ mode="single"
121
+ selected={date}
122
+ onSelect={setDate}
123
+ showWeekNumber
124
+ />
125
+ )
126
+ },
127
+ }
128
+
129
+ export const WithDropdowns: Story = {
130
+ render: () => {
131
+ const [date, setDate] = React.useState<Date | undefined>(new Date())
132
+ return (
133
+ <Calendar
134
+ mode="single"
135
+ selected={date}
136
+ onSelect={setDate}
137
+ captionLayout="dropdown"
138
+ fromYear={2020}
139
+ toYear={2030}
140
+ />
141
+ )
142
+ },
143
+ }
144
+
145
+ export const DisabledDates: Story = {
146
+ render: () => {
147
+ const [date, setDate] = React.useState<Date | undefined>(new Date())
148
+
149
+ // Disable weekends
150
+ const disabledDays = [
151
+ { dayOfWeek: [0, 6] }, // Sunday and Saturday
152
+ ]
153
+
154
+ return (
155
+ <Flex gap={4} align="center">
156
+ <Typography variant="body2" color="main-600">
157
+ Weekends are disabled
158
+ </Typography>
159
+ <Calendar
160
+ mode="single"
161
+ selected={date}
162
+ onSelect={setDate}
163
+ disabled={disabledDays}
164
+ />
165
+ </Flex>
166
+ )
167
+ },
168
+ }
169
+
170
+ export const LimitedDateRange: Story = {
171
+ render: () => {
172
+ const [date, setDate] = React.useState<Date | undefined>(new Date())
173
+
174
+ // Only allow dates in the next 30 days
175
+ const today = new Date()
176
+ const thirtyDaysFromNow = new Date()
177
+ thirtyDaysFromNow.setDate(today.getDate() + 30)
178
+
179
+ return (
180
+ <Flex gap={4} align="center">
181
+ <Typography variant="body2" color="main-600">
182
+ Only next 30 days selectable
183
+ </Typography>
184
+ <Calendar
185
+ mode="single"
186
+ selected={date}
187
+ onSelect={setDate}
188
+ fromDate={today}
189
+ toDate={thirtyDaysFromNow}
190
+ />
191
+ </Flex>
192
+ )
193
+ },
194
+ }
195
+
196
+ export const TwoMonths: Story = {
197
+ render: () => {
198
+ const [date, setDate] = React.useState<Date | undefined>(new Date())
199
+ return (
200
+ <Calendar
201
+ mode="single"
202
+ selected={date}
203
+ onSelect={setDate}
204
+ numberOfMonths={2}
205
+ />
206
+ )
207
+ },
208
+ }
209
+
210
+ export const Error: Story = {
211
+ render: () => {
212
+ const [date, setDate] = React.useState<Date | undefined>()
213
+ return (
214
+ <Calendar
215
+ mode="single"
216
+ selected={date}
217
+ onSelect={setDate}
218
+ error="Please select a valid date"
219
+ />
220
+ )
221
+ },
222
+ }
223
+
224
+ export const ErrorWithSelection: Story = {
225
+ render: () => {
226
+ const [date, setDate] = React.useState<Date | undefined>(new Date())
227
+ // Simulate an invalid selection (e.g., past date when only future dates are allowed)
228
+ return (
229
+ <Flex gap={4} align="center">
230
+ <Calendar
231
+ mode="single"
232
+ selected={date}
233
+ onSelect={setDate}
234
+ error="Selected date cannot be in the past"
235
+ />
236
+ <Typography variant="body2" color="error-600">
237
+ Please select a future date
238
+ </Typography>
239
+ </Flex>
240
+ )
241
+ },
242
+ }
243
+
244
+ export const FormikTouchedUntouched: Story = {
245
+ render: function FormikExample() {
246
+ const [date, setDate] = React.useState<Date | undefined>()
247
+ const [touched, setTouched] = React.useState(false)
248
+ const error = "Please select a date"
249
+
250
+ const handleSelect = (selectedDate: Date | undefined) => {
251
+ setDate(selectedDate)
252
+ setTouched(true)
253
+ }
254
+
255
+ return (
256
+ <Flex gap={6} align="center">
257
+ <Flex gap={2}>
258
+ <Typography variant="subtitle2" color="main-700">
259
+ Formik-style touched/untouched validation
260
+ </Typography>
261
+ <Typography variant="caption" color="grey-500">
262
+ Error message only shows after interaction with calendar
263
+ </Typography>
264
+ </Flex>
265
+
266
+ <Calendar
267
+ mode="single"
268
+ selected={date}
269
+ onSelect={handleSelect}
270
+ error={touched && !date ? error : undefined}
271
+ />
272
+
273
+ <Flex gap={2} align="center">
274
+ <Typography variant="caption" color="grey-400">
275
+ {touched ? "Touched" : "Untouched"} | Selected: {date ? date.toLocaleDateString() : "none"}
276
+ </Typography>
277
+ <Button
278
+ variant="secondary"
279
+ size="small"
280
+ onClick={() => {
281
+ setTouched(false)
282
+ setDate(undefined)
283
+ }}
284
+ >
285
+ Reset
286
+ </Button>
287
+ </Flex>
288
+ </Flex>
289
+ )
290
+ },
291
+ }
@@ -0,0 +1,246 @@
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
+ import { Typography } from "../Typography/Typography"
14
+
15
+ export type CalendarProps = React.ComponentProps<typeof DayPicker> & {
16
+ /**
17
+ * Variant for navigation buttons
18
+ * @default "ghost"
19
+ */
20
+ buttonVariant?: React.ComponentProps<typeof Button>["variant"]
21
+ /**
22
+ * Error message to display below the calendar
23
+ */
24
+ error?: string
25
+ }
26
+
27
+ function Calendar({
28
+ className,
29
+ classNames,
30
+ showOutsideDays = true,
31
+ captionLayout = "label",
32
+ buttonVariant = "ghost",
33
+ formatters,
34
+ components,
35
+ error,
36
+ ...props
37
+ }: CalendarProps) {
38
+ const defaultClassNames = getDefaultClassNames()
39
+
40
+ return (
41
+ <div className="flex flex-col w-fit">
42
+ <DayPicker
43
+ showOutsideDays={showOutsideDays}
44
+ className={cn(
45
+ "bg-white group/calendar p-3 rounded-lg border",
46
+ error ? "border-error-500" : "border-grey-200",
47
+ className
48
+ )}
49
+ captionLayout={captionLayout}
50
+ formatters={{
51
+ formatMonthDropdown: (date) =>
52
+ date.toLocaleString("default", { month: "short" }),
53
+ ...formatters,
54
+ }}
55
+ classNames={{
56
+ root: cn("w-fit", defaultClassNames.root),
57
+ months: cn(
58
+ "flex gap-4 flex-col md:flex-row relative",
59
+ defaultClassNames.months
60
+ ),
61
+ month: cn("flex flex-col w-full gap-4", defaultClassNames.month),
62
+ nav: cn(
63
+ "flex items-center gap-1 w-full absolute top-0 inset-x-0 justify-between",
64
+ defaultClassNames.nav
65
+ ),
66
+ button_previous: cn(
67
+ buttonVariants({ variant: buttonVariant, size: "small" }),
68
+ "size-8 aria-disabled:opacity-50 p-0 select-none",
69
+ defaultClassNames.button_previous
70
+ ),
71
+ button_next: cn(
72
+ buttonVariants({ variant: buttonVariant, size: "small" }),
73
+ "size-8 aria-disabled:opacity-50 p-0 select-none",
74
+ defaultClassNames.button_next
75
+ ),
76
+ month_caption: cn(
77
+ "flex items-center justify-center h-8 w-full px-8",
78
+ defaultClassNames.month_caption
79
+ ),
80
+ dropdowns: cn(
81
+ "w-full flex items-center text-sm font-medium justify-center h-8 gap-1.5",
82
+ defaultClassNames.dropdowns
83
+ ),
84
+ dropdown_root: cn(
85
+ "relative has-focus:border-grey-400 border border-grey-300 shadow-xs has-focus:ring-grey-300/50 has-focus:ring-[3px] rounded-md",
86
+ defaultClassNames.dropdown_root
87
+ ),
88
+ dropdown: cn(
89
+ "absolute bg-white inset-0 opacity-0",
90
+ defaultClassNames.dropdown
91
+ ),
92
+ caption_label: cn(
93
+ "select-none font-medium text-main-900",
94
+ captionLayout === "label"
95
+ ? "text-sm"
96
+ : "rounded-md pl-2 pr-1 flex items-center gap-1 text-sm h-8 [&>svg]:text-main-400 [&>svg]:size-3.5",
97
+ defaultClassNames.caption_label
98
+ ),
99
+ table: "w-full border-collapse",
100
+ weekdays: cn("flex", defaultClassNames.weekdays),
101
+ weekday: cn(
102
+ "text-main-500 rounded-md flex-1 font-normal text-[0.8rem] select-none w-8 h-8 flex items-center justify-center",
103
+ defaultClassNames.weekday
104
+ ),
105
+ week: cn("flex w-full mt-2", defaultClassNames.week),
106
+ week_number_header: cn(
107
+ "select-none w-8",
108
+ defaultClassNames.week_number_header
109
+ ),
110
+ week_number: cn(
111
+ "text-[0.8rem] select-none text-main-400",
112
+ defaultClassNames.week_number
113
+ ),
114
+ day: cn(
115
+ "relative w-8 h-8 p-0 text-center [&:last-child[data-selected=true]_button]:rounded-r-md group/day select-none",
116
+ props.showWeekNumber
117
+ ? "[&:nth-child(2)[data-selected=true]_button]:rounded-l-md"
118
+ : "[&:first-child[data-selected=true]_button]:rounded-l-md",
119
+ defaultClassNames.day
120
+ ),
121
+ range_start: cn(
122
+ "rounded-l-md bg-main-100",
123
+ defaultClassNames.range_start
124
+ ),
125
+ range_middle: cn("rounded-none", defaultClassNames.range_middle),
126
+ range_end: cn("rounded-r-md bg-main-100", defaultClassNames.range_end),
127
+ today: cn(
128
+ "bg-main-100 text-main-900 rounded-md data-[selected=true]:rounded-none",
129
+ defaultClassNames.today
130
+ ),
131
+ outside: cn(
132
+ "text-main-300 aria-selected:text-main-400",
133
+ defaultClassNames.outside
134
+ ),
135
+ disabled: cn(
136
+ "text-main-300 opacity-50",
137
+ defaultClassNames.disabled
138
+ ),
139
+ hidden: cn("invisible", defaultClassNames.hidden),
140
+ ...classNames,
141
+ }}
142
+ components={{
143
+ Root: ({ className, rootRef, ...props }) => {
144
+ return (
145
+ <div
146
+ data-slot="calendar"
147
+ ref={rootRef}
148
+ className={cn(className)}
149
+ {...props}
150
+ />
151
+ )
152
+ },
153
+ Chevron: ({ className, orientation, ...props }) => {
154
+ if (orientation === "left") {
155
+ return (
156
+ <ChevronLeftIcon className={cn("size-4", className)} {...props} />
157
+ )
158
+ }
159
+
160
+ if (orientation === "right") {
161
+ return (
162
+ <ChevronRightIcon
163
+ className={cn("size-4", className)}
164
+ {...props}
165
+ />
166
+ )
167
+ }
168
+
169
+ return (
170
+ <ChevronDownIcon className={cn("size-4", className)} {...props} />
171
+ )
172
+ },
173
+ DayButton: CalendarDayButton,
174
+ WeekNumber: ({ children, ...props }) => {
175
+ return (
176
+ <td {...props}>
177
+ <div className="flex size-8 items-center justify-center text-center">
178
+ {children}
179
+ </div>
180
+ </td>
181
+ )
182
+ },
183
+ ...components,
184
+ }}
185
+ {...props}
186
+ />
187
+ {error && (
188
+ <Typography variant="caption" color="error-600" className="mt-1">
189
+ {error}
190
+ </Typography>
191
+ )}
192
+ </div>
193
+ )
194
+ }
195
+
196
+ function CalendarDayButton({
197
+ className,
198
+ day,
199
+ modifiers,
200
+ ...props
201
+ }: React.ComponentProps<typeof DayButton>) {
202
+ const defaultClassNames = getDefaultClassNames()
203
+
204
+ const ref = React.useRef<HTMLButtonElement>(null)
205
+ React.useEffect(() => {
206
+ if (modifiers.focused) ref.current?.focus()
207
+ }, [modifiers.focused])
208
+
209
+ return (
210
+ <Button
211
+ ref={ref}
212
+ variant="ghost"
213
+ size="small"
214
+ data-day={day.date.toLocaleDateString()}
215
+ data-selected-single={
216
+ modifiers.selected &&
217
+ !modifiers.range_start &&
218
+ !modifiers.range_end &&
219
+ !modifiers.range_middle
220
+ }
221
+ data-range-start={modifiers.range_start}
222
+ data-range-end={modifiers.range_end}
223
+ data-range-middle={modifiers.range_middle}
224
+ className={cn(
225
+ "size-8 p-0 font-normal",
226
+ "data-[selected-single=true]:bg-main-900 data-[selected-single=true]:text-white",
227
+ "data-[range-middle=true]:bg-main-100 data-[range-middle=true]:text-main-900",
228
+ "data-[range-start=true]:bg-main-900 data-[range-start=true]:text-white",
229
+ "data-[range-end=true]:bg-main-900 data-[range-end=true]:text-white",
230
+ "group-data-[focused=true]/day:border-grey-400 group-data-[focused=true]/day:ring-grey-300/50",
231
+ "hover:bg-main-100 hover:text-main-900",
232
+ "group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[3px]",
233
+ "data-[range-end=true]:rounded-md data-[range-end=true]:rounded-r-md",
234
+ "data-[range-middle=true]:rounded-none",
235
+ "data-[range-start=true]:rounded-md data-[range-start=true]:rounded-l-md",
236
+ defaultClassNames.day,
237
+ className
238
+ )}
239
+ {...props}
240
+ />
241
+ )
242
+ }
243
+
244
+ Calendar.displayName = "Calendar"
245
+
246
+ export { Calendar, CalendarDayButton }