@particle-academy/react-fancy 5.54.2 → 5.56.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.
- package/CHANGELOG.md +78 -0
- package/dist/calendar.cjs +2 -2
- package/dist/calendar.d.cts +30 -1
- package/dist/calendar.d.ts +30 -1
- package/dist/calendar.js +1 -1
- package/dist/{chunk-UET6D7B3.cjs → chunk-CQ7QXZLR.cjs} +45 -26
- package/dist/chunk-CQ7QXZLR.cjs.map +1 -0
- package/dist/chunk-SFJ5WOX5.js +703 -0
- package/dist/chunk-SFJ5WOX5.js.map +1 -0
- package/dist/{chunk-HVND2IIB.js → chunk-X2A3NAJW.js} +45 -26
- package/dist/chunk-X2A3NAJW.js.map +1 -0
- package/dist/chunk-XO4UWQCU.cjs +705 -0
- package/dist/chunk-XO4UWQCU.cjs.map +1 -0
- package/dist/index.cjs +4 -4
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/time-grid.cjs +3 -2
- package/dist/time-grid.d.cts +120 -17
- package/dist/time-grid.d.ts +120 -17
- package/dist/time-grid.js +2 -1
- package/docs/Calendar.md +37 -0
- package/package.json +1 -1
- package/dist/chunk-GF23UBOT.js +0 -129
- package/dist/chunk-GF23UBOT.js.map +0 -1
- package/dist/chunk-HVND2IIB.js.map +0 -1
- package/dist/chunk-UET6D7B3.cjs.map +0 -1
- package/dist/chunk-WFHK2DKG.cjs +0 -131
- package/dist/chunk-WFHK2DKG.cjs.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,84 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
11
11
|
|
|
12
12
|
## [Unreleased]
|
|
13
13
|
|
|
14
|
+
## [5.56.0] - 2026-10-21
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **`Calendar` per-day hooks and a controlled visible month.** All additive:
|
|
19
|
+
**nothing to do** for an existing Calendar, which renders and behaves as
|
|
20
|
+
before, plus new data attributes.
|
|
21
|
+
- Every day cell carries a stable id, `data-react-fancy-calendar-day`,
|
|
22
|
+
`YYYY-MM-DD` in local time by default (`dateId` replaces it). Leading and
|
|
23
|
+
trailing days carry `data-outside-month`; the heading carries
|
|
24
|
+
`data-react-fancy-calendar-month`.
|
|
25
|
+
- `renderDay(ctx)` draws a day's content, `dayClassName(ctx)` adds classes
|
|
26
|
+
to one day, `dayAriaLabel(ctx)` sets its accessible name. `ctx` is
|
|
27
|
+
`{ date, id, selected, disabled, inRange, outsideMonth, today }`, exported
|
|
28
|
+
as `CalendarDayContext`. A day shaded with `dayClassName` should get a
|
|
29
|
+
`dayAriaLabel` too: colour alone does not reach a screen reader.
|
|
30
|
+
- `visibleMonth` / `onVisibleMonthChange`: the shown month is controlled when
|
|
31
|
+
`visibleMonth` is given (the chevrons propose, the host decides);
|
|
32
|
+
uncontrolled it starts at today as before, and still reports each move.
|
|
33
|
+
The callback gets the 1st of the month.
|
|
34
|
+
- Opening a day on click is the existing `onChange` (single mode).
|
|
35
|
+
- Verified in Chrome in light, `.dark` and `[data-theme="dark"]`.
|
|
36
|
+
|
|
37
|
+
The requester's wording was `month` / `onMonthChange`. The names here follow
|
|
38
|
+
the spec (`visibleMonth` / `onVisibleMonthChange`), because `value` is already
|
|
39
|
+
a date and a bare `month` reads as the selected one.
|
|
40
|
+
|
|
41
|
+
## [5.55.0] - 2026-10-21
|
|
42
|
+
|
|
43
|
+
### Added
|
|
44
|
+
|
|
45
|
+
- **`TimeGrid` typed cells and range selection.** Pass `cells` (with `states`)
|
|
46
|
+
and the grid renders the host's own vocabulary instead of on/off. **Nothing
|
|
47
|
+
to do for an existing grid:** without `cells` it is the same boolean grid,
|
|
48
|
+
with the same DOM (byte-identical in Chrome against 5.54.2) and the same
|
|
49
|
+
timings. `value` / `onChange` are now optional in the type because a typed
|
|
50
|
+
grid does not use them.
|
|
51
|
+
- `cells: TimeGridCell[][]`: each cell is `{ state, disabled?, reasons?,
|
|
52
|
+
marks?, meta? }`. `meta` is handed back untouched in every callback.
|
|
53
|
+
- `states: Record<id, { tone?, label?, className? }>`: the exclusive
|
|
54
|
+
dimension (open / closed / booked …). `marks: Record<id, { tone?, icon?,
|
|
55
|
+
label? }>`: non-exclusive overlays. The kit declares the shape; the host
|
|
56
|
+
declares the vocabulary.
|
|
57
|
+
- `selectionShape: "rectangle" | "linear"`. A rectangle is the same window
|
|
58
|
+
across rows (09:00-17:00 Mon-Fri); a linear run goes in reading order and
|
|
59
|
+
crosses row ends (23:54 Mon to 00:06 Tue). `selection` is a discriminated
|
|
60
|
+
union, controlled or uncontrolled; `onSelectionChange(next, cells)` hands
|
|
61
|
+
the cells in selection order; `onSelectionCommit` fires when a drag ends
|
|
62
|
+
or Enter is pressed.
|
|
63
|
+
- Selection is derived from anchor + current cell, so a fast drag no longer
|
|
64
|
+
leaves gaps, and pointer capture keeps a drag tracking (clamped to the
|
|
65
|
+
edge) after it leaves the grid.
|
|
66
|
+
- `disabledRange: "clamp" | "refuse" | "span"` (default `clamp`) decides what
|
|
67
|
+
a range crossing a disabled cell does, identically in both shapes.
|
|
68
|
+
`onBlockedSelect(blocked)` reports clipped or refused cells with their
|
|
69
|
+
`reasons`.
|
|
70
|
+
- A disabled cell's `reasons` reach the `title`, the cell's
|
|
71
|
+
`aria-description`, and a tooltip shown while the cell has keyboard focus.
|
|
72
|
+
- Keyboard: the grid takes focus (`aria-activedescendant`), not every cell.
|
|
73
|
+
Arrows move, Shift+arrows extend from the anchor, Space selects the
|
|
74
|
+
focused cell, Enter commits, Escape clears. Header clicks select a whole
|
|
75
|
+
row or column.
|
|
76
|
+
- `onCellHover(cell, { r, c })` for a host-owned detail panel; `null, null`
|
|
77
|
+
on leave. `aria-label` names the grid.
|
|
78
|
+
- `cells` without `states` throws in development: an unknown vocabulary
|
|
79
|
+
would otherwise render as one colour. An unknown state id warns once and
|
|
80
|
+
renders neutral.
|
|
81
|
+
- Colours: state and mark tones from the existing `TimeGridTone` set; the
|
|
82
|
+
neutral fill, selection wash, focus outline, headers and tooltip read the
|
|
83
|
+
`--fancy-*` tokens. Dark mode follows `.dark` and `[data-theme="dark"]`.
|
|
84
|
+
- A mark's `icon` is a `ReactNode` (e.g. `<Icon name="lock" />`), not an
|
|
85
|
+
icon name: resolving names would import every Lucide glyph into any bundle
|
|
86
|
+
that uses TimeGrid (measured: 16k to 56k lines).
|
|
87
|
+
- **Measured (Chrome, 4x CPU throttle, 240 columns, median of 7):** typed grid
|
|
88
|
+
mount 157 ms at 7 days, 260 ms at 14 days, 552 ms at 31 days; update
|
|
89
|
+
44-75 ms. The 14-day mount is still over the 200 ms budget. Column windowing
|
|
90
|
+
is the next release.
|
|
91
|
+
|
|
14
92
|
## [5.54.2] - 2026-10-21
|
|
15
93
|
|
|
16
94
|
### Fixed
|
package/dist/calendar.cjs
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkCQ7QXZLR_cjs = require('./chunk-CQ7QXZLR.cjs');
|
|
4
4
|
require('./chunk-ZU5Q77OT.cjs');
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
Object.defineProperty(exports, "Calendar", {
|
|
9
9
|
enumerable: true,
|
|
10
|
-
get: function () { return
|
|
10
|
+
get: function () { return chunkCQ7QXZLR_cjs.Calendar; }
|
|
11
11
|
});
|
|
12
12
|
//# sourceMappingURL=calendar.cjs.map
|
|
13
13
|
//# sourceMappingURL=calendar.cjs.map
|
package/dist/calendar.d.cts
CHANGED
|
@@ -1,10 +1,24 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
2
3
|
|
|
3
4
|
type CalendarMode = "single" | "range" | "multiple";
|
|
4
5
|
interface DateRange {
|
|
5
6
|
start: Date | null;
|
|
6
7
|
end: Date | null;
|
|
7
8
|
}
|
|
9
|
+
/** Everything a per-day hook needs to know about one day cell. */
|
|
10
|
+
interface CalendarDayContext {
|
|
11
|
+
date: Date;
|
|
12
|
+
/** Stable id: `dateId(date)`, default `YYYY-MM-DD` in local time. Also on the cell as `data-react-fancy-calendar-day`. */
|
|
13
|
+
id: string;
|
|
14
|
+
selected: boolean;
|
|
15
|
+
disabled: boolean;
|
|
16
|
+
/** Strictly between a range's ends (range mode). */
|
|
17
|
+
inRange: boolean;
|
|
18
|
+
/** A leading/trailing day from the neighbouring month. */
|
|
19
|
+
outsideMonth: boolean;
|
|
20
|
+
today: boolean;
|
|
21
|
+
}
|
|
8
22
|
interface CalendarProps {
|
|
9
23
|
mode?: CalendarMode;
|
|
10
24
|
value?: Date | Date[] | DateRange | null;
|
|
@@ -13,8 +27,23 @@ interface CalendarProps {
|
|
|
13
27
|
maxDate?: Date;
|
|
14
28
|
disabledDates?: Date[];
|
|
15
29
|
className?: string;
|
|
30
|
+
/** Stable id per day. Default `YYYY-MM-DD` in local time. */
|
|
31
|
+
dateId?: (date: Date) => string;
|
|
32
|
+
/** The day cell's content. Default: the day number. The cell (button, click, state classes) stays the Calendar's. */
|
|
33
|
+
renderDay?: (ctx: CalendarDayContext) => ReactNode;
|
|
34
|
+
/** Extra classes for one day cell, merged after the built-in ones. */
|
|
35
|
+
dayClassName?: (ctx: CalendarDayContext) => string | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* The day cell's accessible name. Pair it with `dayClassName`: a shaded day
|
|
38
|
+
* must say what the shade means. It REPLACES the name, so include the date.
|
|
39
|
+
*/
|
|
40
|
+
dayAriaLabel?: (ctx: CalendarDayContext) => string | undefined;
|
|
41
|
+
/** Controlled visible month (any day in it). Uncontrolled when omitted; starts at today. */
|
|
42
|
+
visibleMonth?: Date;
|
|
43
|
+
/** Called with the 1st of the month navigated to, controlled or not. */
|
|
44
|
+
onVisibleMonthChange?: (month: Date) => void;
|
|
16
45
|
}
|
|
17
46
|
|
|
18
47
|
declare const Calendar: react.ForwardRefExoticComponent<CalendarProps & react.RefAttributes<HTMLDivElement>>;
|
|
19
48
|
|
|
20
|
-
export { Calendar, type CalendarMode, type CalendarProps, type DateRange };
|
|
49
|
+
export { Calendar, type CalendarDayContext, type CalendarMode, type CalendarProps, type DateRange };
|
package/dist/calendar.d.ts
CHANGED
|
@@ -1,10 +1,24 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
2
3
|
|
|
3
4
|
type CalendarMode = "single" | "range" | "multiple";
|
|
4
5
|
interface DateRange {
|
|
5
6
|
start: Date | null;
|
|
6
7
|
end: Date | null;
|
|
7
8
|
}
|
|
9
|
+
/** Everything a per-day hook needs to know about one day cell. */
|
|
10
|
+
interface CalendarDayContext {
|
|
11
|
+
date: Date;
|
|
12
|
+
/** Stable id: `dateId(date)`, default `YYYY-MM-DD` in local time. Also on the cell as `data-react-fancy-calendar-day`. */
|
|
13
|
+
id: string;
|
|
14
|
+
selected: boolean;
|
|
15
|
+
disabled: boolean;
|
|
16
|
+
/** Strictly between a range's ends (range mode). */
|
|
17
|
+
inRange: boolean;
|
|
18
|
+
/** A leading/trailing day from the neighbouring month. */
|
|
19
|
+
outsideMonth: boolean;
|
|
20
|
+
today: boolean;
|
|
21
|
+
}
|
|
8
22
|
interface CalendarProps {
|
|
9
23
|
mode?: CalendarMode;
|
|
10
24
|
value?: Date | Date[] | DateRange | null;
|
|
@@ -13,8 +27,23 @@ interface CalendarProps {
|
|
|
13
27
|
maxDate?: Date;
|
|
14
28
|
disabledDates?: Date[];
|
|
15
29
|
className?: string;
|
|
30
|
+
/** Stable id per day. Default `YYYY-MM-DD` in local time. */
|
|
31
|
+
dateId?: (date: Date) => string;
|
|
32
|
+
/** The day cell's content. Default: the day number. The cell (button, click, state classes) stays the Calendar's. */
|
|
33
|
+
renderDay?: (ctx: CalendarDayContext) => ReactNode;
|
|
34
|
+
/** Extra classes for one day cell, merged after the built-in ones. */
|
|
35
|
+
dayClassName?: (ctx: CalendarDayContext) => string | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* The day cell's accessible name. Pair it with `dayClassName`: a shaded day
|
|
38
|
+
* must say what the shade means. It REPLACES the name, so include the date.
|
|
39
|
+
*/
|
|
40
|
+
dayAriaLabel?: (ctx: CalendarDayContext) => string | undefined;
|
|
41
|
+
/** Controlled visible month (any day in it). Uncontrolled when omitted; starts at today. */
|
|
42
|
+
visibleMonth?: Date;
|
|
43
|
+
/** Called with the 1st of the month navigated to, controlled or not. */
|
|
44
|
+
onVisibleMonthChange?: (month: Date) => void;
|
|
16
45
|
}
|
|
17
46
|
|
|
18
47
|
declare const Calendar: react.ForwardRefExoticComponent<CalendarProps & react.RefAttributes<HTMLDivElement>>;
|
|
19
48
|
|
|
20
|
-
export { Calendar, type CalendarMode, type CalendarProps, type DateRange };
|
|
49
|
+
export { Calendar, type CalendarDayContext, type CalendarMode, type CalendarProps, type DateRange };
|
package/dist/calendar.js
CHANGED
|
@@ -23,6 +23,10 @@ var MONTHS = [
|
|
|
23
23
|
function isSameDay(a, b) {
|
|
24
24
|
return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
|
|
25
25
|
}
|
|
26
|
+
var pad2 = (n) => n < 10 ? `0${n}` : String(n);
|
|
27
|
+
function defaultDateId(date) {
|
|
28
|
+
return `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())}`;
|
|
29
|
+
}
|
|
26
30
|
function getDaysInMonth(year, month) {
|
|
27
31
|
const days = [];
|
|
28
32
|
const firstDay = new Date(year, month, 1);
|
|
@@ -47,34 +51,36 @@ var Calendar = react.forwardRef(
|
|
|
47
51
|
minDate,
|
|
48
52
|
maxDate,
|
|
49
53
|
disabledDates = [],
|
|
50
|
-
className
|
|
54
|
+
className,
|
|
55
|
+
dateId = defaultDateId,
|
|
56
|
+
renderDay,
|
|
57
|
+
dayClassName,
|
|
58
|
+
dayAriaLabel,
|
|
59
|
+
visibleMonth,
|
|
60
|
+
onVisibleMonthChange
|
|
51
61
|
}, ref) {
|
|
52
62
|
const today = react.useMemo(() => /* @__PURE__ */ new Date(), []);
|
|
53
|
-
const [
|
|
54
|
-
|
|
63
|
+
const [innerMonth, setInnerMonth] = react.useState(
|
|
64
|
+
() => new Date(today.getFullYear(), today.getMonth(), 1)
|
|
65
|
+
);
|
|
55
66
|
const [rangeHover, setRangeHover] = react.useState(null);
|
|
67
|
+
const shown = visibleMonth ?? innerMonth;
|
|
68
|
+
const viewMonth = shown.getMonth();
|
|
69
|
+
const viewYear = shown.getFullYear();
|
|
56
70
|
const days = react.useMemo(
|
|
57
71
|
() => getDaysInMonth(viewYear, viewMonth),
|
|
58
72
|
[viewYear, viewMonth]
|
|
59
73
|
);
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const nextMonth = react.useCallback(() =>
|
|
70
|
-
setViewMonth((m) => {
|
|
71
|
-
if (m === 11) {
|
|
72
|
-
setViewYear((y) => y + 1);
|
|
73
|
-
return 0;
|
|
74
|
-
}
|
|
75
|
-
return m + 1;
|
|
76
|
-
});
|
|
77
|
-
}, []);
|
|
74
|
+
const goToMonth = react.useCallback(
|
|
75
|
+
(delta) => {
|
|
76
|
+
const next = new Date(viewYear, viewMonth + delta, 1);
|
|
77
|
+
if (visibleMonth === void 0) setInnerMonth(next);
|
|
78
|
+
onVisibleMonthChange?.(next);
|
|
79
|
+
},
|
|
80
|
+
[viewYear, viewMonth, visibleMonth, onVisibleMonthChange]
|
|
81
|
+
);
|
|
82
|
+
const prevMonth = react.useCallback(() => goToMonth(-1), [goToMonth]);
|
|
83
|
+
const nextMonth = react.useCallback(() => goToMonth(1), [goToMonth]);
|
|
78
84
|
const isDisabled = react.useCallback(
|
|
79
85
|
(date) => {
|
|
80
86
|
if (minDate && date < minDate) return true;
|
|
@@ -161,7 +167,7 @@ var Calendar = react.forwardRef(
|
|
|
161
167
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { size: 16 })
|
|
162
168
|
}
|
|
163
169
|
),
|
|
164
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm font-semibold", children: [
|
|
170
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm font-semibold", "data-react-fancy-calendar-month": "", children: [
|
|
165
171
|
MONTHS[viewMonth],
|
|
166
172
|
" ",
|
|
167
173
|
viewYear
|
|
@@ -191,11 +197,23 @@ var Calendar = react.forwardRef(
|
|
|
191
197
|
const inRange = isInRange(date);
|
|
192
198
|
const disabled = isDisabled(date);
|
|
193
199
|
const isToday = isSameDay(date, today);
|
|
200
|
+
const ctx = {
|
|
201
|
+
date,
|
|
202
|
+
id: dateId(date),
|
|
203
|
+
selected,
|
|
204
|
+
disabled,
|
|
205
|
+
inRange,
|
|
206
|
+
outsideMonth: !isCurrentMonth,
|
|
207
|
+
today: isToday
|
|
208
|
+
};
|
|
194
209
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
195
210
|
"button",
|
|
196
211
|
{
|
|
197
212
|
type: "button",
|
|
198
213
|
disabled,
|
|
214
|
+
"data-react-fancy-calendar-day": ctx.id,
|
|
215
|
+
"data-outside-month": isCurrentMonth ? void 0 : "",
|
|
216
|
+
"aria-label": dayAriaLabel?.(ctx) || void 0,
|
|
199
217
|
onClick: () => handleSelect(date),
|
|
200
218
|
onMouseEnter: () => mode === "range" && setRangeHover(date),
|
|
201
219
|
className: chunkZU5Q77OT_cjs.cn(
|
|
@@ -205,9 +223,10 @@ var Calendar = react.forwardRef(
|
|
|
205
223
|
inRange && "bg-blue-50 text-blue-700 dark:bg-blue-950 dark:text-blue-300",
|
|
206
224
|
selected && "bg-zinc-900 text-white dark:bg-white dark:text-zinc-900",
|
|
207
225
|
isToday && !selected && "font-bold",
|
|
208
|
-
disabled && "cursor-not-allowed opacity-30"
|
|
226
|
+
disabled && "cursor-not-allowed opacity-30",
|
|
227
|
+
dayClassName?.(ctx)
|
|
209
228
|
),
|
|
210
|
-
children: date.getDate()
|
|
229
|
+
children: renderDay ? renderDay(ctx) : date.getDate()
|
|
211
230
|
},
|
|
212
231
|
i
|
|
213
232
|
);
|
|
@@ -220,5 +239,5 @@ var Calendar = react.forwardRef(
|
|
|
220
239
|
Calendar.displayName = "Calendar";
|
|
221
240
|
|
|
222
241
|
exports.Calendar = Calendar;
|
|
223
|
-
//# sourceMappingURL=chunk-
|
|
224
|
-
//# sourceMappingURL=chunk-
|
|
242
|
+
//# sourceMappingURL=chunk-CQ7QXZLR.cjs.map
|
|
243
|
+
//# sourceMappingURL=chunk-CQ7QXZLR.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/Calendar/Calendar.tsx"],"names":["forwardRef","Calendar","useMemo","useState","useCallback","jsxs","cn","jsx","ChevronLeft","ChevronRight"],"mappings":"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","file":"chunk-CQ7QXZLR.cjs","sourcesContent":["import { forwardRef, useCallback, useMemo, useState } from \"react\";\nimport { ChevronLeft, ChevronRight } from \"lucide-react\";\nimport { cn } from \"../../utils/cn\";\nimport type { CalendarDayContext, CalendarProps, DateRange } from \"./Calendar.types\";\n\nconst DAYS = [\"Su\", \"Mo\", \"Tu\", \"We\", \"Th\", \"Fr\", \"Sa\"];\nconst MONTHS = [\n \"January\", \"February\", \"March\", \"April\", \"May\", \"June\",\n \"July\", \"August\", \"September\", \"October\", \"November\", \"December\",\n];\n\nfunction isSameDay(a: Date, b: Date): boolean {\n return (\n a.getFullYear() === b.getFullYear() &&\n a.getMonth() === b.getMonth() &&\n a.getDate() === b.getDate()\n );\n}\n\nconst pad2 = (n: number) => (n < 10 ? `0${n}` : String(n));\n\n/** `YYYY-MM-DD` in LOCAL time — never `toISOString()`, which shifts the day east of UTC. */\nfunction defaultDateId(date: Date): string {\n return `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())}`;\n}\n\nfunction getDaysInMonth(year: number, month: number): Date[] {\n const days: Date[] = [];\n const firstDay = new Date(year, month, 1);\n const startDayOfWeek = firstDay.getDay();\n\n // Fill leading days from previous month\n for (let i = startDayOfWeek - 1; i >= 0; i--) {\n days.push(new Date(year, month, -i));\n }\n\n // Fill current month\n const lastDate = new Date(year, month + 1, 0).getDate();\n for (let d = 1; d <= lastDate; d++) {\n days.push(new Date(year, month, d));\n }\n\n // Fill trailing days to complete 6 rows\n while (days.length < 42) {\n days.push(new Date(year, month + 1, days.length - startDayOfWeek - lastDate + 1));\n }\n\n return days;\n}\n\nexport const Calendar = forwardRef<HTMLDivElement, CalendarProps>(\n function Calendar(\n {\n mode = \"single\",\n value,\n onChange,\n minDate,\n maxDate,\n disabledDates = [],\n className,\n dateId = defaultDateId,\n renderDay,\n dayClassName,\n dayAriaLabel,\n visibleMonth,\n onVisibleMonthChange,\n },\n ref,\n ) {\n const today = useMemo(() => new Date(), []);\n const [innerMonth, setInnerMonth] = useState(\n () => new Date(today.getFullYear(), today.getMonth(), 1),\n );\n const [rangeHover, setRangeHover] = useState<Date | null>(null);\n\n const shown = visibleMonth ?? innerMonth;\n const viewMonth = shown.getMonth();\n const viewYear = shown.getFullYear();\n\n const days = useMemo(\n () => getDaysInMonth(viewYear, viewMonth),\n [viewYear, viewMonth],\n );\n\n const goToMonth = useCallback(\n (delta: number) => {\n const next = new Date(viewYear, viewMonth + delta, 1);\n if (visibleMonth === undefined) setInnerMonth(next);\n onVisibleMonthChange?.(next);\n },\n [viewYear, viewMonth, visibleMonth, onVisibleMonthChange],\n );\n const prevMonth = useCallback(() => goToMonth(-1), [goToMonth]);\n const nextMonth = useCallback(() => goToMonth(1), [goToMonth]);\n\n const isDisabled = useCallback(\n (date: Date): boolean => {\n if (minDate && date < minDate) return true;\n if (maxDate && date > maxDate) return true;\n return disabledDates.some((d) => isSameDay(d, date));\n },\n [minDate, maxDate, disabledDates],\n );\n\n const isSelected = useCallback(\n (date: Date): boolean => {\n if (!value) return false;\n if (mode === \"single\" && value instanceof Date) {\n return isSameDay(date, value);\n }\n if (mode === \"multiple\" && Array.isArray(value)) {\n return value.some((v) => isSameDay(date, v));\n }\n if (mode === \"range\" && value && typeof value === \"object\" && \"start\" in value) {\n const range = value as DateRange;\n if (range.start && isSameDay(date, range.start)) return true;\n if (range.end && isSameDay(date, range.end)) return true;\n }\n return false;\n },\n [value, mode],\n );\n\n const isInRange = useCallback(\n (date: Date): boolean => {\n if (mode !== \"range\" || !value || typeof value !== \"object\" || !(\"start\" in value))\n return false;\n const range = value as DateRange;\n if (!range.start) return false;\n const end = range.end ?? rangeHover;\n if (!end) return false;\n const [s, e] = range.start <= end ? [range.start, end] : [end, range.start];\n return date > s && date < e;\n },\n [value, mode, rangeHover],\n );\n\n const handleSelect = useCallback(\n (date: Date) => {\n if (isDisabled(date)) return;\n\n if (mode === \"single\") {\n onChange?.(date);\n } else if (mode === \"multiple\") {\n const arr = (Array.isArray(value) ? value : []) as Date[];\n const exists = arr.findIndex((d) => isSameDay(d, date));\n if (exists >= 0) {\n onChange?.(arr.filter((_, i) => i !== exists));\n } else {\n onChange?.([...arr, date]);\n }\n } else if (mode === \"range\") {\n const range = (value && typeof value === \"object\" && \"start\" in value\n ? value\n : { start: null, end: null }) as DateRange;\n\n if (!range.start || range.end) {\n onChange?.({ start: date, end: null });\n } else {\n const [s, e] =\n date >= range.start ? [range.start, date] : [date, range.start];\n onChange?.({ start: s, end: e });\n }\n }\n },\n [mode, value, onChange, isDisabled],\n );\n\n const btnBase =\n \"flex h-8 w-8 items-center justify-center rounded-lg text-sm transition-colors\";\n\n return (\n <div\n data-react-fancy-calendar=\"\"\n ref={ref}\n className={cn(\n \"inline-block w-72 rounded-xl border border-[color:var(--fancy-border,var(--color-zinc-200))] bg-[color:var(--fancy-surface,var(--color-white))] p-4 dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]\",\n className,\n )}\n >\n {/* Header */}\n <div className=\"mb-3 flex items-center justify-between\">\n <button\n type=\"button\"\n onClick={prevMonth}\n className=\"rounded-lg p-1 text-zinc-400 hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))]\"\n aria-label=\"Previous month\"\n >\n <ChevronLeft size={16} />\n </button>\n <span className=\"text-sm font-semibold\" data-react-fancy-calendar-month=\"\">\n {MONTHS[viewMonth]} {viewYear}\n </span>\n <button\n type=\"button\"\n onClick={nextMonth}\n className=\"rounded-lg p-1 text-zinc-400 hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))]\"\n aria-label=\"Next month\"\n >\n <ChevronRight size={16} />\n </button>\n </div>\n\n {/* Day headers */}\n <div className=\"mb-1 grid grid-cols-7 gap-1\">\n {DAYS.map((d) => (\n <div\n key={d}\n className=\"flex h-8 items-center justify-center text-xs font-medium text-zinc-400\"\n >\n {d}\n </div>\n ))}\n </div>\n\n {/* Day grid */}\n <div className=\"grid grid-cols-7 gap-1\">\n {days.map((date, i) => {\n const isCurrentMonth = date.getMonth() === viewMonth;\n const selected = isSelected(date);\n const inRange = isInRange(date);\n const disabled = isDisabled(date);\n const isToday = isSameDay(date, today);\n const ctx: CalendarDayContext = {\n date,\n id: dateId(date),\n selected,\n disabled,\n inRange,\n outsideMonth: !isCurrentMonth,\n today: isToday,\n };\n\n return (\n <button\n key={i}\n type=\"button\"\n disabled={disabled}\n data-react-fancy-calendar-day={ctx.id}\n data-outside-month={isCurrentMonth ? undefined : \"\"}\n aria-label={dayAriaLabel?.(ctx) || undefined}\n onClick={() => handleSelect(date)}\n onMouseEnter={() =>\n mode === \"range\" && setRangeHover(date)\n }\n className={cn(\n btnBase,\n !isCurrentMonth && \"text-zinc-300 dark:text-zinc-600\",\n isCurrentMonth &&\n !selected &&\n !inRange &&\n \"text-[color:var(--fancy-text-soft,var(--color-zinc-700))] hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] dark:text-[color:var(--fancy-text-soft-dark,var(--color-zinc-300))] dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))]\",\n inRange &&\n \"bg-blue-50 text-blue-700 dark:bg-blue-950 dark:text-blue-300\",\n selected &&\n \"bg-zinc-900 text-white dark:bg-white dark:text-zinc-900\",\n isToday && !selected && \"font-bold\",\n disabled && \"cursor-not-allowed opacity-30\",\n dayClassName?.(ctx),\n )}\n >\n {renderDay ? renderDay(ctx) : date.getDate()}\n </button>\n );\n })}\n </div>\n </div>\n );\n },\n);\n\nCalendar.displayName = \"Calendar\";\n"]}
|