@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/calendar-scroll.cjs +261 -0
- package/dist/calendar-scroll.cjs.map +1 -0
- package/dist/components/calendar/CalendarGrid.d.ts.map +1 -1
- package/dist/components/calendar/CalendarGrid.js +9 -27
- package/dist/components/calendar/CalendarGrid.js.map +1 -1
- package/dist/components/calendar/CalendarPeriod.d.ts.map +1 -1
- package/dist/components/calendar/CalendarPeriod.js +9 -27
- package/dist/components/calendar/CalendarPeriod.js.map +1 -1
- package/dist/components/calendar/CalendarPeriodList.d.ts.map +1 -1
- package/dist/components/calendar/CalendarPeriodList.js +9 -27
- package/dist/components/calendar/CalendarPeriodList.js.map +1 -1
- package/dist/components/calendar/calendar-period-drag.d.ts +3 -1
- package/dist/components/calendar/calendar-period-drag.d.ts.map +1 -1
- package/dist/components/calendar/calendar-period-drag.js +29 -0
- package/dist/components/calendar/calendar-period-drag.js.map +1 -1
- package/dist/components/calendar/date-model.d.ts +0 -1
- package/dist/components/calendar/date-model.d.ts.map +1 -1
- package/dist/components/calendar/date-model.js +1 -1
- package/dist/components/calendar/date-model.js.map +1 -1
- package/dist/index.cjs +1711 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js +1673 -2
- package/dist/index.js.map +1 -1
- package/package.json +7 -5
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
|