@catalystsoftware/ui 1.0.4 → 1.0.5

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 (157) hide show
  1. package/data/tailwind.config.js +261 -3821
  2. package/dist/components/catalyst-ui/buttons/burger.tsx +207 -0
  3. package/dist/components/catalyst-ui/core/data-display/timeline.tsx +210 -0
  4. package/dist/components/catalyst-ui/core/feedback/alert.tsx +491 -0
  5. package/dist/components/catalyst-ui/core/feedback/spinner-1.tsx +65 -0
  6. package/dist/components/catalyst-ui/core/feedback/toast.tsx +1857 -0
  7. package/dist/components/catalyst-ui/core/navigation/menu.tsx +164 -0
  8. package/dist/components/catalyst-ui/forms/toggle-class.tsx +176 -0
  9. package/dist/components/catalyst-ui/hooks/use-copy-to-clipboard.tsx +419 -0
  10. package/dist/components/catalyst-ui/hooks/use-counter.tsx +13 -0
  11. package/dist/components/catalyst-ui/hooks/use-event-listener.tsx +23 -0
  12. package/dist/components/catalyst-ui/hooks/use-export-markdown.tsx +47 -0
  13. package/dist/components/catalyst-ui/hooks/use-focus.tsx +17 -0
  14. package/dist/components/catalyst-ui/hooks/use-interval.tsx +23 -0
  15. package/dist/components/catalyst-ui/hooks/use-is-client.tsx +16 -0
  16. package/dist/components/catalyst-ui/hooks/use-media-query.tsx +19 -0
  17. package/dist/components/catalyst-ui/hooks/use-mobile.tsx +19 -0
  18. package/dist/components/catalyst-ui/hooks/use-resize-observer.tsx +81 -0
  19. package/dist/components/catalyst-ui/hooks/use-timeout.tsx +21 -0
  20. package/dist/components/catalyst-ui/hooks/use-timer.tsx +209 -0
  21. package/dist/components/catalyst-ui/hooks/use-toggle.tsx +12 -0
  22. package/dist/components/catalyst-ui/media/image.tsx +13 -0
  23. package/dist/components/catalyst-ui/overlays/dual-sidebar.tsx +4142 -0
  24. package/dist/components/catalyst-ui/overlays/sidebar-original.tsx +726 -0
  25. package/dist/components/catalyst-ui/primitives/accordion.tsx +250 -0
  26. package/dist/components/catalyst-ui/primitives/alert-dialog.tsx +126 -0
  27. package/dist/components/catalyst-ui/primitives/aspect-ratio.tsx +9 -0
  28. package/dist/components/catalyst-ui/primitives/avatar.tsx +296 -0
  29. package/dist/components/catalyst-ui/primitives/badge.tsx +57 -0
  30. package/dist/components/catalyst-ui/primitives/breadcrumb.tsx +101 -0
  31. package/dist/components/catalyst-ui/primitives/button.tsx +265 -0
  32. package/dist/components/catalyst-ui/primitives/calendar-v4.tsx +208 -0
  33. package/dist/components/catalyst-ui/primitives/calendar.tsx +295 -0
  34. package/dist/components/catalyst-ui/primitives/card.tsx +618 -0
  35. package/dist/components/catalyst-ui/primitives/carousel.tsx +238 -0
  36. package/dist/components/catalyst-ui/primitives/chart.tsx +347 -0
  37. package/dist/components/catalyst-ui/primitives/checkbox.tsx +225 -0
  38. package/dist/components/catalyst-ui/primitives/collapsible.tsx +212 -0
  39. package/dist/components/catalyst-ui/primitives/command.tsx +393 -0
  40. package/dist/components/catalyst-ui/primitives/context-menu.tsx +236 -0
  41. package/dist/components/catalyst-ui/primitives/dialog.tsx +471 -0
  42. package/dist/components/catalyst-ui/primitives/drawer.tsx +761 -0
  43. package/dist/components/catalyst-ui/primitives/dropdown-menu.tsx +290 -0
  44. package/dist/components/catalyst-ui/primitives/empty.tsx +104 -0
  45. package/dist/components/catalyst-ui/primitives/field.tsx +244 -0
  46. package/dist/components/catalyst-ui/primitives/hover-card.tsx +124 -0
  47. package/dist/components/catalyst-ui/primitives/input-otp.tsx +76 -0
  48. package/dist/components/catalyst-ui/primitives/input.tsx +64 -0
  49. package/dist/components/catalyst-ui/primitives/item.tsx +196 -0
  50. package/dist/components/catalyst-ui/primitives/kbd.tsx +75 -0
  51. package/dist/components/catalyst-ui/primitives/label.tsx +24 -0
  52. package/dist/components/catalyst-ui/primitives/navigation-menu.tsx +150 -0
  53. package/dist/components/catalyst-ui/primitives/pagination.tsx +198 -0
  54. package/dist/components/catalyst-ui/primitives/popover.tsx +232 -0
  55. package/dist/components/catalyst-ui/primitives/progress.tsx +34 -0
  56. package/dist/components/catalyst-ui/primitives/radio-group.tsx +43 -0
  57. package/dist/components/catalyst-ui/primitives/resizable.tsx +56 -0
  58. package/dist/components/catalyst-ui/primitives/select.tsx +155 -0
  59. package/dist/components/catalyst-ui/primitives/separator.tsx +74 -0
  60. package/dist/components/catalyst-ui/primitives/sheet.tsx +126 -0
  61. package/dist/components/catalyst-ui/primitives/skeleton.tsx +15 -0
  62. package/dist/components/catalyst-ui/primitives/slider.tsx +27 -0
  63. package/dist/components/catalyst-ui/primitives/switch.tsx +187 -0
  64. package/dist/components/catalyst-ui/primitives/tabs.tsx +335 -0
  65. package/dist/components/catalyst-ui/primitives/textarea.tsx +24 -0
  66. package/dist/components/catalyst-ui/primitives/toggle-group.tsx +55 -0
  67. package/dist/components/catalyst-ui/primitives/toggle.tsx +42 -0
  68. package/dist/components/catalyst-ui/primitives/tooltip.tsx +116 -0
  69. package/dist/components/catalyst-ui/utils/basic-auth.tsx +40 -0
  70. package/dist/components/catalyst-ui/utils/context-storage.tsx +19 -0
  71. package/dist/components/catalyst-ui/utils/cors-middleware.tsx +71 -0
  72. package/dist/components/catalyst-ui/utils/deferred-content.tsx +595 -0
  73. package/dist/components/catalyst-ui/utils/honeypot-middleware.tsx +38 -0
  74. package/dist/components/catalyst-ui/utils/incId.tsx +75 -0
  75. package/dist/components/catalyst-ui/utils/jwk-auth.tsx +36 -0
  76. package/dist/components/catalyst-ui/utils/request-id.tsx +14 -0
  77. package/dist/components/catalyst-ui/utils/secure-headers.tsx +37 -0
  78. package/dist/components/catalyst-ui/utils/server-timing.tsx +23 -0
  79. package/dist/components/catalyst-ui/utils/utils.ts +43 -0
  80. package/dist/components/catalyst-ui/utils/with-cookie.tsx +43 -0
  81. package/dist/components/catalyst-ui/x/accordian-x.tsx +428 -0
  82. package/dist/components/catalyst-ui/x/alert-x.tsx +413 -0
  83. package/dist/components/catalyst-ui/x/animated-text-x.tsx +2242 -0
  84. package/dist/components/catalyst-ui/x/avatar-x.tsx +515 -0
  85. package/dist/components/catalyst-ui/x/badge-x.tsx +670 -0
  86. package/dist/components/catalyst-ui/x/button-X.tsx +2857 -0
  87. package/dist/components/catalyst-ui/x/button-group-x.tsx +847 -0
  88. package/dist/components/catalyst-ui/x/calendar-x.tsx +1910 -0
  89. package/dist/components/catalyst-ui/x/card-x.tsx +2597 -0
  90. package/dist/components/catalyst-ui/x/checkbox-x.tsx +656 -0
  91. package/dist/components/catalyst-ui/x/collapsible-x.tsx +1360 -0
  92. package/dist/components/catalyst-ui/x/combobox-x.tsx +911 -0
  93. package/dist/components/catalyst-ui/x/data-table-x.tsx +1753 -0
  94. package/dist/components/catalyst-ui/x/date-picker-x.tsx +648 -0
  95. package/dist/components/catalyst-ui/x/dialog-x.tsx +659 -0
  96. package/dist/components/catalyst-ui/x/dropdown-menu-x.tsx +612 -0
  97. package/dist/components/catalyst-ui/x/hover-card-x.tsx +375 -0
  98. package/dist/components/catalyst-ui/x/icon-x.tsx +840 -0
  99. package/dist/components/catalyst-ui/x/input-mask-x.tsx +981 -0
  100. package/dist/components/catalyst-ui/x/input-otp-x.tsx +659 -0
  101. package/dist/components/catalyst-ui/x/loader-x.tsx +1757 -0
  102. package/dist/components/catalyst-ui/x/pagination-x.tsx +622 -0
  103. package/dist/components/catalyst-ui/x/popover-x.tsx +744 -0
  104. package/dist/components/catalyst-ui/x/radio-group-x.tsx +499 -0
  105. package/dist/components/catalyst-ui/x/select-x.tsx +1127 -0
  106. package/dist/components/catalyst-ui/x/sheet-x.tsx +668 -0
  107. package/dist/components/catalyst-ui/x/switch-x.tsx +681 -0
  108. package/dist/components/catalyst-ui/x/table-x.tsx +574 -0
  109. package/dist/components/catalyst-ui/x/tabs-x.tsx +839 -0
  110. package/dist/components/catalyst-ui/x/textarea-x.tsx +1263 -0
  111. package/dist/components/catalyst-ui/x/tooltip-x.tsx +396 -0
  112. package/dist/components/catalyst-ui/x/tracker-x.tsx +560 -0
  113. package/dist/data/bg-data.tsx +901 -0
  114. package/dist/data/buttons-data.tsx +2327 -0
  115. package/dist/data/charts-data.tsx +102 -0
  116. package/dist/data/chat-data.tsx +83 -0
  117. package/dist/data/code-data.tsx +1040 -0
  118. package/dist/data/comboboxes-data.tsx +1843 -0
  119. package/dist/data/command-data.tsx +1381 -0
  120. package/dist/data/core-data.tsx +15953 -0
  121. package/dist/data/crm-data.tsx +47 -0
  122. package/dist/data/data.tsx +159 -0
  123. package/dist/data/date-and-time-data.tsx +554 -0
  124. package/dist/data/dependencies.tsx +7 -0
  125. package/dist/data/ecommerce-data.tsx +1387 -0
  126. package/dist/data/forms-data.tsx +7890 -0
  127. package/dist/data/hooks-data.tsx +5487 -0
  128. package/dist/data/index.ts +34 -0
  129. package/dist/data/inputs-data.tsx +557 -0
  130. package/dist/data/interactive-data.tsx +5394 -0
  131. package/dist/data/lofi-data.tsx +18295 -0
  132. package/dist/data/marketing-data.tsx +2546 -0
  133. package/dist/data/media-data.tsx +1510 -0
  134. package/dist/data/motion-data.tsx +5801 -0
  135. package/dist/data/overlay-data.tsx +4136 -0
  136. package/dist/data/pdf-data.tsx +124 -0
  137. package/dist/data/pos-data.tsx +213 -0
  138. package/dist/data/postcss.config.js +6 -0
  139. package/dist/data/primitive-data.tsx +5170 -0
  140. package/dist/data/prompt-data.tsx +1226 -0
  141. package/dist/data/requiredLibs.ts +4 -0
  142. package/dist/data/sandbox-data.tsx +1 -0
  143. package/dist/data/sidebars-data.tsx +5421 -0
  144. package/dist/data/stacks-data.tsx +32 -0
  145. package/dist/data/table-data.tsx +706 -0
  146. package/dist/data/tailwind.config.js +270 -0
  147. package/dist/data/tailwind.config.ngin.js +3830 -0
  148. package/dist/data/tailwind.css +431 -0
  149. package/dist/data/tools-data.tsx +6910 -0
  150. package/dist/data/typography-data.tsx +2050 -0
  151. package/dist/data/utils-data.tsx +6500 -0
  152. package/dist/data/x-data.tsx +1171 -0
  153. package/dist/data.tsx +159 -0
  154. package/package.json +1 -1
  155. package/dist/index.d.ts +0 -3
  156. package/dist/index.d.ts.map +0 -1
  157. package/dist/index.js.map +0 -362
@@ -0,0 +1,1910 @@
1
+ import { eachYearOfInterval, startOfYear, endOfYear, eachMonthOfInterval, isBefore, isAfter, addDays, subDays, startOfMonth, subMonths, endOfMonth, addMonths, subYears } from "date-fns";
2
+ import { enUS, hi } from 'react-day-picker/locale'
3
+ import { formatDateRange } from "little-date";
4
+ import { format } from 'date-fns'
5
+ import { PlusIcon, CalendarIcon, ClockIcon, ChevronDownIcon, CircleCheckIcon } from "lucide-react";
6
+ import { useState, useId, useEffect, useRef } from "react";
7
+ import { type DateRange } from 'react-day-picker'
8
+ import { SelectValue, Input, Select, ScrollArea, Collapsible, Card, CardTitle, CardDescription, cn, Label, CardHeader, CardAction, CardContent, CardFooter, CalendarDayButton, Button, SelectTrigger, SelectContent, SelectItem, CollapsibleTrigger, CollapsibleContent, Calendar, calendarNavButtonVariants, calendarDayButtonVariants, calendarVariants } from "~/components/catalyst-ui";
9
+ import type { CaptionLabelProps, MonthGridProps } from 'react-day-picker'
10
+
11
+ /**
12
+ * ★ ━━━━ ☆ ━━━━ CalendarX ━━━━ ☆ ━━━━ ★
13
+ * type CalendarXType =
14
+ * | 'default' | 'MultiMonth' | 'WeekNumber' // Basic Variants
15
+ * | 'RangeSingleMonth' | 'RangeCalendarMultiMonth' | 'RangeWithMinimumDays' // Range Selection
16
+ * | 'MultiSelect' | 'CustomSelectDay' | 'CustomRangeSelect' // Selection Types
17
+ * | 'DisableDay' | 'DisabledWeekends' // Disabled Dates
18
+ * | 'WithMonthYearDropdown' | 'RightYearMonth' | 'LeftYearMonth' // Navigation & Layout
19
+ * | 'WithTodayMonthButton' | 'VariableSize' | 'WithAdvanceSelection'
20
+ * | 'WithDateInput' | 'WithTimeInput' // Input Integration
21
+ * | 'WithPresets' | 'WithRangePresets' // Quick Presets
22
+ * | 'CalendarAppointmentBooking' | 'EventList' | 'Pricing' | 'Localization' // Specialized
23
+ *
24
+ * ★ ━━━━ ☆ ━━━━ USAGE ━━━━ ☆ ━━━━ ★
25
+ *
26
+ * ★ ━━━━━━━━━ Basic Calendar Variants ━━━━━━━━━ ★
27
+ * ```jsx
28
+ * // Default Calendar - Single date selection
29
+ * <CalendarX
30
+ * calendar="default"
31
+ * date={singleDate}
32
+ * setDate={setSingleDate}
33
+ * />
34
+ *
35
+ * // Multi Month - Display multiple months
36
+ * <CalendarX
37
+ * calendar="MultiMonth"
38
+ * date={singleDate}
39
+ * setDate={setSingleDate}
40
+ * numOfMonths={2}
41
+ * />
42
+ *
43
+ * // With Week Numbers - Show week numbers
44
+ * <CalendarX
45
+ * calendar="WeekNumber"
46
+ * date={singleDate}
47
+ * setDate={setSingleDate}
48
+ * />
49
+ * ```
50
+ *
51
+ * ★ ━━━━━━━━━ Range Selection ━━━━━━━━━ ★
52
+ * ```jsx
53
+ * // Single Month Range - Select a date range
54
+ * <CalendarX
55
+ * calendar="RangeSingleMonth"
56
+ * dateRange={rangeDate}
57
+ * setDateRange={setRangeDate}
58
+ * />
59
+ *
60
+ * // Multi Month Range - Range across months
61
+ * <CalendarX
62
+ * calendar="RangeCalendarMultiMonth"
63
+ * dateRange={rangeDate}
64
+ * setDateRange={setRangeDate}
65
+ * numOfMonths={2}
66
+ * />
67
+ *
68
+ * // Minimum Days Range - Minimum 5 days selection
69
+ * <CalendarX
70
+ * calendar="RangeWithMinimumDays"
71
+ * dateRange={rangeDate}
72
+ * setDateRange={setRangeDate}
73
+ * min={5}
74
+ * />
75
+ * ```
76
+ *
77
+ * ★ ━━━━━━━━━ Selection Types ━━━━━━━━━ ★
78
+ * ```jsx
79
+ * // Multiple Selection - Select up to 5 dates
80
+ * <CalendarX
81
+ * calendar="MultiSelect"
82
+ * dates={multiDates}
83
+ * setDates={setMultiDates}
84
+ * />
85
+ *
86
+ * // Custom Day Selection - Custom styling for selected days
87
+ * <CalendarX
88
+ * calendar="CustomSelectDay"
89
+ * date={singleDate}
90
+ * setDate={setSingleDate}
91
+ * />
92
+ *
93
+ * // Custom Range Selection - Styled range with start/end markers
94
+ * <CalendarX
95
+ * calendar="CustomRangeSelect"
96
+ * dateRange={rangeDate}
97
+ * setDateRange={setRangeDate}
98
+ * />
99
+ * ```
100
+ *
101
+ * ★ ━━━━━━━━━ Disabled Dates ━━━━━━━━━ ★
102
+ * ```jsx
103
+ * // Disable Past Dates - Dates before specific date are disabled
104
+ * <CalendarX
105
+ * calendar="DisableDay"
106
+ * date={singleDate}
107
+ * setDate={setSingleDate}
108
+ * before={new Date(2025, 5, 12)}
109
+ * />
110
+ *
111
+ * // Disable Weekends - Saturdays and Sundays disabled
112
+ * <CalendarX
113
+ * calendar="DisabledWeekends"
114
+ * dateRange={rangeDate}
115
+ * setDateRange={setRangeDate}
116
+ * />
117
+ * ```
118
+ *
119
+ * ★ ━━━━━━━━━ Navigation & Layout ━━━━━━━━━ ★
120
+ * ```jsx
121
+ * // With Dropdowns - Month/Year dropdown selection
122
+ * <CalendarX
123
+ * calendar="WithMonthYearDropdown"
124
+ * date={singleDate}
125
+ * setDate={setSingleDate}
126
+ * />
127
+ *
128
+ * // Right Navigation - Navigation on the right side
129
+ * <CalendarX
130
+ * calendar="RightYearMonth"
131
+ * date={singleDate}
132
+ * setDate={setSingleDate}
133
+ * />
134
+ *
135
+ * // Left Navigation - Navigation on the left side
136
+ * <CalendarX
137
+ * calendar="LeftYearMonth"
138
+ * date={singleDate}
139
+ * setDate={setSingleDate}
140
+ * />
141
+ *
142
+ * // With Today Button - Quick navigation to today
143
+ * <CalendarX
144
+ * calendar="WithTodayMonthButton"
145
+ * date={singleDate}
146
+ * setDate={setSingleDate}
147
+ * month={month}
148
+ * setMonth={setMonth}
149
+ * />
150
+ *
151
+ * // Variable Size - Custom cell sizes
152
+ * <CalendarX
153
+ * calendar="VariableSize"
154
+ * date={singleDate}
155
+ * setDate={setSingleDate}
156
+ * />
157
+ *
158
+ * // Advanced Selection - Year/month grid view
159
+ * <CalendarX
160
+ * calendar="WithAdvanceSelection"
161
+ * date={singleDate}
162
+ * setDate={setSingleDate}
163
+ * />
164
+ * ```
165
+ *
166
+ * ★ ━━━━━━━━━ Input Integration ━━━━━━━━━ ★
167
+ * ```jsx
168
+ * // With Date Input - Type or pick a date
169
+ * <CalendarX calendar="WithDateInput" />
170
+ *
171
+ * // With Time Input - Select date and time
172
+ * <CalendarX
173
+ * calendar="WithTimeInput"
174
+ * date={singleDate}
175
+ * setDate={setSingleDate}
176
+ * time={time}
177
+ * setTime={setTime}
178
+ * />
179
+ * ```
180
+ *
181
+ * ★ ━━━━━━━━━ Quick Presets ━━━━━━━━━ ★
182
+ * ```jsx
183
+ * // Date Presets - Quick date selections
184
+ * <CalendarX
185
+ * calendar="WithPresets"
186
+ * date={singleDate}
187
+ * setDate={setSingleDate}
188
+ * />
189
+ *
190
+ * // Range Presets - Common range selections
191
+ * <CalendarX calendar="WithRangePresets" />
192
+ * ```
193
+ *
194
+ * ★ ━━━━━━━━━ Specialized Calendars ━━━━━━━━━ ★
195
+ * ```jsx
196
+ * // Appointment Booking - Book appointments with time slots
197
+ * <CalendarX
198
+ * calendar="CalendarAppointmentBooking"
199
+ * date={singleDate}
200
+ * setDate={setSingleDate}
201
+ * selectedTime={selectedTime}
202
+ * setSelectedTime={setSelectedTime}
203
+ * />
204
+ *
205
+ * // Event List Calendar - View events for selected date
206
+ * <CalendarX
207
+ * calendar="EventList"
208
+ * date={singleDate}
209
+ * setDate={setSingleDate}
210
+ * />
211
+ *
212
+ * // Pricing Calendar - Dynamic pricing per date
213
+ * <CalendarX
214
+ * calendar="Pricing"
215
+ * date={singleDate}
216
+ * setDate={setSingleDate}
217
+ * />
218
+ *
219
+ * // Localization - Hindi and English support
220
+ * <CalendarX
221
+ * calendar="Localization"
222
+ * dateRange={rangeDate}
223
+ * setDateRange={setRangeDate}
224
+ * locale={locale}
225
+ * setLocale={setLocale}
226
+ * />
227
+ * ```
228
+ *
229
+ * ★ ━━━━━━━━━ Props ━━━━━━━━━ ★
230
+ *
231
+ * CalendarX Props:
232
+ * - `calendar`: CalendarXType - Specifies which variant to use
233
+ * - `date`: Date | undefined - Selected single date (for single selection variants)
234
+ * - `setDate`: (date: Date | undefined) => void - Single date change callback
235
+ * - `dateRange`: { from: Date; to: Date } - Date range object (for range variants)
236
+ * - `setDateRange`: (range: { from: Date; to: Date }) => void - Range change callback
237
+ * - `dates`: Date[] - Array of selected dates (for multi-select variants)
238
+ * - `setDates`: (dates: Date[]) => void - Multi-date change callback
239
+ * - `numOfMonths`: number - Number of months to display
240
+ * - `min`: number - Minimum number of days required (for range variants)
241
+ * - `before`: Date - Disable dates before this date
242
+ * - `month`: Date - Current month view
243
+ * - `setMonth`: (month: Date) => void - Month change callback
244
+ * - `time`: string - Selected time string (for time input variants)
245
+ * - `setTime`: (time: string) => void - Time change callback
246
+ * - `selectedTime`: string - Selected appointment time (for booking variants)
247
+ * - `setSelectedTime`: (time: string) => void - Appointment time callback
248
+ * - `locale`: 'en' | 'hi' - Language locale (for localization variants)
249
+ * - `setLocale`: (locale: 'en' | 'hi') => void - Locale change callback
250
+ * - `className`: string - Additional CSS classes
251
+ *
252
+ * ★ ━━━━━━━━━ State Setup Examples ━━━━━━━━━ ★
253
+ * ```typescript
254
+ * // Single date selection
255
+ * const [singleDate, setSingleDate] = useState<Date | undefined>(new Date(2025, 5, 15));
256
+ *
257
+ * // Date range selection
258
+ * const [rangeDate, setRangeDate] = useState({
259
+ * from: new Date(2025, 5, 4),
260
+ * to: new Date(2025, 5, 17)
261
+ * });
262
+ *
263
+ * // Multiple date selection
264
+ * const [multiDates, setMultiDates] = useState<Date[]>([
265
+ * new Date(2025, 5, 12),
266
+ * new Date(2025, 5, 17),
267
+ * new Date(2025, 5, 20)
268
+ * ]);
269
+ *
270
+ * // Time selection
271
+ * const [time, setTime] = useState('12:00:00');
272
+ *
273
+ * // Locale selection
274
+ * const [locale, setLocale] = useState<'en' | 'hi'>('en');
275
+ * ```
276
+ *
277
+ * ★ ━━━━━━━━━ Basic Usage ━━━━━━━━━ ★
278
+ * ```javascript
279
+ * import { useState } from "react";
280
+ * import { CalendarX } from "./CalendarX";
281
+ *
282
+ * function App() {
283
+ * const [date, setDate] = useState<Date | undefined>(new Date());
284
+ *
285
+ * // Default calendar
286
+ * return <CalendarX date={date} setDate={setDate} />;
287
+ *
288
+ * // Specific variant
289
+ * return <CalendarX calendar="MultiMonth" date={date} setDate={setDate} />;
290
+ * }
291
+ * ```
292
+ *
293
+ */
294
+
295
+ export function CalendarX({ calendar = 'default', children, ...props }: any) {
296
+ switch (calendar) {
297
+ case 'MultiMonth': // Multi month calendar
298
+ return <MultiMonth {...props} />
299
+ case 'RangeSingleMonth': // Single month calendar with range selection
300
+ return <RangeSingleMonth {...props} />
301
+ case 'RangeCalendarMultiMonth': // Multi month calendar with range selection
302
+ return <RangeCalendarMultiMonth {...props} />
303
+ case 'RangeWithMinimumDays': // Minimum 5 days selection
304
+ return <RangeWithMinimumDays {...props} />
305
+ case 'DisableDay': // Disabled day calendar
306
+ return <DisableDay {...props} />
307
+ case 'DisabledWeekends': // Disabled weekend calendar
308
+ return <DisabledWeekends {...props} />
309
+ case 'Localization': // Localize calendar
310
+ return <Localization {...props} />
311
+ case 'WithMonthYearDropdown': // Month and year dropdown calendar
312
+ return <WithMonthYearDropdown {...props} />
313
+ case 'VariableSize': // Variable size calendar
314
+ return <VariableSize {...props} />
315
+ case 'EventList': // Calendar with event list
316
+ return <EventList {...props} />
317
+ case 'MultiSelect': // Multi day select calendar
318
+ return <MultiSelect {...props} />
319
+ case 'CustomSelectDay': // Custom day select calendar
320
+ return <CustomSelectDay {...props} />
321
+ case 'CustomRangeSelect': // Custom range selection calendar
322
+ return <CustomRangeSelect {...props} />
323
+ case 'RightYearMonth': // Right side month year navigation calendar
324
+ return <RightYearMonth {...props} />
325
+ case 'LeftYearMonth': // Left side month year navigation calendar
326
+ return <LeftYearMonth {...props} />
327
+ case 'WeekNumber': // Week number calendar
328
+ return <WeekNumber {...props} />
329
+ case 'WithTodayMonthButton': // Calendar with today button
330
+ return <WithTodayMonthButton {...props} />
331
+ case 'WithDateInput': // Calendar with date input
332
+ return <WithDateInput {...props} />
333
+ case 'WithTimeInput': // Calendar with time input
334
+ return <WithTimeInput {...props} />
335
+ case 'WithAdvanceSelection': // Calendar with advance selection
336
+ return <WithAdvanceSelection {...props} />
337
+ case 'WithPresets': // Calendar with presets
338
+ return <WithPresets {...props} />
339
+ case 'WithRangePresets': // Range calendar with presets
340
+ return <WithRangePresets {...props} />
341
+ case 'CalendarAppointmentBooking': // Appointment calendar
342
+ return <AppointmentBooking {...props} />
343
+ case 'Pricing': // Calendar with pricing
344
+ return <Pricing {...props} />
345
+ default:
346
+ <DefaultCalendar {...props} />
347
+ break;
348
+ }
349
+ }
350
+
351
+ function DefaultCalendar({ date = new Date(), setDate, className, variant, size }) {
352
+ return (
353
+ <div>
354
+ <Calendar mode='single' defaultMonth={date} selected={date} onSelect={setDate} className={cn(calendarVariants({ variant, size }), 'rounded-lg border border-border', className)} />
355
+ </div>
356
+ )
357
+ }
358
+ const MultiMonth = ({ date = new Date(), setDate, numOfMonths = 2, className, variant, size }) => {
359
+ return (
360
+ <div>
361
+ <Calendar
362
+ mode='single'
363
+ defaultMonth={date}
364
+ numberOfMonths={numOfMonths}
365
+ selected={date}
366
+ onSelect={setDate}
367
+ className={cn(calendarVariants({ variant, size }), 'rounded-lg border border-border', className)}
368
+ />
369
+ </div>
370
+ )
371
+ }
372
+ const RangeSingleMonth = ({
373
+ dateRange = {
374
+ from: new Date(2025, 5, 4),
375
+ to: new Date(2025, 5, 17)
376
+ },
377
+ setDateRange,
378
+ className, variant, size
379
+ }) => {
380
+ return (
381
+ <div>
382
+ <Calendar
383
+ mode='range'
384
+ selected={dateRange}
385
+ defaultMonth={dateRange?.from}
386
+ onSelect={setDateRange}
387
+ className={cn(calendarVariants({ variant, size }), 'rounded-lg border border-border', className)}
388
+ />
389
+ <p className='text-muted-foreground mt-3 text-center text-xs' role='region'>
390
+
391
+ </p>
392
+ </div>
393
+ )
394
+ }
395
+ const RangeCalendarMultiMonth = ({
396
+ dateRange = {
397
+ from: new Date(2025, 5, 4),
398
+ to: new Date(2025, 5, 17)
399
+ }, setDateRange,
400
+ numOfMonths = 2, className, variant, size
401
+ }) => {
402
+ return (
403
+ <div>
404
+ <Calendar
405
+ mode='range'
406
+ defaultMonth={dateRange?.from}
407
+ selected={dateRange}
408
+ onSelect={setDateRange}
409
+ numberOfMonths={numOfMonths}
410
+ className={cn(calendarVariants({ variant, size }), 'rounded-lg border border-border', className)}
411
+ />
412
+ </div>
413
+ )
414
+ }
415
+ const RangeWithMinimumDays = ({
416
+ dateRange = {
417
+ from: new Date(2025, 5, 4),
418
+ to: new Date(2025, 5, 17)
419
+ }, setDateRange,
420
+ numOfMonths = 2,
421
+ min = 5,
422
+ className, variant, size
423
+ }) => {
424
+
425
+ return (
426
+ <div>
427
+ <Calendar
428
+ mode='range'
429
+ defaultMonth={dateRange?.from}
430
+ selected={dateRange}
431
+ onSelect={setDateRange}
432
+ numberOfMonths={numOfMonths}
433
+ min={min}
434
+ className={cn(calendarVariants({ variant, size }), 'rounded-lg border border-border', className)}
435
+ />
436
+ </div>
437
+ )
438
+ }
439
+ const DisableDay = ({ date = new Date(2025, 5, 18), setDate, className, variant, size, before = new Date(2025, 5, 12) }) => {
440
+
441
+ return (
442
+ <div>
443
+ <Calendar
444
+ mode='single'
445
+ defaultMonth={date}
446
+ selected={date}
447
+ onSelect={setDate}
448
+ disabled={{
449
+ before: before
450
+ }}
451
+ className={cn(calendarVariants({ variant, size }), 'rounded-lg border border-border', className)}
452
+ />
453
+ </div>
454
+ )
455
+ }
456
+ const DisabledWeekends = ({ dateRange = {
457
+ from: new Date(2025, 5, 4),
458
+ to: new Date(2025, 5, 17)
459
+ }, setDateRange,
460
+ className, variant, size, dayOfWeek = [0, 6] }) => {
461
+ return (
462
+ <div>
463
+ <Calendar
464
+ mode='range'
465
+ defaultMonth={dateRange?.from}
466
+ selected={dateRange}
467
+ onSelect={setDateRange}
468
+ disabled={{ dayOfWeek: dayOfWeek }}
469
+ className={cn(calendarVariants({ variant, size }), 'rounded-lg border border-border', className)}
470
+ excludeDisabled
471
+ />
472
+ </div>
473
+ )
474
+ }
475
+ const localizedStrings = {
476
+ en: {
477
+ title: 'Book an appointment',
478
+ description: 'Select the dates for your appointment'
479
+ },
480
+ hi: {
481
+ title: 'अपॉइंटमेंट बुक करें',
482
+ description: 'अपनी अपॉइंटमेंट के लिए तारीखें चुनें'
483
+ }
484
+ } as const
485
+ function Localization({
486
+ dateRange = {
487
+ from: new Date(2025, 5, 4),
488
+ to: new Date(2025, 5, 17)
489
+ }, setDateRange,
490
+ className, variant, size,
491
+ locale = 'en',
492
+ setLocale
493
+ }) {
494
+ return (
495
+ <div>
496
+ <Card className='w-xxs shadow-none'>
497
+ <CardHeader className='border border-border-b'>
498
+ <CardTitle>{localizedStrings[locale].title}</CardTitle>
499
+ <CardDescription>{localizedStrings[locale].description}</CardDescription>
500
+ <CardAction>
501
+ <Select value={locale} onValueChange={value => setLocale(value as keyof typeof localizedStrings)}>
502
+ <SelectTrigger className='w-[100px]' aria-label='Select language'>
503
+ <SelectValue placeholder='Language' />
504
+ </SelectTrigger>
505
+ <SelectContent align='end'>
506
+ <SelectItem value='hi'>Hindi</SelectItem>
507
+ <SelectItem value='en'>English</SelectItem>
508
+ </SelectContent>
509
+ </Select>
510
+ </CardAction>
511
+ </CardHeader>
512
+ <CardContent>
513
+ <Calendar
514
+ mode='range'
515
+ selected={dateRange}
516
+ onSelect={setDateRange}
517
+ defaultMonth={dateRange?.from}
518
+ locale={locale === 'hi' ? hi : enUS}
519
+ numerals={locale === 'hi' ? 'deva' : 'latn'}
520
+ className={cn(calendarVariants({ variant, size }), 'w-full bg-transparent p-0', className)}
521
+ buttonVariant='outline'
522
+ />
523
+ </CardContent>
524
+ </Card>
525
+ </div>
526
+ )
527
+ }
528
+ const WithMonthYearDropdown = ({ date = new Date(), setDate, className, variant, size }) => {
529
+ return (
530
+ <div>
531
+ <Calendar
532
+ mode='single'
533
+ selected={date}
534
+ onSelect={setDate}
535
+ className={cn(calendarVariants({ variant, size }), 'rounded-md border border-border', className)}
536
+ captionLayout='dropdown'
537
+ />
538
+ </div>
539
+ )
540
+ }
541
+ const VariableSize = ({ date = new Date(), setDate, className, variant, size }) => {
542
+ return (
543
+ <div>
544
+ <Calendar
545
+ mode='single'
546
+ selected={date}
547
+ onSelect={setDate}
548
+ className={cn(calendarVariants({ variant, size }), 'rounded-lg border border-border [--cell-size:--spacing(11)] md:[--cell-size:--spacing(13)]', className)}
549
+ />
550
+ <p className='text-muted-foreground mt-3 text-center text-xs' role='region'>
551
+
552
+ </p>
553
+ </div>
554
+ )
555
+ }
556
+ const EventList = ({ date = new Date(), setDate, className, variant, size, events = [
557
+ {
558
+ title: 'Team Sync Meeting',
559
+ from: '2025-06-12T09:00:00',
560
+ to: '2025-06-12T10:00:00'
561
+ },
562
+ {
563
+ title: 'Design Review',
564
+ from: '2025-06-12T11:30:00',
565
+ to: '2025-06-12T12:30:00'
566
+ },
567
+ {
568
+ title: 'Client Presentation',
569
+ from: '2025-06-12T14:00:00',
570
+ to: '2025-06-12T15:00:00'
571
+ }
572
+ ] }) => {
573
+ return (
574
+ <div>
575
+ <Card className='w-2xs py-4'>
576
+ <CardContent className='px-4'>
577
+ <Calendar
578
+ mode='single'
579
+ selected={date}
580
+ onSelect={setDate}
581
+ className={cn(calendarVariants({ variant, size }), 'w-full bg-transparent p-0', className)}
582
+ required
583
+ />
584
+ </CardContent>
585
+ <CardFooter className='flex flex-col items-start gap-3 border border-border-t px-4 !pt-4'>
586
+ <div className='flex w-full items-center justify-between px-1'>
587
+ <div className='text-sm font-medium'>
588
+ {date?.toLocaleDateString('en-US', {
589
+ day: 'numeric',
590
+ month: 'long',
591
+ year: 'numeric'
592
+ })}
593
+ </div>
594
+ <Button variant='ghost' size='icon' className='size-6' title='Add Event'>
595
+ <PlusIcon />
596
+ <span className='sr-only'>Add Event</span>
597
+ </Button>
598
+ </div>
599
+ <div className='flex w-full flex-col gap-2'>
600
+ {events.map(event => (
601
+ <div
602
+ key={event.title}
603
+ className='bg-muted after:bg-primary/70 relative rounded-md p-2 pl-6 text-sm after:absolute after:inset-y-2 after:left-2 after:w-1 after:rounded-full'
604
+ >
605
+ <div className='font-medium'>{event.title}</div>
606
+ <div className='text-muted-foreground text-xs'>
607
+ {formatDateRange(new Date(event.from), new Date(event.to))}
608
+ </div>
609
+ </div>
610
+ ))}
611
+ </div>
612
+ </CardFooter>
613
+ </Card>
614
+
615
+ </div>
616
+ )
617
+ }
618
+ const MultiSelect = ({ dates = [new Date(2025, 5, 12), new Date(2025, 5, 17)], setDates, className, variant, size }) => {
619
+ return (
620
+ <div>
621
+ <Calendar
622
+ mode='multiple'
623
+ required selected={dates}
624
+ onSelect={setDates} max={5}
625
+ className={cn(calendarVariants({ variant, size }), 'rounded-lg border border-border', className)}
626
+ />
627
+ </div>
628
+ )
629
+ }
630
+ const CustomSelectDay = ({ date = new Date(), setDate, className, variant, size }) => {
631
+ return (
632
+ <div>
633
+ <Calendar
634
+ mode='single'
635
+ selected={date}
636
+ onSelect={setDate}
637
+ className={cn(calendarVariants({ variant, size }), 'rounded-lg border border-border', className)}
638
+ classNames={{
639
+ day_button:
640
+ 'rounded-full! data-[selected-single=true]:bg-sky-600! data-[selected-single=true]:text-white! data-[selected-single=true]:dark:bg-sky-400! data-[selected-single=true]:group-data-[focused=true]/day:ring-sky-600/20 data-[selected-single=true]:dark:group-data-[focused=true]/day:ring-sky-400/40',
641
+ today: 'rounded-full! bg-accent!'
642
+ }}
643
+ />
644
+ </div>
645
+ )
646
+ }
647
+ const CustomRangeSelect = ({
648
+ dateRange = {
649
+ from: new Date(2025, 5, 4),
650
+ to: new Date(2025, 5, 17)
651
+ },
652
+ setDateRange,
653
+ className, variant, size
654
+ }) => {
655
+ return (
656
+ <div>
657
+ <Calendar
658
+ mode='range'
659
+ defaultMonth={dateRange?.from}
660
+ selected={dateRange}
661
+ onSelect={setDateRange}
662
+ className={cn(calendarVariants({ variant, size }), 'rounded-md border border-border', className)}
663
+ classNames={{
664
+ range_start: 'bg-sky-600/20 dark:bg-sky-400/10 rounded-l-full',
665
+ range_end: 'bg-sky-600/20 dark:bg-sky-400/10 rounded-r-full',
666
+ day_button:
667
+ 'data-[range-end=true]:rounded-full! data-[range-start=true]:rounded-full! data-[range-start=true]:bg-sky-600! data-[range-start=true]:text-white! data-[range-start=true]:dark:bg-sky-400! data-[range-start=true]:group-data-[focused=true]/day:ring-sky-600/20 data-[range-start=true]:dark:group-data-[focused=true]/day:ring-sky-400/40 data-[range-end=true]:bg-sky-600! data-[range-end=true]:text-white! data-[range-end=true]:dark:bg-sky-400! data-[range-end=true]:group-data-[focused=true]/day:ring-sky-600/20 data-[range-end=true]:dark:group-data-[focused=true]/day:ring-sky-400/40 data-[range-middle=true]:rounded-none data-[range-middle=true]:bg-sky-600/20 data-[range-middle=true]:dark:bg-sky-400/10 hover:rounded-full',
668
+ today:
669
+ 'data-[selected=true]:rounded-l-none! rounded-full bg-accent! data-[selected=true]:bg-sky-600/20! dark:data-[selected=true]:bg-sky-400/10! [&_button[data-range-middle=true]]:bg-transparent!'
670
+ }}
671
+ />
672
+ </div>
673
+ )
674
+ }
675
+ const RightYearMonth = ({ date = new Date(), setDate, className, variant, size }) => {
676
+ return (
677
+ <div>
678
+ <Calendar
679
+ mode='single'
680
+ selected={date}
681
+ defaultMonth={date}
682
+ onSelect={setDate}
683
+ className={cn(calendarVariants({ variant, size }), 'rounded-md border border-border', className)}
684
+ classNames={{
685
+ month_caption: 'flex items-center h-8 justify-start',
686
+ nav: 'flex justify-end absolute w-full items-center'
687
+ }}
688
+ />
689
+ </div>
690
+ )
691
+ }
692
+ const LeftYearMonth = ({ date = new Date(), setDate, className, variant, size }) => {
693
+ return (
694
+ <div>
695
+ <Calendar
696
+ mode='single'
697
+ selected={date}
698
+ defaultMonth={date}
699
+ onSelect={setDate}
700
+ className={cn(calendarVariants({ variant, size }), 'rounded-md border border-border', className)}
701
+ classNames={{
702
+ month_caption: 'flex items-center h-8 justify-end',
703
+ nav: 'flex justify-start absolute w-full items-center'
704
+ }}
705
+ />
706
+ </div>
707
+ )
708
+ }
709
+ const WeekNumber = ({ date = new Date(), setDate, className, variant, size }) => {
710
+ return (
711
+ <div>
712
+ <Calendar
713
+ mode='single'
714
+ defaultMonth={date}
715
+ selected={date}
716
+ onSelect={setDate}
717
+ className={cn(calendarVariants({ variant, size }), 'rounded-lg border border-border shadow-sm', className)}
718
+ showWeekNumber
719
+ />
720
+ </div>
721
+ )
722
+ }
723
+ const WithTodayMonthButton = ({
724
+ date = new Date(),
725
+ setDate,
726
+ month = new Date(),
727
+ setMonth,
728
+ className,
729
+ variant,
730
+ size
731
+ }) => {
732
+ return (
733
+ <div>
734
+ <Card>
735
+ <CardHeader>
736
+ <CardTitle>Book the show</CardTitle>
737
+ <CardDescription>Find a date</CardDescription>
738
+ <CardAction>
739
+ <Button
740
+ size='sm'
741
+ variant='outline'
742
+ onClick={() => {
743
+ setMonth(new Date())
744
+ setDate(new Date())
745
+ }}
746
+ >
747
+ Today
748
+ </Button>
749
+ </CardAction>
750
+ </CardHeader>
751
+ <CardContent>
752
+ <Calendar
753
+ mode='single'
754
+ month={month}
755
+ onMonthChange={setMonth}
756
+ selected={date}
757
+ onSelect={setDate}
758
+ className=''
759
+ className={cn(calendarVariants({ variant, size }), 'bg-transparent p-0', className)}
760
+ />
761
+ </CardContent>
762
+ </Card>
763
+
764
+ </div>
765
+ )
766
+ }
767
+
768
+ const WithDateInput = ({ today = new Date(), className, variant, size }) => {
769
+ const id = useId()
770
+ const [month, setMonth] = useState(today)
771
+ const [date, setDate] = useState<Date | undefined>(today)
772
+ const [inputValue, setInputValue] = useState('')
773
+
774
+ const handleDayPickerSelect = (date: Date | undefined) => {
775
+ if (!date) {
776
+ setInputValue('')
777
+ setDate(undefined)
778
+ } else {
779
+ setDate(date)
780
+ setMonth(date)
781
+ setInputValue(format(date, 'yyyy-MM-dd'))
782
+ }
783
+ }
784
+
785
+ const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
786
+ const value = e.target.value
787
+
788
+ setInputValue(value)
789
+
790
+ if (value) {
791
+ const parsedDate = new Date(value)
792
+
793
+ setDate(parsedDate)
794
+ setMonth(parsedDate)
795
+ } else {
796
+ setDate(undefined)
797
+ }
798
+ }
799
+
800
+ useEffect(() => {
801
+ setInputValue(format(today, 'yyyy-MM-dd'))
802
+ // eslint-disable-next-line react-hooks/exhaustive-deps
803
+ }, [])
804
+
805
+ return (
806
+ <div>
807
+ <Card className='gap-5 py-5'>
808
+ <CardHeader className='flex items-center border border-border-b px-3 !pb-3'>
809
+ <Label htmlFor={id} className='shrink-0 text-xs'>
810
+ Enter date
811
+ </Label>
812
+ <div className='relative grow'>
813
+ <Input
814
+ id={id}
815
+ type='date'
816
+ value={inputValue}
817
+ onChange={handleInputChange}
818
+ className='peer appearance-none pl-9 [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none'
819
+ aria-label='Select date'
820
+ />
821
+ <div className='text-muted-foreground/80 pointer-events-none absolute inset-y-0 left-0 flex items-center justify-center pl-3 peer-disabled:opacity-50'>
822
+ <CalendarIcon size={16} aria-hidden='true' />
823
+ </div>
824
+ </div>
825
+ </CardHeader>
826
+ <CardContent className='px-5'>
827
+ <Calendar
828
+ mode='single'
829
+ selected={date}
830
+ onSelect={handleDayPickerSelect}
831
+ month={month}
832
+ onMonthChange={setMonth}
833
+ className={cn(calendarVariants({ variant, size }), 'bg-transparent p-0', className)}
834
+ />
835
+ </CardContent>
836
+ </Card>
837
+
838
+ </div>
839
+ )
840
+ }
841
+ const WithTimeInput = ({ date = new Date(), setDate, time = '12:00:00', setTime, className, variant, size }) => {
842
+ const id = useId()
843
+
844
+ return (
845
+ <div>
846
+ <Card className='gap-5 py-5'>
847
+ <CardHeader className='flex items-center border border-border-b px-3 !pb-3'>
848
+ <Label htmlFor={id} className='text-xs'>
849
+ Enter time
850
+ </Label>
851
+ <div className='relative grow'>
852
+ <Input
853
+ id={id}
854
+ type='time'
855
+ step='1'
856
+ value={time}
857
+ onChange={setTime}
858
+ className='peer appearance-none pl-9 [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none'
859
+ />
860
+ <div className='text-muted-foreground/80 pointer-events-none absolute inset-y-0 left-0 flex items-center justify-center pl-3 peer-disabled:opacity-50'>
861
+ <ClockIcon size={16} aria-hidden='true' />
862
+ </div>
863
+ </div>
864
+ </CardHeader>
865
+ <CardContent className='px-5'>
866
+ <Calendar
867
+ mode='single'
868
+ selected={date}
869
+ onSelect={setDate}
870
+ className={cn(calendarVariants({ variant, size }), 'bg-transparent p-0', className)}
871
+ />
872
+ </CardContent>
873
+ </Card>
874
+ <p className='text-muted-foreground mt-4 text-center text-xs' role='region'>
875
+
876
+ </p>
877
+ </div>
878
+ )
879
+ }
880
+ const WithAdvanceSelection = ({ today = new Date(), startDate = new Date(1980, 6), endDate = new Date(2030, 6), className, variant, size }) => {
881
+ const [month, setMonth] = useState(today)
882
+ const [date, setDate] = useState<Date | undefined>(today)
883
+ const [isYearView, setIsYearView] = useState(false)
884
+
885
+ const years = eachYearOfInterval({
886
+ start: startOfYear(startDate),
887
+ end: endOfYear(endDate)
888
+ })
889
+
890
+ return (
891
+ <div>
892
+ <Calendar
893
+ mode='single'
894
+ selected={date}
895
+ onSelect={setDate}
896
+ month={month}
897
+ onMonthChange={setMonth}
898
+ defaultMonth={new Date()}
899
+ startMonth={startDate}
900
+ endMonth={endDate}
901
+ className={cn(calendarVariants({ variant, size }), 'overflow-hidden rounded-md border border-border p-2', className)}
902
+ classNames={{
903
+ month_caption: 'ml-2.5 mr-20 justify-start',
904
+ nav: 'flex absolute w-fit right-0 items-center'
905
+ }}
906
+ components={{
907
+ CaptionLabel: (props: CaptionLabelProps) => (
908
+ <CaptionLabel isYearView={isYearView} setIsYearView={setIsYearView} {...props} />
909
+ ),
910
+ MonthGrid: (props: MonthGridProps) => {
911
+ return (
912
+ <MonthGrid
913
+ className={props.className}
914
+ isYearView={isYearView}
915
+ setIsYearView={setIsYearView}
916
+ startDate={startDate}
917
+ endDate={endDate}
918
+ years={years}
919
+ currentYear={month.getFullYear()}
920
+ currentMonth={month.getMonth()}
921
+ onMonthSelect={(selectedMonth: Date) => {
922
+ setMonth(selectedMonth)
923
+ setIsYearView(false)
924
+ }}
925
+ >
926
+ {props.children}
927
+ </MonthGrid>
928
+ )
929
+ }
930
+ }}
931
+ />
932
+
933
+ </div>
934
+ )
935
+ }
936
+
937
+ function MonthGrid({
938
+ className,
939
+ children,
940
+ isYearView,
941
+ startDate,
942
+ endDate,
943
+ years,
944
+ currentYear,
945
+ currentMonth,
946
+ onMonthSelect
947
+ }: {
948
+ className?: string
949
+ children: React.ReactNode
950
+ isYearView: boolean
951
+ setIsYearView: React.Dispatch<React.SetStateAction<boolean>>
952
+ startDate: Date
953
+ endDate: Date
954
+ years: Date[]
955
+ currentYear: number
956
+ currentMonth: number
957
+ onMonthSelect: (date: Date) => void
958
+ }) {
959
+ const currentYearRef = useRef<HTMLDivElement>(null)
960
+ const currentMonthButtonRef = useRef<HTMLButtonElement>(null)
961
+ const scrollAreaRef = useRef<HTMLDivElement>(null)
962
+
963
+ useEffect(() => {
964
+ if (isYearView && currentYearRef.current && scrollAreaRef.current) {
965
+ const viewport = scrollAreaRef.current.querySelector('[data-radix-scroll-area-viewport]') as HTMLElement
966
+
967
+ if (viewport) {
968
+ const yearTop = currentYearRef.current.offsetTop
969
+
970
+ viewport.scrollTop = yearTop
971
+ }
972
+
973
+ setTimeout(() => {
974
+ currentMonthButtonRef.current?.focus()
975
+ }, 100)
976
+ }
977
+ }, [isYearView])
978
+
979
+ return (
980
+ <div className='relative'>
981
+ <table className={className}>{children}</table>
982
+ {isYearView && (
983
+ <div className='bg-background absolute inset-0 z-20 -mx-2 -mb-2'>
984
+ <ScrollArea ref={scrollAreaRef} className='h-full'>
985
+ {years.map(year => {
986
+ const months = eachMonthOfInterval({
987
+ start: startOfYear(year),
988
+ end: endOfYear(year)
989
+ })
990
+
991
+ const isCurrentYear = year.getFullYear() === currentYear
992
+
993
+ return (
994
+ <div key={year.getFullYear()} ref={isCurrentYear ? currentYearRef : undefined}>
995
+ <CollapsibleYear title={year.getFullYear().toString()} open={isCurrentYear}>
996
+ <div className='grid grid-cols-3 gap-2'>
997
+ {months.map(month => {
998
+ const isDisabled = isBefore(month, startDate) || isAfter(month, endDate)
999
+ const isCurrentMonth = month.getMonth() === currentMonth && year.getFullYear() === currentYear
1000
+
1001
+ return (
1002
+ <Button
1003
+ key={month.getTime()}
1004
+ ref={isCurrentMonth ? currentMonthButtonRef : undefined}
1005
+ variant={isCurrentMonth ? 'default' : 'outline'}
1006
+ size='sm'
1007
+ className='h-7'
1008
+ disabled={isDisabled}
1009
+ onClick={() => onMonthSelect(month)}
1010
+ >
1011
+ {format(month, 'MMM')}
1012
+ </Button>
1013
+ )
1014
+ })}
1015
+ </div>
1016
+ </CollapsibleYear>
1017
+ </div>
1018
+ )
1019
+ })}
1020
+ </ScrollArea>
1021
+ </div>
1022
+ )}
1023
+ </div>
1024
+ )
1025
+ }
1026
+
1027
+ function CaptionLabel({
1028
+ children,
1029
+ isYearView,
1030
+ setIsYearView
1031
+ }: {
1032
+ isYearView: boolean
1033
+ setIsYearView: React.Dispatch<React.SetStateAction<boolean>>
1034
+ } & React.HTMLAttributes<HTMLSpanElement>) {
1035
+ return (
1036
+ <Button
1037
+ className='data-[state=open]:text-muted-foreground/80 -ms-2 flex items-center gap-2 text-sm font-medium hover:bg-transparent [&[data-state=open]>svg]:rotate-180'
1038
+ variant='ghost'
1039
+ size='sm'
1040
+ onClick={() => setIsYearView(prev => !prev)}
1041
+ data-state={isYearView ? 'open' : 'closed'}
1042
+ >
1043
+ {children}
1044
+ <ChevronDownIcon
1045
+ className='text-muted-foreground/80 shrink-0 transition-transform duration-200'
1046
+ aria-hidden='true'
1047
+ />
1048
+ </Button>
1049
+ )
1050
+ }
1051
+
1052
+ function CollapsibleYear({ title, children, open }: { title: string; children: React.ReactNode; open?: boolean }) {
1053
+ return (
1054
+ <Collapsible className='border border-border-t px-2 py-1.5' defaultOpen={open}>
1055
+ <CollapsibleTrigger asChild>
1056
+ <Button
1057
+ className='flex w-full justify-start gap-2 text-sm font-medium hover:bg-transparent [&[data-state=open]>svg]:rotate-180'
1058
+ variant='ghost'
1059
+ size='sm'
1060
+ >
1061
+ <ChevronDownIcon
1062
+ className='text-muted-foreground/80 shrink-0 transition-transform duration-200'
1063
+ aria-hidden='true'
1064
+ />
1065
+ {title}
1066
+ </Button>
1067
+ </CollapsibleTrigger>
1068
+ <CollapsibleContent className='data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down overflow-hidden px-3 py-1 text-sm transition-all'>
1069
+ {children}
1070
+ </CollapsibleContent>
1071
+ </Collapsible>
1072
+ )
1073
+ }
1074
+
1075
+ const WithPresets = ({
1076
+ date = new Date(),
1077
+ setDate,
1078
+ className,
1079
+ variant,
1080
+ size,
1081
+ presets = [
1082
+ { label: 'Today', value: 0 },
1083
+ { label: 'Yesterday', value: -1 },
1084
+ { label: 'Tomorrow', value: 1 },
1085
+ { label: 'In 3 days', value: 3 },
1086
+ { label: 'In a week', value: 7 },
1087
+ { label: 'In 2 weeks', value: 14 }
1088
+ ] }) => {
1089
+ return (
1090
+ <div>
1091
+ <Card className='max-w-xs py-4'>
1092
+ <CardContent className='px-4'>
1093
+ <Calendar
1094
+ mode='single'
1095
+ selected={date}
1096
+ onSelect={setDate}
1097
+ defaultMonth={date}
1098
+ className={cn(calendarVariants({ variant, size }), 'w-full bg-transparent p-0', className)}
1099
+ />
1100
+ </CardContent>
1101
+ <CardFooter className='flex flex-wrap gap-2 border border-border-t px-4 !pt-4'>
1102
+ {presets.map(preset => (
1103
+ <Button
1104
+ key={preset.value}
1105
+ variant='outline'
1106
+ size='sm'
1107
+ className='flex-1'
1108
+ onClick={() => {
1109
+ const newDate = addDays(new Date(), preset.value)
1110
+
1111
+ setDate(newDate)
1112
+ }}
1113
+ >
1114
+ {preset.label}
1115
+ </Button>
1116
+ ))}
1117
+ </CardFooter>
1118
+ </Card>
1119
+
1120
+ </div>
1121
+ )
1122
+ }
1123
+ const WithRangePresets = ({ today = new Date(), className, variant, size }) => {
1124
+
1125
+ const yesterday = {
1126
+ from: subDays(today, 1),
1127
+ to: subDays(today, 1)
1128
+ }
1129
+
1130
+ const tomorrow = {
1131
+ from: today,
1132
+ to: addDays(today, 1)
1133
+ }
1134
+
1135
+ const last7Days = {
1136
+ from: subDays(today, 6),
1137
+ to: today
1138
+ }
1139
+
1140
+ const next7Days = {
1141
+ from: addDays(today, 1),
1142
+ to: addDays(today, 7)
1143
+ }
1144
+
1145
+ const last30Days = {
1146
+ from: subDays(today, 29),
1147
+ to: today
1148
+ }
1149
+
1150
+ const monthToDate = {
1151
+ from: startOfMonth(today),
1152
+ to: today
1153
+ }
1154
+
1155
+ const lastMonth = {
1156
+ from: startOfMonth(subMonths(today, 1)),
1157
+ to: endOfMonth(subMonths(today, 1))
1158
+ }
1159
+
1160
+ const nextMonth = {
1161
+ from: startOfMonth(addMonths(today, 1)),
1162
+ to: endOfMonth(addMonths(today, 1))
1163
+ }
1164
+
1165
+ const yearToDate = {
1166
+ from: startOfYear(today),
1167
+ to: today
1168
+ }
1169
+
1170
+ const lastYear = {
1171
+ from: startOfYear(subYears(today, 1)),
1172
+ to: endOfYear(subYears(today, 1))
1173
+ }
1174
+
1175
+ const [month, setMonth] = useState(today)
1176
+ const [date, setDate] = useState<DateRange | undefined>(last7Days)
1177
+
1178
+ return (
1179
+ <div>
1180
+ <Card className='max-w-xs py-4'>
1181
+ <CardContent className='px-4'>
1182
+ <Calendar
1183
+ mode='range'
1184
+ selected={date}
1185
+ onSelect={newDate => {
1186
+ if (newDate) {
1187
+ setDate(newDate)
1188
+ }
1189
+ }}
1190
+ month={month}
1191
+ onMonthChange={setMonth}
1192
+ className={cn(calendarVariants({ variant, size }), 'w-full bg-transparent p-0', className)}
1193
+ />
1194
+ </CardContent>
1195
+ <CardFooter className='flex flex-wrap gap-2 border border-border-t px-4 !pt-4'>
1196
+ <Button
1197
+ variant='outline'
1198
+ size='sm'
1199
+ onClick={() => {
1200
+ setDate({
1201
+ from: today,
1202
+ to: today
1203
+ })
1204
+ setMonth(today)
1205
+ }}
1206
+ >
1207
+ Today
1208
+ </Button>
1209
+ <Button
1210
+ variant='outline'
1211
+ size='sm'
1212
+ onClick={() => {
1213
+ setDate(yesterday)
1214
+ setMonth(yesterday.to)
1215
+ }}
1216
+ >
1217
+ Yesterday
1218
+ </Button>
1219
+ <Button
1220
+ variant='outline'
1221
+ size='sm'
1222
+ onClick={() => {
1223
+ setDate(tomorrow)
1224
+ setMonth(tomorrow.to)
1225
+ }}
1226
+ >
1227
+ Tomorrow
1228
+ </Button>
1229
+ <Button
1230
+ variant='outline'
1231
+ size='sm'
1232
+ onClick={() => {
1233
+ setDate(last7Days)
1234
+ setMonth(last7Days.to)
1235
+ }}
1236
+ >
1237
+ Last 7 days
1238
+ </Button>
1239
+ <Button
1240
+ variant='outline'
1241
+ size='sm'
1242
+ onClick={() => {
1243
+ setDate(next7Days)
1244
+ setMonth(next7Days.to)
1245
+ }}
1246
+ >
1247
+ Next 7 days
1248
+ </Button>
1249
+ <Button
1250
+ variant='outline'
1251
+ size='sm'
1252
+ onClick={() => {
1253
+ setDate(last30Days)
1254
+ setMonth(last30Days.to)
1255
+ }}
1256
+ >
1257
+ Last 30 days
1258
+ </Button>
1259
+ <Button
1260
+ variant='outline'
1261
+ size='sm'
1262
+ onClick={() => {
1263
+ setDate(monthToDate)
1264
+ setMonth(monthToDate.to)
1265
+ }}
1266
+ >
1267
+ Month to date
1268
+ </Button>
1269
+ <Button
1270
+ variant='outline'
1271
+ size='sm'
1272
+ onClick={() => {
1273
+ setDate(lastMonth)
1274
+ setMonth(lastMonth.to)
1275
+ }}
1276
+ >
1277
+ Last month
1278
+ </Button>
1279
+ <Button
1280
+ variant='outline'
1281
+ size='sm'
1282
+ onClick={() => {
1283
+ setDate(nextMonth)
1284
+ setMonth(nextMonth.to)
1285
+ }}
1286
+ >
1287
+ Next month
1288
+ </Button>
1289
+ <Button
1290
+ variant='outline'
1291
+ size='sm'
1292
+ onClick={() => {
1293
+ setDate(yearToDate)
1294
+ setMonth(yearToDate.to)
1295
+ }}
1296
+ >
1297
+ Year to date
1298
+ </Button>
1299
+ <Button
1300
+ variant='outline'
1301
+ size='sm'
1302
+ onClick={() => {
1303
+ setDate(lastYear)
1304
+ setMonth(lastYear.to)
1305
+ }}
1306
+ >
1307
+ Last year
1308
+ </Button>
1309
+ </CardFooter>
1310
+ </Card>
1311
+
1312
+ </div>
1313
+ )
1314
+ }
1315
+ const AppointmentBooking = ({
1316
+ date = new Date(),
1317
+ setDate,
1318
+ selectedTime = '10:00',
1319
+ setSelectedTime,
1320
+ className,
1321
+ variant,
1322
+ size,
1323
+ timeSlots = Array.from({ length: 37 }, (_, i) => {
1324
+ const totalMinutes = i * 15
1325
+ const hour = Math.floor(totalMinutes / 60) + 9
1326
+ const minute = totalMinutes % 60
1327
+
1328
+ return `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`
1329
+ }),
1330
+ bookedDates = Array.from({ length: 3 }, (_, i) => new Date(2025, 5, 17 + i)),
1331
+
1332
+ }) => {
1333
+
1334
+ return (
1335
+ <div>
1336
+ <Card className='gap-0 p-0'>
1337
+ <CardHeader className='flex h-max justify-center border border-border-b !p-4'>
1338
+ <CardTitle>Book your appointment</CardTitle>
1339
+ </CardHeader>
1340
+ <CardContent className='relative p-0 md:pr-48'>
1341
+ <div className='p-6'>
1342
+ <Calendar
1343
+ mode='single'
1344
+ selected={date}
1345
+ onSelect={setDate}
1346
+ defaultMonth={date}
1347
+ disabled={bookedDates}
1348
+ showOutsideDays={false}
1349
+ modifiers={{
1350
+ booked: bookedDates
1351
+ }}
1352
+ modifiersClassNames={{
1353
+ booked: '[&>button]:line-through opacity-100'
1354
+ }}
1355
+ className={cn(calendarVariants({ variant, size }), 'bg-transparent p-0 [--cell-size:--spacing(10)]', className)}
1356
+ formatters={{
1357
+ formatWeekdayName: date => {
1358
+ return date.toLocaleString('en-US', { weekday: 'short' })
1359
+ }
1360
+ }}
1361
+ />
1362
+ </div>
1363
+ <div className='inset-y-0 right-0 flex w-full flex-col gap-4 border border-border-t max-md:h-60 md:absolute md:w-48 md:border border-border-t-0 md:border border-border-l'>
1364
+ <ScrollArea className='h-full'>
1365
+ <div className='flex flex-col gap-2 p-6'>
1366
+ {timeSlots.map(time => (
1367
+ <Button
1368
+ key={time}
1369
+ variant={selectedTime === time ? 'default' : 'outline'}
1370
+ onClick={() => setSelectedTime(time)}
1371
+ className='w-full shadow-none'
1372
+ >
1373
+ {time}
1374
+ </Button>
1375
+ ))}
1376
+ </div>
1377
+ </ScrollArea>
1378
+ </div>
1379
+ </CardContent>
1380
+ <CardFooter className='flex flex-col gap-4 border border-border-t px-6 !py-5 md:flex-row'>
1381
+ <div className='flex items-center gap-2 text-sm'>
1382
+ {date && selectedTime ? (
1383
+ <>
1384
+ <CircleCheckIcon className='size-5 stroke-green-600 dark:stroke-green-400' />
1385
+ <span>
1386
+ Your meeting is booked for{' '}
1387
+ <span className='font-medium'>
1388
+ {' '}
1389
+ {date?.toLocaleDateString('en-US', {
1390
+ weekday: 'long',
1391
+ day: 'numeric',
1392
+ month: 'long'
1393
+ })}{' '}
1394
+ </span>
1395
+ at <span className='font-medium'>{selectedTime}</span>.
1396
+ </span>
1397
+ </>
1398
+ ) : (
1399
+ <>Select a date and time for your meeting.</>
1400
+ )}
1401
+ </div>
1402
+ <Button disabled={!date || !selectedTime} className='w-full md:ml-auto md:w-auto' variant='outline'>
1403
+ Continue
1404
+ </Button>
1405
+ </CardFooter>
1406
+ </Card>
1407
+
1408
+ </div>
1409
+ )
1410
+ }
1411
+
1412
+
1413
+ const Pricing = ({ date = new Date(), setDate, className, variant, size, getPrice = null }) => {
1414
+
1415
+ function getPriceForDate(date: Date) {
1416
+ const seed = date.getFullYear() * 10000 + (date.getMonth() + 1) * 100 + date.getDate()
1417
+
1418
+ const val = (seed * 9301 + 49297) % 233280
1419
+
1420
+ return Math.floor(50 + (val / 233280) * 200)
1421
+ }
1422
+
1423
+ return (
1424
+ <div>
1425
+ <Calendar
1426
+ mode='single'
1427
+ selected={date}
1428
+ onSelect={setDate}
1429
+ showOutsideDays={false}
1430
+ className={cn(calendarVariants({ variant, size }), 'rounded-lg border border-border [--cell-size:--spacing(12)]', className)}
1431
+ components={{
1432
+ DayButton: ({ children, modifiers, day, ...props }) => {
1433
+ const price = getPrice ? getPrice() : getPriceForDate(day.date)
1434
+ const isGreen = price < 100
1435
+
1436
+ return (
1437
+ <CalendarDayButton day={day} modifiers={modifiers} {...props}>
1438
+ {children}
1439
+ {!modifiers.outside && (
1440
+ <span className={isGreen ? 'text-green-600 dark:text-green-400' : ''}>${price}</span>
1441
+ )}
1442
+ </CalendarDayButton>
1443
+ )
1444
+ }
1445
+ }}
1446
+ disabled={{ before: new Date() }}
1447
+ />
1448
+
1449
+ </div>
1450
+ )
1451
+ }
1452
+
1453
+
1454
+
1455
+ export function CalendarXDemo() {
1456
+ const [singleDate, setSingleDate] = useState<Date | undefined>(new Date(2025, 5, 15));
1457
+ const [rangeDate, setRangeDate] = useState({
1458
+ from: new Date(2025, 5, 4),
1459
+ to: new Date(2025, 5, 17)
1460
+ });
1461
+ const [multiDates, setMultiDates] = useState<Date[]>([
1462
+ new Date(2025, 5, 12),
1463
+ new Date(2025, 5, 17),
1464
+ new Date(2025, 5, 20)
1465
+ ]);
1466
+ const [locale, setLocale] = useState<'en' | 'hi'>('en');
1467
+ const [time, setTime] = useState('12:00:00');
1468
+ const [selectedTime, setSelectedTime] = useState('10:00');
1469
+ const [month, setMonth] = useState(new Date(2025, 5));
1470
+
1471
+ return (
1472
+ <div className="p-8 max-w-7xl mx-auto bg-background">
1473
+ <header className="mb-12">
1474
+ <h1 className="text-4xl font-bold mb-2">CalendarX Demo</h1>
1475
+ <p className="text-muted-foreground">
1476
+ A comprehensive collection of calendar variants built with Catalyst UI and React Day Picker
1477
+ </p>
1478
+ </header>
1479
+
1480
+ <div className="space-y-12">
1481
+ {/* Section 1: Basic Calendar Variants */}
1482
+ <section>
1483
+ <h2 className="text-2xl font-semibold mb-6 pb-2 border-b">Basic Calendar Variants</h2>
1484
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
1485
+ <div className="space-y-4 p-4 border rounded-lg">
1486
+ <h3 className="font-medium">Default Calendar</h3>
1487
+ <p className="text-sm text-muted-foreground mb-2">Single date selection</p>
1488
+ <CalendarX
1489
+ calendar="default"
1490
+ date={singleDate}
1491
+ setDate={setSingleDate}
1492
+ />
1493
+ </div>
1494
+
1495
+ <div className="space-y-4 p-4 border rounded-lg">
1496
+ <h3 className="font-medium">Multi Month</h3>
1497
+ <p className="text-sm text-muted-foreground mb-2">Display multiple months</p>
1498
+ <CalendarX
1499
+ calendar="MultiMonth"
1500
+ date={singleDate}
1501
+ setDate={setSingleDate}
1502
+ numOfMonths={2}
1503
+ />
1504
+ </div>
1505
+
1506
+ <div className="space-y-4 p-4 border rounded-lg">
1507
+ <h3 className="font-medium">With Week Numbers</h3>
1508
+ <p className="text-sm text-muted-foreground mb-2">Show week numbers</p>
1509
+ <CalendarX
1510
+ calendar="WeekNumber"
1511
+ date={singleDate}
1512
+ setDate={setSingleDate}
1513
+ />
1514
+ </div>
1515
+ </div>
1516
+ </section>
1517
+
1518
+ {/* Section 2: Range Selection */}
1519
+ <section>
1520
+ <h2 className="text-2xl font-semibold mb-6 pb-2 border-b">Range Selection</h2>
1521
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
1522
+ <div className="space-y-4 p-4 border rounded-lg">
1523
+ <h3 className="font-medium">Single Month Range</h3>
1524
+ <p className="text-sm text-muted-foreground mb-2">Select a date range</p>
1525
+ <CalendarX
1526
+ calendar="RangeSingleMonth"
1527
+ dateRange={rangeDate}
1528
+ setDateRange={setRangeDate}
1529
+ />
1530
+ </div>
1531
+
1532
+ <div className="space-y-4 p-4 border rounded-lg">
1533
+ <h3 className="font-medium">Multi Month Range</h3>
1534
+ <p className="text-sm text-muted-foreground mb-2">Range across months</p>
1535
+ <CalendarX
1536
+ calendar="RangeCalendarMultiMonth"
1537
+ dateRange={rangeDate}
1538
+ setDateRange={setRangeDate}
1539
+ numOfMonths={2}
1540
+ />
1541
+ </div>
1542
+
1543
+ <div className="space-y-4 p-4 border rounded-lg">
1544
+ <h3 className="font-medium">Minimum Days Range</h3>
1545
+ <p className="text-sm text-muted-foreground mb-2">Minimum 5 days selection</p>
1546
+ <CalendarX
1547
+ calendar="RangeWithMinimumDays"
1548
+ dateRange={rangeDate}
1549
+ setDateRange={setRangeDate}
1550
+ min={5}
1551
+ />
1552
+ </div>
1553
+ </div>
1554
+ </section>
1555
+
1556
+ {/* Section 3: Selection Types */}
1557
+ <section>
1558
+ <h2 className="text-2xl font-semibold mb-6 pb-2 border-b">Selection Types</h2>
1559
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
1560
+ <div className="space-y-4 p-4 border rounded-lg">
1561
+ <h3 className="font-medium">Multiple Selection</h3>
1562
+ <p className="text-sm text-muted-foreground mb-2">Select up to 5 dates</p>
1563
+ <CalendarX
1564
+ calendar="MultiSelect"
1565
+ dates={multiDates}
1566
+ setDates={setMultiDates}
1567
+ />
1568
+ </div>
1569
+
1570
+ <div className="space-y-4 p-4 border rounded-lg">
1571
+ <h3 className="font-medium">Custom Day Selection</h3>
1572
+ <p className="text-sm text-muted-foreground mb-2">Custom styling for selected days</p>
1573
+ <CalendarX
1574
+ calendar="CustomSelectDay"
1575
+ date={singleDate}
1576
+ setDate={setSingleDate}
1577
+ />
1578
+ </div>
1579
+
1580
+ <div className="space-y-4 p-4 border rounded-lg">
1581
+ <h3 className="font-medium">Custom Range Selection</h3>
1582
+ <p className="text-sm text-muted-foreground mb-2">Styled range with start/end markers</p>
1583
+ <CalendarX
1584
+ calendar="CustomRangeSelect"
1585
+ dateRange={rangeDate}
1586
+ setDateRange={setRangeDate}
1587
+ />
1588
+ </div>
1589
+ </div>
1590
+ </section>
1591
+
1592
+ {/* Section 4: Disabled Dates */}
1593
+ <section>
1594
+ <h2 className="text-2xl font-semibold mb-6 pb-2 border-b">Disabled Dates</h2>
1595
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
1596
+ <div className="space-y-4 p-4 border rounded-lg">
1597
+ <h3 className="font-medium">Disable Past Dates</h3>
1598
+ <p className="text-sm text-muted-foreground mb-2">Dates before June 12, 2025 are disabled</p>
1599
+ <CalendarX
1600
+ calendar="DisableDay"
1601
+ date={singleDate}
1602
+ setDate={setSingleDate}
1603
+ before={new Date(2025, 5, 12)}
1604
+ />
1605
+ </div>
1606
+
1607
+ <div className="space-y-4 p-4 border rounded-lg">
1608
+ <h3 className="font-medium">Disable Weekends</h3>
1609
+ <p className="text-sm text-muted-foreground mb-2">Saturdays and Sundays disabled</p>
1610
+ <CalendarX
1611
+ calendar="DisabledWeekends"
1612
+ dateRange={rangeDate}
1613
+ setDateRange={setRangeDate}
1614
+ />
1615
+ </div>
1616
+ </div>
1617
+ </section>
1618
+
1619
+ {/* Section 5: Navigation & Layout */}
1620
+ <section>
1621
+ <h2 className="text-2xl font-semibold mb-6 pb-2 border-b">Navigation & Layout</h2>
1622
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
1623
+ <div className="space-y-4 p-4 border rounded-lg">
1624
+ <h3 className="font-medium">With Dropdowns</h3>
1625
+ <p className="text-sm text-muted-foreground mb-2">Month/Year dropdown selection</p>
1626
+ <CalendarX
1627
+ calendar="WithMonthYearDropdown"
1628
+ date={singleDate}
1629
+ setDate={setSingleDate}
1630
+ />
1631
+ </div>
1632
+
1633
+ <div className="space-y-4 p-4 border rounded-lg">
1634
+ <h3 className="font-medium">Right Navigation</h3>
1635
+ <p className="text-sm text-muted-foreground mb-2">Navigation on the right side</p>
1636
+ <CalendarX
1637
+ calendar="RightYearMonth"
1638
+ date={singleDate}
1639
+ setDate={setSingleDate}
1640
+ />
1641
+ </div>
1642
+
1643
+ <div className="space-y-4 p-4 border rounded-lg">
1644
+ <h3 className="font-medium">Left Navigation</h3>
1645
+ <p className="text-sm text-muted-foreground mb-2">Navigation on the left side</p>
1646
+ <CalendarX
1647
+ calendar="LeftYearMonth"
1648
+ date={singleDate}
1649
+ setDate={setSingleDate}
1650
+ />
1651
+ </div>
1652
+
1653
+ <div className="space-y-4 p-4 border rounded-lg">
1654
+ <h3 className="font-medium">With Today Button</h3>
1655
+ <p className="text-sm text-muted-foreground mb-2">Quick navigation to today</p>
1656
+ <CalendarX
1657
+ calendar="WithTodayMonthButton"
1658
+ date={singleDate}
1659
+ setDate={setSingleDate}
1660
+ month={month}
1661
+ setMonth={setMonth}
1662
+ />
1663
+ </div>
1664
+
1665
+ <div className="space-y-4 p-4 border rounded-lg">
1666
+ <h3 className="font-medium">Variable Size</h3>
1667
+ <p className="text-sm text-muted-foreground mb-2">Custom cell sizes</p>
1668
+ <CalendarX
1669
+ calendar="VariableSize"
1670
+ date={singleDate}
1671
+ setDate={setSingleDate}
1672
+ />
1673
+ </div>
1674
+
1675
+ <div className="space-y-4 p-4 border rounded-lg">
1676
+ <h3 className="font-medium">Advanced Selection</h3>
1677
+ <p className="text-sm text-muted-foreground mb-2">Year/month grid view</p>
1678
+ <CalendarX
1679
+ calendar="WithAdvanceSelection"
1680
+ date={singleDate}
1681
+ setDate={setSingleDate}
1682
+ />
1683
+ </div>
1684
+ </div>
1685
+ </section>
1686
+
1687
+ {/* Section 6: Input Integration */}
1688
+ <section>
1689
+ <h2 className="text-2xl font-semibold mb-6 pb-2 border-b">Input Integration</h2>
1690
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
1691
+ <div className="space-y-4 p-4 border rounded-lg">
1692
+ <h3 className="font-medium">With Date Input</h3>
1693
+ <p className="text-sm text-muted-foreground mb-2">Type or pick a date</p>
1694
+ <CalendarX
1695
+ calendar="WithDateInput"
1696
+ />
1697
+ </div>
1698
+
1699
+ <div className="space-y-4 p-4 border rounded-lg">
1700
+ <h3 className="font-medium">With Time Input</h3>
1701
+ <p className="text-sm text-muted-foreground mb-2">Select date and time</p>
1702
+ <CalendarX
1703
+ calendar="WithTimeInput"
1704
+ date={singleDate}
1705
+ setDate={setSingleDate}
1706
+ time={time}
1707
+ setTime={setTime}
1708
+ />
1709
+ </div>
1710
+ </div>
1711
+ </section>
1712
+
1713
+ {/* Section 7: Presets */}
1714
+ <section>
1715
+ <h2 className="text-2xl font-semibold mb-6 pb-2 border-b">Quick Presets</h2>
1716
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
1717
+ <div className="space-y-4 p-4 border rounded-lg">
1718
+ <h3 className="font-medium">Date Presets</h3>
1719
+ <p className="text-sm text-muted-foreground mb-2">Quick date selections</p>
1720
+ <CalendarX
1721
+ calendar="WithPresets"
1722
+ date={singleDate}
1723
+ setDate={setSingleDate}
1724
+ />
1725
+ </div>
1726
+
1727
+ <div className="space-y-4 p-4 border rounded-lg">
1728
+ <h3 className="font-medium">Range Presets</h3>
1729
+ <p className="text-sm text-muted-foreground mb-2">Common range selections</p>
1730
+ <CalendarX
1731
+ calendar="WithRangePresets"
1732
+ />
1733
+ </div>
1734
+ </div>
1735
+ </section>
1736
+
1737
+ {/* Section 8: Specialized Calendars */}
1738
+ <section>
1739
+ <h2 className="text-2xl font-semibold mb-6 pb-2 border-b">Specialized Calendars</h2>
1740
+ <div className="grid grid-cols-1 gap-6">
1741
+ <div className="space-y-4 p-4 border rounded-lg">
1742
+ <h3 className="font-medium">Appointment Booking</h3>
1743
+ <p className="text-sm text-muted-foreground mb-2">Book appointments with time slots</p>
1744
+ <CalendarX
1745
+ calendar="CalendarAppointmentBooking"
1746
+ date={singleDate}
1747
+ setDate={setSingleDate}
1748
+ selectedTime={selectedTime}
1749
+ setSelectedTime={setSelectedTime}
1750
+ />
1751
+ </div>
1752
+
1753
+ <div className="space-y-4 p-4 border rounded-lg">
1754
+ <h3 className="font-medium">Event List Calendar</h3>
1755
+ <p className="text-sm text-muted-foreground mb-2">View events for selected date</p>
1756
+ <CalendarX
1757
+ calendar="EventList"
1758
+ date={singleDate}
1759
+ setDate={setSingleDate}
1760
+ />
1761
+ </div>
1762
+
1763
+ <div className="space-y-4 p-4 border rounded-lg">
1764
+ <h3 className="font-medium">Pricing Calendar</h3>
1765
+ <p className="text-sm text-muted-foreground mb-2">Dynamic pricing per date</p>
1766
+ <CalendarX
1767
+ calendar="Pricing"
1768
+ date={singleDate}
1769
+ setDate={setSingleDate}
1770
+ />
1771
+ </div>
1772
+
1773
+ <div className="space-y-4 p-4 border rounded-lg">
1774
+ <h3 className="font-medium">Localization</h3>
1775
+ <p className="text-sm text-muted-foreground mb-2">Hindi and English support</p>
1776
+ <CalendarX
1777
+ calendar="Localization"
1778
+ dateRange={rangeDate}
1779
+ setDateRange={setRangeDate}
1780
+ locale={locale}
1781
+ setLocale={setLocale}
1782
+ />
1783
+ </div>
1784
+ </div>
1785
+ </section>
1786
+
1787
+ {/* Section 9: Usage Examples */}
1788
+ <section>
1789
+ <h2 className="text-2xl font-semibold mb-6 pb-2 border-b">Usage Examples</h2>
1790
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
1791
+ <div className="p-6 border rounded-lg bg-card">
1792
+ <h3 className="font-medium mb-4">Booking System Example</h3>
1793
+ <div className="space-y-4">
1794
+ <div className="flex items-center justify-between">
1795
+ <div>
1796
+ <h4 className="font-medium">Select Check-in Date</h4>
1797
+ <p className="text-sm text-muted-foreground">Choose your arrival date</p>
1798
+ </div>
1799
+ <CalendarX
1800
+ calendar="DisableDay"
1801
+ date={singleDate}
1802
+ setDate={setSingleDate}
1803
+ before={new Date()}
1804
+ className="w-[280px]"
1805
+ />
1806
+ </div>
1807
+ <div className="flex items-center justify-between">
1808
+ <div>
1809
+ <h4 className="font-medium">Select Check-out Date</h4>
1810
+ <p className="text-sm text-muted-foreground">Choose your departure date</p>
1811
+ </div>
1812
+ <CalendarX
1813
+ calendar="RangeSingleMonth"
1814
+ dateRange={rangeDate}
1815
+ setDateRange={setRangeDate}
1816
+ className="w-[280px]"
1817
+ />
1818
+ </div>
1819
+ </div>
1820
+ </div>
1821
+
1822
+ <div className="p-6 border rounded-lg bg-card">
1823
+ <h3 className="font-medium mb-4">Team Planning Example</h3>
1824
+ <div className="space-y-4">
1825
+ <div className="flex items-center gap-4">
1826
+ <div className="flex-1">
1827
+ <h4 className="font-medium">Available Dates</h4>
1828
+ <p className="text-sm text-muted-foreground">Select available dates for team meetings</p>
1829
+ <CalendarX
1830
+ calendar="MultiSelect"
1831
+ dates={multiDates}
1832
+ setDates={setMultiDates}
1833
+ className="mt-2"
1834
+ />
1835
+ </div>
1836
+ <div className="flex-1">
1837
+ <h4 className="font-medium">Meeting Duration</h4>
1838
+ <CalendarX
1839
+ calendar="WithTimeInput"
1840
+ date={singleDate}
1841
+ setDate={setSingleDate}
1842
+ time={time}
1843
+ setTime={setTime}
1844
+ />
1845
+ </div>
1846
+ </div>
1847
+ </div>
1848
+ </div>
1849
+ </div>
1850
+ </section>
1851
+
1852
+ {/* Section 10: Usage Instructions */}
1853
+ <section className="mt-12 p-6 border rounded-lg bg-muted/30">
1854
+ <h2 className="text-2xl font-semibold mb-4">Usage</h2>
1855
+ <div className="space-y-4">
1856
+ <div>
1857
+ <h3 className="font-medium mb-2">Basic Usage</h3>
1858
+ <pre className="bg-muted p-3 rounded text-sm overflow-x-auto">
1859
+ {`import { useState } from "react";
1860
+ import { CalendarX } from "./CalendarX";
1861
+
1862
+ function App() {
1863
+ const [date, setDate] = useState<Date | undefined>(new Date());
1864
+
1865
+ // Default calendar
1866
+ return <CalendarX date={date} setDate={setDate} />;
1867
+
1868
+ // Specific variant
1869
+ return <CalendarX calendar="MultiMonth" date={date} setDate={setDate} />;
1870
+ }`}
1871
+ </pre>
1872
+ </div>
1873
+
1874
+ <div>
1875
+ <h3 className="font-medium mb-2">Common Props</h3>
1876
+ <ul className="list-disc pl-5 space-y-1 text-sm">
1877
+ <li><code>calendar</code>: Specifies which variant to use</li>
1878
+ <li><code>date</code>/<code>setDate</code>: For single date selection</li>
1879
+ <li><code>dateRange</code>/<code>setDateRange</code>: For range selection</li>
1880
+ <li><code>dates</code>/<code>setDates</code>: For multiple date selection</li>
1881
+ <li><code>numOfMonths</code>: Number of months to display</li>
1882
+ <li><code>className</code>: Additional CSS classes</li>
1883
+ <li><code>variant</code>/<code>size</code>: Catalyst UI variant/size props</li>
1884
+ </ul>
1885
+ </div>
1886
+
1887
+ <div>
1888
+ <h3 className="font-medium mb-2">Range Selection Example</h3>
1889
+ <pre className="bg-muted p-3 rounded text-sm overflow-x-auto">
1890
+ {`const [rangeDate, setRangeDate] = useState({
1891
+ from: new Date(2025, 5, 4),
1892
+ to: new Date(2025, 5, 17)
1893
+ });
1894
+
1895
+ return (
1896
+ <CalendarX
1897
+ calendar="RangeSingleMonth"
1898
+ dateRange={rangeDate}
1899
+ setDateRange={setRangeDate}
1900
+ />
1901
+ );`}
1902
+ </pre>
1903
+ </div>
1904
+ </div>
1905
+ </section>
1906
+ </div>
1907
+ </div>
1908
+ );
1909
+ }
1910
+