@super-calendar/dom 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1393 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let _legendapp_list_react = require("@legendapp/list/react");
3
+ let date_fns = require("date-fns");
4
+ let react = require("react");
5
+ let _super_calendar_core = require("@super-calendar/core");
6
+ let react_jsx_runtime = require("react/jsx-runtime");
7
+ //#region src/theme.ts
8
+ const METRICS = {
9
+ cellHeight: 48,
10
+ dayBadgeSize: 34,
11
+ rangeBandHeight: 32,
12
+ fontFamily: "-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif"
13
+ };
14
+ const defaultDomTheme = {
15
+ ..._super_calendar_core.lightColors,
16
+ ...METRICS
17
+ };
18
+ const darkDomTheme = {
19
+ ..._super_calendar_core.darkColors,
20
+ ...METRICS
21
+ };
22
+ /** Merge a partial override onto a base theme (defaults to {@link defaultDomTheme}). */
23
+ function mergeDomTheme(overrides, base = defaultDomTheme) {
24
+ return overrides ? {
25
+ ...base,
26
+ ...overrides
27
+ } : base;
28
+ }
29
+ //#endregion
30
+ //#region src/Agenda.tsx
31
+ function DefaultAgendaRow({ event, isAllDay, ampm = false, theme }) {
32
+ const time = (0, _super_calendar_core.eventTimeLabel)({
33
+ mode: "schedule",
34
+ isAllDay,
35
+ start: event.start,
36
+ end: event.end,
37
+ ampm,
38
+ showTime: true
39
+ }) ?? "";
40
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
41
+ style: {
42
+ padding: "6px 10px",
43
+ borderRadius: 8,
44
+ background: theme.eventBackground,
45
+ color: theme.eventText
46
+ },
47
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
48
+ style: {
49
+ fontWeight: 600,
50
+ fontSize: 14
51
+ },
52
+ children: event.title
53
+ }), time ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
54
+ style: {
55
+ fontSize: 13,
56
+ opacity: .75
57
+ },
58
+ children: time
59
+ }) : null]
60
+ });
61
+ }
62
+ /**
63
+ * A vertical, day-grouped list of events: the schedule view, with plain DOM
64
+ * elements. Events are sorted by start and grouped under a date header per day;
65
+ * the consumer controls which events (and therefore which dates) are shown. The
66
+ * react-dom counterpart of the React Native `Agenda`.
67
+ */
68
+ function Agenda({ events, locale, ampm = false, activeDate, theme: themeOverrides, height = 480, renderEvent, onPressEvent, onPressDay, className, style }) {
69
+ const theme = (0, react.useMemo)(() => mergeDomTheme(themeOverrides), [themeOverrides]);
70
+ const Renderer = renderEvent;
71
+ const rows = (0, react.useMemo)(() => {
72
+ const sorted = [...events].sort((a, b) => a.start.getTime() - b.start.getTime());
73
+ const out = [];
74
+ let currentDay = null;
75
+ sorted.forEach((event, index) => {
76
+ if (!currentDay || !(0, date_fns.isSameDay)(event.start, currentDay)) {
77
+ currentDay = (0, date_fns.startOfDay)(event.start);
78
+ out.push({
79
+ kind: "header",
80
+ date: currentDay,
81
+ key: `h-${currentDay.toISOString()}`
82
+ });
83
+ }
84
+ out.push({
85
+ kind: "event",
86
+ event,
87
+ key: `e-${event.start.toISOString()}-${index}`
88
+ });
89
+ });
90
+ return out;
91
+ }, [events]);
92
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
93
+ className,
94
+ style: {
95
+ fontFamily: theme.fontFamily,
96
+ color: theme.text,
97
+ ...style
98
+ },
99
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_legendapp_list_react.LegendList, {
100
+ data: rows,
101
+ keyExtractor: (row) => row.key,
102
+ recycleItems: false,
103
+ estimatedItemSize: 44,
104
+ style: {
105
+ height,
106
+ overflowY: "auto"
107
+ },
108
+ renderItem: ({ item }) => {
109
+ if (item.kind === "header") {
110
+ const highlighted = activeDate ? (0, date_fns.isSameDay)(item.date, activeDate) : (0, _super_calendar_core.getIsToday)(item.date);
111
+ const label = (0, date_fns.format)(item.date, "EEEE, d LLLL", locale ? { locale } : void 0);
112
+ const color = highlighted ? theme.todayBackground : theme.textMuted;
113
+ return onPressDay ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
114
+ type: "button",
115
+ onClick: () => onPressDay(item.date),
116
+ style: {
117
+ ...headerStyle,
118
+ color,
119
+ border: "none",
120
+ background: "transparent",
121
+ cursor: "pointer",
122
+ font: "inherit",
123
+ textAlign: "left"
124
+ },
125
+ children: label
126
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
127
+ style: {
128
+ ...headerStyle,
129
+ color
130
+ },
131
+ children: label
132
+ });
133
+ }
134
+ const event = item.event;
135
+ const isAllDay = (0, _super_calendar_core.isAllDayEvent)(event);
136
+ const onPress = () => onPressEvent?.(event);
137
+ const args = {
138
+ event,
139
+ mode: "schedule",
140
+ isAllDay,
141
+ ampm,
142
+ onPress
143
+ };
144
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
145
+ type: "button",
146
+ onClick: onPress,
147
+ style: {
148
+ display: "block",
149
+ width: "100%",
150
+ textAlign: "left",
151
+ border: "none",
152
+ background: "transparent",
153
+ cursor: "pointer",
154
+ font: "inherit",
155
+ padding: "2px 12px"
156
+ },
157
+ children: Renderer ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Renderer, { ...args }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultAgendaRow, {
158
+ ...args,
159
+ theme
160
+ })
161
+ });
162
+ }
163
+ }), rows.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
164
+ style: {
165
+ padding: "16px 12px",
166
+ color: theme.textMuted,
167
+ fontSize: 14
168
+ },
169
+ children: "No events"
170
+ }) : null]
171
+ });
172
+ }
173
+ const headerStyle = {
174
+ display: "block",
175
+ width: "100%",
176
+ padding: "12px 12px 4px",
177
+ fontSize: 13,
178
+ fontWeight: 600
179
+ };
180
+ //#endregion
181
+ //#region src/MonthView.tsx
182
+ const DATE_ROW = 24;
183
+ const CHIP_HEIGHT = 18;
184
+ const CHIP_GAP = 2;
185
+ const CELL_PAD = 4;
186
+ function dayCellStyle(day, theme) {
187
+ return {
188
+ position: "relative",
189
+ height: theme.cellHeight,
190
+ border: "none",
191
+ background: day.isWeekend && !day.isInRange ? theme.weekendBackground : "transparent",
192
+ font: "inherit",
193
+ fontSize: 15,
194
+ color: day.isDisabled ? theme.textDisabled : theme.text,
195
+ cursor: day.isDisabled ? "default" : "pointer",
196
+ display: "flex",
197
+ alignItems: "center",
198
+ justifyContent: "center",
199
+ padding: 0,
200
+ WebkitTapHighlightColor: "transparent"
201
+ };
202
+ }
203
+ /**
204
+ * The range band behind a day, rendered as its own layer so it can be a centered
205
+ * rounded strip (the default) or fill the whole cell (`fillCell`). Returns null
206
+ * for days with no band. Endpoints get the leading/trailing pill rounding.
207
+ */
208
+ function rangeBandStyle(day, theme, fillCell) {
209
+ const kind = (0, _super_calendar_core.rangeBandKind)(day, fillCell);
210
+ if (kind === "none") return null;
211
+ const fill = kind === "fill";
212
+ const inset = fill ? 0 : Math.max(0, (theme.cellHeight - theme.rangeBandHeight) / 2);
213
+ const radius = fill ? 0 : theme.rangeBandHeight / 2;
214
+ const rounding = (0, _super_calendar_core.bandRounding)(kind);
215
+ const cap = `calc(50% - ${theme.dayBadgeSize / 2}px)`;
216
+ const style = {
217
+ position: "absolute",
218
+ left: rounding.start ? cap : 0,
219
+ right: rounding.end ? cap : 0,
220
+ top: inset,
221
+ bottom: inset,
222
+ background: theme.rangeBackground,
223
+ zIndex: 0
224
+ };
225
+ if (rounding.start) {
226
+ style.borderTopLeftRadius = radius;
227
+ style.borderBottomLeftRadius = radius;
228
+ }
229
+ if (rounding.end) {
230
+ style.borderTopRightRadius = radius;
231
+ style.borderBottomRightRadius = radius;
232
+ }
233
+ return style;
234
+ }
235
+ function badgeStyle(day, theme, hovered) {
236
+ const badge = (0, _super_calendar_core.dayBadgeKind)(day, day.isToday);
237
+ const filled = badge !== "none";
238
+ const background = filled ? badge === "today" ? theme.todayBackground : theme.selectedBackground : hovered ? theme.hoverBackground : "transparent";
239
+ return {
240
+ position: "relative",
241
+ zIndex: 1,
242
+ width: theme.dayBadgeSize,
243
+ height: theme.dayBadgeSize,
244
+ borderRadius: "50%",
245
+ display: "flex",
246
+ alignItems: "center",
247
+ justifyContent: "center",
248
+ background,
249
+ color: filled ? day.isToday ? theme.todayText : theme.selectedText : "inherit"
250
+ };
251
+ }
252
+ function eventCellStyle(day, theme, height) {
253
+ return {
254
+ position: "relative",
255
+ minHeight: height,
256
+ minWidth: 0,
257
+ border: "none",
258
+ borderTop: `1px solid ${theme.gridLine}`,
259
+ background: day.isWeekend && !day.isInRange ? theme.weekendBackground : "transparent",
260
+ color: day.isDisabled ? theme.textDisabled : theme.text,
261
+ cursor: day.isDisabled ? "default" : "pointer",
262
+ display: "flex",
263
+ flexDirection: "column",
264
+ gap: CHIP_GAP,
265
+ padding: CELL_PAD,
266
+ boxSizing: "border-box",
267
+ textAlign: "left",
268
+ WebkitTapHighlightColor: "transparent"
269
+ };
270
+ }
271
+ function compactBadgeStyle(day, theme) {
272
+ const badge = (0, _super_calendar_core.dayBadgeKind)(day, day.isToday);
273
+ const filled = badge !== "none";
274
+ return {
275
+ position: "relative",
276
+ zIndex: 1,
277
+ alignSelf: "flex-end",
278
+ width: DATE_ROW - 2,
279
+ height: DATE_ROW - 2,
280
+ borderRadius: "50%",
281
+ display: "flex",
282
+ alignItems: "center",
283
+ justifyContent: "center",
284
+ fontSize: 13,
285
+ background: filled ? badge === "today" ? theme.todayBackground : theme.selectedBackground : "transparent",
286
+ color: filled ? day.isToday ? theme.todayText : theme.selectedText : "inherit"
287
+ };
288
+ }
289
+ const chipButtonStyle = {
290
+ position: "relative",
291
+ zIndex: 1,
292
+ border: "none",
293
+ padding: 0,
294
+ margin: 0,
295
+ background: "transparent",
296
+ cursor: "pointer",
297
+ textAlign: "left",
298
+ fontFamily: "inherit"
299
+ };
300
+ function chipStyle(theme) {
301
+ return {
302
+ display: "block",
303
+ height: CHIP_HEIGHT,
304
+ lineHeight: `${CHIP_HEIGHT}px`,
305
+ padding: "0 6px",
306
+ borderRadius: 4,
307
+ background: theme.eventBackground,
308
+ color: theme.eventText,
309
+ fontSize: 11,
310
+ fontWeight: 600,
311
+ whiteSpace: "nowrap",
312
+ overflow: "hidden",
313
+ textOverflow: "ellipsis"
314
+ };
315
+ }
316
+ function moreButtonStyle(theme) {
317
+ return {
318
+ position: "relative",
319
+ zIndex: 1,
320
+ border: "none",
321
+ background: "transparent",
322
+ cursor: "pointer",
323
+ textAlign: "left",
324
+ padding: "0 6px",
325
+ height: CHIP_HEIGHT,
326
+ lineHeight: `${CHIP_HEIGHT}px`,
327
+ fontSize: 11,
328
+ fontWeight: 600,
329
+ fontFamily: "inherit",
330
+ color: theme.textMuted
331
+ };
332
+ }
333
+ /** A single static month grid, rendered with plain DOM elements. */
334
+ function MonthView({ date, weekStartsOn = 0, events, eventsByDay: eventsByDayProp, renderEvent, maxVisibleEventCount = 3, moreLabel = "{moreCount} More", onPressEvent, onPressMore, selectedRange, selectedDates, showAdjacentMonths = true, fillCellOnSelection = false, showTitle = true, showWeekdays = true, locale, theme: themeOverrides, minDate, maxDate, isDateDisabled, onPressDay, keyboardDayNavigation = false, className, style }) {
335
+ const theme = (0, react.useMemo)(() => mergeDomTheme(themeOverrides), [themeOverrides]);
336
+ const eventsMode = events !== void 0;
337
+ const dayRoving = !eventsMode || keyboardDayNavigation;
338
+ const eventsByDay = (0, react.useMemo)(() => {
339
+ if (eventsByDayProp) return eventsByDayProp;
340
+ const map = (0, _super_calendar_core.groupEventsByDay)(events ?? []);
341
+ for (const list of map.values()) list.sort(_super_calendar_core.compareDayEvents);
342
+ return map;
343
+ }, [eventsByDayProp, events]);
344
+ const Chip = renderEvent;
345
+ const eventRowHeight = 32 + maxVisibleEventCount * 20;
346
+ const { weeks, weekdays } = (0, react.useMemo)(() => (0, _super_calendar_core.buildMonthGrid)(date, {
347
+ weekStartsOn,
348
+ selectedRange,
349
+ selectedDates,
350
+ minDate,
351
+ maxDate,
352
+ isDateDisabled,
353
+ locale
354
+ }), [
355
+ date,
356
+ weekStartsOn,
357
+ selectedRange,
358
+ selectedDates,
359
+ minDate,
360
+ maxDate,
361
+ isDateDisabled,
362
+ locale
363
+ ]);
364
+ const initialFocus = (0, react.useMemo)(() => {
365
+ const inMonth = (d) => !!d && (0, date_fns.isSameMonth)(d, date);
366
+ if (inMonth(selectedRange?.start)) return (0, date_fns.startOfDay)(selectedRange.start);
367
+ const picked = selectedDates?.find(inMonth);
368
+ if (picked) return (0, date_fns.startOfDay)(picked);
369
+ const today = /* @__PURE__ */ new Date();
370
+ return (0, date_fns.isSameMonth)(today, date) ? (0, date_fns.startOfDay)(today) : (0, date_fns.startOfMonth)(date);
371
+ }, [
372
+ date,
373
+ selectedRange,
374
+ selectedDates
375
+ ]);
376
+ const [focusedDate, setFocusedDate] = (0, react.useState)(initialFocus);
377
+ const initialFocusRef = (0, react.useRef)(initialFocus);
378
+ initialFocusRef.current = initialFocus;
379
+ (0, react.useEffect)(() => setFocusedDate(initialFocusRef.current), [(0, date_fns.format)(date, "yyyy-MM")]);
380
+ const [hoveredKey, setHoveredKey] = (0, react.useState)(null);
381
+ const gridRef = (0, react.useRef)(null);
382
+ const focusKey = (0, date_fns.format)(focusedDate, "yyyy-MM-dd");
383
+ const onKeyDown = (e) => {
384
+ let next = null;
385
+ if (e.key === "ArrowLeft") next = (0, date_fns.addDays)(focusedDate, -1);
386
+ else if (e.key === "ArrowRight") next = (0, date_fns.addDays)(focusedDate, 1);
387
+ else if (e.key === "ArrowUp") next = (0, date_fns.addDays)(focusedDate, -7);
388
+ else if (e.key === "ArrowDown") next = (0, date_fns.addDays)(focusedDate, 7);
389
+ else if (e.key === "Home") next = (0, date_fns.startOfMonth)(date);
390
+ else if (e.key === "End") next = (0, date_fns.endOfMonth)(date);
391
+ else return;
392
+ e.preventDefault();
393
+ if (!(0, date_fns.isSameMonth)(next, date)) return;
394
+ setFocusedDate(next);
395
+ const key = (0, date_fns.format)(next, "yyyy-MM-dd");
396
+ gridRef.current?.querySelector(`[data-day="${key}"]`)?.focus();
397
+ };
398
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
399
+ className,
400
+ style: {
401
+ fontFamily: theme.fontFamily,
402
+ color: theme.text,
403
+ ...style
404
+ },
405
+ children: [
406
+ showTitle ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
407
+ style: {
408
+ fontSize: 17,
409
+ fontWeight: 700,
410
+ padding: "10px 14px 6px"
411
+ },
412
+ children: (0, date_fns.format)(date, "MMMM yyyy", locale ? { locale } : void 0)
413
+ }) : null,
414
+ showWeekdays ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
415
+ style: {
416
+ display: "grid",
417
+ gridTemplateColumns: "repeat(7, minmax(0, 1fr))",
418
+ borderBottom: `1px solid ${theme.gridLine}`,
419
+ padding: "6px 0"
420
+ },
421
+ children: weekdays.map((wd) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
422
+ style: {
423
+ textAlign: "center",
424
+ fontSize: 12,
425
+ fontWeight: 600,
426
+ color: theme.textMuted
427
+ },
428
+ children: wd.label
429
+ }, wd.label))
430
+ }) : null,
431
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
432
+ ref: gridRef,
433
+ role: "grid",
434
+ "aria-label": (0, date_fns.format)(date, "MMMM yyyy", locale ? { locale } : void 0),
435
+ onKeyDown: dayRoving ? onKeyDown : void 0,
436
+ children: weeks.map((week) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
437
+ role: "row",
438
+ style: {
439
+ display: "grid",
440
+ gridTemplateColumns: "repeat(7, minmax(0, 1fr))"
441
+ },
442
+ children: week.days.map((day) => {
443
+ const hidden = !showAdjacentMonths && !day.isCurrentMonth;
444
+ const cellHeight = eventsMode ? eventRowHeight : theme.cellHeight;
445
+ if (hidden) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
446
+ role: "gridcell",
447
+ "aria-hidden": true,
448
+ style: { height: cellHeight }
449
+ }, day.id);
450
+ const band = rangeBandStyle(day, theme, fillCellOnSelection);
451
+ const label = (0, date_fns.format)(day.date, "EEEE, d MMMM yyyy", locale ? { locale } : void 0);
452
+ if (eventsMode) {
453
+ const dayEvents = eventsByDay.get((0, date_fns.startOfDay)(day.date).toISOString()) ?? [];
454
+ const visible = (0, _super_calendar_core.monthVisibleCount)(dayEvents.length, {
455
+ full: maxVisibleEventCount,
456
+ withMore: Math.max(maxVisibleEventCount - 1, 1)
457
+ });
458
+ const shown = dayEvents.slice(0, visible);
459
+ const rest = dayEvents.slice(visible);
460
+ const overflow = rest.length > 0;
461
+ const dayLabel = (0, date_fns.format)(day.date, "d MMMM", locale ? { locale } : void 0);
462
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
463
+ role: "gridcell",
464
+ "data-day": day.id,
465
+ tabIndex: keyboardDayNavigation ? day.id === focusKey ? 0 : -1 : void 0,
466
+ "aria-disabled": day.isDisabled || void 0,
467
+ "aria-label": label,
468
+ style: eventCellStyle(day, theme, cellHeight),
469
+ onClick: day.isDisabled ? void 0 : () => onPressDay?.(day.date),
470
+ onKeyDown: keyboardDayNavigation && !day.isDisabled ? (e) => {
471
+ if (e.target !== e.currentTarget) return;
472
+ if (e.key === "Enter" || e.key === " ") {
473
+ e.preventDefault();
474
+ onPressDay?.(day.date);
475
+ }
476
+ } : void 0,
477
+ children: [
478
+ band ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
479
+ "data-band": true,
480
+ "aria-hidden": true,
481
+ style: band
482
+ }) : null,
483
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
484
+ style: compactBadgeStyle(day, theme),
485
+ children: day.label
486
+ }),
487
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
488
+ style: {
489
+ display: "flex",
490
+ flexDirection: "column",
491
+ gap: CHIP_GAP
492
+ },
493
+ children: [shown.map((event) => {
494
+ const onPress = () => onPressEvent?.(event);
495
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
496
+ type: "button",
497
+ onClick: (e) => {
498
+ e.stopPropagation();
499
+ onPress();
500
+ },
501
+ style: chipButtonStyle,
502
+ title: event.title,
503
+ "aria-label": `${event.title}, ${dayLabel}`,
504
+ children: Chip ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chip, {
505
+ event,
506
+ onPress
507
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
508
+ style: chipStyle(theme),
509
+ children: event.title
510
+ })
511
+ }, `${event.start.toISOString()}:${event.title}`);
512
+ }), overflow ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
513
+ type: "button",
514
+ onClick: (e) => {
515
+ e.stopPropagation();
516
+ onPressMore?.(rest, day.date);
517
+ },
518
+ style: moreButtonStyle(theme),
519
+ "aria-label": `${rest.length} more events, ${dayLabel}`,
520
+ children: moreLabel.replace("{moreCount}", String(rest.length))
521
+ }) : null]
522
+ })
523
+ ]
524
+ }, day.id);
525
+ }
526
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
527
+ type: "button",
528
+ role: "gridcell",
529
+ "data-day": day.id,
530
+ tabIndex: day.id === focusKey ? 0 : -1,
531
+ "aria-disabled": day.isDisabled || void 0,
532
+ "aria-label": label,
533
+ "aria-pressed": day.isSelected || day.isInRange,
534
+ style: dayCellStyle(day, theme),
535
+ onClick: day.isDisabled ? void 0 : () => onPressDay?.(day.date),
536
+ onMouseEnter: day.isDisabled ? void 0 : () => setHoveredKey(day.id),
537
+ onMouseLeave: () => setHoveredKey((k) => k === day.id ? null : k),
538
+ children: [band ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
539
+ "data-band": true,
540
+ "aria-hidden": true,
541
+ style: band
542
+ }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
543
+ style: badgeStyle(day, theme, hoveredKey === day.id),
544
+ children: day.label
545
+ })]
546
+ }, day.id);
547
+ })
548
+ }, week.id))
549
+ })
550
+ ]
551
+ });
552
+ }
553
+ //#endregion
554
+ //#region src/TimeGrid.tsx
555
+ const HOURS = Array.from({ length: 24 }, (_, h) => h);
556
+ const GUTTER_WIDTH = 56;
557
+ const clamp = (v, lo, hi) => Math.min(hi, Math.max(lo, v));
558
+ const DOM_TITLE_LINE_HEIGHT = 16;
559
+ const DOM_TIME_LINE_HEIGHT = 30;
560
+ const DOM_BOX_PADDING_V = 2;
561
+ function DefaultDomEvent({ event, mode, isAllDay, boxHeight, ampm = false, theme }) {
562
+ const timeLabel = (0, _super_calendar_core.eventTimeLabel)({
563
+ mode,
564
+ isAllDay,
565
+ start: event.start,
566
+ end: event.end,
567
+ ampm,
568
+ showTime: true
569
+ });
570
+ const { titleMaxLines, showTime } = (0, _super_calendar_core.eventChipLayout)({
571
+ boxHeightPx: boxHeight,
572
+ mode,
573
+ hasTime: !isAllDay && timeLabel != null,
574
+ titleLineHeightPx: DOM_TITLE_LINE_HEIGHT,
575
+ timeLineHeightPx: DOM_TIME_LINE_HEIGHT,
576
+ paddingYPx: DOM_BOX_PADDING_V
577
+ });
578
+ const oneLine = (0, _super_calendar_core.titleNumberOfLines)(mode, isAllDay) === 1;
579
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
580
+ style: {
581
+ height: "100%",
582
+ boxSizing: "border-box",
583
+ overflow: "hidden",
584
+ padding: `${DOM_BOX_PADDING_V}px 6px`,
585
+ borderRadius: 6,
586
+ background: theme.eventBackground,
587
+ color: theme.eventText,
588
+ fontSize: 12,
589
+ lineHeight: `${DOM_TITLE_LINE_HEIGHT}px`
590
+ },
591
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
592
+ style: {
593
+ fontWeight: 600,
594
+ overflow: "hidden",
595
+ ...oneLine ? { whiteSpace: "nowrap" } : {
596
+ wordBreak: "break-word",
597
+ ...titleMaxLines > 0 ? { maxHeight: titleMaxLines * DOM_TITLE_LINE_HEIGHT } : null
598
+ }
599
+ },
600
+ children: event.title
601
+ }), showTime ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
602
+ style: {
603
+ opacity: .75,
604
+ overflow: "hidden",
605
+ maxHeight: DOM_TIME_LINE_HEIGHT
606
+ },
607
+ children: timeLabel
608
+ }) : null]
609
+ });
610
+ }
611
+ /**
612
+ * A day / week / N-day time grid rendered with plain DOM elements. Events are
613
+ * positioned with the library's pure `layoutDayEvents`, so overlap columns and
614
+ * multi-day clipping match the React Native renderer. Supports Ctrl/⌘-scroll and
615
+ * two-finger pinch to zoom, and pointer drag to move / resize events.
616
+ */
617
+ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStartsOn = 0, hourHeight: initialHourHeight = 48, scrollOffsetMinutes = 480, zoomable = true, minHourHeight = 24, maxHourHeight = 160, dragStepMinutes = 15, ampm = false, timeslots = 1, businessHours, showNowIndicator = true, showAllDayEventCell = true, locale, theme: themeOverrides, height = 600, renderEvent, hourComponent, onPressEvent, onPressDateHeader, onPressCell, onCreateEvent, onDragStart, onDragEvent, className, style }) {
618
+ const theme = (0, react.useMemo)(() => mergeDomTheme(themeOverrides), [themeOverrides]);
619
+ const scrollRef = (0, react.useRef)(null);
620
+ const dfns = locale ? { locale } : void 0;
621
+ const snapHours = dragStepMinutes / 60;
622
+ const [hourHeight, setHourHeight] = (0, react.useState)(initialHourHeight);
623
+ (0, react.useEffect)(() => setHourHeight(initialHourHeight), [initialHourHeight]);
624
+ const hourHeightRef = (0, react.useRef)(hourHeight);
625
+ hourHeightRef.current = hourHeight;
626
+ const [drag, setDrag] = (0, react.useState)(null);
627
+ const dragRef = (0, react.useRef)(null);
628
+ const dragOrigin = (0, react.useRef)(null);
629
+ const applyDrag = (next) => {
630
+ dragRef.current = next;
631
+ setDrag(next);
632
+ };
633
+ const [createBox, setCreateBox] = (0, react.useState)(null);
634
+ const createOrigin = (0, react.useRef)(null);
635
+ const cellEnabled = !!onPressCell || !!onCreateEvent;
636
+ const days = (0, react.useMemo)(() => (0, _super_calendar_core.getViewDays)(mode, date, weekStartsOn, numberOfDays), [
637
+ mode,
638
+ date,
639
+ weekStartsOn,
640
+ numberOfDays
641
+ ]);
642
+ const allDayByDay = (0, react.useMemo)(() => days.map((day) => {
643
+ const dayStart = (0, date_fns.startOfDay)(day);
644
+ const dayEnd = (0, date_fns.addDays)(dayStart, 1);
645
+ return events.filter((e) => (0, _super_calendar_core.isAllDayEvent)(e) && e.start < dayEnd && e.end > dayStart);
646
+ }), [days, events]);
647
+ const hasAllDay = allDayByDay.some((list) => list.length > 0);
648
+ (0, react.useEffect)(() => {
649
+ if (scrollRef.current) scrollRef.current.scrollTop = scrollOffsetMinutes / 60 * hourHeightRef.current;
650
+ }, [scrollOffsetMinutes]);
651
+ (0, react.useEffect)(() => {
652
+ const el = scrollRef.current;
653
+ if (!el || !zoomable) return;
654
+ const onWheel = (e) => {
655
+ if (!e.ctrlKey && !e.metaKey) return;
656
+ e.preventDefault();
657
+ setHourHeight((h) => clamp(h * (1 - e.deltaY * .0015), minHourHeight, maxHourHeight));
658
+ };
659
+ el.addEventListener("wheel", onWheel, { passive: false });
660
+ return () => el.removeEventListener("wheel", onWheel);
661
+ }, [
662
+ zoomable,
663
+ minHourHeight,
664
+ maxHourHeight
665
+ ]);
666
+ const pinch = (0, react.useRef)(/* @__PURE__ */ new Map());
667
+ const pinchBase = (0, react.useRef)(null);
668
+ const onBodyPointerDown = (e) => {
669
+ if (!zoomable || e.pointerType !== "touch") return;
670
+ pinch.current.set(e.pointerId, e.clientY);
671
+ if (pinch.current.size === 2) {
672
+ const ys = [...pinch.current.values()];
673
+ pinchBase.current = {
674
+ dist: Math.abs(ys[0] - ys[1]),
675
+ height: hourHeight
676
+ };
677
+ }
678
+ };
679
+ const onBodyPointerMove = (e) => {
680
+ if (!pinch.current.has(e.pointerId)) return;
681
+ pinch.current.set(e.pointerId, e.clientY);
682
+ if (pinch.current.size === 2 && pinchBase.current) {
683
+ const ys = [...pinch.current.values()];
684
+ const ratio = Math.abs(ys[0] - ys[1]) / (pinchBase.current.dist || 1);
685
+ setHourHeight(clamp(pinchBase.current.height * ratio, minHourHeight, maxHourHeight));
686
+ }
687
+ };
688
+ const onBodyPointerUp = (e) => {
689
+ pinch.current.delete(e.pointerId);
690
+ if (pinch.current.size < 2) pinchBase.current = null;
691
+ };
692
+ const Renderer = renderEvent;
693
+ const totalHeight = 24 * hourHeight;
694
+ const beginDrag = (e, event, key, kind, startHours, durationHours) => {
695
+ if (!onDragEvent) return;
696
+ e.stopPropagation();
697
+ try {
698
+ e.target.setPointerCapture?.(e.pointerId);
699
+ } catch {}
700
+ dragOrigin.current = {
701
+ pointerY: e.clientY,
702
+ startHours,
703
+ durationHours
704
+ };
705
+ applyDrag({
706
+ key,
707
+ kind,
708
+ startHours,
709
+ durationHours,
710
+ moved: false
711
+ });
712
+ onDragStart?.(event);
713
+ };
714
+ const cancelDrag = () => {
715
+ applyDrag(null);
716
+ dragOrigin.current = null;
717
+ };
718
+ const moveDrag = (e) => {
719
+ const d = dragRef.current;
720
+ if (!d || !dragOrigin.current) return;
721
+ const dHours = (e.clientY - dragOrigin.current.pointerY) / hourHeightRef.current;
722
+ const snap = (v) => Math.round(v / snapHours) * snapHours;
723
+ if (d.kind === "move") {
724
+ const startHours = clamp(snap(dragOrigin.current.startHours + dHours), 0, 24 - d.durationHours);
725
+ applyDrag({
726
+ ...d,
727
+ startHours,
728
+ moved: true
729
+ });
730
+ } else {
731
+ const durationHours = clamp(snap(dragOrigin.current.durationHours + dHours), snapHours, 24 - d.startHours);
732
+ applyDrag({
733
+ ...d,
734
+ durationHours,
735
+ moved: true
736
+ });
737
+ }
738
+ };
739
+ const endDrag = (e, day, event, onPress) => {
740
+ const d = dragRef.current;
741
+ if (!d) return;
742
+ try {
743
+ e.target.releasePointerCapture?.(e.pointerId);
744
+ } catch {}
745
+ if (!d.moved) {
746
+ applyDrag(null);
747
+ onPress();
748
+ return;
749
+ }
750
+ const base = (0, date_fns.startOfDay)(day);
751
+ const start = (0, date_fns.addMinutes)(base, Math.round(d.startHours * 60));
752
+ const end = (0, date_fns.addMinutes)(base, Math.round((d.startHours + d.durationHours) * 60));
753
+ onDragEvent?.(event, start, end);
754
+ applyDrag(null);
755
+ dragOrigin.current = null;
756
+ };
757
+ const pxFromTop = (el, clientY) => clientY - el.getBoundingClientRect().top;
758
+ const beginCreate = (e, dayIndex) => {
759
+ if (!cellEnabled || e.pointerType === "touch") return;
760
+ if (e.target !== e.currentTarget || e.button > 0) return;
761
+ const el = e.currentTarget;
762
+ const startPx = pxFromTop(el, e.clientY);
763
+ try {
764
+ el.setPointerCapture?.(e.pointerId);
765
+ } catch {}
766
+ createOrigin.current = {
767
+ el,
768
+ dayIndex,
769
+ startPx
770
+ };
771
+ setCreateBox({
772
+ dayIndex,
773
+ topPx: startPx,
774
+ heightPx: 0
775
+ });
776
+ };
777
+ const moveCreate = (e) => {
778
+ const o = createOrigin.current;
779
+ if (!o) return;
780
+ const cur = pxFromTop(o.el, e.clientY);
781
+ setCreateBox({
782
+ dayIndex: o.dayIndex,
783
+ topPx: Math.min(o.startPx, cur),
784
+ heightPx: Math.abs(cur - o.startPx)
785
+ });
786
+ };
787
+ const endCreate = (e) => {
788
+ const o = createOrigin.current;
789
+ if (!o) return;
790
+ try {
791
+ o.el.releasePointerCapture?.(e.pointerId);
792
+ } catch {}
793
+ const endPx = pxFromTop(o.el, e.clientY);
794
+ const day = days[o.dayIndex];
795
+ const moved = Math.abs(endPx - o.startPx) > 4;
796
+ const h = hourHeightRef.current;
797
+ if (moved && onCreateEvent) {
798
+ const range = (0, _super_calendar_core.cellRangeFromDrag)(day, o.startPx, endPx, h, 0, dragStepMinutes);
799
+ if (range) onCreateEvent(range.start, range.end);
800
+ } else if (onPressCell) {
801
+ const at = (0, _super_calendar_core.cellRangeFromDrag)(day, o.startPx, o.startPx, h, 0, dragStepMinutes);
802
+ if (at) onPressCell(at.start);
803
+ }
804
+ createOrigin.current = null;
805
+ setCreateBox(null);
806
+ };
807
+ const cancelCreate = () => {
808
+ createOrigin.current = null;
809
+ setCreateBox(null);
810
+ };
811
+ const positionedByDay = (0, react.useMemo)(() => days.map((day) => (0, _super_calendar_core.layoutDayEvents)(events, day)), [days, events]);
812
+ const bandsByDay = (0, react.useMemo)(() => days.map((day) => (0, _super_calendar_core.closedHourBands)(day, businessHours)), [days, businessHours]);
813
+ const gridLines = (0, react.useMemo)(() => {
814
+ const hourLines = `repeating-linear-gradient(to bottom, transparent 0, transparent ${hourHeight - 1}px, ${theme.gridLine} ${hourHeight - 1}px, ${theme.gridLine} ${hourHeight}px)`;
815
+ if (timeslots <= 1) return hourLines;
816
+ const slotHeight = hourHeight / timeslots;
817
+ return `${hourLines}, repeating-linear-gradient(to bottom, transparent 0, transparent ${slotHeight - 1}px, ${theme.gridLine}80 ${slotHeight - 1}px, ${theme.gridLine}80 ${slotHeight}px)`;
818
+ }, [
819
+ hourHeight,
820
+ timeslots,
821
+ theme.gridLine
822
+ ]);
823
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
824
+ className,
825
+ style: {
826
+ fontFamily: theme.fontFamily,
827
+ color: theme.text,
828
+ display: "flex",
829
+ flexDirection: "column",
830
+ ...style
831
+ },
832
+ children: [
833
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
834
+ style: {
835
+ display: "flex",
836
+ borderBottom: `1px solid ${theme.gridLine}`
837
+ },
838
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
839
+ width: GUTTER_WIDTH,
840
+ flex: "none"
841
+ } }), days.map((day) => {
842
+ const today = (0, _super_calendar_core.getIsToday)(day);
843
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
844
+ type: "button",
845
+ tabIndex: onPressDateHeader ? 0 : -1,
846
+ "aria-hidden": onPressDateHeader ? void 0 : true,
847
+ onClick: onPressDateHeader ? () => onPressDateHeader(day) : void 0,
848
+ style: {
849
+ flex: 1,
850
+ border: "none",
851
+ background: "transparent",
852
+ font: "inherit",
853
+ color: theme.textMuted,
854
+ cursor: onPressDateHeader ? "pointer" : "default",
855
+ padding: "6px 0",
856
+ display: "flex",
857
+ flexDirection: "column",
858
+ alignItems: "center",
859
+ gap: 2
860
+ },
861
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
862
+ style: {
863
+ fontSize: 11,
864
+ fontWeight: 600
865
+ },
866
+ children: (0, date_fns.format)(day, "EEE", dfns)
867
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
868
+ style: {
869
+ width: 28,
870
+ height: 28,
871
+ borderRadius: "50%",
872
+ display: "flex",
873
+ alignItems: "center",
874
+ justifyContent: "center",
875
+ fontSize: 15,
876
+ fontWeight: 600,
877
+ background: today ? theme.todayBackground : "transparent",
878
+ color: today ? theme.todayText : theme.text
879
+ },
880
+ children: (0, date_fns.format)(day, "d", dfns)
881
+ })]
882
+ }, day.toISOString());
883
+ })]
884
+ }),
885
+ showAllDayEventCell && hasAllDay ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
886
+ style: {
887
+ display: "flex",
888
+ borderBottom: `1px solid ${theme.gridLine}`
889
+ },
890
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
891
+ style: {
892
+ width: GUTTER_WIDTH,
893
+ flex: "none",
894
+ fontSize: 10,
895
+ color: theme.textMuted,
896
+ textAlign: "right",
897
+ padding: "4px 6px 0 0"
898
+ },
899
+ children: "all-day"
900
+ }), allDayByDay.map((list, i) => {
901
+ const dayStart = (0, date_fns.startOfDay)(days[i]);
902
+ const dayEnd = (0, date_fns.addDays)(dayStart, 1);
903
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
904
+ style: {
905
+ flex: 1,
906
+ minWidth: 0,
907
+ borderLeft: "1px solid transparent",
908
+ padding: "2px 1px",
909
+ display: "flex",
910
+ flexDirection: "column",
911
+ gap: 2
912
+ },
913
+ children: list.map((event) => {
914
+ const args = {
915
+ event,
916
+ mode,
917
+ isAllDay: true,
918
+ continuesBefore: event.start < dayStart,
919
+ continuesAfter: event.end > dayEnd,
920
+ ampm,
921
+ onPress: () => onPressEvent?.(event)
922
+ };
923
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
924
+ type: "button",
925
+ onClick: () => onPressEvent?.(event),
926
+ "aria-label": (0, _super_calendar_core.eventAccessibilityLabel)({
927
+ title: event.title,
928
+ isAllDay: true,
929
+ start: event.start,
930
+ end: event.end,
931
+ ampm
932
+ }),
933
+ style: {
934
+ border: "none",
935
+ padding: 0,
936
+ background: "transparent",
937
+ cursor: "pointer",
938
+ textAlign: "left",
939
+ height: 22
940
+ },
941
+ children: Renderer ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Renderer, { ...args }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultDomEvent, {
942
+ ...args,
943
+ theme
944
+ })
945
+ }, `${event.start.toISOString()}:${event.title}`);
946
+ })
947
+ }, days[i].toISOString());
948
+ })]
949
+ }) : null,
950
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
951
+ ref: scrollRef,
952
+ onPointerDown: onBodyPointerDown,
953
+ onPointerMove: onBodyPointerMove,
954
+ onPointerUp: onBodyPointerUp,
955
+ onPointerCancel: onBodyPointerUp,
956
+ style: {
957
+ overflowY: "auto",
958
+ height,
959
+ position: "relative",
960
+ touchAction: zoomable ? "pan-y" : "auto"
961
+ },
962
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
963
+ style: {
964
+ display: "flex",
965
+ height: totalHeight,
966
+ position: "relative"
967
+ },
968
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
969
+ style: {
970
+ width: GUTTER_WIDTH,
971
+ flex: "none",
972
+ position: "relative"
973
+ },
974
+ children: HOURS.map((h) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
975
+ style: {
976
+ position: "absolute",
977
+ top: h * hourHeight - 6,
978
+ right: 6,
979
+ fontSize: 10,
980
+ color: theme.textMuted
981
+ },
982
+ children: hourComponent ? hourComponent(h, ampm) : h === 0 ? "" : (0, _super_calendar_core.formatHour)(h, { ampm })
983
+ }, h))
984
+ }), days.map((day, dayIndex) => {
985
+ const positioned = positionedByDay[dayIndex];
986
+ const showNow = showNowIndicator && (0, _super_calendar_core.isSameCalendarDay)(day, /* @__PURE__ */ new Date());
987
+ const nowDate = /* @__PURE__ */ new Date();
988
+ const nowTop = (nowDate.getHours() * 60 + nowDate.getMinutes()) / 60 * hourHeight;
989
+ const bands = bandsByDay[dayIndex];
990
+ const ghost = createBox?.dayIndex === dayIndex ? createBox : null;
991
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
992
+ onPointerDown: cellEnabled ? (e) => beginCreate(e, dayIndex) : void 0,
993
+ onPointerMove: cellEnabled ? moveCreate : void 0,
994
+ onPointerUp: cellEnabled ? endCreate : void 0,
995
+ onPointerCancel: cellEnabled ? cancelCreate : void 0,
996
+ style: {
997
+ flex: 1,
998
+ position: "relative",
999
+ borderLeft: `1px solid ${theme.gridLine}`
1000
+ },
1001
+ children: [
1002
+ bands.map((b) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1003
+ "aria-hidden": true,
1004
+ style: {
1005
+ position: "absolute",
1006
+ left: 0,
1007
+ right: 0,
1008
+ top: b.start * hourHeight,
1009
+ height: (b.end - b.start) * hourHeight,
1010
+ background: theme.outsideHoursBackground,
1011
+ pointerEvents: "none",
1012
+ zIndex: 0
1013
+ }
1014
+ }, b.start)),
1015
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1016
+ "aria-hidden": true,
1017
+ style: {
1018
+ position: "absolute",
1019
+ inset: 0,
1020
+ pointerEvents: "none",
1021
+ zIndex: 0,
1022
+ backgroundImage: gridLines
1023
+ }
1024
+ }),
1025
+ positioned.map((pe, idx) => {
1026
+ const key = `${dayIndex}:${idx}`;
1027
+ const active = drag?.key === key ? drag : null;
1028
+ const startHours = active ? active.startHours : pe.startHours;
1029
+ const durationHours = active ? active.durationHours : pe.durationHours;
1030
+ const top = startHours * hourHeight;
1031
+ const boxHeight = Math.max(durationHours * hourHeight, 14);
1032
+ const widthPct = 100 / pe.columns;
1033
+ const onPress = () => onPressEvent?.(pe.event);
1034
+ const args = {
1035
+ event: pe.event,
1036
+ mode,
1037
+ isAllDay: false,
1038
+ boxHeight,
1039
+ continuesBefore: pe.continuesBefore,
1040
+ continuesAfter: pe.continuesAfter,
1041
+ ampm,
1042
+ onPress
1043
+ };
1044
+ const draggable = !!onDragEvent;
1045
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1046
+ role: "button",
1047
+ tabIndex: 0,
1048
+ "aria-label": (0, _super_calendar_core.eventAccessibilityLabel)({
1049
+ title: pe.event.title,
1050
+ isAllDay: false,
1051
+ start: pe.event.start,
1052
+ end: pe.event.end,
1053
+ ampm
1054
+ }),
1055
+ onKeyDown: (e) => {
1056
+ if (e.key === "Enter" || e.key === " ") {
1057
+ e.preventDefault();
1058
+ onPress();
1059
+ }
1060
+ },
1061
+ onPointerDown: draggable ? (e) => beginDrag(e, pe.event, key, "move", pe.startHours, pe.durationHours) : void 0,
1062
+ onPointerMove: draggable ? moveDrag : void 0,
1063
+ onPointerUp: draggable ? (e) => endDrag(e, day, pe.event, onPress) : void 0,
1064
+ onPointerCancel: draggable ? cancelDrag : void 0,
1065
+ onClick: draggable ? void 0 : onPress,
1066
+ style: {
1067
+ position: "absolute",
1068
+ top,
1069
+ height: boxHeight,
1070
+ left: `calc(${pe.column * widthPct}% + 1px)`,
1071
+ width: `calc(${widthPct}% - 2px)`,
1072
+ cursor: draggable ? "grab" : "pointer",
1073
+ touchAction: draggable ? "none" : "auto",
1074
+ zIndex: active ? 3 : 1,
1075
+ opacity: active ? .85 : 1
1076
+ },
1077
+ children: [Renderer ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Renderer, { ...args }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultDomEvent, {
1078
+ ...args,
1079
+ theme
1080
+ }), draggable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1081
+ onPointerDown: (e) => {
1082
+ e.stopPropagation();
1083
+ beginDrag(e, pe.event, key, "resize", pe.startHours, pe.durationHours);
1084
+ },
1085
+ style: {
1086
+ position: "absolute",
1087
+ left: 0,
1088
+ right: 0,
1089
+ bottom: 0,
1090
+ height: 8,
1091
+ cursor: "ns-resize",
1092
+ touchAction: "none"
1093
+ }
1094
+ }) : null]
1095
+ }, idx);
1096
+ }),
1097
+ showNow ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1098
+ style: {
1099
+ position: "absolute",
1100
+ top: nowTop,
1101
+ left: 0,
1102
+ right: 0,
1103
+ height: 0,
1104
+ zIndex: 2,
1105
+ pointerEvents: "none"
1106
+ },
1107
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
1108
+ height: 2,
1109
+ background: theme.nowIndicator
1110
+ } }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
1111
+ position: "absolute",
1112
+ left: -3,
1113
+ top: -3,
1114
+ width: 8,
1115
+ height: 8,
1116
+ borderRadius: "50%",
1117
+ background: theme.nowIndicator
1118
+ } })]
1119
+ }) : null,
1120
+ ghost ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1121
+ "aria-hidden": true,
1122
+ style: {
1123
+ position: "absolute",
1124
+ left: 1,
1125
+ right: 1,
1126
+ top: ghost.topPx,
1127
+ height: Math.max(ghost.heightPx, 2),
1128
+ background: theme.rangeBackground,
1129
+ border: `1px solid ${theme.selectedBackground}`,
1130
+ borderRadius: 6,
1131
+ opacity: .7,
1132
+ pointerEvents: "none",
1133
+ zIndex: 2
1134
+ }
1135
+ }) : null
1136
+ ]
1137
+ }, day.toISOString());
1138
+ })]
1139
+ })
1140
+ })
1141
+ ]
1142
+ });
1143
+ }
1144
+ //#endregion
1145
+ //#region src/Calendar.tsx
1146
+ /**
1147
+ * Batteries-included entry point for the react-dom renderer: it picks the right
1148
+ * view for `mode`. `month` renders a single {@link MonthView}; `schedule` renders
1149
+ * an {@link Agenda}; the time-grid modes render a {@link TimeGrid}. For a
1150
+ * scrolling month picker, use {@link MonthList} directly.
1151
+ */
1152
+ function Calendar({ mode = "week", date, events, weekStartsOn = 0, numberOfDays, locale, theme, height, className, style, onPressEvent, ampm, hourHeight, scrollOffsetMinutes, timeslots, businessHours, showNowIndicator, showAllDayEventCell, dragStepMinutes, onPressCell, onCreateEvent, onDragStart, onDragEvent, onPressDateHeader, renderTimeEvent, hourComponent, maxVisibleEventCount, moreLabel, showAdjacentMonths, fillCellOnSelection, selectedRange, selectedDates, minDate, maxDate, isDateDisabled, keyboardDayNavigation, onPressDay, onPressMore, renderMonthEvent, renderScheduleEvent }) {
1153
+ if (mode === "schedule") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Agenda, {
1154
+ events: events ?? [],
1155
+ locale,
1156
+ ampm,
1157
+ theme,
1158
+ height,
1159
+ activeDate: date,
1160
+ className,
1161
+ style,
1162
+ renderEvent: renderScheduleEvent,
1163
+ onPressEvent,
1164
+ onPressDay
1165
+ });
1166
+ if (mode === "month") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MonthView, {
1167
+ date,
1168
+ events: events ?? [],
1169
+ weekStartsOn,
1170
+ locale,
1171
+ theme,
1172
+ className,
1173
+ style,
1174
+ maxVisibleEventCount,
1175
+ moreLabel,
1176
+ showAdjacentMonths,
1177
+ fillCellOnSelection,
1178
+ selectedRange,
1179
+ selectedDates,
1180
+ minDate,
1181
+ maxDate,
1182
+ isDateDisabled,
1183
+ keyboardDayNavigation,
1184
+ onPressDay,
1185
+ onPressEvent,
1186
+ onPressMore,
1187
+ renderEvent: renderMonthEvent
1188
+ });
1189
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TimeGrid, {
1190
+ date,
1191
+ mode,
1192
+ events,
1193
+ weekStartsOn,
1194
+ numberOfDays,
1195
+ locale,
1196
+ theme,
1197
+ height,
1198
+ className,
1199
+ style,
1200
+ ampm,
1201
+ hourHeight,
1202
+ scrollOffsetMinutes,
1203
+ timeslots,
1204
+ businessHours,
1205
+ showNowIndicator,
1206
+ showAllDayEventCell,
1207
+ dragStepMinutes,
1208
+ onPressEvent,
1209
+ onPressCell,
1210
+ onCreateEvent,
1211
+ onDragStart,
1212
+ onDragEvent,
1213
+ onPressDateHeader,
1214
+ renderEvent: renderTimeEvent,
1215
+ hourComponent
1216
+ });
1217
+ }
1218
+ //#endregion
1219
+ //#region src/MonthList.tsx
1220
+ /**
1221
+ * A vertically scrolling, virtualized list of months: the date picker. Built on
1222
+ * Legend List's DOM renderer and the library's headless grid logic. Selection is
1223
+ * controlled, pass `selectedRange` (or `selectedDates`) and handle `onPressDay`.
1224
+ */
1225
+ function MonthList({ date, pastMonths = 1, futureMonths = 12, weekStartsOn = 0, events, renderEvent, maxVisibleEventCount, moreLabel, onPressEvent, onPressMore, selectedRange, selectedDates, fillCellOnSelection = false, locale, theme: themeOverrides, height = 480, minDate, maxDate, isDateDisabled, keyboardDayNavigation, onPressDay, className, style }) {
1226
+ const theme = (0, react.useMemo)(() => mergeDomTheme(themeOverrides), [themeOverrides]);
1227
+ const months = (0, react.useMemo)(() => {
1228
+ const first = (0, date_fns.startOfMonth)((0, date_fns.addMonths)(date, -pastMonths));
1229
+ const count = pastMonths + futureMonths + 1;
1230
+ return Array.from({ length: count }, (_, i) => (0, date_fns.addMonths)(first, i));
1231
+ }, [
1232
+ date,
1233
+ pastMonths,
1234
+ futureMonths
1235
+ ]);
1236
+ const weekdays = (0, react.useMemo)(() => (0, _super_calendar_core.buildMonthGrid)(date, {
1237
+ weekStartsOn,
1238
+ locale
1239
+ }).weekdays, [
1240
+ date,
1241
+ weekStartsOn,
1242
+ locale
1243
+ ]);
1244
+ const eventsByDay = (0, react.useMemo)(() => {
1245
+ if (!events) return void 0;
1246
+ const map = (0, _super_calendar_core.groupEventsByDay)(events);
1247
+ for (const list of map.values()) list.sort(_super_calendar_core.compareDayEvents);
1248
+ return map;
1249
+ }, [events]);
1250
+ const extraData = (0, react.useMemo)(() => [
1251
+ selectedRange,
1252
+ selectedDates,
1253
+ events
1254
+ ], [
1255
+ selectedRange,
1256
+ selectedDates,
1257
+ events
1258
+ ]);
1259
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1260
+ className,
1261
+ style: {
1262
+ fontFamily: theme.fontFamily,
1263
+ color: theme.text,
1264
+ ...style
1265
+ },
1266
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1267
+ style: {
1268
+ display: "grid",
1269
+ gridTemplateColumns: "repeat(7, minmax(0, 1fr))",
1270
+ borderBottom: `1px solid ${theme.gridLine}`,
1271
+ padding: "8px 0"
1272
+ },
1273
+ children: weekdays.map((wd) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1274
+ style: {
1275
+ textAlign: "center",
1276
+ fontSize: 12,
1277
+ fontWeight: 600,
1278
+ color: theme.textMuted
1279
+ },
1280
+ children: wd.label
1281
+ }, wd.label))
1282
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_legendapp_list_react.LegendList, {
1283
+ data: months,
1284
+ extraData,
1285
+ keyExtractor: (m) => m.toISOString(),
1286
+ recycleItems: false,
1287
+ estimatedItemSize: theme.cellHeight * 7 + 40,
1288
+ initialScrollIndex: pastMonths,
1289
+ style: {
1290
+ height,
1291
+ overflowY: "auto"
1292
+ },
1293
+ renderItem: ({ item }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MonthView, {
1294
+ date: item,
1295
+ weekStartsOn,
1296
+ events,
1297
+ eventsByDay,
1298
+ renderEvent,
1299
+ maxVisibleEventCount,
1300
+ moreLabel,
1301
+ onPressEvent,
1302
+ onPressMore,
1303
+ selectedRange,
1304
+ selectedDates,
1305
+ fillCellOnSelection,
1306
+ showAdjacentMonths: false,
1307
+ showWeekdays: false,
1308
+ locale,
1309
+ theme: themeOverrides,
1310
+ minDate,
1311
+ maxDate,
1312
+ isDateDisabled,
1313
+ keyboardDayNavigation,
1314
+ onPressDay
1315
+ })
1316
+ })]
1317
+ });
1318
+ }
1319
+ //#endregion
1320
+ exports.Agenda = Agenda;
1321
+ exports.Calendar = Calendar;
1322
+ exports.MonthList = MonthList;
1323
+ exports.MonthView = MonthView;
1324
+ exports.TimeGrid = TimeGrid;
1325
+ Object.defineProperty(exports, "buildMonthGrid", {
1326
+ enumerable: true,
1327
+ get: function() {
1328
+ return _super_calendar_core.buildMonthGrid;
1329
+ }
1330
+ });
1331
+ exports.darkDomTheme = darkDomTheme;
1332
+ Object.defineProperty(exports, "daySelectionState", {
1333
+ enumerable: true,
1334
+ get: function() {
1335
+ return _super_calendar_core.daySelectionState;
1336
+ }
1337
+ });
1338
+ exports.defaultDomTheme = defaultDomTheme;
1339
+ Object.defineProperty(exports, "getViewDays", {
1340
+ enumerable: true,
1341
+ get: function() {
1342
+ return _super_calendar_core.getViewDays;
1343
+ }
1344
+ });
1345
+ Object.defineProperty(exports, "isAllDayEvent", {
1346
+ enumerable: true,
1347
+ get: function() {
1348
+ return _super_calendar_core.isAllDayEvent;
1349
+ }
1350
+ });
1351
+ Object.defineProperty(exports, "isDateSelectable", {
1352
+ enumerable: true,
1353
+ get: function() {
1354
+ return _super_calendar_core.isDateSelectable;
1355
+ }
1356
+ });
1357
+ Object.defineProperty(exports, "isRangeEndpoint", {
1358
+ enumerable: true,
1359
+ get: function() {
1360
+ return _super_calendar_core.isRangeEndpoint;
1361
+ }
1362
+ });
1363
+ Object.defineProperty(exports, "isWithinDateRange", {
1364
+ enumerable: true,
1365
+ get: function() {
1366
+ return _super_calendar_core.isWithinDateRange;
1367
+ }
1368
+ });
1369
+ Object.defineProperty(exports, "layoutDayEvents", {
1370
+ enumerable: true,
1371
+ get: function() {
1372
+ return _super_calendar_core.layoutDayEvents;
1373
+ }
1374
+ });
1375
+ exports.mergeDomTheme = mergeDomTheme;
1376
+ Object.defineProperty(exports, "nextDateRange", {
1377
+ enumerable: true,
1378
+ get: function() {
1379
+ return _super_calendar_core.nextDateRange;
1380
+ }
1381
+ });
1382
+ Object.defineProperty(exports, "useDateRange", {
1383
+ enumerable: true,
1384
+ get: function() {
1385
+ return _super_calendar_core.useDateRange;
1386
+ }
1387
+ });
1388
+ Object.defineProperty(exports, "useMonthGrid", {
1389
+ enumerable: true,
1390
+ get: function() {
1391
+ return _super_calendar_core.useMonthGrid;
1392
+ }
1393
+ });