@arcfusionz/arc-primitive-ui 0.0.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/dist/components/AILoader/AILoader.d.ts +32 -0
  2. package/dist/components/AILoader/AILoader.js +157 -0
  3. package/dist/components/AILoader/index.d.ts +2 -0
  4. package/dist/components/AILoader/index.js +2 -0
  5. package/dist/components/Accordion/Accordion.d.ts +96 -0
  6. package/dist/components/Accordion/Accordion.js +149 -0
  7. package/dist/components/Accordion/index.d.ts +2 -0
  8. package/dist/components/Accordion/index.js +2 -0
  9. package/dist/components/Avatar/Avatar.d.ts +152 -0
  10. package/dist/components/Avatar/Avatar.js +216 -0
  11. package/dist/components/Avatar/index.d.ts +2 -0
  12. package/dist/components/Avatar/index.js +2 -0
  13. package/dist/components/Badge/Badge.d.ts +87 -0
  14. package/dist/components/Badge/Badge.js +118 -0
  15. package/dist/components/Badge/index.d.ts +2 -0
  16. package/dist/components/Badge/index.js +2 -0
  17. package/dist/components/Breadcrumb/Breadcrumb.d.ts +109 -0
  18. package/dist/components/Breadcrumb/Breadcrumb.js +182 -0
  19. package/dist/components/Breadcrumb/index.d.ts +2 -0
  20. package/dist/components/Breadcrumb/index.js +2 -0
  21. package/dist/components/Button/Button.d.ts +57 -0
  22. package/dist/components/Button/Button.js +177 -0
  23. package/dist/components/Button/index.d.ts +2 -0
  24. package/dist/components/Button/index.js +2 -0
  25. package/dist/components/Calendar/Calendar.d.ts +140 -0
  26. package/dist/components/Calendar/Calendar.js +468 -0
  27. package/dist/components/Calendar/index.d.ts +2 -0
  28. package/dist/components/Calendar/index.js +2 -0
  29. package/dist/components/Checkbox/Checkbox.d.ts +60 -0
  30. package/dist/components/Checkbox/Checkbox.js +129 -0
  31. package/dist/components/Checkbox/index.d.ts +2 -0
  32. package/dist/components/Checkbox/index.js +2 -0
  33. package/dist/components/Combobox/Combobox.d.ts +251 -0
  34. package/dist/components/Combobox/Combobox.js +354 -0
  35. package/dist/components/Combobox/index.d.ts +2 -0
  36. package/dist/components/Combobox/index.js +2 -0
  37. package/dist/components/ContextMenu/ContextMenu.d.ts +129 -0
  38. package/dist/components/ContextMenu/ContextMenu.js +93 -0
  39. package/dist/components/ContextMenu/index.d.ts +2 -0
  40. package/dist/components/ContextMenu/index.js +2 -0
  41. package/dist/components/DatePicker/DatePicker.d.ts +109 -0
  42. package/dist/components/DatePicker/DatePicker.js +145 -0
  43. package/dist/components/DatePicker/index.d.ts +2 -0
  44. package/dist/components/DatePicker/index.js +2 -0
  45. package/dist/components/DateRangePicker/DateRangePicker.d.ts +107 -0
  46. package/dist/components/DateRangePicker/DateRangePicker.js +161 -0
  47. package/dist/components/DateRangePicker/index.d.ts +2 -0
  48. package/dist/components/DateRangePicker/index.js +2 -0
  49. package/dist/components/Dialog/Dialog.d.ts +112 -0
  50. package/dist/components/Dialog/Dialog.js +141 -0
  51. package/dist/components/Dialog/index.d.ts +2 -0
  52. package/dist/components/Dialog/index.js +2 -0
  53. package/dist/components/Drawer/Drawer.d.ts +188 -0
  54. package/dist/components/Drawer/Drawer.js +268 -0
  55. package/dist/components/Drawer/index.d.ts +2 -0
  56. package/dist/components/Drawer/index.js +2 -0
  57. package/dist/components/Field/Field.d.ts +123 -0
  58. package/dist/components/Field/Field.js +241 -0
  59. package/dist/components/Field/FieldContext.d.ts +43 -0
  60. package/dist/components/Field/FieldContext.js +28 -0
  61. package/dist/components/Field/index.d.ts +3 -0
  62. package/dist/components/Field/index.js +3 -0
  63. package/dist/components/Fieldset/Fieldset.d.ts +43 -0
  64. package/dist/components/Fieldset/Fieldset.js +48 -0
  65. package/dist/components/Fieldset/index.d.ts +2 -0
  66. package/dist/components/Fieldset/index.js +2 -0
  67. package/dist/components/Form/Form.d.ts +54 -0
  68. package/dist/components/Form/Form.js +57 -0
  69. package/dist/components/Form/index.d.ts +2 -0
  70. package/dist/components/Form/index.js +2 -0
  71. package/dist/components/Input/Input.d.ts +64 -0
  72. package/dist/components/Input/Input.js +111 -0
  73. package/dist/components/Input/index.d.ts +2 -0
  74. package/dist/components/Input/index.js +2 -0
  75. package/dist/components/Menu/Menu.d.ts +212 -0
  76. package/dist/components/Menu/Menu.js +255 -0
  77. package/dist/components/Menu/index.d.ts +2 -0
  78. package/dist/components/Menu/index.js +2 -0
  79. package/dist/components/Popover/Popover.d.ts +122 -0
  80. package/dist/components/Popover/Popover.js +116 -0
  81. package/dist/components/Popover/index.d.ts +2 -0
  82. package/dist/components/Popover/index.js +2 -0
  83. package/dist/components/Progress/Progress.d.ts +87 -0
  84. package/dist/components/Progress/Progress.js +73 -0
  85. package/dist/components/Progress/index.d.ts +2 -0
  86. package/dist/components/Progress/index.js +2 -0
  87. package/dist/components/Radio/Radio.d.ts +65 -0
  88. package/dist/components/Radio/Radio.js +93 -0
  89. package/dist/components/Radio/index.d.ts +2 -0
  90. package/dist/components/Radio/index.js +2 -0
  91. package/dist/components/Select/Select.d.ts +136 -0
  92. package/dist/components/Select/Select.js +219 -0
  93. package/dist/components/Select/index.d.ts +2 -0
  94. package/dist/components/Select/index.js +2 -0
  95. package/dist/components/Skeleton/Skeleton.d.ts +39 -0
  96. package/dist/components/Skeleton/Skeleton.js +44 -0
  97. package/dist/components/Skeleton/index.d.ts +2 -0
  98. package/dist/components/Skeleton/index.js +2 -0
  99. package/dist/components/Stepper/Stepper.d.ts +161 -0
  100. package/dist/components/Stepper/Stepper.js +344 -0
  101. package/dist/components/Stepper/index.d.ts +2 -0
  102. package/dist/components/Stepper/index.js +2 -0
  103. package/dist/components/Switch/Switch.d.ts +56 -0
  104. package/dist/components/Switch/Switch.js +87 -0
  105. package/dist/components/Switch/index.d.ts +2 -0
  106. package/dist/components/Switch/index.js +2 -0
  107. package/dist/components/Table/Table.d.ts +116 -0
  108. package/dist/components/Table/Table.js +143 -0
  109. package/dist/components/Table/index.d.ts +2 -0
  110. package/dist/components/Table/index.js +2 -0
  111. package/dist/components/Tabs/Tabs.d.ts +116 -0
  112. package/dist/components/Tabs/Tabs.js +158 -0
  113. package/dist/components/Tabs/index.d.ts +2 -0
  114. package/dist/components/Tabs/index.js +2 -0
  115. package/dist/components/Timeline/Timeline.d.ts +137 -0
  116. package/dist/components/Timeline/Timeline.js +243 -0
  117. package/dist/components/Timeline/index.d.ts +2 -0
  118. package/dist/components/Timeline/index.js +2 -0
  119. package/dist/components/Toast/Toast.d.ts +135 -0
  120. package/dist/components/Toast/Toast.js +314 -0
  121. package/dist/components/Toast/index.d.ts +2 -0
  122. package/dist/components/Toast/index.js +2 -0
  123. package/dist/components/Tooltip/Tooltip.d.ts +104 -0
  124. package/dist/components/Tooltip/Tooltip.js +95 -0
  125. package/dist/components/Tooltip/index.d.ts +2 -0
  126. package/dist/components/Tooltip/index.js +2 -0
  127. package/dist/components/Typography/Typography.d.ts +38 -0
  128. package/dist/components/Typography/Typography.js +53 -0
  129. package/dist/components/Typography/index.d.ts +2 -0
  130. package/dist/components/Typography/index.js +2 -0
  131. package/dist/index.d.ts +66 -3398
  132. package/dist/index.js +34 -5343
  133. package/dist/lib/cn.d.ts +6 -0
  134. package/dist/lib/cn.js +9 -0
  135. package/package.json +6 -2
  136. package/src/styles/theme.css +341 -341
@@ -0,0 +1,468 @@
1
+ import { cn } from "../../lib/cn.js";
2
+ import { forwardRef, useEffect, useMemo, useRef, useState } from "react";
3
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
+ import { useRender } from "@base-ui/react/use-render";
5
+ import { mergeProps } from "@base-ui/react/merge-props";
6
+ //#region src/components/Calendar/Calendar.tsx
7
+ function isSameDay(a, b) {
8
+ return !!a && !!b && a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
9
+ }
10
+ /** Day-precision comparator; ignores time-of-day. */
11
+ function compareDays(a, b) {
12
+ return a.getFullYear() - b.getFullYear() || a.getMonth() - b.getMonth() || a.getDate() - b.getDate();
13
+ }
14
+ function isSameMonth(a, b) {
15
+ return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth();
16
+ }
17
+ function toDayStart(date) {
18
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate());
19
+ }
20
+ function startOfMonth(date) {
21
+ return new Date(date.getFullYear(), date.getMonth(), 1);
22
+ }
23
+ function addDays(date, amount) {
24
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate() + amount);
25
+ }
26
+ function daysInMonth(year, monthIndex) {
27
+ return new Date(year, monthIndex + 1, 0).getDate();
28
+ }
29
+ /** Month arithmetic that clamps the day-of-month (Jan 31 + 1 month = Feb 28/29). */
30
+ function addMonths(date, amount) {
31
+ const year = date.getFullYear();
32
+ const month = date.getMonth() + amount;
33
+ const target = new Date(year, month, 1);
34
+ return new Date(target.getFullYear(), target.getMonth(), Math.min(date.getDate(), daysInMonth(target.getFullYear(), target.getMonth())));
35
+ }
36
+ function dayKey(date) {
37
+ return `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`;
38
+ }
39
+ function orderRange(a, b) {
40
+ return compareDays(a, b) <= 0 ? {
41
+ start: a,
42
+ end: b
43
+ } : {
44
+ start: b,
45
+ end: a
46
+ };
47
+ }
48
+ /** A Sunday, used to render weekday names for any `weekStartsOn`. */
49
+ const REFERENCE_SUNDAY = new Date(2026, 0, 4);
50
+ function buildWeeks(month, weekStartsOn, fixedWeeks) {
51
+ const first = startOfMonth(month);
52
+ const lead = (first.getDay() - weekStartsOn + 7) % 7;
53
+ const gridStart = addDays(first, -lead);
54
+ const total = lead + daysInMonth(month.getFullYear(), month.getMonth());
55
+ const weekCount = fixedWeeks ? 6 : Math.ceil(total / 7);
56
+ return Array.from({ length: weekCount }, (_, week) => Array.from({ length: 7 }, (_, day) => addDays(gridStart, week * 7 + day)));
57
+ }
58
+ /** First day of week for a BCP-47 locale (0 = Sunday), when the runtime knows it. */
59
+ function localeWeekStart(locale) {
60
+ try {
61
+ const asAny = new Intl.Locale(locale ?? new Intl.DateTimeFormat().resolvedOptions().locale);
62
+ const firstDay = (asAny.getWeekInfo?.() ?? asAny.weekInfo)?.firstDay;
63
+ if (typeof firstDay === "number") return firstDay % 7;
64
+ } catch {}
65
+ return 0;
66
+ }
67
+ function createFormatter(locale, options) {
68
+ try {
69
+ return new Intl.DateTimeFormat(locale, options);
70
+ } catch {
71
+ return new Intl.DateTimeFormat(void 0, options);
72
+ }
73
+ }
74
+ /** Minimal controllable-state pair: `undefined` = uncontrolled, `null` = controlled-empty. */
75
+ function useControlled(controlled, initial) {
76
+ const [internal, setInternal] = useState(initial);
77
+ return [controlled !== void 0 ? controlled : internal, setInternal];
78
+ }
79
+ const dayButtonClasses = "relative inline-flex size-9 cursor-pointer select-none items-center justify-center rounded-full font-sans text-sm text-foreground duration-150 hover:bg-muted active:bg-slate-200 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 data-today:border data-today:border-primary-500 data-outside:text-muted-foreground data-hidden:invisible data-unavailable:cursor-default data-unavailable:text-muted-foreground data-unavailable:line-through data-unavailable:hover:bg-transparent data-unavailable:active:bg-transparent data-selected:bg-primary data-selected:text-primary-foreground data-selected:hover:bg-primary-700 data-selected:active:bg-primary-800 data-range-middle:rounded-none data-range-middle:bg-primary-100 data-range-middle:text-foreground data-range-middle:data-today:rounded-full data-range-middle:hover:bg-primary-200 data-range-middle:active:bg-primary-200";
80
+ const navButtonClasses = "inline-flex size-9 cursor-pointer items-center justify-center rounded-md text-foreground transition-colors duration-150 hover:bg-muted active:bg-slate-200 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4";
81
+ const captionClasses = "font-sans text-sm font-semibold text-foreground";
82
+ const weekdayClasses = "size-9 p-0 text-center align-middle font-sans text-xs font-medium text-muted-foreground";
83
+ function ChevronLeftIcon(props) {
84
+ return /* @__PURE__ */ jsx("svg", {
85
+ viewBox: "0 0 24 24",
86
+ fill: "none",
87
+ stroke: "currentColor",
88
+ strokeWidth: 2,
89
+ strokeLinecap: "round",
90
+ strokeLinejoin: "round",
91
+ "aria-hidden": "true",
92
+ ...props,
93
+ children: /* @__PURE__ */ jsx("path", { d: "m15 18-6-6 6-6" })
94
+ });
95
+ }
96
+ function ChevronRightIcon(props) {
97
+ return /* @__PURE__ */ jsx("svg", {
98
+ viewBox: "0 0 24 24",
99
+ fill: "none",
100
+ stroke: "currentColor",
101
+ strokeWidth: 2,
102
+ strokeLinecap: "round",
103
+ strokeLinejoin: "round",
104
+ "aria-hidden": "true",
105
+ ...props,
106
+ children: /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" })
107
+ });
108
+ }
109
+ const Calendar = forwardRef((props, forwardedRef) => {
110
+ const { mode = "single", value: valueProp, defaultValue, onValueChange, allowDeselect = false, month: monthProp, defaultMonth, onMonthChange, numberOfMonths: numberOfMonthsProp = 1, minDate, maxDate, isDateUnavailable, disabled = false, readOnly = false, locale, weekStartsOn: weekStartsOnProp, showOutsideDays = true, fixedWeeks = false, today: todayProp, autoFocus = false, previousMonthLabel = "Previous month", nextMonthLabel = "Next month", "aria-label": ariaLabel, className, render, ...rest } = props;
111
+ const numberOfMonths = Math.max(1, Math.floor(numberOfMonthsProp));
112
+ const weekStartsOn = weekStartsOnProp ?? localeWeekStart(locale);
113
+ const [fallbackToday] = useState(() => toDayStart(/* @__PURE__ */ new Date()));
114
+ const today = todayProp ? toDayStart(todayProp) : fallbackToday;
115
+ const minDay = minDate ? toDayStart(minDate) : null;
116
+ const maxDay = maxDate ? toDayStart(maxDate) : null;
117
+ const [rawValue, setRawValue] = useControlled(valueProp, defaultValue ?? (mode === "multiple" ? [] : null));
118
+ const singleValue = mode === "single" && rawValue ? rawValue : null;
119
+ const multipleValue = useMemo(() => mode === "multiple" && Array.isArray(rawValue) ? rawValue : [], [mode, rawValue]);
120
+ const committedRange = mode === "range" && rawValue ? rawValue : null;
121
+ const [anchor, setAnchor] = useState(null);
122
+ const [previewDate, setPreviewDate] = useState(null);
123
+ const displayRange = anchor ? orderRange(anchor, previewDate ?? anchor) : committedRange ? orderRange(committedRange.start, committedRange.end) : null;
124
+ const firstSelected = mode === "single" ? singleValue : mode === "multiple" ? multipleValue[0] ?? null : committedRange?.start ?? null;
125
+ const initialMonth = () => startOfMonth(defaultMonth ?? firstSelected ?? today);
126
+ const [firstMonthRaw, setFirstMonthState] = useControlled(monthProp ? startOfMonth(monthProp) : void 0, initialMonth());
127
+ const firstMonth = startOfMonth(firstMonthRaw);
128
+ const visibleMonths = useMemo(() => Array.from({ length: numberOfMonths }, (_, i) => addMonths(firstMonth, i)), [firstMonth, numberOfMonths]);
129
+ const lastMonth = visibleMonths[visibleMonths.length - 1];
130
+ const [focusedDate, setFocusedDate] = useState(null);
131
+ const [announcement, setAnnouncement] = useState("");
132
+ const dayRefs = useRef(/* @__PURE__ */ new Map());
133
+ const shouldFocusRef = useRef(false);
134
+ const pagedDayRef = useRef(null);
135
+ const navRescueRef = useRef(null);
136
+ const formatters = useMemo(() => ({
137
+ caption: createFormatter(locale, {
138
+ month: "long",
139
+ year: "numeric"
140
+ }),
141
+ weekday: createFormatter(locale, { weekday: "short" }),
142
+ day: createFormatter(locale, { day: "numeric" }),
143
+ dayLabel: createFormatter(locale, {
144
+ weekday: "long",
145
+ day: "numeric",
146
+ month: "long",
147
+ year: "numeric"
148
+ })
149
+ }), [locale]);
150
+ const weekdayLabels = useMemo(() => Array.from({ length: 7 }, (_, i) => formatters.weekday.format(addDays(REFERENCE_SUNDAY, weekStartsOn + i))), [formatters, weekStartsOn]);
151
+ const isOutOfBounds = (day) => minDay !== null && compareDays(day, minDay) < 0 || maxDay !== null && compareDays(day, maxDay) > 0;
152
+ const clampToBounds = (day) => {
153
+ if (minDay && compareDays(day, minDay) < 0) return minDay;
154
+ if (maxDay && compareDays(day, maxDay) > 0) return maxDay;
155
+ return day;
156
+ };
157
+ const contiguousBounds = useMemo(() => {
158
+ if (mode !== "range" || !anchor || !isDateUnavailable) return null;
159
+ const SCAN_LIMIT = 400;
160
+ let low = anchor;
161
+ for (let i = 0; i < SCAN_LIMIT; i++) {
162
+ const previous = addDays(low, -1);
163
+ if (minDay && compareDays(previous, minDay) < 0 || isDateUnavailable(previous)) break;
164
+ low = previous;
165
+ }
166
+ let high = anchor;
167
+ for (let i = 0; i < SCAN_LIMIT; i++) {
168
+ const next = addDays(high, 1);
169
+ if (maxDay && compareDays(next, maxDay) > 0 || isDateUnavailable(next)) break;
170
+ high = next;
171
+ }
172
+ return {
173
+ low,
174
+ high
175
+ };
176
+ }, [
177
+ mode,
178
+ anchor,
179
+ isDateUnavailable,
180
+ minDay,
181
+ maxDay
182
+ ]);
183
+ const isUnavailable = (day) => isDateUnavailable?.(day) === true || contiguousBounds !== null && (compareDays(day, contiguousBounds.low) < 0 || compareDays(day, contiguousBounds.high) > 0);
184
+ const focusTarget = useMemo(() => {
185
+ const visible = (day) => day !== null && visibleMonths.some((m) => isSameMonth(m, day)) && !isOutOfBounds(day);
186
+ if (visible(focusedDate)) return focusedDate;
187
+ if (mode === "range" && visible(anchor)) return anchor;
188
+ if (visible(firstSelected)) return firstSelected;
189
+ if (visible(today)) return today;
190
+ const first = visibleMonths[0];
191
+ return minDay && compareDays(minDay, first) > 0 ? minDay : first;
192
+ }, [
193
+ focusedDate,
194
+ anchor,
195
+ firstSelected,
196
+ today,
197
+ visibleMonths,
198
+ minDay,
199
+ maxDay,
200
+ mode
201
+ ]);
202
+ const commitValue = (next) => {
203
+ setRawValue(next);
204
+ onValueChange?.(next);
205
+ };
206
+ const setMonthWindow = (next) => {
207
+ let clamped = startOfMonth(next);
208
+ if (maxDay) {
209
+ const maxMonth = startOfMonth(maxDay);
210
+ if (compareDays(addMonths(clamped, numberOfMonths - 1), maxMonth) > 0) clamped = addMonths(maxMonth, -(numberOfMonths - 1));
211
+ }
212
+ if (minDay) {
213
+ const minMonth = startOfMonth(minDay);
214
+ if (compareDays(clamped, minMonth) < 0) clamped = minMonth;
215
+ }
216
+ if (!isSameMonth(clamped, firstMonth)) {
217
+ setFirstMonthState(clamped);
218
+ onMonthChange?.(clamped);
219
+ }
220
+ return clamped;
221
+ };
222
+ /** Move the roving focus to a day, shifting the visible window if needed. */
223
+ const focusDay = (target) => {
224
+ const day = clampToBounds(target);
225
+ const monthsFromFirst = (day.getFullYear() - firstMonth.getFullYear()) * 12 + (day.getMonth() - firstMonth.getMonth());
226
+ if (monthsFromFirst < 0) setMonthWindow(day);
227
+ else if (monthsFromFirst >= numberOfMonths) setMonthWindow(addMonths(startOfMonth(day), -(numberOfMonths - 1)));
228
+ shouldFocusRef.current = true;
229
+ setFocusedDate(day);
230
+ if (anchor) setPreviewDate(day);
231
+ };
232
+ const handleDaySelect = (day) => {
233
+ if (disabled || readOnly || isUnavailable(day)) return;
234
+ pagedDayRef.current = null;
235
+ if (mode === "single") {
236
+ if (allowDeselect && isSameDay(singleValue, day)) commitValue(null);
237
+ else commitValue(day);
238
+ setFocusedDate(day);
239
+ } else if (mode === "multiple") {
240
+ const next = multipleValue.some((d) => isSameDay(d, day)) ? multipleValue.filter((d) => !isSameDay(d, day)) : [...multipleValue, day].sort(compareDays);
241
+ commitValue(next);
242
+ setFocusedDate(day);
243
+ } else if (!anchor) {
244
+ setAnchor(day);
245
+ setPreviewDate(day);
246
+ focusDay(addDays(day, 1));
247
+ } else {
248
+ commitValue(orderRange(anchor, day));
249
+ setAnchor(null);
250
+ setPreviewDate(null);
251
+ setFocusedDate(day);
252
+ }
253
+ };
254
+ const handleGridKeyDown = (event) => {
255
+ if (disabled) return;
256
+ if (event.key === "Escape") {
257
+ if (mode === "range" && anchor) {
258
+ setAnchor(null);
259
+ setPreviewDate(null);
260
+ event.preventDefault();
261
+ event.stopPropagation();
262
+ }
263
+ return;
264
+ }
265
+ const focused = focusTarget;
266
+ const weekOffset = (focused.getDay() - weekStartsOn + 7) % 7;
267
+ let next = null;
268
+ let paging = false;
269
+ switch (event.key) {
270
+ case "ArrowRight":
271
+ case "ArrowLeft": {
272
+ const rtl = getComputedStyle(event.currentTarget).direction === "rtl";
273
+ next = addDays(focused, event.key === "ArrowRight" !== rtl ? 1 : -1);
274
+ break;
275
+ }
276
+ case "ArrowDown":
277
+ next = addDays(focused, 7);
278
+ break;
279
+ case "ArrowUp":
280
+ next = addDays(focused, -7);
281
+ break;
282
+ case "Home":
283
+ next = addDays(focused, -weekOffset);
284
+ break;
285
+ case "End":
286
+ next = addDays(focused, 6 - weekOffset);
287
+ break;
288
+ case "PageUp":
289
+ case "PageDown": {
290
+ const monthDelta = (event.key === "PageDown" ? 1 : -1) * (event.shiftKey ? 12 : numberOfMonths);
291
+ pagedDayRef.current ??= focused.getDate();
292
+ const targetMonth = addMonths(startOfMonth(focused), monthDelta);
293
+ next = new Date(targetMonth.getFullYear(), targetMonth.getMonth(), Math.min(pagedDayRef.current, daysInMonth(targetMonth.getFullYear(), targetMonth.getMonth())));
294
+ paging = true;
295
+ break;
296
+ }
297
+ default: return;
298
+ }
299
+ event.preventDefault();
300
+ if (!paging) pagedDayRef.current = null;
301
+ focusDay(next);
302
+ };
303
+ const previousDisabled = disabled || minDay !== null && compareDays(firstMonth, startOfMonth(minDay)) <= 0;
304
+ const nextDisabled = disabled || maxDay !== null && compareDays(lastMonth, startOfMonth(maxDay)) >= 0;
305
+ const handleNavigate = (direction) => {
306
+ navRescueRef.current = direction === -1 ? "previous" : "next";
307
+ const landed = setMonthWindow(addMonths(firstMonth, direction * numberOfMonths));
308
+ const from = formatters.caption.format(landed);
309
+ const to = formatters.caption.format(addMonths(landed, numberOfMonths - 1));
310
+ setAnnouncement(numberOfMonths > 1 ? `${from} – ${to}` : from);
311
+ };
312
+ useEffect(() => {
313
+ if (shouldFocusRef.current && focusedDate) {
314
+ shouldFocusRef.current = false;
315
+ dayRefs.current.get(dayKey(focusedDate))?.focus();
316
+ }
317
+ }, [focusedDate]);
318
+ useEffect(() => {
319
+ if (!navRescueRef.current) return;
320
+ const which = navRescueRef.current;
321
+ navRescueRef.current = null;
322
+ if (which === "previous" && previousDisabled || which === "next" && nextDisabled) dayRefs.current.get(dayKey(focusTarget))?.focus();
323
+ });
324
+ useEffect(() => {
325
+ if (autoFocus) dayRefs.current.get(dayKey(focusTarget))?.focus();
326
+ }, []);
327
+ const renderDay = (day, month) => {
328
+ const key = dayKey(day);
329
+ const outside = !isSameMonth(day, month);
330
+ const duplicate = outside && visibleMonths.some((m) => isSameMonth(m, day));
331
+ const hidden = outside && (!showOutsideDays || duplicate);
332
+ const hardDisabled = disabled || outside || isOutOfBounds(day);
333
+ const unavailable = !outside && !hardDisabled && isUnavailable(day);
334
+ const inRange = displayRange !== null && compareDays(day, displayRange.start) >= 0 && compareDays(day, displayRange.end) <= 0;
335
+ const rangeStart = mode === "range" && inRange && isSameDay(day, displayRange.start);
336
+ const rangeEnd = mode === "range" && inRange && isSameDay(day, displayRange.end);
337
+ const rangeMiddle = mode === "range" && inRange && !rangeStart && !rangeEnd;
338
+ const columnIndex = (day.getDay() - weekStartsOn + 7) % 7;
339
+ const rangeConnectorClass = !outside && rangeStart && !rangeEnd && columnIndex < 6 ? "end-0" : !outside && rangeEnd && !rangeStart && columnIndex > 0 ? "start-0" : null;
340
+ const selected = mode === "single" ? isSameDay(singleValue, day) : mode === "multiple" ? multipleValue.some((d) => isSameDay(d, day)) : rangeStart || rangeEnd;
341
+ const ariaSelected = mode === "range" ? inRange : selected;
342
+ const dataAttributes = {};
343
+ if (selected) dataAttributes["data-selected"] = "";
344
+ if (rangeMiddle) dataAttributes["data-range-middle"] = "";
345
+ if (rangeStart) dataAttributes["data-range-start"] = "";
346
+ if (rangeEnd) dataAttributes["data-range-end"] = "";
347
+ if (isSameDay(today, day)) dataAttributes["data-today"] = "";
348
+ if (outside) dataAttributes["data-outside"] = "";
349
+ if (hidden) dataAttributes["data-hidden"] = "";
350
+ if (unavailable) dataAttributes["data-unavailable"] = "";
351
+ if (hardDisabled) dataAttributes["data-disabled"] = "";
352
+ return /* @__PURE__ */ jsxs("td", {
353
+ role: "gridcell",
354
+ "aria-selected": !outside && ariaSelected ? true : void 0,
355
+ "aria-hidden": outside || void 0,
356
+ className: cn("relative p-0", !outside && rangeMiddle && "bg-primary-100"),
357
+ children: [rangeConnectorClass && /* @__PURE__ */ jsx("span", {
358
+ "aria-hidden": "true",
359
+ className: cn("pointer-events-none absolute inset-y-0 w-1/2 bg-primary-100", rangeConnectorClass)
360
+ }), /* @__PURE__ */ jsx("button", {
361
+ ref: (node) => {
362
+ if (node) dayRefs.current.set(key, node);
363
+ else dayRefs.current.delete(key);
364
+ },
365
+ type: "button",
366
+ tabIndex: !outside && isSameDay(day, focusTarget) ? 0 : -1,
367
+ disabled: hardDisabled || void 0,
368
+ "aria-disabled": unavailable || void 0,
369
+ "aria-label": formatters.dayLabel.format(day),
370
+ "aria-current": isSameDay(today, day) ? "date" : void 0,
371
+ onClick: () => handleDaySelect(day),
372
+ onFocus: () => {
373
+ if (!isSameDay(focusedDate, day)) setFocusedDate(day);
374
+ if (anchor) setPreviewDate(day);
375
+ },
376
+ onPointerEnter: anchor ? () => setPreviewDate(day) : void 0,
377
+ className: cn(dayButtonClasses, mode === "range" ? "transition-[color,border-color]" : "transition-colors", readOnly && "pointer-events-none"),
378
+ ...dataAttributes,
379
+ children: formatters.day.format(day)
380
+ })]
381
+ }, key);
382
+ };
383
+ const renderMonth = (month, index) => {
384
+ const caption = formatters.caption.format(month);
385
+ const spacer = /* @__PURE__ */ jsx("span", {
386
+ "aria-hidden": "true",
387
+ className: "size-9"
388
+ });
389
+ return /* @__PURE__ */ jsxs("div", {
390
+ className: "flex flex-col gap-3",
391
+ children: [/* @__PURE__ */ jsxs("div", {
392
+ className: "flex items-center justify-between gap-2",
393
+ children: [
394
+ index === 0 ? /* @__PURE__ */ jsx("button", {
395
+ type: "button",
396
+ "aria-label": previousMonthLabel,
397
+ disabled: previousDisabled || void 0,
398
+ onClick: () => handleNavigate(-1),
399
+ className: navButtonClasses,
400
+ children: /* @__PURE__ */ jsx(ChevronLeftIcon, { className: "rtl:-scale-x-100" })
401
+ }) : spacer,
402
+ /* @__PURE__ */ jsx("span", {
403
+ "aria-hidden": "true",
404
+ className: captionClasses,
405
+ children: caption
406
+ }),
407
+ index === numberOfMonths - 1 ? /* @__PURE__ */ jsx("button", {
408
+ type: "button",
409
+ "aria-label": nextMonthLabel,
410
+ disabled: nextDisabled || void 0,
411
+ onClick: () => handleNavigate(1),
412
+ className: navButtonClasses,
413
+ children: /* @__PURE__ */ jsx(ChevronRightIcon, { className: "rtl:-scale-x-100" })
414
+ }) : spacer
415
+ ]
416
+ }), /* @__PURE__ */ jsxs("table", {
417
+ role: "grid",
418
+ "aria-label": ariaLabel ? `${ariaLabel}, ${caption}` : caption,
419
+ "aria-multiselectable": mode !== "single" || void 0,
420
+ "aria-readonly": readOnly || void 0,
421
+ "aria-disabled": disabled || void 0,
422
+ onKeyDown: handleGridKeyDown,
423
+ className: "border-collapse",
424
+ children: [/* @__PURE__ */ jsx("thead", {
425
+ "aria-hidden": "true",
426
+ children: /* @__PURE__ */ jsx("tr", { children: weekdayLabels.map((label, i) => /* @__PURE__ */ jsx("th", {
427
+ scope: "col",
428
+ className: weekdayClasses,
429
+ children: label
430
+ }, i)) })
431
+ }), /* @__PURE__ */ jsx("tbody", { children: buildWeeks(month, weekStartsOn, fixedWeeks).map((week, w) => /* @__PURE__ */ jsx("tr", { children: week.map((day) => renderDay(day, month)) }, w)) })]
432
+ })]
433
+ }, dayKey(month));
434
+ };
435
+ const state = {
436
+ mode,
437
+ disabled,
438
+ readOnly
439
+ };
440
+ const defaultProps = {
441
+ role: "group",
442
+ "aria-label": ariaLabel,
443
+ className: cn("inline-flex flex-col p-3 font-sans", className),
444
+ children: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
445
+ role: "status",
446
+ className: "sr-only",
447
+ children: announcement
448
+ }), /* @__PURE__ */ jsx("div", {
449
+ className: "flex flex-wrap items-start gap-8",
450
+ children: visibleMonths.map(renderMonth)
451
+ })] })
452
+ };
453
+ return useRender({
454
+ defaultTagName: "div",
455
+ render,
456
+ state,
457
+ stateAttributesMapping: {
458
+ mode: (value) => ({ "data-mode": value }),
459
+ disabled: (value) => value ? { "data-disabled": "" } : null,
460
+ readOnly: (value) => value ? { "data-readonly": "" } : null
461
+ },
462
+ ref: [forwardedRef],
463
+ props: mergeProps(defaultProps, rest)
464
+ });
465
+ });
466
+ Calendar.displayName = "Calendar";
467
+ //#endregion
468
+ export { Calendar };
@@ -0,0 +1,2 @@
1
+ import { Calendar, CalendarDateRange, CalendarMode, CalendarProps, CalendarState } from "./Calendar.js";
2
+ export { Calendar, type CalendarDateRange, type CalendarMode, type CalendarProps, type CalendarState };
@@ -0,0 +1,2 @@
1
+ import { Calendar } from "./Calendar.js";
2
+ export { Calendar };
@@ -0,0 +1,60 @@
1
+ import { ComponentPropsWithoutRef, ReactNode } from "react";
2
+ import { Checkbox } from "@base-ui/react/checkbox";
3
+ import { CheckboxGroup } from "@base-ui/react/checkbox-group";
4
+ //#region src/components/Checkbox/Checkbox.d.ts
5
+ type CheckboxSize = "sm" | "md" | "lg";
6
+ interface CheckboxVariantsOptions {
7
+ size?: CheckboxSize;
8
+ className?: string;
9
+ }
10
+ /**
11
+ * Class list for the checkbox box itself. State styling hangs off Base UI's
12
+ * data-attributes (`data-checked`, `data-indeterminate`, `data-disabled`,
13
+ * `data-readonly`, `data-invalid`) plus `aria-invalid`.
14
+ */
15
+ declare function checkboxVariants({ size, className }?: CheckboxVariantsOptions): string;
16
+ interface CheckboxProps extends Omit<ComponentPropsWithoutRef<typeof Checkbox.Root>, "className"> {
17
+ /**
18
+ * Box size. The 24px `lg` box meets the WCAG 2.5.8 target-size floor on its
19
+ * own; `sm`/`md` rely on the clickable `label` (or surrounding spacing) to
20
+ * extend the target. Prefer `md`+ with a label on touch-first surfaces.
21
+ */
22
+ size?: CheckboxSize;
23
+ /**
24
+ * Visible label rendered in an enclosing `<label>`, so clicking it toggles
25
+ * the checkbox and it names the control. Without it, provide an accessible
26
+ * name another way: `aria-label`, `aria-labelledby`, or an external
27
+ * `<label>`. When `required` is set, the label shows a decorative asterisk.
28
+ */
29
+ label?: ReactNode;
30
+ /**
31
+ * Supporting copy below the label, linked via `aria-describedby`. Rendered
32
+ * outside the `<label>` element so screen readers announce it as the
33
+ * description instead of reading it into the accessible name.
34
+ */
35
+ description?: ReactNode;
36
+ /** Applied to the checkbox box itself, also when `label`/`description` wrap it. */
37
+ className?: string;
38
+ }
39
+ /**
40
+ * Binary/mixed-state control built on Base UI's Checkbox. The indicator is
41
+ * built in: a check mark, or a minus while `indeterminate` — so a `parent`
42
+ * checkbox inside a controlled `CheckboxGroup` with `allValues` needs no
43
+ * extra wiring. `ref` and `className` always target the box element, whether
44
+ * or not the labelled layout wraps it.
45
+ */
46
+ declare const Checkbox$1: import("react").ForwardRefExoticComponent<CheckboxProps & import("react").RefAttributes<HTMLSpanElement>>;
47
+ interface CheckboxGroupProps extends Omit<ComponentPropsWithoutRef<typeof CheckboxGroup>, "className"> {
48
+ className?: string;
49
+ }
50
+ /**
51
+ * Shares ticked-value state (`value`/`defaultValue`/`onValueChange`) across
52
+ * child checkboxes matched by their `value` prop, and drives a `parent`
53
+ * checkbox's indeterminate state when `allValues` is set. Lays the items out
54
+ * as a vertical stack — pass `className="flex-row"` for a horizontal row.
55
+ * The group needs an accessible name: `aria-labelledby` pointing at a caption,
56
+ * or `aria-label`.
57
+ */
58
+ declare const CheckboxGroup$1: import("react").ForwardRefExoticComponent<CheckboxGroupProps & import("react").RefAttributes<HTMLDivElement>>;
59
+ //#endregion
60
+ export { Checkbox$1 as Checkbox, CheckboxGroup$1 as CheckboxGroup, CheckboxGroupProps, CheckboxProps, CheckboxSize, CheckboxVariantsOptions, checkboxVariants };
@@ -0,0 +1,129 @@
1
+ import { cn } from "../../lib/cn.js";
2
+ import { forwardRef, useId } from "react";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ import { Checkbox } from "@base-ui/react/checkbox";
5
+ import { CheckboxGroup } from "@base-ui/react/checkbox-group";
6
+ //#region src/components/Checkbox/Checkbox.tsx
7
+ const boxClasses = "inline-flex shrink-0 cursor-pointer select-none items-center justify-center rounded-md border border-border bg-background text-primary-foreground transition-colors duration-150 hover:border-slate-300 data-checked:border-primary data-checked:bg-primary data-indeterminate:border-primary data-indeterminate:bg-primary hover:data-checked:border-primary-700 hover:data-checked:bg-primary-700 hover:data-indeterminate:border-primary-700 hover:data-indeterminate:bg-primary-700 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring aria-invalid:border-destructive data-invalid:border-destructive aria-invalid:focus-visible:outline-destructive data-invalid:focus-visible:outline-destructive data-readonly:cursor-default disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0";
8
+ const boxSizeClasses = {
9
+ sm: "size-4 [&_svg]:size-3",
10
+ md: "size-5 [&_svg]:size-3.5",
11
+ lg: "size-6 [&_svg]:size-4"
12
+ };
13
+ const boxOffsetClasses = {
14
+ sm: "mt-0.5",
15
+ md: "",
16
+ lg: ""
17
+ };
18
+ const labelTextClasses = {
19
+ sm: "text-sm",
20
+ md: "text-sm",
21
+ lg: "text-base"
22
+ };
23
+ const descriptionTextClasses = {
24
+ sm: "text-xs ps-6",
25
+ md: "text-xs ps-7",
26
+ lg: "text-sm ps-8"
27
+ };
28
+ /**
29
+ * Class list for the checkbox box itself. State styling hangs off Base UI's
30
+ * data-attributes (`data-checked`, `data-indeterminate`, `data-disabled`,
31
+ * `data-readonly`, `data-invalid`) plus `aria-invalid`.
32
+ */
33
+ function checkboxVariants({ size = "md", className } = {}) {
34
+ return cn(boxClasses, boxSizeClasses[size], className);
35
+ }
36
+ const glyphDrawClasses = "[stroke-dasharray:1] motion-safe:transition-[stroke-dashoffset,opacity] motion-safe:duration-200 motion-safe:ease-out motion-safe:in-data-starting-style:[stroke-dashoffset:1] motion-safe:in-data-starting-style:opacity-0";
37
+ function CheckIcon() {
38
+ return /* @__PURE__ */ jsx("svg", {
39
+ viewBox: "0 0 24 24",
40
+ fill: "none",
41
+ stroke: "currentColor",
42
+ strokeWidth: 3,
43
+ strokeLinecap: "round",
44
+ strokeLinejoin: "round",
45
+ "aria-hidden": "true",
46
+ children: /* @__PURE__ */ jsx("path", {
47
+ d: "m5 13 4 4L19 7",
48
+ pathLength: 1,
49
+ className: glyphDrawClasses
50
+ })
51
+ });
52
+ }
53
+ function MinusIcon() {
54
+ return /* @__PURE__ */ jsx("svg", {
55
+ viewBox: "0 0 24 24",
56
+ fill: "none",
57
+ stroke: "currentColor",
58
+ strokeWidth: 3,
59
+ strokeLinecap: "round",
60
+ "aria-hidden": "true",
61
+ children: /* @__PURE__ */ jsx("path", {
62
+ d: "M6 12h12",
63
+ pathLength: 1,
64
+ className: glyphDrawClasses
65
+ })
66
+ });
67
+ }
68
+ /**
69
+ * Binary/mixed-state control built on Base UI's Checkbox. The indicator is
70
+ * built in: a check mark, or a minus while `indeterminate` — so a `parent`
71
+ * checkbox inside a controlled `CheckboxGroup` with `allValues` needs no
72
+ * extra wiring. `ref` and `className` always target the box element, whether
73
+ * or not the labelled layout wraps it.
74
+ */
75
+ const Checkbox$1 = forwardRef(({ size = "md", label, description, required, className, "aria-describedby": ariaDescribedBy, ...rest }, ref) => {
76
+ const descriptionId = useId();
77
+ const hasLabel = label != null;
78
+ const hasText = hasLabel || description != null;
79
+ const control = /* @__PURE__ */ jsx(Checkbox.Root, {
80
+ ref,
81
+ required,
82
+ "aria-describedby": [ariaDescribedBy, description != null ? descriptionId : void 0].filter(Boolean).join(" ") || void 0,
83
+ className: cn(checkboxVariants({ size }), hasText && "data-disabled:opacity-100", hasLabel && boxOffsetClasses[size], className),
84
+ ...rest,
85
+ children: /* @__PURE__ */ jsx(Checkbox.Indicator, {
86
+ className: cn("flex", "motion-safe:transition-[scale,opacity] motion-safe:duration-200 motion-safe:ease-out", "motion-safe:data-ending-style:scale-75 motion-safe:data-ending-style:opacity-0", "motion-safe:data-ending-style:duration-100 motion-safe:data-ending-style:ease-in"),
87
+ render: (props, state) => /* @__PURE__ */ jsx("span", {
88
+ ...props,
89
+ children: state.indeterminate ? /* @__PURE__ */ jsx(MinusIcon, {}) : /* @__PURE__ */ jsx(CheckIcon, {})
90
+ })
91
+ })
92
+ });
93
+ if (!hasText) return control;
94
+ return /* @__PURE__ */ jsxs("span", {
95
+ className: cn("inline-flex max-w-full flex-col gap-1 font-sans", "has-data-disabled:pointer-events-none has-data-disabled:opacity-50"),
96
+ children: [hasLabel ? /* @__PURE__ */ jsxs("label", {
97
+ className: cn("inline-flex cursor-pointer items-start gap-2", "has-data-readonly:cursor-default"),
98
+ children: [control, /* @__PURE__ */ jsxs("span", {
99
+ className: cn("font-medium text-foreground select-none", labelTextClasses[size]),
100
+ children: [label, required && /* @__PURE__ */ jsx("span", {
101
+ "aria-hidden": "true",
102
+ className: "ms-0.5 text-destructive",
103
+ children: "*"
104
+ })]
105
+ })]
106
+ }) : control, description != null && /* @__PURE__ */ jsx("span", {
107
+ id: descriptionId,
108
+ className: cn("text-muted-foreground", descriptionTextClasses[size]),
109
+ children: description
110
+ })]
111
+ });
112
+ });
113
+ Checkbox$1.displayName = "Checkbox";
114
+ /**
115
+ * Shares ticked-value state (`value`/`defaultValue`/`onValueChange`) across
116
+ * child checkboxes matched by their `value` prop, and drives a `parent`
117
+ * checkbox's indeterminate state when `allValues` is set. Lays the items out
118
+ * as a vertical stack — pass `className="flex-row"` for a horizontal row.
119
+ * The group needs an accessible name: `aria-labelledby` pointing at a caption,
120
+ * or `aria-label`.
121
+ */
122
+ const CheckboxGroup$1 = forwardRef(({ className, ...rest }, ref) => /* @__PURE__ */ jsx(CheckboxGroup, {
123
+ ref,
124
+ className: cn("flex flex-col items-start gap-3", className),
125
+ ...rest
126
+ }));
127
+ CheckboxGroup$1.displayName = "CheckboxGroup";
128
+ //#endregion
129
+ export { Checkbox$1 as Checkbox, CheckboxGroup$1 as CheckboxGroup, checkboxVariants };
@@ -0,0 +1,2 @@
1
+ import { Checkbox, CheckboxGroup, CheckboxGroupProps, CheckboxProps, CheckboxSize, CheckboxVariantsOptions, checkboxVariants } from "./Checkbox.js";
2
+ export { Checkbox, CheckboxGroup, type CheckboxGroupProps, type CheckboxProps, type CheckboxSize, type CheckboxVariantsOptions, checkboxVariants };
@@ -0,0 +1,2 @@
1
+ import { Checkbox, CheckboxGroup, checkboxVariants } from "./Checkbox.js";
2
+ export { Checkbox, CheckboxGroup, checkboxVariants };