@calchemy/date-react 0.1.0 → 0.1.2

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