@calchemy/date-react 0.1.0 → 0.1.1

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/dist/index.js CHANGED
@@ -1,3 +1,1682 @@
1
- export { Calchemy, useCalchemyCalendar, useCalchemyContext } from "./components/Calchemy";
2
- export { useCalchemy } from "./hooks/useCalchemy";
1
+ import {
2
+ CalchemyContext,
3
+ CalendarContext,
4
+ CalendarPeriodContext,
5
+ CalendarScrollContext,
6
+ useCalchemyCalendar,
7
+ useCalchemyContext,
8
+ useCalendarPeriod
9
+ } from "./chunk-NGJGJXY4.js";
10
+
11
+ // src/hooks/useCalchemy.ts
12
+ import { useEffect as useEffect2, useMemo, useState as useState2 } from "react";
13
+ import { resolveExpectedDateValue } from "@calchemy/date-core";
14
+
15
+ // src/inline-completion.ts
16
+ function composeInlineCompletion(inputValue, completion) {
17
+ return `${inputValue}${completion.suffix}`;
18
+ }
19
+ function formatInlineCompletionDescription(inputValue, completion) {
20
+ return `Suggestion: ${composeInlineCompletion(inputValue, completion)}. Press Tab to accept.`;
21
+ }
22
+
23
+ // src/hooks/useDebouncedValue.ts
24
+ import { useEffect, useState } from "react";
25
+ var PARSE_QUERY_DEBOUNCE_MS = 150;
26
+ function useDebouncedValue(value, delayMs = PARSE_QUERY_DEBOUNCE_MS) {
27
+ const [debouncedValue, setDebouncedValue] = useState(value);
28
+ useEffect(() => {
29
+ const timeoutId = setTimeout(() => {
30
+ setDebouncedValue(value);
31
+ }, delayMs);
32
+ return () => {
33
+ clearTimeout(timeoutId);
34
+ };
35
+ }, [value, delayMs]);
36
+ return debouncedValue;
37
+ }
38
+
39
+ // src/hooks/useCalchemy.ts
40
+ function useCalchemy(options) {
41
+ const [uncontrolledInputValue, setUncontrolledInputValue] = useState2(options.defaultInputValue ?? "");
42
+ const [uncontrolledValue, setUncontrolledValue] = useState2(options.defaultValue ?? null);
43
+ const [uncontrolledInputMode, setUncontrolledInputMode] = useState2(
44
+ options.defaultInputMode ?? "field"
45
+ );
46
+ const inputValue = options.inputValue ?? uncontrolledInputValue;
47
+ const value = options.value ?? uncontrolledValue;
48
+ const inputMode = options.inputMode ?? uncontrolledInputMode;
49
+ const fieldInputActive = inputMode === "field";
50
+ const queryValue = useDebouncedValue(inputValue);
51
+ const queryPending = inputValue !== queryValue;
52
+ const result = useMemo(
53
+ () => options.calchemy.parseDate(queryValue, options.parseContext),
54
+ [queryValue, options.calchemy, options.parseContext]
55
+ );
56
+ const expectedValue = options.expectedValue;
57
+ const expectedOptions = {
58
+ ...options.multipleRangeExpansionLimit === void 0 ? {} : { multipleRangeExpansionLimit: options.multipleRangeExpansionLimit }
59
+ };
60
+ const expectedResult = resolveExpectedDateValue(result, expectedValue, expectedOptions);
61
+ const valueKindMismatch = expectedResult.status === "invalid" && result.status === "valid";
62
+ const settledInlineCompletion = useMemo(
63
+ () => options.calchemy.getInlineCompletion(queryValue, options.parseContext),
64
+ [queryValue, options.calchemy, options.parseContext]
65
+ );
66
+ const inlineCompletion = queryPending ? null : settledInlineCompletion;
67
+ function updateValue(nextValue, nextResult = result) {
68
+ if (options.value === void 0) {
69
+ setUncontrolledValue(nextValue);
70
+ }
71
+ options.onValueChange?.(nextValue, nextResult);
72
+ }
73
+ useEffect2(() => {
74
+ const nextResult = options.calchemy.parseDate(queryValue, options.parseContext);
75
+ const nextExpectedResult = resolveExpectedDateValue(
76
+ nextResult,
77
+ expectedValue,
78
+ expectedOptions
79
+ );
80
+ if (nextExpectedResult.status === "valid") {
81
+ if (options.value === void 0) {
82
+ setUncontrolledValue(nextExpectedResult.value);
83
+ }
84
+ options.onValueChange?.(nextExpectedResult.value, nextExpectedResult);
85
+ }
86
+ }, [
87
+ queryValue,
88
+ expectedValue,
89
+ expectedOptions.multipleRangeExpansionLimit,
90
+ options.calchemy,
91
+ options.parseContext
92
+ ]);
93
+ function updateInputValue(nextValue) {
94
+ if (options.inputValue === void 0) {
95
+ setUncontrolledInputValue(nextValue);
96
+ }
97
+ options.onInputValueChange?.(nextValue);
98
+ }
99
+ function getActiveInlineCompletion() {
100
+ if (!fieldInputActive || queryPending) {
101
+ return null;
102
+ }
103
+ return settledInlineCompletion;
104
+ }
105
+ function setInputMode(nextMode) {
106
+ if (options.inputMode === void 0) {
107
+ setUncontrolledInputMode(nextMode);
108
+ }
109
+ options.onInputModeChange?.(nextMode);
110
+ }
111
+ function acceptCompletion() {
112
+ const activeCompletion = getActiveInlineCompletion();
113
+ if (!activeCompletion) {
114
+ return;
115
+ }
116
+ updateInputValue(composeInlineCompletion(inputValue, activeCompletion));
117
+ }
118
+ function selectCandidate(candidateId) {
119
+ if (!fieldInputActive || result.status !== "ambiguous") {
120
+ return;
121
+ }
122
+ const candidate = result.candidates.find((item) => item.id === candidateId);
123
+ if (!candidate) {
124
+ return;
125
+ }
126
+ const candidateResult = {
127
+ status: "valid",
128
+ input: inputValue,
129
+ value: candidate.value,
130
+ candidates: [candidate],
131
+ corrections: result.corrections,
132
+ warnings: result.warnings
133
+ };
134
+ const resolvedCandidateResult = resolveExpectedDateValue(candidateResult, expectedValue, expectedOptions);
135
+ if (resolvedCandidateResult.status !== "valid") {
136
+ return;
137
+ }
138
+ updateValue(resolvedCandidateResult.value, resolvedCandidateResult);
139
+ }
140
+ function selectDate(nextValue) {
141
+ const nextResult = {
142
+ status: "valid",
143
+ input: inputValue,
144
+ value: nextValue,
145
+ candidates: [],
146
+ corrections: [],
147
+ warnings: []
148
+ };
149
+ const resolvedResult = resolveExpectedDateValue(nextResult, expectedValue, expectedOptions);
150
+ if (resolvedResult.status !== "valid") {
151
+ return;
152
+ }
153
+ updateValue(resolvedResult.value, resolvedResult);
154
+ if (resolvedResult.value.kind === "single") {
155
+ updateInputValue(resolvedResult.value.date.toString());
156
+ }
157
+ }
158
+ return {
159
+ calchemy: options.calchemy,
160
+ parseContext: options.parseContext,
161
+ inputValue,
162
+ value,
163
+ result: expectedResult,
164
+ expectedValue,
165
+ inputMode,
166
+ valueKindMismatch,
167
+ inlineCompletion,
168
+ setInputValue: updateInputValue,
169
+ setInputMode,
170
+ acceptCompletion,
171
+ selectCandidate,
172
+ selectDate,
173
+ getInputProps() {
174
+ const activeCompletion = getActiveInlineCompletion();
175
+ return {
176
+ value: inputValue,
177
+ readOnly: !fieldInputActive,
178
+ onChange(event) {
179
+ if (!fieldInputActive) {
180
+ return;
181
+ }
182
+ updateInputValue(event.currentTarget.value);
183
+ },
184
+ onKeyDown(event) {
185
+ if (!fieldInputActive) {
186
+ return;
187
+ }
188
+ if (event.key === "Tab" && activeCompletion) {
189
+ event.preventDefault();
190
+ acceptCompletion();
191
+ }
192
+ },
193
+ "aria-invalid": expectedResult.status === "invalid",
194
+ ...fieldInputActive && inlineCompletion ? {
195
+ "aria-description": formatInlineCompletionDescription(
196
+ inputValue,
197
+ inlineCompletion
198
+ )
199
+ } : {},
200
+ "calchemy-status": valueKindMismatch ? "kind-mismatch" : expectedResult.status,
201
+ "calchemy-expected-value": expectedValue,
202
+ "calchemy-value-kind": expectedResult.status === "valid" ? expectedResult.value.kind : void 0
203
+ };
204
+ }
205
+ };
206
+ }
207
+
208
+ // src/components/calendar/Calendar.tsx
209
+ import { useEffect as useEffect3, useLayoutEffect, useMemo as useMemo4, useRef as useRef2, useState as useState4 } from "react";
210
+
211
+ // src/components/calendar/CalendarGrid.tsx
212
+ import { useMemo as useMemo3 } from "react";
213
+
214
+ // src/components/calendar/calendar-period-drag.tsx
215
+ import {
216
+ createContext,
217
+ useCallback,
218
+ useContext,
219
+ useMemo as useMemo2,
220
+ useRef,
221
+ useState as useState3
222
+ } from "react";
223
+ import { jsx } from "react/jsx-runtime";
224
+ var CalendarPeriodDragContext = createContext(null);
225
+ var multipleDragSurfaceStyle = {
226
+ position: "relative",
227
+ userSelect: "none",
228
+ WebkitUserSelect: "none",
229
+ touchAction: "none"
230
+ };
231
+ var multiplePeriodListDragSurfaceStyle = {
232
+ ...multipleDragSurfaceStyle,
233
+ position: "relative",
234
+ background: "transparent"
235
+ };
236
+ function useOptionalCalendarPeriodDrag() {
237
+ return useContext(CalendarPeriodDragContext);
238
+ }
239
+ function useCalendarPeriodDragSurface(dragSelection = true, surfaceElementRef) {
240
+ const calendar = useCalchemyCalendar();
241
+ const multipleSelection = dragSelection && calendar.calchemy.expectedValue === "multiple";
242
+ const surfaceRef = useRef(null);
243
+ const dayCells = useRef(/* @__PURE__ */ new Map());
244
+ const suppressClickRef = useRef(false);
245
+ const dragStateRef = useRef(null);
246
+ const dragPreviewFrameRef = useRef(null);
247
+ const dragGestureCleanupRef = useRef(null);
248
+ const [dragState, setDragState] = useState3(null);
249
+ const [dragRectangle, setDragRectangle] = useState3(null);
250
+ const previewSelectedKeys = useMemo2(
251
+ () => new Set(dragState?.previewKeys ?? []),
252
+ [dragState]
253
+ );
254
+ const acquireDragGestureLock = useCallback(() => {
255
+ if (dragGestureCleanupRef.current || typeof document === "undefined") {
256
+ return;
257
+ }
258
+ const preventGestureDefault = (event) => {
259
+ event.preventDefault();
260
+ };
261
+ const clearDocumentSelection = () => {
262
+ document.getSelection()?.removeAllRanges();
263
+ };
264
+ const previousBodyUserSelect = document.body.style.userSelect;
265
+ const previousDocumentUserSelect = document.documentElement.style.userSelect;
266
+ document.body.style.userSelect = "none";
267
+ document.documentElement.style.userSelect = "none";
268
+ document.addEventListener("pointermove", preventGestureDefault, { capture: true, passive: false });
269
+ document.addEventListener("touchmove", preventGestureDefault, { capture: true, passive: false });
270
+ document.addEventListener("wheel", preventGestureDefault, { capture: true, passive: false });
271
+ document.addEventListener("selectstart", preventGestureDefault, { capture: true });
272
+ document.addEventListener("dragstart", preventGestureDefault, { capture: true });
273
+ clearDocumentSelection();
274
+ dragGestureCleanupRef.current = () => {
275
+ document.removeEventListener("pointermove", preventGestureDefault, { capture: true });
276
+ document.removeEventListener("touchmove", preventGestureDefault, { capture: true });
277
+ document.removeEventListener("wheel", preventGestureDefault, { capture: true });
278
+ document.removeEventListener("selectstart", preventGestureDefault, { capture: true });
279
+ document.removeEventListener("dragstart", preventGestureDefault, { capture: true });
280
+ document.body.style.userSelect = previousBodyUserSelect;
281
+ document.documentElement.style.userSelect = previousDocumentUserSelect;
282
+ clearDocumentSelection();
283
+ dragGestureCleanupRef.current = null;
284
+ };
285
+ }, []);
286
+ const releaseDragGestureLock = useCallback(() => {
287
+ dragGestureCleanupRef.current?.();
288
+ if (dragPreviewFrameRef.current !== null) {
289
+ cancelAnimationFrame(dragPreviewFrameRef.current);
290
+ dragPreviewFrameRef.current = null;
291
+ }
292
+ }, []);
293
+ const scheduleDragPreviewUpdate = useCallback((nextDragState) => {
294
+ dragStateRef.current = nextDragState;
295
+ if (dragPreviewFrameRef.current !== null) {
296
+ return;
297
+ }
298
+ dragPreviewFrameRef.current = requestAnimationFrame(() => {
299
+ setDragState(dragStateRef.current);
300
+ dragPreviewFrameRef.current = null;
301
+ });
302
+ }, []);
303
+ const registerDay = useCallback((element, date, disabled) => {
304
+ const key = date.toString();
305
+ if (element) {
306
+ dayCells.current.set(key, { date, disabled, element });
307
+ return;
308
+ }
309
+ dayCells.current.delete(key);
310
+ }, []);
311
+ const getDayCellFromTarget = useCallback((target) => {
312
+ if (!(target instanceof Element)) {
313
+ return null;
314
+ }
315
+ const button = target.closest("[calchemy-date]");
316
+ if (!(button instanceof HTMLButtonElement)) {
317
+ return null;
318
+ }
319
+ for (const cell of dayCells.current.values()) {
320
+ if (cell.element === button) {
321
+ return cell;
322
+ }
323
+ }
324
+ return null;
325
+ }, []);
326
+ const commitMultipleSelection = useCallback(
327
+ (keys, fallbackDates = []) => {
328
+ const dates = resolveDateKeys(keys, dayCells.current, fallbackDates);
329
+ calendar.selectValue({
330
+ kind: "multiple",
331
+ dates
332
+ });
333
+ },
334
+ [calendar]
335
+ );
336
+ const endDragGesture = useCallback(
337
+ (pointerId) => {
338
+ if (pointerId !== void 0) {
339
+ surfaceRef.current?.releasePointerCapture?.(pointerId);
340
+ }
341
+ releaseDragGestureLock();
342
+ dragStateRef.current = null;
343
+ setDragState(null);
344
+ setDragRectangle(null);
345
+ },
346
+ [releaseDragGestureLock]
347
+ );
348
+ const handlePointerMove = useCallback(
349
+ (event) => {
350
+ const activeDrag = dragStateRef.current;
351
+ if (!multipleSelection || !activeDrag || event.pointerId !== activeDrag.pointerId) {
352
+ return;
353
+ }
354
+ event.preventDefault();
355
+ const current = getPointerPoint(event);
356
+ setDragRectangle({ start: activeDrag.start, current });
357
+ const dragRect = getDragRect(activeDrag.start, current);
358
+ const clipRect = getDragClipRect(surfaceRef.current);
359
+ const gestureKeys = getIntersectingDateKeys(
360
+ dayCells.current,
361
+ dragRect,
362
+ activeDrag.cellBounds,
363
+ clipRect
364
+ );
365
+ const baseKeys = activeDrag.baseDates.map((date) => date.toString());
366
+ const previewKeys = toggleDateKeys(baseKeys, gestureKeys);
367
+ const hasMoved = activeDrag.hasMoved || hasPointerMoved(activeDrag.start, current);
368
+ scheduleDragPreviewUpdate({
369
+ ...activeDrag,
370
+ current,
371
+ previewKeys,
372
+ hasMoved
373
+ });
374
+ },
375
+ [multipleSelection, scheduleDragPreviewUpdate]
376
+ );
377
+ const handlePointerDownCapture = useCallback(
378
+ (event) => {
379
+ if (!multipleSelection || event.button !== 0 || !event.isPrimary) {
380
+ return;
381
+ }
382
+ const baseDates = getMultipleDates(calendar.selected);
383
+ event.preventDefault();
384
+ if (typeof document !== "undefined") {
385
+ document.getSelection()?.removeAllRanges();
386
+ }
387
+ surfaceRef.current = surfaceElementRef?.current ?? event.currentTarget;
388
+ surfaceRef.current?.setPointerCapture?.(event.pointerId);
389
+ acquireDragGestureLock();
390
+ const nextDragState = {
391
+ pointerId: event.pointerId,
392
+ start: getPointerPoint(event),
393
+ current: getPointerPoint(event),
394
+ baseDates,
395
+ previewKeys: baseDates.map((selectedDate) => selectedDate.toString()),
396
+ hasMoved: false,
397
+ startDayCell: getDayCellFromTarget(event.target),
398
+ cellBounds: snapshotCellBounds(dayCells.current)
399
+ };
400
+ dragStateRef.current = nextDragState;
401
+ setDragState(nextDragState);
402
+ setDragRectangle({
403
+ start: nextDragState.start,
404
+ current: nextDragState.current
405
+ });
406
+ },
407
+ [acquireDragGestureLock, calendar.selected, getDayCellFromTarget, multipleSelection, surfaceElementRef]
408
+ );
409
+ const handlePointerUp = useCallback(
410
+ (event) => {
411
+ const activeDrag = dragStateRef.current;
412
+ if (!multipleSelection || !activeDrag || event.pointerId !== activeDrag.pointerId) {
413
+ return;
414
+ }
415
+ if (dragPreviewFrameRef.current !== null) {
416
+ cancelAnimationFrame(dragPreviewFrameRef.current);
417
+ dragPreviewFrameRef.current = null;
418
+ setDragState(activeDrag);
419
+ }
420
+ event.preventDefault();
421
+ suppressClickRef.current = true;
422
+ setTimeout(() => {
423
+ suppressClickRef.current = false;
424
+ }, 0);
425
+ if (activeDrag.hasMoved) {
426
+ commitMultipleSelection(activeDrag.previewKeys, activeDrag.baseDates);
427
+ } else {
428
+ const dayCell = activeDrag.startDayCell;
429
+ if (dayCell && !dayCell.disabled) {
430
+ const selectedDates = getMultipleDates(calendar.selected);
431
+ const nextKeys = toggleDateKeys(
432
+ selectedDates.map((selectedDate) => selectedDate.toString()),
433
+ [dayCell.date.toString()]
434
+ );
435
+ commitMultipleSelection(nextKeys, selectedDates.concat(dayCell.date));
436
+ }
437
+ }
438
+ endDragGesture(event.pointerId);
439
+ const focused = document.activeElement;
440
+ if (focused instanceof HTMLElement && event.currentTarget.contains(focused)) {
441
+ focused.blur();
442
+ }
443
+ },
444
+ [calendar.selected, commitMultipleSelection, endDragGesture, getDayCellFromTarget, multipleSelection]
445
+ );
446
+ const handlePointerCancel = useCallback(
447
+ (event) => {
448
+ const activeDrag = dragStateRef.current;
449
+ if (!activeDrag || event.pointerId !== activeDrag.pointerId) {
450
+ return;
451
+ }
452
+ endDragGesture(event.pointerId);
453
+ },
454
+ [endDragGesture]
455
+ );
456
+ return useMemo2(() => {
457
+ if (!multipleSelection) {
458
+ return null;
459
+ }
460
+ return {
461
+ multipleSelection,
462
+ dragState,
463
+ dragRectangle,
464
+ previewSelectedKeys,
465
+ suppressClickRef,
466
+ surfaceRef,
467
+ registerDay,
468
+ handlePointerDownCapture,
469
+ handlePointerMove,
470
+ handlePointerUp,
471
+ handlePointerCancel
472
+ };
473
+ }, [
474
+ dragRectangle,
475
+ dragState,
476
+ handlePointerCancel,
477
+ handlePointerDownCapture,
478
+ handlePointerMove,
479
+ handlePointerUp,
480
+ multipleSelection,
481
+ previewSelectedKeys,
482
+ registerDay
483
+ ]);
484
+ }
485
+ function CalendarDragRectangleOverlay({
486
+ dragRectangle: dragRectangleProp,
487
+ surfaceRef: surfaceRefProp
488
+ } = {}) {
489
+ const drag = useOptionalCalendarPeriodDrag();
490
+ const dragRectangle = dragRectangleProp ?? drag?.dragRectangle ?? null;
491
+ const surfaceRef = surfaceRefProp ?? drag?.surfaceRef;
492
+ if (!dragRectangle) {
493
+ return null;
494
+ }
495
+ const surface = surfaceRef?.current;
496
+ if (!surface) {
497
+ return null;
498
+ }
499
+ const surfaceRect = surface.getBoundingClientRect();
500
+ const left = Math.min(dragRectangle.start.x, dragRectangle.current.x) - surfaceRect.left;
501
+ const top = Math.min(dragRectangle.start.y, dragRectangle.current.y) - surfaceRect.top;
502
+ const width = Math.abs(dragRectangle.current.x - dragRectangle.start.x);
503
+ const height = Math.abs(dragRectangle.current.y - dragRectangle.start.y);
504
+ return /* @__PURE__ */ jsx(
505
+ "div",
506
+ {
507
+ "aria-hidden": "true",
508
+ "calchemy-drag-rect": "",
509
+ style: {
510
+ position: "absolute",
511
+ left,
512
+ top,
513
+ width,
514
+ height,
515
+ pointerEvents: "none",
516
+ boxSizing: "border-box"
517
+ }
518
+ }
519
+ );
520
+ }
521
+ function CalendarPeriodDragProvider({ value, children }) {
522
+ return /* @__PURE__ */ jsx(CalendarPeriodDragContext.Provider, { value, children });
523
+ }
524
+ function getMultipleDates(value) {
525
+ return value?.kind === "multiple" ? value.dates : [];
526
+ }
527
+ function getSelectedDateKeys(value) {
528
+ return getMultipleDates(value).map((date) => date.toString());
529
+ }
530
+ function toggleDateKeys(baseKeys, toggledKeys) {
531
+ const next = new Set(baseKeys);
532
+ for (const key of toggledKeys) {
533
+ if (next.has(key)) {
534
+ next.delete(key);
535
+ } else {
536
+ next.add(key);
537
+ }
538
+ }
539
+ return Array.from(next).sort();
540
+ }
541
+ function getPointerPoint(event) {
542
+ return { x: event.clientX, y: event.clientY };
543
+ }
544
+ function hasPointerMoved(start, current) {
545
+ return Math.abs(start.x - current.x) > 2 || Math.abs(start.y - current.y) > 2;
546
+ }
547
+ function getDragRect(start, current) {
548
+ const left = Math.min(start.x, current.x);
549
+ const right = Math.max(start.x, current.x);
550
+ const top = Math.min(start.y, current.y);
551
+ const bottom = Math.max(start.y, current.y);
552
+ return {
553
+ left,
554
+ right,
555
+ top,
556
+ bottom,
557
+ x: left,
558
+ y: top,
559
+ width: right - left,
560
+ height: bottom - top,
561
+ toJSON: () => ({})
562
+ };
563
+ }
564
+ function snapshotCellBounds(cells) {
565
+ const bounds = /* @__PURE__ */ new Map();
566
+ for (const [key, cell] of cells) {
567
+ const rect = cell.element.getBoundingClientRect();
568
+ bounds.set(key, {
569
+ left: rect.left,
570
+ right: rect.right,
571
+ top: rect.top,
572
+ bottom: rect.bottom
573
+ });
574
+ }
575
+ return bounds;
576
+ }
577
+ function getDragClipRect(surface) {
578
+ if (!surface) {
579
+ return null;
580
+ }
581
+ const scrollContainer = surface.closest("[calchemy-scroll]");
582
+ if (!(scrollContainer instanceof HTMLElement)) {
583
+ return null;
584
+ }
585
+ const rect = scrollContainer.getBoundingClientRect();
586
+ return {
587
+ left: rect.left,
588
+ right: rect.right,
589
+ top: rect.top,
590
+ bottom: rect.bottom
591
+ };
592
+ }
593
+ function clipBoundsToRect(bounds, clip) {
594
+ const left = Math.max(bounds.left, clip.left);
595
+ const right = Math.min(bounds.right, clip.right);
596
+ const top = Math.max(bounds.top, clip.top);
597
+ const bottom = Math.min(bounds.bottom, clip.bottom);
598
+ if (left > right || top > bottom) {
599
+ return null;
600
+ }
601
+ return { left, right, top, bottom };
602
+ }
603
+ function getIntersectingDateKeys(cells, dragRect, cellBounds, clipRect = null) {
604
+ const clippedDragRect = clipRect ? clipBoundsToRect(dragRect, clipRect) : dragRect;
605
+ if (!clippedDragRect) {
606
+ return [];
607
+ }
608
+ return Array.from(cells.entries()).filter(([key, cell]) => {
609
+ const bounds = cellBounds.get(key);
610
+ if (!bounds || cell.disabled) {
611
+ return false;
612
+ }
613
+ const visibleBounds = clipRect ? clipBoundsToRect(bounds, clipRect) : bounds;
614
+ return visibleBounds && rectsIntersect(clippedDragRect, visibleBounds);
615
+ }).map(([key]) => key);
616
+ }
617
+ function rectsIntersect(left, right) {
618
+ return left.left <= right.right && left.right >= right.left && left.top <= right.bottom && left.bottom >= right.top;
619
+ }
620
+ function resolveDateKeys(keys, cells, fallbackDates) {
621
+ const fallbackByKey = new Map(fallbackDates.map((date) => [date.toString(), date]));
622
+ return Array.from(keys).sort().flatMap((key) => {
623
+ const date = cells.get(key)?.date ?? fallbackByKey.get(key);
624
+ return date ? [date] : [];
625
+ });
626
+ }
627
+
628
+ // src/components/calendar/date-model.ts
629
+ var defaultDateOrderPreference = ["DMY", "MDY", "YMD"];
630
+ function getFirstVisibleCalendarPeriod(calendar) {
631
+ const period = calendar.visiblePeriods[0];
632
+ if (!period) {
633
+ throw new Error("Calchemy.Calendar requires at least one visible generated period.");
634
+ }
635
+ return period;
636
+ }
637
+ function getCalendarDayState(calendar, period, date) {
638
+ const bounded = isDateWithinBounds(date, calendar.bounds);
639
+ const namedDates = getNamedDatesForDate(calendar, date);
640
+ const disabled = !bounded || (calendar.isDateDisabled?.(date, calendar) ?? false);
641
+ return {
642
+ outside: isBefore(date, period.start) || isAfter(date, period.end),
643
+ selected: isSelectedDate(calendar.selected, date),
644
+ today: calendar.today.equals(date),
645
+ weekend: isWeekend(date),
646
+ firstOfPeriod: date.equals(period.start),
647
+ lastOfPeriod: date.equals(period.end),
648
+ bounded,
649
+ disabled,
650
+ namedDates
651
+ };
652
+ }
653
+ function parseCalendarDuration(value, propName) {
654
+ const hasMonths = "months" in value;
655
+ const hasWeeks = "weeks" in value;
656
+ if (hasMonths === hasWeeks) {
657
+ throw new Error(`Calchemy.Calendar ${propName} must include exactly one of months or weeks.`);
658
+ }
659
+ const count = hasMonths ? value.months : value.weeks;
660
+ if (!Number.isInteger(count) || count < 1) {
661
+ throw new Error(`Calchemy.Calendar ${propName} must be a positive integer duration.`);
662
+ }
663
+ return {
664
+ unit: hasMonths ? "month" : "week",
665
+ count
666
+ };
667
+ }
668
+ function validateCalendarBounds(bounds) {
669
+ if (bounds?.start && bounds.end && isAfter(bounds.start, bounds.end)) {
670
+ throw new Error("Calchemy.Calendar bounds.start must be on or before bounds.end.");
671
+ }
672
+ }
673
+ function clampDateToBounds(date, bounds) {
674
+ if (bounds?.start && isBefore(date, bounds.start)) {
675
+ return bounds.start;
676
+ }
677
+ if (bounds?.end && isAfter(date, bounds.end)) {
678
+ return bounds.end;
679
+ }
680
+ return date;
681
+ }
682
+ function isDateWithinBounds(date, bounds) {
683
+ return (!bounds?.start || !isBefore(date, bounds.start)) && (!bounds?.end || !isAfter(date, bounds.end));
684
+ }
685
+ function periodIntersectsBounds(period, bounds) {
686
+ return (!bounds?.start || !isBefore(period.end, bounds.start)) && (!bounds?.end || !isAfter(period.start, bounds.end));
687
+ }
688
+ function getCalendarPeriodAtOffset(anchor, period, weekStartsOn, locale, offset) {
689
+ const firstStart = period.unit === "month" ? startOfMonth(anchor) : startOfWeek(anchor, weekStartsOn);
690
+ const start = addCalendarPeriod(firstStart, period.unit, offset);
691
+ const end = period.unit === "month" ? endOfMonth(start) : start.add({ days: 6 });
692
+ return {
693
+ id: `${period.unit}-${start.toString()}`,
694
+ unit: period.unit,
695
+ index: offset,
696
+ start,
697
+ end,
698
+ label: formatPeriodLabel(start, end, period.unit, locale)
699
+ };
700
+ }
701
+ function getInitialPeriodExtensions(period) {
702
+ return {
703
+ before: period.count * 6,
704
+ after: period.count * 3
705
+ };
706
+ }
707
+ function getSelectedValue(state) {
708
+ const resultValue = getResultValue(state);
709
+ return state.value ?? resultValue;
710
+ }
711
+ function getDateValueAnchor(value) {
712
+ if (!value) {
713
+ return null;
714
+ }
715
+ switch (value.kind) {
716
+ case "single":
717
+ return value.date;
718
+ case "range":
719
+ return value.start;
720
+ case "multiple":
721
+ return value.dates[0] ?? null;
722
+ }
723
+ }
724
+ function getDateValueKey(value) {
725
+ if (!value) {
726
+ return "";
727
+ }
728
+ switch (value.kind) {
729
+ case "single":
730
+ return `single:${value.date.toString()}`;
731
+ case "range":
732
+ return `range:${value.start.toString()}:${value.end.toString()}`;
733
+ case "multiple":
734
+ return `multiple:${value.dates.map((date) => date.toString()).join(",")}`;
735
+ }
736
+ }
737
+ function isDateInCalendarViewport(date, periods, visiblePeriodIndex, windowCount) {
738
+ return periods.filter(
739
+ (period) => period.index >= visiblePeriodIndex && period.index < visiblePeriodIndex + windowCount
740
+ ).some((period) => !isBefore(date, period.start) && !isAfter(date, period.end));
741
+ }
742
+ function getResultValue(state) {
743
+ return state.result.status === "valid" ? state.result.value : null;
744
+ }
745
+ function getToday(state) {
746
+ if (state.parseContext?.referenceDate) {
747
+ return state.parseContext.referenceDate;
748
+ }
749
+ const todayResult = state.calchemy.parseDate("today", state.parseContext);
750
+ if (todayResult.status === "valid" && todayResult.value.kind === "single") {
751
+ return todayResult.value.date;
752
+ }
753
+ return state.calchemy.Temporal.Now.plainDateISO(state.parseContext?.timeZone);
754
+ }
755
+ function buildCalendarPeriods(anchor, period, weekStartsOn, locale, extensions, bounds) {
756
+ const total = extensions.before + period.count + extensions.after;
757
+ return Array.from({ length: total }, (_, index) => {
758
+ const offset = index - extensions.before;
759
+ return getCalendarPeriodAtOffset(anchor, period, weekStartsOn, locale, offset);
760
+ }).filter((item) => periodIntersectsBounds(item, bounds));
761
+ }
762
+ function buildCalendarWeeks(period, weekStartsOn) {
763
+ const start = startOfWeek(period.start, weekStartsOn);
764
+ const end = endOfWeek(period.end, weekStartsOn);
765
+ const weeks = [];
766
+ let cursor = start;
767
+ while (!isAfter(cursor, end)) {
768
+ const week = Array.from({ length: 7 }, (_, index) => cursor.add({ days: index }));
769
+ weeks.push(week);
770
+ cursor = cursor.add({ days: 7 });
771
+ }
772
+ return weeks;
773
+ }
774
+ function buildWeekdays(calendar, weekdayFormat = "short") {
775
+ const sunday = startOfWeek(calendar.today, 0);
776
+ const first = startOfWeek(calendar.today, calendar.weekStartsOn);
777
+ return Array.from({ length: 7 }, (_, index) => {
778
+ const date = first.add({ days: index });
779
+ const weekdayIndex = sunday.until(date).days % 7;
780
+ return {
781
+ index: weekdayIndex,
782
+ label: date.toLocaleString(calendar.locale, { weekday: weekdayFormat }),
783
+ weekend: isWeekend(date)
784
+ };
785
+ });
786
+ }
787
+ function addCalendarPeriod(date, unit, count) {
788
+ return unit === "month" ? date.add({ months: count }) : date.add({ weeks: count });
789
+ }
790
+ function formatCalendarWindowLabel(periods, locale) {
791
+ const first = periods[0];
792
+ const last = periods.at(-1);
793
+ if (!first || !last) {
794
+ return "";
795
+ }
796
+ if (first.start.equals(last.start) && first.end.equals(last.end)) {
797
+ return first.label;
798
+ }
799
+ return `${formatDateLabel(first.start, locale)} - ${formatDateLabel(last.end, locale)}`;
800
+ }
801
+ function formatMonthLabel(date, locale) {
802
+ return date.toLocaleString(locale, { month: "long" });
803
+ }
804
+ function startOfMonth(date) {
805
+ return date.with({ day: 1 });
806
+ }
807
+ function endOfMonth(date) {
808
+ return startOfMonth(date).add({ months: 1 }).subtract({ days: 1 });
809
+ }
810
+ function startOfWeek(date, weekStartsOn) {
811
+ const temporalWeekday = weekStartsOn === 0 ? 7 : weekStartsOn;
812
+ let cursor = date;
813
+ while (cursor.dayOfWeek !== temporalWeekday) {
814
+ cursor = cursor.subtract({ days: 1 });
815
+ }
816
+ return cursor;
817
+ }
818
+ function endOfWeek(date, weekStartsOn) {
819
+ return startOfWeek(date, weekStartsOn).add({ days: 6 });
820
+ }
821
+ function isBefore(left, right) {
822
+ return left.toString() < right.toString();
823
+ }
824
+ function isAfter(left, right) {
825
+ return left.toString() > right.toString();
826
+ }
827
+ function isSelectedDate(value, date) {
828
+ if (!value) {
829
+ return false;
830
+ }
831
+ switch (value.kind) {
832
+ case "single":
833
+ return value.date.equals(date);
834
+ case "range":
835
+ return !isBefore(date, value.start) && !isAfter(date, value.end);
836
+ case "multiple":
837
+ return value.dates.some((selectedDate) => selectedDate.equals(date));
838
+ }
839
+ }
840
+ function isWeekend(date) {
841
+ return date.dayOfWeek === 6 || date.dayOfWeek === 7;
842
+ }
843
+ function getNamedDatesForDate(calendar, date) {
844
+ if (!calendar.namedDates) {
845
+ return [];
846
+ }
847
+ const context = getResolvedNamedDateContext(calendar);
848
+ return calendar.calchemy.calchemy.namedDatesVocabulary.filter((entry) => {
849
+ if (calendar.namedDates === "holidays" && !entry.isHoliday) {
850
+ return false;
851
+ }
852
+ return entry.resolveDate({ year: date.year, context })?.equals(date) ?? false;
853
+ });
854
+ }
855
+ function getResolvedNamedDateContext(calendar) {
856
+ const context = calendar.calchemy.parseContext;
857
+ return {
858
+ referenceDate: context?.referenceDate ?? calendar.calchemy.calchemy.Temporal.Now.plainDateISO(context?.timeZone),
859
+ locale: context?.locale ?? "en-US",
860
+ weekStartsOn: context?.weekStartsOn ?? 0,
861
+ dateOrderPreference: normalizeDateOrderPreference(context?.dateOrderPreference),
862
+ lastNDaysIncludesToday: context?.lastNDaysIncludesToday ?? true
863
+ };
864
+ }
865
+ function normalizeDateOrderPreference(value) {
866
+ if (!value || value.length === 0) {
867
+ return defaultDateOrderPreference;
868
+ }
869
+ return Array.from(new Set(value));
870
+ }
871
+ function formatPeriodLabel(start, end, unit, locale) {
872
+ if (unit === "month") {
873
+ return formatDateLabel(start, locale);
874
+ }
875
+ return `${start.toLocaleString(locale, { month: "short", day: "numeric" })} - ${end.toLocaleString(locale, {
876
+ month: "short",
877
+ day: "numeric",
878
+ year: "numeric"
879
+ })}`;
880
+ }
881
+ function formatDateLabel(date, locale) {
882
+ return date.toLocaleString(locale, { month: "long", year: "numeric" });
883
+ }
884
+
885
+ // src/components/calendar/CalendarGrid.tsx
886
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
887
+ var defaultCalendarWeekdayFormat = "short";
888
+ function CalendarWeekdays({
889
+ weekdayFormat = defaultCalendarWeekdayFormat,
890
+ ...props
891
+ }) {
892
+ const calendar = useCalchemyCalendar();
893
+ const weekdays = buildWeekdays(calendar, weekdayFormat);
894
+ return /* @__PURE__ */ jsx2("div", { ...props, "calchemy-days": "", children: weekdays.map((weekday) => /* @__PURE__ */ jsx2(
895
+ "div",
896
+ {
897
+ "calchemy-weekday": "",
898
+ "calchemy-weekend": weekday.weekend ? "" : void 0,
899
+ children: weekday.label
900
+ },
901
+ weekday.index
902
+ )) });
903
+ }
904
+ function CalendarGrid({
905
+ showBookends = false,
906
+ dragSelection = true,
907
+ style,
908
+ onPointerDownCapture,
909
+ onPointerMove,
910
+ onPointerUp,
911
+ onPointerCancel,
912
+ onDragStart,
913
+ ...props
914
+ }) {
915
+ const calendar = useCalchemyCalendar();
916
+ const period = useCalendarPeriod() ?? getFirstVisibleCalendarPeriod(calendar);
917
+ const weeks = buildCalendarWeeks(period, calendar.weekStartsOn);
918
+ const parentDrag = useOptionalCalendarPeriodDrag();
919
+ const localDrag = useCalendarPeriodDragSurface(
920
+ calendar.editable && dragSelection && parentDrag === null
921
+ );
922
+ const drag = parentDrag ?? localDrag;
923
+ const multipleSelection = Boolean(drag?.multipleSelection);
924
+ const useLocalDragHandlers = Boolean(drag && parentDrag === null);
925
+ const committedSelectedKeys = useMemo3(() => new Set(getSelectedDateKeys(calendar.selected)), [calendar.selected]);
926
+ const previewSelectedKeys = drag?.previewSelectedKeys ?? /* @__PURE__ */ new Set();
927
+ const dragState = drag?.dragState ?? null;
928
+ return /* @__PURE__ */ jsxs(
929
+ "div",
930
+ {
931
+ ...props,
932
+ "calchemy-grid": "",
933
+ "calchemy-multiple-drag": useLocalDragHandlers ? "" : void 0,
934
+ "calchemy-dragging": useLocalDragHandlers && dragState ? "" : void 0,
935
+ style: useLocalDragHandlers ? { ...multipleDragSurfaceStyle, ...style } : style,
936
+ onPointerDownCapture: useLocalDragHandlers ? (event) => {
937
+ drag?.handlePointerDownCapture(event);
938
+ onPointerDownCapture?.(event);
939
+ } : onPointerDownCapture,
940
+ onPointerMove: useLocalDragHandlers ? (event) => {
941
+ drag?.handlePointerMove(event);
942
+ onPointerMove?.(event);
943
+ } : onPointerMove,
944
+ onPointerUp: useLocalDragHandlers ? (event) => {
945
+ drag?.handlePointerUp(event);
946
+ onPointerUp?.(event);
947
+ } : onPointerUp,
948
+ onPointerCancel: useLocalDragHandlers ? (event) => {
949
+ drag?.handlePointerCancel(event);
950
+ onPointerCancel?.(event);
951
+ } : onPointerCancel,
952
+ onDragStart: useLocalDragHandlers ? (event) => {
953
+ event.preventDefault();
954
+ onDragStart?.(event);
955
+ } : onDragStart,
956
+ children: [
957
+ useLocalDragHandlers && drag?.dragRectangle ? /* @__PURE__ */ jsx2(CalendarDragRectangleOverlay, { dragRectangle: drag.dragRectangle, surfaceRef: drag.surfaceRef }) : null,
958
+ weeks.map((week) => /* @__PURE__ */ jsx2("div", { "calchemy-week": "", children: week.map((date) => {
959
+ const dayState = getCalendarDayState(calendar, period, date);
960
+ const namedDateLabels = dayState.namedDates.map((item) => item.value).join(", ");
961
+ const dateKey = date.toString();
962
+ const selected = multipleSelection ? dragState ? previewSelectedKeys.has(dateKey) : committedSelectedKeys.has(dateKey) : dayState.selected;
963
+ const dragPreview = Boolean(dragState && selected !== dayState.selected);
964
+ if (dayState.outside && !showBookends) {
965
+ return /* @__PURE__ */ jsx2(
966
+ "div",
967
+ {
968
+ "aria-hidden": "true",
969
+ "calchemy-cell": "",
970
+ "calchemy-blank": ""
971
+ },
972
+ date.toString()
973
+ );
974
+ }
975
+ return /* @__PURE__ */ jsx2(
976
+ "button",
977
+ {
978
+ type: "button",
979
+ ref: (element) => drag?.registerDay(element, date, dayState.disabled || !calendar.editable),
980
+ "calchemy-date": "",
981
+ "calchemy-selected": selected ? "" : void 0,
982
+ "calchemy-drag-preview": dragPreview ? "" : void 0,
983
+ "calchemy-drag-preview-selected": dragPreview && selected ? "" : void 0,
984
+ "calchemy-drag-preview-deselected": dragPreview && !selected ? "" : void 0,
985
+ "calchemy-today": dayState.today ? "" : void 0,
986
+ "calchemy-weekend": dayState.weekend ? "" : void 0,
987
+ "calchemy-outside": dayState.outside ? "" : void 0,
988
+ "calchemy-first-of-period": dayState.firstOfPeriod ? "" : void 0,
989
+ "calchemy-last-of-period": dayState.lastOfPeriod ? "" : void 0,
990
+ "calchemy-disabled": dayState.disabled ? "" : void 0,
991
+ "calchemy-out-of-bounds": !dayState.bounded ? "" : void 0,
992
+ "calchemy-named-date": dayState.namedDates.length > 0 ? "" : void 0,
993
+ "calchemy-holiday": dayState.namedDates.some((item) => item.isHoliday) ? "" : void 0,
994
+ "calchemy-named-date-labels": namedDateLabels || void 0,
995
+ "aria-disabled": !calendar.editable && !dayState.disabled ? true : void 0,
996
+ disabled: dayState.disabled,
997
+ onClick: () => {
998
+ if (!calendar.editable) {
999
+ return;
1000
+ }
1001
+ if (drag?.suppressClickRef.current) {
1002
+ drag.suppressClickRef.current = false;
1003
+ return;
1004
+ }
1005
+ if (!dayState.disabled) {
1006
+ if (multipleSelection) {
1007
+ const selectedDates = getMultipleDates(calendar.selected);
1008
+ const nextKeys = toggleDateKeys(
1009
+ selectedDates.map((selectedDate) => selectedDate.toString()),
1010
+ [dateKey]
1011
+ );
1012
+ const nextDates = nextKeys.flatMap((key) => {
1013
+ if (key === dateKey) {
1014
+ return [date];
1015
+ }
1016
+ const existing = selectedDates.find((selectedDate) => selectedDate.toString() === key);
1017
+ return existing ? [existing] : [];
1018
+ });
1019
+ calendar.selectValue({
1020
+ kind: "multiple",
1021
+ dates: nextDates
1022
+ });
1023
+ } else {
1024
+ calendar.selectDate(date);
1025
+ }
1026
+ }
1027
+ },
1028
+ children: date.day
1029
+ },
1030
+ date.toString()
1031
+ );
1032
+ }) }, week[0]?.toString()))
1033
+ ]
1034
+ }
1035
+ );
1036
+ }
1037
+
1038
+ // src/components/calendar/Calendar.tsx
1039
+ import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
1040
+ var defaultCalendarPeriod = { months: 1 };
1041
+ function Calendar({
1042
+ period = defaultCalendarPeriod,
1043
+ bounds,
1044
+ isDateDisabled,
1045
+ namedDates,
1046
+ children,
1047
+ ...divProps
1048
+ }) {
1049
+ const state = useCalchemyContext();
1050
+ const editable = state.inputMode === "calendar";
1051
+ validateCalendarBounds(bounds);
1052
+ const parsedPeriod = useMemo4(() => parseCalendarDuration(period, "period"), [period]);
1053
+ const selected = getSelectedValue(state);
1054
+ const today = getToday(state);
1055
+ const [periodAnchor, setPeriodAnchor] = useState4(
1056
+ () => clampDateToBounds(getDateValueAnchor(selected) ?? today, bounds)
1057
+ );
1058
+ const [navigationAnchor, setNavigationAnchor] = useState4(null);
1059
+ const [periodExtensions, setPeriodExtensions] = useState4(() => getInitialPeriodExtensions(parsedPeriod));
1060
+ const [visiblePeriodIndex, setScrolledVisiblePeriodIndex] = useState4(null);
1061
+ const prevInputValueRef = useRef2(state.inputValue);
1062
+ const prevSelectedKeyRef = useRef2(getDateValueKey(selected));
1063
+ const prevExpectedValueRef = useRef2(state.expectedValue);
1064
+ useEffect3(() => {
1065
+ if (prevExpectedValueRef.current === state.expectedValue) {
1066
+ return;
1067
+ }
1068
+ prevExpectedValueRef.current = state.expectedValue;
1069
+ setScrolledVisiblePeriodIndex(null);
1070
+ setNavigationAnchor(null);
1071
+ setPeriodAnchor(clampDateToBounds(getDateValueAnchor(selected) ?? today, bounds));
1072
+ prevInputValueRef.current = state.inputValue;
1073
+ prevSelectedKeyRef.current = getDateValueKey(selected);
1074
+ }, [bounds, selected, state.expectedValue, state.inputValue, today]);
1075
+ const periodAnchorKey = periodAnchor.toString();
1076
+ const activeVisiblePeriodIndex = visiblePeriodIndex?.anchor === periodAnchorKey && visiblePeriodIndex.inputValue === state.inputValue ? visiblePeriodIndex.index : 0;
1077
+ const weekStartsOn = state.parseContext?.weekStartsOn ?? 0;
1078
+ const locale = state.parseContext?.locale ?? "en-US";
1079
+ const periods = useMemo4(
1080
+ () => buildCalendarPeriods(periodAnchor, parsedPeriod, weekStartsOn, locale, periodExtensions, bounds),
1081
+ [periodAnchor, parsedPeriod.count, parsedPeriod.unit, weekStartsOn, locale, periodExtensions, bounds]
1082
+ );
1083
+ const visiblePeriods = useMemo4(
1084
+ () => {
1085
+ const visible = periods.filter(
1086
+ (item) => item.index >= activeVisiblePeriodIndex && item.index < activeVisiblePeriodIndex + parsedPeriod.count
1087
+ );
1088
+ return visible.length > 0 ? visible : periods.slice(0, parsedPeriod.count);
1089
+ },
1090
+ [periods, activeVisiblePeriodIndex, parsedPeriod.count]
1091
+ );
1092
+ const visiblePeriodAnchor = visiblePeriods[0]?.start ?? periodAnchor;
1093
+ useLayoutEffect(() => {
1094
+ const inputChanged = prevInputValueRef.current !== state.inputValue;
1095
+ prevInputValueRef.current = state.inputValue;
1096
+ const selectedKey = getDateValueKey(selected);
1097
+ const selectionChanged = prevSelectedKeyRef.current !== selectedKey;
1098
+ prevSelectedKeyRef.current = selectedKey;
1099
+ if (!inputChanged && !selectionChanged) {
1100
+ return;
1101
+ }
1102
+ if (selectionChanged && editable) {
1103
+ return;
1104
+ }
1105
+ const selectionAnchor = clampDateToBounds(getDateValueAnchor(selected) ?? today, bounds);
1106
+ const inputReflectsCalendarSelection = state.inputValue === selectionAnchor.toString();
1107
+ const shouldRevealSelection = inputChanged && !inputReflectsCalendarSelection;
1108
+ if (!shouldRevealSelection && isDateInCalendarViewport(
1109
+ selectionAnchor,
1110
+ periods,
1111
+ activeVisiblePeriodIndex,
1112
+ parsedPeriod.count
1113
+ )) {
1114
+ return;
1115
+ }
1116
+ setPeriodAnchor((current) => current.equals(selectionAnchor) ? current : selectionAnchor);
1117
+ setScrolledVisiblePeriodIndex(null);
1118
+ }, [
1119
+ activeVisiblePeriodIndex,
1120
+ bounds,
1121
+ editable,
1122
+ parsedPeriod.count,
1123
+ periods,
1124
+ selected,
1125
+ state.inputValue,
1126
+ today
1127
+ ]);
1128
+ function setCalendarPeriodAnchor(date) {
1129
+ const clamped = clampDateToBounds(date, bounds);
1130
+ setPeriodAnchor(clamped);
1131
+ setNavigationAnchor({ date: clamped, inputValue: state.inputValue });
1132
+ setPeriodExtensions(getInitialPeriodExtensions(parsedPeriod));
1133
+ setScrolledVisiblePeriodIndex(null);
1134
+ }
1135
+ function canMoveCalendar(unit, count) {
1136
+ const target = addCalendarPeriod(visiblePeriodAnchor, unit, count);
1137
+ return target.equals(clampDateToBounds(target, bounds));
1138
+ }
1139
+ function canExtendCalendarPeriods(direction, windows = 1) {
1140
+ if (!bounds) {
1141
+ return true;
1142
+ }
1143
+ const extendCount = parsedPeriod.count * windows;
1144
+ const firstIndex = periods[0]?.index ?? 0;
1145
+ const lastIndex = periods.at(-1)?.index ?? parsedPeriod.count - 1;
1146
+ const targetIndex = direction === "before" ? firstIndex - extendCount : lastIndex + 1;
1147
+ const targetPeriod = getCalendarPeriodAtOffset(periodAnchor, parsedPeriod, weekStartsOn, locale, targetIndex);
1148
+ return periodIntersectsBounds(targetPeriod, bounds);
1149
+ }
1150
+ const calendarState = useMemo4(
1151
+ () => ({
1152
+ calchemy: state,
1153
+ period: parsedPeriod,
1154
+ periodAnchor,
1155
+ visiblePeriodAnchor,
1156
+ today,
1157
+ selected,
1158
+ periods,
1159
+ visiblePeriods,
1160
+ weekStartsOn,
1161
+ locale,
1162
+ bounds,
1163
+ namedDates,
1164
+ editable,
1165
+ isDateDisabled,
1166
+ setPeriodAnchor: setCalendarPeriodAnchor,
1167
+ setVisiblePeriodIndex(index) {
1168
+ setScrolledVisiblePeriodIndex((current) => {
1169
+ if (current?.anchor === periodAnchorKey && current.inputValue === state.inputValue && current.index === index) {
1170
+ return current;
1171
+ }
1172
+ return { anchor: periodAnchorKey, inputValue: state.inputValue, index };
1173
+ });
1174
+ },
1175
+ canMove: canMoveCalendar,
1176
+ move(unit, count) {
1177
+ if (!canMoveCalendar(unit, count)) {
1178
+ return;
1179
+ }
1180
+ const clamped = clampDateToBounds(addCalendarPeriod(visiblePeriodAnchor, unit, count), bounds);
1181
+ setPeriodAnchor(clamped);
1182
+ setNavigationAnchor({
1183
+ date: clamped,
1184
+ inputValue: state.inputValue
1185
+ });
1186
+ setPeriodExtensions(getInitialPeriodExtensions(parsedPeriod));
1187
+ setScrolledVisiblePeriodIndex(null);
1188
+ },
1189
+ canExtendPeriods: canExtendCalendarPeriods,
1190
+ extendPeriods(direction, windows = 1) {
1191
+ if (!canExtendCalendarPeriods(direction, windows)) {
1192
+ return;
1193
+ }
1194
+ setPeriodExtensions((current) => ({
1195
+ ...current,
1196
+ [direction]: current[direction] + parsedPeriod.count * windows
1197
+ }));
1198
+ },
1199
+ selectDate(date) {
1200
+ if (!editable) {
1201
+ return;
1202
+ }
1203
+ if (state.expectedValue === "range") {
1204
+ const current = selected;
1205
+ if (current?.kind === "range" && current.start.equals(current.end)) {
1206
+ const start = isBefore(current.start, date) ? current.start : date;
1207
+ const end = isBefore(current.start, date) ? date : current.start;
1208
+ state.selectDate({ kind: "range", start, end });
1209
+ return;
1210
+ }
1211
+ state.selectDate({ kind: "range", start: date, end: date });
1212
+ return;
1213
+ }
1214
+ state.selectDate({ kind: "single", date });
1215
+ },
1216
+ selectValue(value) {
1217
+ if (!editable) {
1218
+ return;
1219
+ }
1220
+ state.selectDate(value);
1221
+ }
1222
+ }),
1223
+ [
1224
+ state,
1225
+ parsedPeriod,
1226
+ periodAnchor,
1227
+ periodAnchorKey,
1228
+ visiblePeriodAnchor,
1229
+ today,
1230
+ selected,
1231
+ periods,
1232
+ visiblePeriods,
1233
+ weekStartsOn,
1234
+ locale,
1235
+ bounds,
1236
+ namedDates,
1237
+ editable,
1238
+ isDateDisabled
1239
+ ]
1240
+ );
1241
+ const content = children ?? /* @__PURE__ */ jsxs2(Fragment, { children: [
1242
+ /* @__PURE__ */ jsxs2(CalendarHeader, { children: [
1243
+ /* @__PURE__ */ jsx3(CalendarPrevious, {}),
1244
+ /* @__PURE__ */ jsx3(CalendarHeading, {}),
1245
+ /* @__PURE__ */ jsx3(CalendarNext, {})
1246
+ ] }),
1247
+ /* @__PURE__ */ jsx3(CalendarWeekdays, {}),
1248
+ /* @__PURE__ */ jsx3(CalendarGrid, {})
1249
+ ] });
1250
+ return /* @__PURE__ */ jsx3(CalendarContext.Provider, { value: calendarState, children: /* @__PURE__ */ jsx3(
1251
+ "div",
1252
+ {
1253
+ ...divProps,
1254
+ "calchemy-calendar": "",
1255
+ "calchemy-editable": editable ? "" : void 0,
1256
+ style: {
1257
+ ...divProps.style,
1258
+ "--calchemy-calendar-period-count": parsedPeriod.count
1259
+ },
1260
+ children: content
1261
+ }
1262
+ ) });
1263
+ }
1264
+ function CalendarHeader(props) {
1265
+ return /* @__PURE__ */ jsx3("div", { ...props, "calchemy-header": "" });
1266
+ }
1267
+ function CalendarHeading(props) {
1268
+ const calendar = useCalchemyCalendar();
1269
+ return /* @__PURE__ */ jsx3("h2", { ...props, "calchemy-heading": "", children: props.children ?? formatCalendarWindowLabel(calendar.visiblePeriods, calendar.locale) });
1270
+ }
1271
+ function CalendarPrevious({
1272
+ onClick,
1273
+ children,
1274
+ ...props
1275
+ }) {
1276
+ return /* @__PURE__ */ jsx3(
1277
+ CalendarNavigationButton,
1278
+ {
1279
+ ...props,
1280
+ direction: -1,
1281
+ "calchemy-previous": "",
1282
+ onClick,
1283
+ children: children ?? "Previous"
1284
+ }
1285
+ );
1286
+ }
1287
+ function CalendarNext({
1288
+ onClick,
1289
+ children,
1290
+ ...props
1291
+ }) {
1292
+ return /* @__PURE__ */ jsx3(
1293
+ CalendarNavigationButton,
1294
+ {
1295
+ ...props,
1296
+ direction: 1,
1297
+ "calchemy-next": "",
1298
+ onClick,
1299
+ children: children ?? "Next"
1300
+ }
1301
+ );
1302
+ }
1303
+ function CalendarNavigationButton({
1304
+ direction,
1305
+ onClick,
1306
+ type = "button",
1307
+ ...props
1308
+ }) {
1309
+ const calendar = useCalchemyCalendar();
1310
+ const disabled = props.disabled ?? !calendar.canMove(calendar.period.unit, calendar.period.count * direction);
1311
+ function handleClick(event) {
1312
+ onClick?.(event);
1313
+ if (event.defaultPrevented || disabled) {
1314
+ return;
1315
+ }
1316
+ calendar.move(calendar.period.unit, calendar.period.count * direction);
1317
+ }
1318
+ return /* @__PURE__ */ jsx3("button", { ...props, type, disabled, onClick: handleClick });
1319
+ }
1320
+
1321
+ // src/components/calendar/CalendarPeriodHeading.tsx
1322
+ import { jsx as jsx4 } from "react/jsx-runtime";
1323
+ function CalendarPeriodHeading(props) {
1324
+ const calendar = useCalchemyCalendar();
1325
+ const period = useCalendarPeriod() ?? getFirstVisibleCalendarPeriod(calendar);
1326
+ return /* @__PURE__ */ jsx4("h3", { ...props, "calchemy-period-heading": "", children: props.children ?? period.label });
1327
+ }
1328
+
1329
+ // src/components/calendar/CalendarPeriod.tsx
1330
+ import { useContext as useContext2 } from "react";
1331
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
1332
+ function CalendarPeriod({
1333
+ dragSelection = true,
1334
+ style,
1335
+ children,
1336
+ onPointerDownCapture,
1337
+ onPointerMove,
1338
+ onPointerUp,
1339
+ onPointerCancel,
1340
+ onDragStart,
1341
+ ...props
1342
+ }) {
1343
+ const period = useContext2(CalendarPeriodContext);
1344
+ const calendar = useCalchemyCalendar();
1345
+ const parentDrag = useOptionalCalendarPeriodDrag();
1346
+ const drag = useCalendarPeriodDragSurface(
1347
+ calendar.editable && dragSelection && parentDrag === null
1348
+ );
1349
+ const content = drag ? /* @__PURE__ */ jsxs3(CalendarPeriodDragProvider, { value: drag, children: [
1350
+ /* @__PURE__ */ jsx5(CalendarDragRectangleOverlay, {}),
1351
+ children
1352
+ ] }) : children;
1353
+ return /* @__PURE__ */ jsx5(
1354
+ "section",
1355
+ {
1356
+ ...props,
1357
+ "calchemy-period": "",
1358
+ "calchemy-period-id": period?.id,
1359
+ "calchemy-period-index": period?.index,
1360
+ "calchemy-multiple-drag": drag ? "" : void 0,
1361
+ "calchemy-dragging": drag?.dragState ? "" : void 0,
1362
+ style: drag ? { ...multipleDragSurfaceStyle, ...style } : parentDrag ? { touchAction: "none", ...style } : style,
1363
+ onPointerDownCapture: drag ? (event) => {
1364
+ drag.handlePointerDownCapture(event);
1365
+ onPointerDownCapture?.(event);
1366
+ } : onPointerDownCapture,
1367
+ onPointerMove: drag ? (event) => {
1368
+ drag.handlePointerMove(event);
1369
+ onPointerMove?.(event);
1370
+ } : onPointerMove,
1371
+ onPointerUp: drag ? (event) => {
1372
+ drag.handlePointerUp(event);
1373
+ onPointerUp?.(event);
1374
+ } : onPointerUp,
1375
+ onPointerCancel: drag ? (event) => {
1376
+ drag.handlePointerCancel(event);
1377
+ onPointerCancel?.(event);
1378
+ } : onPointerCancel,
1379
+ onDragStart: drag ? (event) => {
1380
+ event.preventDefault();
1381
+ onDragStart?.(event);
1382
+ } : onDragStart,
1383
+ children: content
1384
+ }
1385
+ );
1386
+ }
1387
+
1388
+ // src/components/calendar/CalendarPeriodList.tsx
1389
+ import { useContext as useContext3, useLayoutEffect as useLayoutEffect2, useRef as useRef3 } from "react";
1390
+ import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
1391
+ function CalendarPeriodList({
1392
+ children,
1393
+ dragSelection = true,
1394
+ style,
1395
+ onPointerDownCapture,
1396
+ onPointerMove,
1397
+ onPointerUp,
1398
+ onPointerCancel,
1399
+ onDragStart,
1400
+ ...props
1401
+ }) {
1402
+ const calendar = useCalchemyCalendar();
1403
+ const scrollContext = useContext3(CalendarScrollContext);
1404
+ const parentDrag = useOptionalCalendarPeriodDrag();
1405
+ const surfaceRef = useRef3(null);
1406
+ const hitCaptureRef = useRef3(null);
1407
+ const drag = useCalendarPeriodDragSurface(
1408
+ calendar.editable && dragSelection && parentDrag === null,
1409
+ surfaceRef
1410
+ );
1411
+ const periods = scrollContext ? calendar.periods : calendar.visiblePeriods;
1412
+ useLayoutEffect2(() => {
1413
+ if (!drag) {
1414
+ return;
1415
+ }
1416
+ const surface = surfaceRef.current;
1417
+ const hitCapture = hitCaptureRef.current;
1418
+ if (!surface || !hitCapture) {
1419
+ return;
1420
+ }
1421
+ const updateDragHitBounds = () => {
1422
+ const periodElements = surface.querySelectorAll("[calchemy-period]");
1423
+ if (periodElements.length === 0) {
1424
+ hitCapture.style.removeProperty("inline-size");
1425
+ hitCapture.style.removeProperty("block-size");
1426
+ return;
1427
+ }
1428
+ let maxInlineEnd = 0;
1429
+ let maxBlockEnd = 0;
1430
+ for (const periodElement of periodElements) {
1431
+ maxInlineEnd = Math.max(maxInlineEnd, periodElement.offsetLeft + periodElement.offsetWidth);
1432
+ maxBlockEnd = Math.max(maxBlockEnd, periodElement.offsetTop + periodElement.offsetHeight);
1433
+ }
1434
+ hitCapture.style.inlineSize = `${maxInlineEnd}px`;
1435
+ hitCapture.style.blockSize = `${maxBlockEnd}px`;
1436
+ };
1437
+ updateDragHitBounds();
1438
+ if (typeof ResizeObserver === "undefined") {
1439
+ return;
1440
+ }
1441
+ const resizeObserver = new ResizeObserver(updateDragHitBounds);
1442
+ resizeObserver.observe(surface);
1443
+ for (const periodElement of surface.querySelectorAll("[calchemy-period]")) {
1444
+ resizeObserver.observe(periodElement);
1445
+ }
1446
+ return () => {
1447
+ resizeObserver.disconnect();
1448
+ };
1449
+ }, [drag, periods]);
1450
+ const spacerStyle = scrollContext?.direction === "horizontal" ? { gridColumn: `span ${scrollContext.leadingSpacerPeriodCount}` } : { blockSize: `${scrollContext?.leadingSpacerPixelSize ?? 0}px` };
1451
+ const periodContent = /* @__PURE__ */ jsxs4(Fragment2, { children: [
1452
+ scrollContext && scrollContext.leadingSpacerPeriodCount > 0 ? /* @__PURE__ */ jsx6(
1453
+ "div",
1454
+ {
1455
+ "aria-hidden": "true",
1456
+ "calchemy-scroll-spacer": "",
1457
+ style: spacerStyle
1458
+ }
1459
+ ) : null,
1460
+ periods.map((period) => /* @__PURE__ */ jsx6(CalendarPeriodContext.Provider, { value: period, children: children ?? /* @__PURE__ */ jsxs4(CalendarPeriod, { children: [
1461
+ /* @__PURE__ */ jsx6(CalendarPeriodHeading, {}),
1462
+ /* @__PURE__ */ jsx6(CalendarWeekdays, {}),
1463
+ /* @__PURE__ */ jsx6(CalendarGrid, {})
1464
+ ] }) }, period.id))
1465
+ ] });
1466
+ const content = drag ? /* @__PURE__ */ jsxs4(CalendarPeriodDragProvider, { value: drag, children: [
1467
+ /* @__PURE__ */ jsx6(
1468
+ "div",
1469
+ {
1470
+ ref: hitCaptureRef,
1471
+ "aria-hidden": "true",
1472
+ style: {
1473
+ position: "absolute",
1474
+ top: 0,
1475
+ left: 0,
1476
+ zIndex: 0,
1477
+ pointerEvents: "auto",
1478
+ touchAction: "none"
1479
+ }
1480
+ }
1481
+ ),
1482
+ /* @__PURE__ */ jsx6(CalendarDragRectangleOverlay, {}),
1483
+ periodContent
1484
+ ] }) : periodContent;
1485
+ return /* @__PURE__ */ jsx6(
1486
+ "div",
1487
+ {
1488
+ ...props,
1489
+ ref: surfaceRef,
1490
+ "calchemy-period-list": "",
1491
+ "calchemy-multiple-drag": drag ? "" : void 0,
1492
+ "calchemy-dragging": drag?.dragState ? "" : void 0,
1493
+ style: drag ? { ...multiplePeriodListDragSurfaceStyle, ...style } : style,
1494
+ onPointerDownCapture: drag ? (event) => {
1495
+ drag.handlePointerDownCapture(event);
1496
+ onPointerDownCapture?.(event);
1497
+ } : onPointerDownCapture,
1498
+ onPointerMove: drag ? (event) => {
1499
+ drag.handlePointerMove(event);
1500
+ onPointerMove?.(event);
1501
+ } : onPointerMove,
1502
+ onPointerUp: drag ? (event) => {
1503
+ drag.handlePointerUp(event);
1504
+ onPointerUp?.(event);
1505
+ } : onPointerUp,
1506
+ onPointerCancel: drag ? (event) => {
1507
+ drag.handlePointerCancel(event);
1508
+ onPointerCancel?.(event);
1509
+ } : onPointerCancel,
1510
+ onDragStart: drag ? (event) => {
1511
+ event.preventDefault();
1512
+ onDragStart?.(event);
1513
+ } : onDragStart,
1514
+ children: content
1515
+ }
1516
+ );
1517
+ }
1518
+
1519
+ // src/components/calendar/CalendarSelects.tsx
1520
+ import { jsx as jsx7 } from "react/jsx-runtime";
1521
+ function CalendarMonthSelect({ onChange, ...props }) {
1522
+ const calendar = useCalchemyCalendar();
1523
+ const months = Array.from({ length: 12 }, (_, index) => index + 1).filter(
1524
+ (month) => isMonthWithinBounds(calendar.visiblePeriodAnchor.with({ month, day: 1 }), calendar)
1525
+ );
1526
+ return /* @__PURE__ */ jsx7(
1527
+ "select",
1528
+ {
1529
+ ...props,
1530
+ "calchemy-month-select": "",
1531
+ value: String(calendar.visiblePeriodAnchor.month),
1532
+ onChange: (event) => handleCalendarSelectChange(
1533
+ event,
1534
+ onChange,
1535
+ (value) => calendar.setPeriodAnchor(calendar.visiblePeriodAnchor.with({ month: value, day: 1 }))
1536
+ ),
1537
+ children: months.map((month) => {
1538
+ const date = calendar.visiblePeriodAnchor.with({ month, day: 1 });
1539
+ return /* @__PURE__ */ jsx7("option", { value: String(month), children: formatMonthLabel(date, calendar.locale) }, month);
1540
+ })
1541
+ }
1542
+ );
1543
+ }
1544
+ function CalendarYearSelect({ startYear, endYear, onChange, ...props }) {
1545
+ const calendar = useCalchemyCalendar();
1546
+ const visibleYear = calendar.visiblePeriodAnchor.year;
1547
+ const firstYear = Math.min(calendar.bounds?.start?.year ?? startYear ?? visibleYear - 100, visibleYear);
1548
+ const lastYear = Math.max(calendar.bounds?.end?.year ?? endYear ?? visibleYear + 100, visibleYear);
1549
+ return /* @__PURE__ */ jsx7(
1550
+ "select",
1551
+ {
1552
+ ...props,
1553
+ "calchemy-year-select": "",
1554
+ value: String(visibleYear),
1555
+ onChange: (event) => handleCalendarSelectChange(
1556
+ event,
1557
+ onChange,
1558
+ (value) => calendar.setPeriodAnchor(calendar.visiblePeriodAnchor.with({ year: value, day: 1 }))
1559
+ ),
1560
+ children: Array.from({ length: lastYear - firstYear + 1 }, (_, index) => firstYear + index).map((year) => /* @__PURE__ */ jsx7("option", { value: String(year), children: year }, year))
1561
+ }
1562
+ );
1563
+ }
1564
+ function handleCalendarSelectChange(event, onChange, updatePeriodAnchor) {
1565
+ onChange?.(event);
1566
+ if (event.defaultPrevented) {
1567
+ return;
1568
+ }
1569
+ updatePeriodAnchor(Number(event.currentTarget.value));
1570
+ }
1571
+ function isMonthWithinBounds(monthStart, calendar) {
1572
+ const monthEnd = monthStart.add({ months: 1 }).subtract({ days: 1 });
1573
+ return (!calendar.bounds?.start || !isBefore(monthEnd, calendar.bounds.start)) && (!calendar.bounds?.end || !isAfter(monthStart, calendar.bounds.end));
1574
+ }
1575
+
1576
+ // src/components/Calchemy.tsx
1577
+ import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
1578
+ function Root(props) {
1579
+ const { children, ...options } = props;
1580
+ const state = useCalchemy(options);
1581
+ return /* @__PURE__ */ jsx8(CalchemyContext.Provider, { value: state, children });
1582
+ }
1583
+ function Field({
1584
+ renderInlineCompletion = true,
1585
+ readOnly,
1586
+ ...props
1587
+ }) {
1588
+ const state = useCalchemyContext();
1589
+ const inputProps = state.getInputProps();
1590
+ const completion = state.inputMode === "field" ? state.inlineCompletion : null;
1591
+ return /* @__PURE__ */ jsxs5(
1592
+ "span",
1593
+ {
1594
+ "calchemy-field": "",
1595
+ "calchemy-input-mode": state.inputMode,
1596
+ "calchemy-has-completion": completion ? "" : void 0,
1597
+ children: [
1598
+ renderInlineCompletion && completion ? /* @__PURE__ */ jsxs5("span", { "calchemy-field-backdrop": "", "aria-hidden": "true", children: [
1599
+ /* @__PURE__ */ jsx8("span", { "calchemy-field-typed": "", children: state.inputValue }),
1600
+ /* @__PURE__ */ jsx8("span", { "calchemy-completions": "", children: completion.suffix })
1601
+ ] }) : null,
1602
+ /* @__PURE__ */ jsx8("input", { ...props, ...inputProps, readOnly: readOnly ?? inputProps.readOnly })
1603
+ ]
1604
+ }
1605
+ );
1606
+ }
1607
+ function InputMode({
1608
+ fieldLabel = "Type",
1609
+ calendarLabel = "Pick",
1610
+ ...props
1611
+ }) {
1612
+ const state = useCalchemyContext();
1613
+ return /* @__PURE__ */ jsxs5("div", { ...props, "calchemy-mode": "", "calchemy-active": state.inputMode, role: "group", children: [
1614
+ /* @__PURE__ */ jsx8(
1615
+ "button",
1616
+ {
1617
+ type: "button",
1618
+ "calchemy-value": "field",
1619
+ "aria-pressed": state.inputMode === "field",
1620
+ onClick: () => state.setInputMode("field"),
1621
+ children: fieldLabel
1622
+ }
1623
+ ),
1624
+ /* @__PURE__ */ jsx8(
1625
+ "button",
1626
+ {
1627
+ type: "button",
1628
+ "calchemy-value": "calendar",
1629
+ "aria-pressed": state.inputMode === "calendar",
1630
+ onClick: () => state.setInputMode("calendar"),
1631
+ children: calendarLabel
1632
+ }
1633
+ )
1634
+ ] });
1635
+ }
1636
+ function Candidates(props) {
1637
+ const state = useCalchemyContext();
1638
+ if (state.inputMode !== "field" || state.result.status !== "ambiguous") {
1639
+ return null;
1640
+ }
1641
+ const candidates = state.expectedValue && state.expectedValue !== "multiple" ? state.result.candidates.filter(
1642
+ (candidate) => candidate.value.kind === state.expectedValue
1643
+ ) : state.result.candidates;
1644
+ if (candidates.length === 0) {
1645
+ return null;
1646
+ }
1647
+ return /* @__PURE__ */ jsx8("div", { ...props, "calchemy-candidates": "", children: candidates.map((candidate) => /* @__PURE__ */ jsx8(
1648
+ "button",
1649
+ {
1650
+ type: "button",
1651
+ "calchemy-candidate": "",
1652
+ onClick: () => state.selectCandidate(candidate.id),
1653
+ children: candidate.label
1654
+ },
1655
+ candidate.id
1656
+ )) });
1657
+ }
1658
+ var Calchemy = {
1659
+ Root,
1660
+ Field,
1661
+ InputMode,
1662
+ Candidates,
1663
+ Calendar,
1664
+ CalendarHeader,
1665
+ CalendarHeading,
1666
+ CalendarPrevious,
1667
+ CalendarNext,
1668
+ CalendarPeriodList,
1669
+ CalendarPeriod,
1670
+ CalendarPeriodHeading,
1671
+ CalendarWeekdays,
1672
+ CalendarGrid,
1673
+ CalendarMonthSelect,
1674
+ CalendarYearSelect
1675
+ };
1676
+ export {
1677
+ Calchemy,
1678
+ useCalchemy,
1679
+ useCalchemyCalendar,
1680
+ useCalchemyContext
1681
+ };
3
1682
  //# sourceMappingURL=index.js.map