@super-calendar/dom 2.1.5 → 2.3.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 CHANGED
@@ -4,6 +4,43 @@ let date_fns = require("date-fns");
4
4
  let react = require("react");
5
5
  let _super_calendar_core = require("@super-calendar/core");
6
6
  let react_jsx_runtime = require("react/jsx-runtime");
7
+ //#region src/slots.ts
8
+ /** Join truthy class names (a dependency-free clsx-lite). */
9
+ function cn(...parts) {
10
+ return parts.filter(Boolean).join(" ") || void 0;
11
+ }
12
+ /**
13
+ * Build a slot resolver for one render. `slot(name, defaults)` returns the props to
14
+ * spread on that element: a merged `className`, the resolved inline `style`, and a
15
+ * stable `data-slot` hook for CSS/testing.
16
+ */
17
+ function createSlots({ classNames, styles }) {
18
+ return (name, defaults) => {
19
+ const slotClass = classNames?.[name];
20
+ const override = styles?.[name];
21
+ const style = {
22
+ ...defaults?.base,
23
+ ...slotClass ? void 0 : defaults?.themed,
24
+ ...override
25
+ };
26
+ return {
27
+ className: cn(slotClass),
28
+ style: Object.keys(style).length ? style : void 0,
29
+ "data-slot": name
30
+ };
31
+ };
32
+ }
33
+ /**
34
+ * Turn a map of boolean state flags into present/absent `data-*` attributes, so
35
+ * consumers can target state with CSS/Tailwind variants (e.g. `data-[today]:...`).
36
+ * A true flag renders the attribute (empty value); a false/undefined one omits it.
37
+ */
38
+ function dataState(flags) {
39
+ const out = {};
40
+ for (const key in flags) if (flags[key]) out[key] = "";
41
+ return out;
42
+ }
43
+ //#endregion
7
44
  //#region src/theme.ts
8
45
  const METRICS = {
9
46
  cellHeight: 48,
@@ -30,7 +67,7 @@ function mergeDomTheme(overrides, base = defaultDomTheme) {
30
67
  }
31
68
  //#endregion
32
69
  //#region src/Agenda.tsx
33
- function DefaultAgendaRow({ event, isAllDay, ampm = false, theme }) {
70
+ function DefaultAgendaRow({ event, isAllDay, ampm = false, cardProps }) {
34
71
  const time = (0, _super_calendar_core.eventTimeLabel)({
35
72
  mode: "schedule",
36
73
  isAllDay,
@@ -40,12 +77,7 @@ function DefaultAgendaRow({ event, isAllDay, ampm = false, theme }) {
40
77
  showTime: true
41
78
  }) ?? "";
42
79
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
43
- style: {
44
- padding: "6px 10px",
45
- borderRadius: 8,
46
- background: theme.eventBackground,
47
- color: theme.eventText
48
- },
80
+ ...cardProps,
49
81
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
50
82
  style: {
51
83
  fontWeight: 600,
@@ -72,8 +104,12 @@ function DefaultAgendaRow({ event, isAllDay, ampm = false, theme }) {
72
104
  * <Agenda events={events} onPressEvent={(e) => console.log(e.title)} />
73
105
  * ```
74
106
  */
75
- function Agenda({ events, locale, ampm = false, activeDate, theme: themeOverrides, height = 480, renderEvent, onPressEvent, onPressDay, className, style }) {
107
+ function Agenda({ events, locale, ampm = false, activeDate, theme: themeOverrides, height = 480, renderEvent, eventAccessibilityLabel, onPressEvent, onPressDay, className, style, classNames, styles }) {
76
108
  const theme = (0, react.useMemo)(() => mergeDomTheme(themeOverrides), [themeOverrides]);
109
+ const slot = createSlots({
110
+ classNames,
111
+ styles
112
+ });
77
113
  const Renderer = renderEvent;
78
114
  const rows = (0, react.useMemo)(() => {
79
115
  const sorted = [...events].sort((a, b) => a.start.getTime() - b.start.getTime());
@@ -117,24 +153,33 @@ function Agenda({ events, locale, ampm = false, activeDate, theme: themeOverride
117
153
  const highlighted = activeDate ? (0, date_fns.isSameDay)(item.date, activeDate) : (0, _super_calendar_core.getIsToday)(item.date);
118
154
  const label = (0, date_fns.format)(item.date, "EEEE, d LLLL", locale ? { locale } : void 0);
119
155
  const color = highlighted ? theme.todayBackground : theme.textMuted;
120
- return onPressDay ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
121
- type: "button",
122
- onClick: () => onPressDay(item.date),
123
- style: {
124
- ...headerStyle,
125
- color,
156
+ const headerProps = slot("dayHeader", {
157
+ base: onPressDay ? {
158
+ display: "block",
159
+ width: "100%",
126
160
  border: "none",
127
161
  background: "transparent",
128
162
  cursor: "pointer",
129
163
  font: "inherit",
130
164
  textAlign: "left"
165
+ } : {
166
+ display: "block",
167
+ width: "100%"
131
168
  },
169
+ themed: {
170
+ padding: "12px 12px 4px",
171
+ fontSize: 13,
172
+ fontWeight: 600,
173
+ color
174
+ }
175
+ });
176
+ return onPressDay ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
177
+ type: "button",
178
+ onClick: () => onPressDay(item.date),
179
+ ...headerProps,
132
180
  children: label
133
181
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
134
- style: {
135
- ...headerStyle,
136
- color
137
- },
182
+ ...headerProps,
138
183
  children: label
139
184
  });
140
185
  }
@@ -151,68 +196,78 @@ function Agenda({ events, locale, ampm = false, activeDate, theme: themeOverride
151
196
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
152
197
  type: "button",
153
198
  onClick: onPress,
154
- style: {
155
- display: "block",
156
- width: "100%",
157
- textAlign: "left",
158
- border: "none",
159
- background: "transparent",
160
- cursor: "pointer",
161
- font: "inherit",
162
- padding: "2px 12px"
163
- },
199
+ "aria-label": eventAccessibilityLabel ? eventAccessibilityLabel(event, {
200
+ mode: "schedule",
201
+ isAllDay,
202
+ ampm
203
+ }) : void 0,
204
+ ...slot("eventRow", {
205
+ base: {
206
+ display: "block",
207
+ width: "100%",
208
+ textAlign: "left",
209
+ border: "none",
210
+ background: "transparent",
211
+ cursor: "pointer",
212
+ font: "inherit"
213
+ },
214
+ themed: { padding: "2px 12px" }
215
+ }),
164
216
  children: Renderer ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Renderer, { ...args }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultAgendaRow, {
165
217
  ...args,
166
- theme
218
+ cardProps: slot("event", { themed: {
219
+ padding: "6px 10px",
220
+ borderRadius: 8,
221
+ background: theme.eventBackground,
222
+ color: theme.eventText
223
+ } })
167
224
  })
168
225
  });
169
226
  }
170
227
  }), rows.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
171
- style: {
228
+ ...slot("empty", { themed: {
172
229
  padding: "16px 12px",
173
230
  color: theme.textMuted,
174
231
  fontSize: 14
175
- },
232
+ } }),
176
233
  children: "No events"
177
234
  }) : null]
178
235
  });
179
236
  }
180
- const headerStyle = {
181
- display: "block",
182
- width: "100%",
183
- padding: "12px 12px 4px",
184
- fontSize: 13,
185
- fontWeight: 600
186
- };
187
237
  //#endregion
188
238
  //#region src/MonthView.tsx
189
239
  const DATE_ROW = 24;
190
240
  const CHIP_HEIGHT = 18;
191
241
  const CHIP_GAP = 2;
192
242
  const CELL_PAD = 4;
193
- function dayCellStyle(day, theme) {
243
+ function dayCellDefault(day, theme) {
194
244
  return {
195
- position: "relative",
196
- height: theme.cellHeight,
197
- border: "none",
198
- background: day.isWeekend && !day.isInRange ? theme.weekendBackground : "transparent",
199
- font: "inherit",
200
- fontSize: 15,
201
- color: day.isDisabled ? theme.textDisabled : theme.text,
202
- cursor: day.isDisabled ? "default" : "pointer",
203
- display: "flex",
204
- alignItems: "center",
205
- justifyContent: "center",
206
- padding: 0,
207
- WebkitTapHighlightColor: "transparent"
245
+ base: {
246
+ position: "relative",
247
+ height: theme.cellHeight,
248
+ border: "none",
249
+ font: "inherit",
250
+ cursor: day.isDisabled ? "default" : "pointer",
251
+ display: "flex",
252
+ alignItems: "center",
253
+ justifyContent: "center",
254
+ padding: 0,
255
+ WebkitTapHighlightColor: "transparent"
256
+ },
257
+ themed: {
258
+ background: day.isWeekend && !day.isInRange ? theme.weekendBackground : "transparent",
259
+ fontSize: 15,
260
+ color: day.isDisabled ? theme.textDisabled : theme.text
261
+ }
208
262
  };
209
263
  }
210
264
  /**
211
265
  * The range band behind a day, rendered as its own layer so it can be a centered
212
266
  * rounded strip (the default) or fill the whole cell (`fillCell`). Returns null
213
- * for days with no band. Endpoints get the leading/trailing pill rounding.
267
+ * for days with no band. Endpoints get the leading/trailing pill rounding. Its
268
+ * geometry is structural; only the fill colour is themed.
214
269
  */
215
- function rangeBandStyle(day, theme, fillCell) {
270
+ function rangeBandDefault(day, theme, fillCell) {
216
271
  const kind = (0, _super_calendar_core.rangeBandKind)(day, fillCell);
217
272
  if (kind === "none") return null;
218
273
  const fill = kind === "fill";
@@ -220,80 +275,94 @@ function rangeBandStyle(day, theme, fillCell) {
220
275
  const radius = fill ? 0 : theme.rangeBandHeight / 2;
221
276
  const rounding = (0, _super_calendar_core.bandRounding)(kind);
222
277
  const cap = `calc(50% - ${theme.dayBadgeSize / 2}px)`;
223
- const style = {
278
+ const base = {
224
279
  position: "absolute",
225
280
  left: rounding.start ? cap : 0,
226
281
  right: rounding.end ? cap : 0,
227
282
  top: inset,
228
283
  bottom: inset,
229
- background: theme.rangeBackground,
230
284
  zIndex: 0
231
285
  };
232
286
  if (rounding.start) {
233
- style.borderTopLeftRadius = radius;
234
- style.borderBottomLeftRadius = radius;
287
+ base.borderTopLeftRadius = radius;
288
+ base.borderBottomLeftRadius = radius;
235
289
  }
236
290
  if (rounding.end) {
237
- style.borderTopRightRadius = radius;
238
- style.borderBottomRightRadius = radius;
291
+ base.borderTopRightRadius = radius;
292
+ base.borderBottomRightRadius = radius;
239
293
  }
240
- return style;
294
+ return {
295
+ base,
296
+ themed: { background: theme.rangeBackground }
297
+ };
241
298
  }
242
- function badgeStyle(day, theme, hovered) {
299
+ function badgeDefault(day, theme, hovered) {
243
300
  const badge = (0, _super_calendar_core.dayBadgeKind)(day, day.isToday);
244
301
  const filled = badge !== "none";
245
302
  const background = filled ? badge === "today" ? theme.todayBackground : theme.selectedBackground : hovered ? theme.hoverBackground : "transparent";
246
303
  return {
247
- position: "relative",
248
- zIndex: 1,
249
- width: theme.dayBadgeSize,
250
- height: theme.dayBadgeSize,
251
- borderRadius: "50%",
252
- display: "flex",
253
- alignItems: "center",
254
- justifyContent: "center",
255
- background,
256
- color: filled ? day.isToday ? theme.todayText : theme.selectedText : "inherit"
304
+ base: {
305
+ position: "relative",
306
+ zIndex: 1,
307
+ width: theme.dayBadgeSize,
308
+ height: theme.dayBadgeSize,
309
+ borderRadius: "50%",
310
+ display: "flex",
311
+ alignItems: "center",
312
+ justifyContent: "center"
313
+ },
314
+ themed: {
315
+ background,
316
+ color: filled ? day.isToday ? theme.todayText : theme.selectedText : "inherit"
317
+ }
257
318
  };
258
319
  }
259
- function eventCellStyle(day, theme, height) {
320
+ function eventCellDefault(day, theme, height) {
260
321
  return {
261
- position: "relative",
262
- minHeight: height,
263
- minWidth: 0,
264
- border: "none",
265
- borderTop: `1px solid ${theme.gridLine}`,
266
- background: day.isWeekend && !day.isInRange ? theme.weekendBackground : "transparent",
267
- color: day.isDisabled ? theme.textDisabled : theme.text,
268
- cursor: day.isDisabled ? "default" : "pointer",
269
- display: "flex",
270
- flexDirection: "column",
271
- gap: CHIP_GAP,
272
- padding: CELL_PAD,
273
- boxSizing: "border-box",
274
- textAlign: "left",
275
- WebkitTapHighlightColor: "transparent"
322
+ base: {
323
+ position: "relative",
324
+ minHeight: height,
325
+ minWidth: 0,
326
+ border: "none",
327
+ cursor: day.isDisabled ? "default" : "pointer",
328
+ display: "flex",
329
+ flexDirection: "column",
330
+ gap: CHIP_GAP,
331
+ padding: CELL_PAD,
332
+ boxSizing: "border-box",
333
+ textAlign: "left",
334
+ WebkitTapHighlightColor: "transparent"
335
+ },
336
+ themed: {
337
+ borderTop: `1px solid ${theme.gridLine}`,
338
+ background: day.isWeekend && !day.isInRange ? theme.weekendBackground : "transparent",
339
+ color: day.isDisabled ? theme.textDisabled : theme.text
340
+ }
276
341
  };
277
342
  }
278
- function compactBadgeStyle(day, theme) {
343
+ function compactBadgeDefault(day, theme) {
279
344
  const badge = (0, _super_calendar_core.dayBadgeKind)(day, day.isToday);
280
345
  const filled = badge !== "none";
281
346
  return {
282
- position: "relative",
283
- zIndex: 1,
284
- alignSelf: "flex-end",
285
- width: DATE_ROW - 2,
286
- height: DATE_ROW - 2,
287
- borderRadius: "50%",
288
- display: "flex",
289
- alignItems: "center",
290
- justifyContent: "center",
291
- fontSize: 13,
292
- background: filled ? badge === "today" ? theme.todayBackground : theme.selectedBackground : "transparent",
293
- color: filled ? day.isToday ? theme.todayText : theme.selectedText : "inherit"
347
+ base: {
348
+ position: "relative",
349
+ zIndex: 1,
350
+ alignSelf: "flex-end",
351
+ width: DATE_ROW - 2,
352
+ height: DATE_ROW - 2,
353
+ borderRadius: "50%",
354
+ display: "flex",
355
+ alignItems: "center",
356
+ justifyContent: "center"
357
+ },
358
+ themed: {
359
+ fontSize: 13,
360
+ background: filled ? badge === "today" ? theme.todayBackground : theme.selectedBackground : "transparent",
361
+ color: filled ? day.isToday ? theme.todayText : theme.selectedText : "inherit"
362
+ }
294
363
  };
295
364
  }
296
- const chipButtonStyle = {
365
+ const chipButtonDefault = { base: {
297
366
  position: "relative",
298
367
  zIndex: 1,
299
368
  border: "none",
@@ -303,38 +372,46 @@ const chipButtonStyle = {
303
372
  cursor: "pointer",
304
373
  textAlign: "left",
305
374
  fontFamily: "inherit"
306
- };
307
- function chipStyle(theme) {
375
+ } };
376
+ function chipDefault(theme) {
308
377
  return {
309
- display: "block",
310
- height: CHIP_HEIGHT,
311
- lineHeight: `${CHIP_HEIGHT}px`,
312
- padding: "0 6px",
313
- borderRadius: 4,
314
- background: theme.eventBackground,
315
- color: theme.eventText,
316
- fontSize: 11,
317
- fontWeight: 600,
318
- whiteSpace: "nowrap",
319
- overflow: "hidden",
320
- textOverflow: "ellipsis"
378
+ base: {
379
+ display: "block",
380
+ height: CHIP_HEIGHT,
381
+ lineHeight: `${CHIP_HEIGHT}px`,
382
+ whiteSpace: "nowrap",
383
+ overflow: "hidden",
384
+ textOverflow: "ellipsis"
385
+ },
386
+ themed: {
387
+ padding: "0 6px",
388
+ borderRadius: 4,
389
+ background: theme.eventBackground,
390
+ color: theme.eventText,
391
+ fontSize: 11,
392
+ fontWeight: 600
393
+ }
321
394
  };
322
395
  }
323
- function moreButtonStyle(theme) {
396
+ function moreButtonDefault(theme) {
324
397
  return {
325
- position: "relative",
326
- zIndex: 1,
327
- border: "none",
328
- background: "transparent",
329
- cursor: "pointer",
330
- textAlign: "left",
331
- padding: "0 6px",
332
- height: CHIP_HEIGHT,
333
- lineHeight: `${CHIP_HEIGHT}px`,
334
- fontSize: 11,
335
- fontWeight: 600,
336
- fontFamily: "inherit",
337
- color: theme.textMuted
398
+ base: {
399
+ position: "relative",
400
+ zIndex: 1,
401
+ border: "none",
402
+ background: "transparent",
403
+ cursor: "pointer",
404
+ textAlign: "left",
405
+ height: CHIP_HEIGHT,
406
+ lineHeight: `${CHIP_HEIGHT}px`,
407
+ fontFamily: "inherit"
408
+ },
409
+ themed: {
410
+ padding: "0 6px",
411
+ fontSize: 11,
412
+ fontWeight: 600,
413
+ color: theme.textMuted
414
+ }
338
415
  };
339
416
  }
340
417
  /**
@@ -345,8 +422,12 @@ function moreButtonStyle(theme) {
345
422
  * <MonthView date={new Date()} events={events} onPressDay={(d) => setDate(d)} />
346
423
  * ```
347
424
  */
348
- 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 }) {
425
+ function MonthView({ date, weekStartsOn = 0, weekdayFormat = "short", events, eventsByDay: eventsByDayProp, renderEvent, eventAccessibilityLabel, maxVisibleEventCount = 3, moreLabel = "{moreCount} More", onPressEvent, onPressMore, selectedRange, selectedDates, showAdjacentMonths = true, fillCellOnSelection = false, showTitle = true, showWeekdays = true, locale, theme: themeOverrides, minDate, maxDate, isDateDisabled, onPressDay, onCreateEvent, keyboardDayNavigation = false, className, style, classNames, styles }) {
349
426
  const theme = (0, react.useMemo)(() => mergeDomTheme(themeOverrides), [themeOverrides]);
427
+ const slot = createSlots({
428
+ classNames,
429
+ styles
430
+ });
350
431
  const eventsMode = events !== void 0;
351
432
  const dayRoving = !eventsMode || keyboardDayNavigation;
352
433
  const eventsByDay = (0, react.useMemo)(() => {
@@ -359,6 +440,7 @@ function MonthView({ date, weekStartsOn = 0, events, eventsByDay: eventsByDayPro
359
440
  const eventRowHeight = 32 + maxVisibleEventCount * 20;
360
441
  const { weeks, weekdays } = (0, react.useMemo)(() => (0, _super_calendar_core.buildMonthGrid)(date, {
361
442
  weekStartsOn,
443
+ weekdayFormat,
362
444
  selectedRange,
363
445
  selectedDates,
364
446
  minDate,
@@ -368,6 +450,7 @@ function MonthView({ date, weekStartsOn = 0, events, eventsByDay: eventsByDayPro
368
450
  }), [
369
451
  date,
370
452
  weekStartsOn,
453
+ weekdayFormat,
371
454
  selectedRange,
372
455
  selectedDates,
373
456
  minDate,
@@ -394,6 +477,46 @@ function MonthView({ date, weekStartsOn = 0, events, eventsByDay: eventsByDayPro
394
477
  const [hoveredKey, setHoveredKey] = (0, react.useState)(null);
395
478
  const gridRef = (0, react.useRef)(null);
396
479
  const focusKey = (0, date_fns.format)(focusedDate, "yyyy-MM-dd");
480
+ const [creating, setCreating] = (0, react.useState)(null);
481
+ const creatingRef = (0, react.useRef)(creating);
482
+ creatingRef.current = creating;
483
+ const movedRef = (0, react.useRef)(false);
484
+ const suppressClickRef = (0, react.useRef)(false);
485
+ const beginCreate = (day) => {
486
+ if (!onCreateEvent) return;
487
+ const t = (0, date_fns.startOfDay)(day).getTime();
488
+ movedRef.current = false;
489
+ setCreating({
490
+ anchor: t,
491
+ hover: t
492
+ });
493
+ };
494
+ const extendCreate = (day) => {
495
+ if (!creatingRef.current) return;
496
+ const t = (0, date_fns.startOfDay)(day).getTime();
497
+ if (t !== creatingRef.current.hover) {
498
+ movedRef.current = true;
499
+ setCreating((c) => c ? {
500
+ ...c,
501
+ hover: t
502
+ } : c);
503
+ }
504
+ };
505
+ const isCreating = creating !== null;
506
+ (0, react.useEffect)(() => {
507
+ if (!isCreating) return;
508
+ const finish = () => {
509
+ const c = creatingRef.current;
510
+ setCreating(null);
511
+ if (!c || !movedRef.current) return;
512
+ suppressClickRef.current = true;
513
+ const lo = Math.min(c.anchor, c.hover);
514
+ const hi = Math.max(c.anchor, c.hover);
515
+ onCreateEvent?.(new Date(lo), (0, date_fns.addDays)(new Date(hi), 1));
516
+ };
517
+ window.addEventListener("pointerup", finish);
518
+ return () => window.removeEventListener("pointerup", finish);
519
+ }, [isCreating, onCreateEvent]);
397
520
  const onKeyDown = (e) => {
398
521
  let next = null;
399
522
  if (e.key === "ArrowLeft") next = (0, date_fns.addDays)(focusedDate, -1);
@@ -418,27 +541,31 @@ function MonthView({ date, weekStartsOn = 0, events, eventsByDay: eventsByDayPro
418
541
  },
419
542
  children: [
420
543
  showTitle ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
421
- style: {
544
+ ...slot("title", { themed: {
422
545
  fontSize: 17,
423
546
  fontWeight: 700,
424
547
  padding: "10px 14px 6px"
425
- },
548
+ } }),
426
549
  children: (0, date_fns.format)(date, "MMMM yyyy", locale ? { locale } : void 0)
427
550
  }) : null,
428
551
  showWeekdays ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
429
- style: {
430
- display: "grid",
431
- gridTemplateColumns: "repeat(7, minmax(0, 1fr))",
432
- borderBottom: `1px solid ${theme.gridLine}`,
433
- padding: "6px 0"
434
- },
552
+ ...slot("weekdays", {
553
+ base: {
554
+ display: "grid",
555
+ gridTemplateColumns: "repeat(7, minmax(0, 1fr))"
556
+ },
557
+ themed: {
558
+ borderBottom: `1px solid ${theme.gridLine}`,
559
+ padding: "6px 0"
560
+ }
561
+ }),
435
562
  children: weekdays.map((wd) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
436
- style: {
563
+ ...slot("weekday", { themed: {
437
564
  textAlign: "center",
438
565
  fontSize: 12,
439
566
  fontWeight: 600,
440
567
  color: theme.textMuted
441
- },
568
+ } }),
442
569
  children: wd.label
443
570
  }, wd.label))
444
571
  }) : null,
@@ -447,12 +574,13 @@ function MonthView({ date, weekStartsOn = 0, events, eventsByDay: eventsByDayPro
447
574
  role: "grid",
448
575
  "aria-label": (0, date_fns.format)(date, "MMMM yyyy", locale ? { locale } : void 0),
449
576
  onKeyDown: dayRoving ? onKeyDown : void 0,
577
+ ...slot("grid"),
450
578
  children: weeks.map((week) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
451
579
  role: "row",
452
- style: {
580
+ ...slot("week", { base: {
453
581
  display: "grid",
454
582
  gridTemplateColumns: "repeat(7, minmax(0, 1fr))"
455
- },
583
+ } }),
456
584
  children: week.days.map((day) => {
457
585
  const hidden = !showAdjacentMonths && !day.isCurrentMonth;
458
586
  const cellHeight = eventsMode ? eventRowHeight : theme.cellHeight;
@@ -461,8 +589,19 @@ function MonthView({ date, weekStartsOn = 0, events, eventsByDay: eventsByDayPro
461
589
  "aria-hidden": true,
462
590
  style: { height: cellHeight }
463
591
  }, day.id);
464
- const band = rangeBandStyle(day, theme, fillCellOnSelection);
592
+ const band = rangeBandDefault(day, theme, fillCellOnSelection);
465
593
  const label = (0, date_fns.format)(day.date, "EEEE, d MMMM yyyy", locale ? { locale } : void 0);
594
+ const dayTime = (0, date_fns.startOfDay)(day.date).getTime();
595
+ const inCreate = creating != null && dayTime >= Math.min(creating.anchor, creating.hover) && dayTime <= Math.max(creating.anchor, creating.hover);
596
+ const dayData = dataState({
597
+ "data-today": day.isToday,
598
+ "data-selected": day.isSelected,
599
+ "data-range": day.isInRange,
600
+ "data-weekend": day.isWeekend,
601
+ "data-outside": !day.isCurrentMonth,
602
+ "data-disabled": day.isDisabled,
603
+ "data-creating": inCreate
604
+ });
466
605
  if (eventsMode) {
467
606
  const dayEvents = eventsByDay.get((0, date_fns.startOfDay)(day.date).toISOString()) ?? [];
468
607
  const visible = (0, _super_calendar_core.monthVisibleCount)(dayEvents.length, {
@@ -473,14 +612,30 @@ function MonthView({ date, weekStartsOn = 0, events, eventsByDay: eventsByDayPro
473
612
  const rest = dayEvents.slice(visible);
474
613
  const overflow = rest.length > 0;
475
614
  const dayLabel = (0, date_fns.format)(day.date, "d MMMM", locale ? { locale } : void 0);
615
+ const dayCellProps = slot("day", eventCellDefault(day, theme, cellHeight));
476
616
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
477
617
  role: "gridcell",
478
618
  "data-day": day.id,
619
+ ...dayData,
479
620
  tabIndex: keyboardDayNavigation ? day.id === focusKey ? 0 : -1 : void 0,
480
621
  "aria-disabled": day.isDisabled || void 0,
481
622
  "aria-label": label,
482
- style: eventCellStyle(day, theme, cellHeight),
483
- onClick: day.isDisabled ? void 0 : () => onPressDay?.(day.date),
623
+ ...dayCellProps,
624
+ style: onCreateEvent ? {
625
+ ...dayCellProps.style,
626
+ touchAction: "none"
627
+ } : dayCellProps.style,
628
+ onPointerDown: onCreateEvent && !day.isDisabled ? (e) => {
629
+ if (e.button === 0) beginCreate(day.date);
630
+ } : void 0,
631
+ onPointerEnter: onCreateEvent ? () => extendCreate(day.date) : void 0,
632
+ onClick: day.isDisabled ? void 0 : () => {
633
+ if (suppressClickRef.current) {
634
+ suppressClickRef.current = false;
635
+ return;
636
+ }
637
+ onPressDay?.(day.date);
638
+ },
484
639
  onKeyDown: keyboardDayNavigation && !day.isDisabled ? (e) => {
485
640
  if (e.target !== e.currentTarget) return;
486
641
  if (e.key === "Enter" || e.key === " ") {
@@ -492,18 +647,19 @@ function MonthView({ date, weekStartsOn = 0, events, eventsByDay: eventsByDayPro
492
647
  band ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
493
648
  "data-band": true,
494
649
  "aria-hidden": true,
495
- style: band
650
+ ...slot("rangeBand", band)
496
651
  }) : null,
497
652
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
498
- style: compactBadgeStyle(day, theme),
653
+ ...dayData,
654
+ ...slot("dayBadge", compactBadgeDefault(day, theme)),
499
655
  children: day.label
500
656
  }),
501
657
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
502
- style: {
658
+ ...slot("events", { base: {
503
659
  display: "flex",
504
660
  flexDirection: "column",
505
661
  gap: CHIP_GAP
506
- },
662
+ } }),
507
663
  children: [shown.map((event) => {
508
664
  const onPress = () => onPressEvent?.(event);
509
665
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
@@ -512,14 +668,18 @@ function MonthView({ date, weekStartsOn = 0, events, eventsByDay: eventsByDayPro
512
668
  e.stopPropagation();
513
669
  onPress();
514
670
  },
515
- style: chipButtonStyle,
671
+ ...slot("chipButton", chipButtonDefault),
516
672
  title: event.title,
517
- "aria-label": `${event.title}, ${dayLabel}`,
673
+ "aria-label": eventAccessibilityLabel ? eventAccessibilityLabel(event, {
674
+ mode: "month",
675
+ isAllDay: (0, _super_calendar_core.isAllDayEvent)(event),
676
+ ampm: false
677
+ }) : `${event.title}, ${dayLabel}`,
518
678
  children: Chip ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chip, {
519
679
  event,
520
680
  onPress
521
681
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
522
- style: chipStyle(theme),
682
+ ...slot("chip", chipDefault(theme)),
523
683
  children: event.title
524
684
  })
525
685
  }, `${event.start.toISOString()}:${event.title}`);
@@ -529,7 +689,7 @@ function MonthView({ date, weekStartsOn = 0, events, eventsByDay: eventsByDayPro
529
689
  e.stopPropagation();
530
690
  onPressMore?.(rest, day.date);
531
691
  },
532
- style: moreButtonStyle(theme),
692
+ ...slot("more", moreButtonDefault(theme)),
533
693
  "aria-label": `${rest.length} more events, ${dayLabel}`,
534
694
  children: moreLabel.replace("{moreCount}", String(rest.length))
535
695
  }) : null]
@@ -541,20 +701,22 @@ function MonthView({ date, weekStartsOn = 0, events, eventsByDay: eventsByDayPro
541
701
  type: "button",
542
702
  role: "gridcell",
543
703
  "data-day": day.id,
704
+ ...dayData,
544
705
  tabIndex: day.id === focusKey ? 0 : -1,
545
706
  "aria-disabled": day.isDisabled || void 0,
546
707
  "aria-label": label,
547
708
  "aria-pressed": day.isSelected || day.isInRange,
548
- style: dayCellStyle(day, theme),
709
+ ...slot("day", dayCellDefault(day, theme)),
549
710
  onClick: day.isDisabled ? void 0 : () => onPressDay?.(day.date),
550
711
  onMouseEnter: day.isDisabled ? void 0 : () => setHoveredKey(day.id),
551
712
  onMouseLeave: () => setHoveredKey((k) => k === day.id ? null : k),
552
713
  children: [band ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
553
714
  "data-band": true,
554
715
  "aria-hidden": true,
555
- style: band
716
+ ...slot("rangeBand", band)
556
717
  }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
557
- style: badgeStyle(day, theme, hoveredKey === day.id),
718
+ ...dayData,
719
+ ...slot("dayBadge", badgeDefault(day, theme, hoveredKey === day.id)),
558
720
  children: day.label
559
721
  })]
560
722
  }, day.id);
@@ -568,11 +730,11 @@ function MonthView({ date, weekStartsOn = 0, events, eventsByDay: eventsByDayPro
568
730
  //#region src/TimeGrid.tsx
569
731
  const HOURS = Array.from({ length: 24 }, (_, h) => h);
570
732
  const GUTTER_WIDTH = 56;
571
- const clamp = (v, lo, hi) => Math.min(hi, Math.max(lo, v));
733
+ const clamp$1 = (v, lo, hi) => Math.min(hi, Math.max(lo, v));
572
734
  const DOM_TITLE_LINE_HEIGHT = 16;
573
735
  const DOM_TIME_LINE_HEIGHT = 30;
574
736
  const DOM_BOX_PADDING_V = 2;
575
- function DefaultDomEvent({ event, mode, isAllDay, boxHeight, ampm = false, theme }) {
737
+ function DefaultDomEvent({ event, mode, isAllDay, boxHeight, ampm = false, theme, boxProps }) {
576
738
  const timeLabel = (0, _super_calendar_core.eventTimeLabel)({
577
739
  mode,
578
740
  isAllDay,
@@ -590,17 +752,34 @@ function DefaultDomEvent({ event, mode, isAllDay, boxHeight, ampm = false, theme
590
752
  paddingYPx: DOM_BOX_PADDING_V
591
753
  });
592
754
  const oneLine = (0, _super_calendar_core.titleNumberOfLines)(mode, isAllDay) === 1;
755
+ const boxBase = {
756
+ height: "100%",
757
+ boxSizing: "border-box",
758
+ overflow: "hidden",
759
+ lineHeight: `${DOM_TITLE_LINE_HEIGHT}px`,
760
+ ...titleMaxLines === 1 && !showTime ? {
761
+ display: "flex",
762
+ flexDirection: "column",
763
+ justifyContent: "center"
764
+ } : null
765
+ };
766
+ const boxThemed = {
767
+ padding: `${DOM_BOX_PADDING_V}px 6px`,
768
+ borderRadius: 6,
769
+ background: theme.eventBackground,
770
+ color: theme.eventText,
771
+ fontSize: 12
772
+ };
593
773
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
594
- style: {
595
- height: "100%",
596
- boxSizing: "border-box",
597
- overflow: "hidden",
598
- padding: `${DOM_BOX_PADDING_V}px 6px`,
599
- borderRadius: 6,
600
- background: theme.eventBackground,
601
- color: theme.eventText,
602
- fontSize: 12,
603
- lineHeight: `${DOM_TITLE_LINE_HEIGHT}px`
774
+ className: boxProps?.className,
775
+ "data-slot": boxProps?.["data-slot"],
776
+ style: boxProps?.className ? {
777
+ ...boxBase,
778
+ ...boxProps.style
779
+ } : {
780
+ ...boxBase,
781
+ ...boxThemed,
782
+ ...boxProps?.style
604
783
  },
605
784
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
606
785
  style: {
@@ -633,8 +812,12 @@ function DefaultDomEvent({ event, mode, isAllDay, boxHeight, ampm = false, theme
633
812
  * <TimeGrid mode="week" date={new Date()} events={events} />
634
813
  * ```
635
814
  */
636
- 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 }) {
815
+ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStartsOn = 0, weekdayFormat = "short", 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, eventAccessibilityLabel, hourComponent, onPressEvent, onPressDateHeader, onPressCell, onCreateEvent, onDragStart, onDragEvent, className, style, classNames, styles }) {
637
816
  const theme = (0, react.useMemo)(() => mergeDomTheme(themeOverrides), [themeOverrides]);
817
+ const slot = createSlots({
818
+ classNames,
819
+ styles
820
+ });
638
821
  const scrollRef = (0, react.useRef)(null);
639
822
  const dfns = locale ? { locale } : void 0;
640
823
  const snapHours = dragStepMinutes / 60;
@@ -673,7 +856,7 @@ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStart
673
856
  const onWheel = (e) => {
674
857
  if (!e.ctrlKey && !e.metaKey) return;
675
858
  e.preventDefault();
676
- setHourHeight((h) => clamp(h * (1 - e.deltaY * .0015), minHourHeight, maxHourHeight));
859
+ setHourHeight((h) => clamp$1(h * (1 - e.deltaY * .0015), minHourHeight, maxHourHeight));
677
860
  };
678
861
  el.addEventListener("wheel", onWheel, { passive: false });
679
862
  return () => el.removeEventListener("wheel", onWheel);
@@ -701,7 +884,7 @@ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStart
701
884
  if (pinch.current.size === 2 && pinchBase.current) {
702
885
  const ys = [...pinch.current.values()];
703
886
  const ratio = Math.abs(ys[0] - ys[1]) / (pinchBase.current.dist || 1);
704
- setHourHeight(clamp(pinchBase.current.height * ratio, minHourHeight, maxHourHeight));
887
+ setHourHeight(clamp$1(pinchBase.current.height * ratio, minHourHeight, maxHourHeight));
705
888
  }
706
889
  };
707
890
  const onBodyPointerUp = (e) => {
@@ -740,14 +923,14 @@ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStart
740
923
  const dHours = (e.clientY - dragOrigin.current.pointerY) / hourHeightRef.current;
741
924
  const snap = (v) => Math.round(v / snapHours) * snapHours;
742
925
  if (d.kind === "move") {
743
- const startHours = clamp(snap(dragOrigin.current.startHours + dHours), 0, 24 - d.durationHours);
926
+ const startHours = clamp$1(snap(dragOrigin.current.startHours + dHours), 0, 24 - d.durationHours);
744
927
  applyDrag({
745
928
  ...d,
746
929
  startHours,
747
930
  moved: true
748
931
  });
749
932
  } else {
750
- const durationHours = clamp(snap(dragOrigin.current.durationHours + dHours), snapHours, 24 - d.startHours);
933
+ const durationHours = clamp$1(snap(dragOrigin.current.durationHours + dHours), snapHours, 24 - d.startHours);
751
934
  applyDrag({
752
935
  ...d,
753
936
  durationHours,
@@ -850,10 +1033,10 @@ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStart
850
1033
  },
851
1034
  children: [
852
1035
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
853
- style: {
854
- display: "flex",
855
- borderBottom: `1px solid ${theme.gridLine}`
856
- },
1036
+ ...slot("header", {
1037
+ base: { display: "flex" },
1038
+ themed: { borderBottom: `1px solid ${theme.gridLine}` }
1039
+ }),
857
1040
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
858
1041
  width: GUTTER_WIDTH,
859
1042
  flex: "none"
@@ -864,63 +1047,77 @@ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStart
864
1047
  tabIndex: onPressDateHeader ? 0 : -1,
865
1048
  "aria-hidden": onPressDateHeader ? void 0 : true,
866
1049
  onClick: onPressDateHeader ? () => onPressDateHeader(day) : void 0,
867
- style: {
868
- flex: 1,
869
- border: "none",
870
- background: "transparent",
871
- font: "inherit",
872
- color: theme.textMuted,
873
- cursor: onPressDateHeader ? "pointer" : "default",
874
- padding: "6px 0",
875
- display: "flex",
876
- flexDirection: "column",
877
- alignItems: "center",
878
- gap: 2
879
- },
1050
+ ...dataState({ "data-today": today }),
1051
+ ...slot("columnHeader", {
1052
+ base: {
1053
+ flex: 1,
1054
+ border: "none",
1055
+ background: "transparent",
1056
+ font: "inherit",
1057
+ cursor: onPressDateHeader ? "pointer" : "default",
1058
+ display: "flex",
1059
+ flexDirection: "column",
1060
+ alignItems: "center",
1061
+ gap: 2
1062
+ },
1063
+ themed: {
1064
+ color: theme.textMuted,
1065
+ padding: "6px 0"
1066
+ }
1067
+ }),
880
1068
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
881
- style: {
1069
+ ...slot("columnHeaderWeekday", { themed: {
882
1070
  fontSize: 11,
883
1071
  fontWeight: 600
884
- },
885
- children: (0, date_fns.format)(day, "EEE", dfns)
1072
+ } }),
1073
+ children: (0, date_fns.format)(day, (0, _super_calendar_core.weekdayFormatToken)(weekdayFormat), dfns)
886
1074
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
887
- style: {
888
- width: 28,
889
- height: 28,
890
- borderRadius: "50%",
891
- display: "flex",
892
- alignItems: "center",
893
- justifyContent: "center",
894
- fontSize: 15,
895
- fontWeight: 600,
896
- background: today ? theme.todayBackground : "transparent",
897
- color: today ? theme.todayText : theme.text
898
- },
1075
+ ...dataState({ "data-today": today }),
1076
+ ...slot("columnHeaderDate", {
1077
+ base: {
1078
+ width: 28,
1079
+ height: 28,
1080
+ borderRadius: "50%",
1081
+ display: "flex",
1082
+ alignItems: "center",
1083
+ justifyContent: "center"
1084
+ },
1085
+ themed: {
1086
+ fontSize: 15,
1087
+ fontWeight: 600,
1088
+ background: today ? theme.todayBackground : "transparent",
1089
+ color: today ? theme.todayText : theme.text
1090
+ }
1091
+ }),
899
1092
  children: (0, date_fns.format)(day, "d", dfns)
900
1093
  })]
901
1094
  }, day.toISOString());
902
1095
  })]
903
1096
  }),
904
1097
  showAllDayEventCell && hasAllDay ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
905
- style: {
906
- display: "flex",
907
- borderBottom: `1px solid ${theme.gridLine}`
908
- },
1098
+ ...slot("allDayLane", {
1099
+ base: { display: "flex" },
1100
+ themed: { borderBottom: `1px solid ${theme.gridLine}` }
1101
+ }),
909
1102
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
910
- style: {
911
- width: GUTTER_WIDTH,
912
- flex: "none",
913
- fontSize: 10,
914
- color: theme.textMuted,
915
- textAlign: "right",
916
- padding: "4px 6px 0 0"
917
- },
1103
+ ...slot("allDayLabel", {
1104
+ base: {
1105
+ width: GUTTER_WIDTH,
1106
+ flex: "none",
1107
+ textAlign: "right"
1108
+ },
1109
+ themed: {
1110
+ fontSize: 10,
1111
+ color: theme.textMuted,
1112
+ padding: "4px 6px 0 0"
1113
+ }
1114
+ }),
918
1115
  children: "all-day"
919
1116
  }), allDayByDay.map((list, i) => {
920
1117
  const dayStart = (0, date_fns.startOfDay)(days[i]);
921
1118
  const dayEnd = (0, date_fns.addDays)(dayStart, 1);
922
1119
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
923
- style: {
1120
+ ...slot("allDayColumn", { base: {
924
1121
  flex: 1,
925
1122
  minWidth: 0,
926
1123
  borderLeft: "1px solid transparent",
@@ -928,7 +1125,7 @@ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStart
928
1125
  display: "flex",
929
1126
  flexDirection: "column",
930
1127
  gap: 2
931
- },
1128
+ } }),
932
1129
  children: list.map((event) => {
933
1130
  const args = {
934
1131
  event,
@@ -942,24 +1139,29 @@ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStart
942
1139
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
943
1140
  type: "button",
944
1141
  onClick: () => onPressEvent?.(event),
945
- "aria-label": (0, _super_calendar_core.eventAccessibilityLabel)({
1142
+ "aria-label": eventAccessibilityLabel ? eventAccessibilityLabel(event, {
1143
+ mode,
1144
+ isAllDay: true,
1145
+ ampm
1146
+ }) : (0, _super_calendar_core.eventAccessibilityLabel)({
946
1147
  title: event.title,
947
1148
  isAllDay: true,
948
1149
  start: event.start,
949
1150
  end: event.end,
950
1151
  ampm
951
1152
  }),
952
- style: {
1153
+ ...slot("allDayEvent", { base: {
953
1154
  border: "none",
954
1155
  padding: 0,
955
1156
  background: "transparent",
956
1157
  cursor: "pointer",
957
1158
  textAlign: "left",
958
1159
  height: 22
959
- },
1160
+ } }),
960
1161
  children: Renderer ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Renderer, { ...args }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultDomEvent, {
961
1162
  ...args,
962
- theme
1163
+ theme,
1164
+ boxProps: slot("eventBox")
963
1165
  })
964
1166
  }, `${event.start.toISOString()}:${event.title}`);
965
1167
  })
@@ -985,19 +1187,23 @@ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStart
985
1187
  position: "relative"
986
1188
  },
987
1189
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
988
- style: {
1190
+ ...slot("hourGutter", { base: {
989
1191
  width: GUTTER_WIDTH,
990
1192
  flex: "none",
991
1193
  position: "relative"
992
- },
1194
+ } }),
993
1195
  children: HOURS.map((h) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
994
- style: {
995
- position: "absolute",
996
- top: h * hourHeight - 6,
997
- right: 6,
998
- fontSize: 10,
999
- color: theme.textMuted
1000
- },
1196
+ ...slot("hourLabel", {
1197
+ base: {
1198
+ position: "absolute",
1199
+ top: h * hourHeight - 6,
1200
+ right: 6
1201
+ },
1202
+ themed: {
1203
+ fontSize: 10,
1204
+ color: theme.textMuted
1205
+ }
1206
+ }),
1001
1207
  children: hourComponent ? hourComponent(h, ampm) : h === 0 ? "" : (0, _super_calendar_core.formatHour)(h, { ampm })
1002
1208
  }, h))
1003
1209
  }), days.map((day, dayIndex) => {
@@ -1012,34 +1218,47 @@ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStart
1012
1218
  onPointerMove: cellEnabled ? moveCreate : void 0,
1013
1219
  onPointerUp: cellEnabled ? endCreate : void 0,
1014
1220
  onPointerCancel: cellEnabled ? cancelCreate : void 0,
1015
- style: {
1016
- flex: 1,
1017
- position: "relative",
1018
- borderLeft: `1px solid ${theme.gridLine}`
1019
- },
1221
+ ...dataState({
1222
+ "data-today": (0, _super_calendar_core.getIsToday)(day),
1223
+ "data-weekend": (0, _super_calendar_core.isWeekend)(day)
1224
+ }),
1225
+ ...slot("dayColumn", {
1226
+ base: {
1227
+ flex: 1,
1228
+ position: "relative"
1229
+ },
1230
+ themed: {
1231
+ borderLeft: `1px solid ${theme.gridLine}`,
1232
+ ...(0, _super_calendar_core.isWeekend)(day) ? { background: theme.weekendBackground } : null
1233
+ }
1234
+ }),
1020
1235
  children: [
1021
1236
  bands.map((b) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1022
1237
  "aria-hidden": true,
1023
- style: {
1024
- position: "absolute",
1025
- left: 0,
1026
- right: 0,
1027
- top: b.start * hourHeight,
1028
- height: (b.end - b.start) * hourHeight,
1029
- background: theme.outsideHoursBackground,
1030
- pointerEvents: "none",
1031
- zIndex: 0
1032
- }
1238
+ ...slot("businessHours", {
1239
+ base: {
1240
+ position: "absolute",
1241
+ left: 0,
1242
+ right: 0,
1243
+ top: b.start * hourHeight,
1244
+ height: (b.end - b.start) * hourHeight,
1245
+ pointerEvents: "none",
1246
+ zIndex: 0
1247
+ },
1248
+ themed: { background: theme.outsideHoursBackground }
1249
+ })
1033
1250
  }, b.start)),
1034
1251
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1035
1252
  "aria-hidden": true,
1036
- style: {
1037
- position: "absolute",
1038
- inset: 0,
1039
- pointerEvents: "none",
1040
- zIndex: 0,
1041
- backgroundImage: gridLines
1042
- }
1253
+ ...slot("gridLines", {
1254
+ base: {
1255
+ position: "absolute",
1256
+ inset: 0,
1257
+ pointerEvents: "none",
1258
+ zIndex: 0
1259
+ },
1260
+ themed: { backgroundImage: gridLines }
1261
+ })
1043
1262
  }),
1044
1263
  positioned.map((pe, idx) => {
1045
1264
  const key = `${dayIndex}:${idx}`;
@@ -1064,7 +1283,11 @@ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStart
1064
1283
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1065
1284
  role: "button",
1066
1285
  tabIndex: 0,
1067
- "aria-label": (0, _super_calendar_core.eventAccessibilityLabel)({
1286
+ "aria-label": eventAccessibilityLabel ? eventAccessibilityLabel(pe.event, {
1287
+ mode,
1288
+ isAllDay: false,
1289
+ ampm
1290
+ }) : (0, _super_calendar_core.eventAccessibilityLabel)({
1068
1291
  title: pe.event.title,
1069
1292
  isAllDay: false,
1070
1293
  start: pe.event.start,
@@ -1082,7 +1305,8 @@ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStart
1082
1305
  onPointerUp: draggable ? (e) => endDrag(e, day, pe.event, onPress) : void 0,
1083
1306
  onPointerCancel: draggable ? cancelDrag : void 0,
1084
1307
  onClick: draggable ? void 0 : onPress,
1085
- style: {
1308
+ ...dataState({ "data-dragging": !!active }),
1309
+ ...slot("event", { base: {
1086
1310
  position: "absolute",
1087
1311
  top,
1088
1312
  height: boxHeight,
@@ -1092,10 +1316,11 @@ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStart
1092
1316
  touchAction: draggable ? "none" : "auto",
1093
1317
  zIndex: active ? 3 : 1,
1094
1318
  opacity: active ? .85 : 1
1095
- },
1319
+ } }),
1096
1320
  children: [Renderer ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Renderer, { ...args }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultDomEvent, {
1097
1321
  ...args,
1098
- theme
1322
+ theme,
1323
+ boxProps: slot("eventBox")
1099
1324
  }), draggable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1100
1325
  onPointerDown: (e) => {
1101
1326
  e.stopPropagation();
@@ -1114,7 +1339,7 @@ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStart
1114
1339
  }, idx);
1115
1340
  }),
1116
1341
  showNow ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1117
- style: {
1342
+ ...slot("nowIndicator", { base: {
1118
1343
  position: "absolute",
1119
1344
  top: nowTop,
1120
1345
  left: 0,
@@ -1122,7 +1347,7 @@ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStart
1122
1347
  height: 0,
1123
1348
  zIndex: 2,
1124
1349
  pointerEvents: "none"
1125
- },
1350
+ } }),
1126
1351
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
1127
1352
  height: 2,
1128
1353
  background: theme.nowIndicator
@@ -1138,19 +1363,23 @@ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStart
1138
1363
  }) : null,
1139
1364
  ghost ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1140
1365
  "aria-hidden": true,
1141
- style: {
1142
- position: "absolute",
1143
- left: 1,
1144
- right: 1,
1145
- top: ghost.topPx,
1146
- height: Math.max(ghost.heightPx, 2),
1147
- background: theme.rangeBackground,
1148
- border: `1px solid ${theme.selectedBackground}`,
1149
- borderRadius: 6,
1150
- opacity: .7,
1151
- pointerEvents: "none",
1152
- zIndex: 2
1153
- }
1366
+ ...slot("createGhost", {
1367
+ base: {
1368
+ position: "absolute",
1369
+ left: 1,
1370
+ right: 1,
1371
+ top: ghost.topPx,
1372
+ height: Math.max(ghost.heightPx, 2),
1373
+ opacity: .7,
1374
+ pointerEvents: "none",
1375
+ zIndex: 2
1376
+ },
1377
+ themed: {
1378
+ background: theme.rangeBackground,
1379
+ border: `1px solid ${theme.selectedBackground}`,
1380
+ borderRadius: 6
1381
+ }
1382
+ })
1154
1383
  }) : null
1155
1384
  ]
1156
1385
  }, day.toISOString());
@@ -1173,7 +1402,7 @@ function TimeGrid({ date, events = [], mode = "day", numberOfDays = 1, weekStart
1173
1402
  * <Calendar mode="week" date={new Date()} events={events} />
1174
1403
  * ```
1175
1404
  */
1176
- 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 }) {
1405
+ function Calendar({ mode = "week", date, events, weekStartsOn = 0, numberOfDays, locale, theme, height, className, style, onPressEvent, eventAccessibilityLabel, ampm, hourHeight, scrollOffsetMinutes, timeslots, businessHours, showNowIndicator, showAllDayEventCell, dragStepMinutes, onPressCell, onCreateEvent, onDragStart, onDragEvent, onPressDateHeader, renderTimeEvent, hourComponent, maxVisibleEventCount, moreLabel, showAdjacentMonths, weekdayFormat, fillCellOnSelection, selectedRange, selectedDates, minDate, maxDate, isDateDisabled, keyboardDayNavigation, onPressDay, onPressMore, renderMonthEvent, renderScheduleEvent, classNames, styles }) {
1177
1406
  if (mode === "schedule") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Agenda, {
1178
1407
  events: events ?? [],
1179
1408
  locale,
@@ -1183,7 +1412,10 @@ function Calendar({ mode = "week", date, events, weekStartsOn = 0, numberOfDays,
1183
1412
  activeDate: date,
1184
1413
  className,
1185
1414
  style,
1415
+ classNames,
1416
+ styles,
1186
1417
  renderEvent: renderScheduleEvent,
1418
+ eventAccessibilityLabel,
1187
1419
  onPressEvent,
1188
1420
  onPressDay
1189
1421
  });
@@ -1191,10 +1423,13 @@ function Calendar({ mode = "week", date, events, weekStartsOn = 0, numberOfDays,
1191
1423
  date,
1192
1424
  events: events ?? [],
1193
1425
  weekStartsOn,
1426
+ weekdayFormat,
1194
1427
  locale,
1195
1428
  theme,
1196
1429
  className,
1197
1430
  style,
1431
+ classNames,
1432
+ styles,
1198
1433
  maxVisibleEventCount,
1199
1434
  moreLabel,
1200
1435
  showAdjacentMonths,
@@ -1206,21 +1441,26 @@ function Calendar({ mode = "week", date, events, weekStartsOn = 0, numberOfDays,
1206
1441
  isDateDisabled,
1207
1442
  keyboardDayNavigation,
1208
1443
  onPressDay,
1444
+ onCreateEvent,
1209
1445
  onPressEvent,
1210
1446
  onPressMore,
1211
- renderEvent: renderMonthEvent
1447
+ renderEvent: renderMonthEvent,
1448
+ eventAccessibilityLabel
1212
1449
  });
1213
1450
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TimeGrid, {
1214
1451
  date,
1215
1452
  mode,
1216
1453
  events,
1217
1454
  weekStartsOn,
1455
+ weekdayFormat,
1218
1456
  numberOfDays,
1219
1457
  locale,
1220
1458
  theme,
1221
1459
  height,
1222
1460
  className,
1223
1461
  style,
1462
+ classNames,
1463
+ styles,
1224
1464
  ampm,
1225
1465
  hourHeight,
1226
1466
  scrollOffsetMinutes,
@@ -1236,6 +1476,7 @@ function Calendar({ mode = "week", date, events, weekStartsOn = 0, numberOfDays,
1236
1476
  onDragEvent,
1237
1477
  onPressDateHeader,
1238
1478
  renderEvent: renderTimeEvent,
1479
+ eventAccessibilityLabel,
1239
1480
  hourComponent
1240
1481
  });
1241
1482
  }
@@ -1246,8 +1487,12 @@ function Calendar({ mode = "week", date, events, weekStartsOn = 0, numberOfDays,
1246
1487
  * Legend List's DOM renderer and the library's headless grid logic. Selection is
1247
1488
  * controlled, pass `selectedRange` (or `selectedDates`) and handle `onPressDay`.
1248
1489
  */
1249
- 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 }) {
1490
+ function MonthList({ date, pastMonths = 1, futureMonths = 12, weekdayFormat = "short", weekStartsOn = 0, events, renderEvent, eventAccessibilityLabel, maxVisibleEventCount, moreLabel, onPressEvent, onPressMore, selectedRange, selectedDates, fillCellOnSelection = false, locale, theme: themeOverrides, height = 480, minDate, maxDate, isDateDisabled, keyboardDayNavigation, onPressDay, className, style, classNames, styles }) {
1250
1491
  const theme = (0, react.useMemo)(() => mergeDomTheme(themeOverrides), [themeOverrides]);
1492
+ const slot = createSlots({
1493
+ classNames,
1494
+ styles
1495
+ });
1251
1496
  const months = (0, react.useMemo)(() => {
1252
1497
  const first = (0, date_fns.startOfMonth)((0, date_fns.addMonths)(date, -pastMonths));
1253
1498
  const count = pastMonths + futureMonths + 1;
@@ -1259,10 +1504,12 @@ function MonthList({ date, pastMonths = 1, futureMonths = 12, weekStartsOn = 0,
1259
1504
  ]);
1260
1505
  const weekdays = (0, react.useMemo)(() => (0, _super_calendar_core.buildMonthGrid)(date, {
1261
1506
  weekStartsOn,
1507
+ weekdayFormat,
1262
1508
  locale
1263
1509
  }).weekdays, [
1264
1510
  date,
1265
1511
  weekStartsOn,
1512
+ weekdayFormat,
1266
1513
  locale
1267
1514
  ]);
1268
1515
  const eventsByDay = (0, react.useMemo)(() => {
@@ -1288,19 +1535,23 @@ function MonthList({ date, pastMonths = 1, futureMonths = 12, weekStartsOn = 0,
1288
1535
  ...style
1289
1536
  },
1290
1537
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1291
- style: {
1292
- display: "grid",
1293
- gridTemplateColumns: "repeat(7, minmax(0, 1fr))",
1294
- borderBottom: `1px solid ${theme.gridLine}`,
1295
- padding: "8px 0"
1296
- },
1538
+ ...slot("weekdays", {
1539
+ base: {
1540
+ display: "grid",
1541
+ gridTemplateColumns: "repeat(7, minmax(0, 1fr))"
1542
+ },
1543
+ themed: {
1544
+ borderBottom: `1px solid ${theme.gridLine}`,
1545
+ padding: "8px 0"
1546
+ }
1547
+ }),
1297
1548
  children: weekdays.map((wd) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1298
- style: {
1549
+ ...slot("weekday", { themed: {
1299
1550
  textAlign: "center",
1300
1551
  fontSize: 12,
1301
1552
  fontWeight: 600,
1302
1553
  color: theme.textMuted
1303
- },
1554
+ } }),
1304
1555
  children: wd.label
1305
1556
  }, wd.label))
1306
1557
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_legendapp_list_react.LegendList, {
@@ -1320,6 +1571,7 @@ function MonthList({ date, pastMonths = 1, futureMonths = 12, weekStartsOn = 0,
1320
1571
  events,
1321
1572
  eventsByDay,
1322
1573
  renderEvent,
1574
+ eventAccessibilityLabel,
1323
1575
  maxVisibleEventCount,
1324
1576
  moreLabel,
1325
1577
  onPressEvent,
@@ -1335,16 +1587,571 @@ function MonthList({ date, pastMonths = 1, futureMonths = 12, weekStartsOn = 0,
1335
1587
  maxDate,
1336
1588
  isDateDisabled,
1337
1589
  keyboardDayNavigation,
1338
- onPressDay
1590
+ onPressDay,
1591
+ classNames,
1592
+ styles
1593
+ })
1594
+ })]
1595
+ });
1596
+ }
1597
+ //#endregion
1598
+ //#region src/DateRangePicker.tsx
1599
+ const triggerDefault = {
1600
+ display: "inline-flex",
1601
+ alignItems: "center",
1602
+ justifyContent: "space-between",
1603
+ gap: 8,
1604
+ minWidth: 220,
1605
+ padding: "8px 12px",
1606
+ borderRadius: 8,
1607
+ cursor: "pointer",
1608
+ font: "inherit",
1609
+ textAlign: "left"
1610
+ };
1611
+ const popoverDefault = {
1612
+ position: "absolute",
1613
+ zIndex: 20,
1614
+ marginTop: 6,
1615
+ overflow: "hidden",
1616
+ borderRadius: 12,
1617
+ minWidth: 300
1618
+ };
1619
+ /**
1620
+ * The shared popover shell: a trigger button and a calendar panel that closes on
1621
+ * outside-click and Escape, and returns focus to the trigger. `renderCalendar`
1622
+ * gets a close callback so a single-date pick can dismiss immediately.
1623
+ */
1624
+ function usePopover() {
1625
+ const [open, setOpen] = (0, react.useState)(false);
1626
+ const rootRef = (0, react.useRef)(null);
1627
+ const triggerRef = (0, react.useRef)(null);
1628
+ (0, react.useEffect)(() => {
1629
+ if (!open) return;
1630
+ const onDown = (e) => {
1631
+ if (!rootRef.current?.contains(e.target)) setOpen(false);
1632
+ };
1633
+ const onKey = (e) => {
1634
+ if (e.key === "Escape") {
1635
+ setOpen(false);
1636
+ triggerRef.current?.focus();
1637
+ }
1638
+ };
1639
+ document.addEventListener("mousedown", onDown);
1640
+ document.addEventListener("keydown", onKey);
1641
+ return () => {
1642
+ document.removeEventListener("mousedown", onDown);
1643
+ document.removeEventListener("keydown", onKey);
1644
+ };
1645
+ }, [open]);
1646
+ return {
1647
+ open,
1648
+ setOpen,
1649
+ rootRef,
1650
+ triggerRef
1651
+ };
1652
+ }
1653
+ function PickerShell({ open, setOpen, rootRef, triggerRef, theme, slot, label, hasValue, placeholder, disabled, className, style, ariaLabel, children }) {
1654
+ const dialogId = (0, react.useId)();
1655
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1656
+ ref: rootRef,
1657
+ className,
1658
+ style: {
1659
+ position: "relative",
1660
+ display: "inline-block",
1661
+ fontFamily: theme.fontFamily,
1662
+ ...style
1663
+ },
1664
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1665
+ ref: triggerRef,
1666
+ type: "button",
1667
+ disabled,
1668
+ "aria-haspopup": "dialog",
1669
+ "aria-expanded": open,
1670
+ "aria-controls": open ? dialogId : void 0,
1671
+ onClick: () => setOpen(!open),
1672
+ ...slot("trigger", {
1673
+ base: triggerDefault,
1674
+ themed: {
1675
+ border: `1px solid ${theme.gridLine}`,
1676
+ background: theme.eventBackground,
1677
+ color: hasValue ? theme.text : theme.textMuted,
1678
+ opacity: disabled ? .5 : 1
1679
+ }
1680
+ }),
1681
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: hasValue ? label : placeholder }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1682
+ "aria-hidden": true,
1683
+ children: "▾"
1684
+ })]
1685
+ }), open ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1686
+ id: dialogId,
1687
+ role: "dialog",
1688
+ "aria-label": ariaLabel,
1689
+ ...slot("popover", {
1690
+ base: popoverDefault,
1691
+ themed: {
1692
+ background: theme.eventBackground,
1693
+ border: `1px solid ${theme.gridLine}`
1694
+ }
1695
+ }),
1696
+ children
1697
+ }) : null]
1698
+ });
1699
+ }
1700
+ /**
1701
+ * A single-date input with a popover calendar. Controlled via `value` / `onChange`.
1702
+ * Composed from {@link MonthList}; forward calendar slots (e.g. `dayBadge`) through
1703
+ * `classNames` to restyle the popover.
1704
+ *
1705
+ * @example
1706
+ * ```tsx
1707
+ * const [value, setValue] = useState<Date | null>(null);
1708
+ * <DatePicker value={value} onChange={setValue} />
1709
+ * ```
1710
+ */
1711
+ function DatePicker({ value, onChange, formatLabel = (d) => (0, date_fns.format)(d, "d MMM yyyy"), weekStartsOn = 0, height = 320, placeholder = "Select a date", disabled, theme: themeOverrides, className, style, classNames, styles, minDate, maxDate, isDateDisabled }) {
1712
+ const theme = mergeDomTheme(themeOverrides);
1713
+ const slot = createSlots({
1714
+ classNames,
1715
+ styles
1716
+ });
1717
+ const { open, setOpen, rootRef, triggerRef } = usePopover();
1718
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PickerShell, {
1719
+ open,
1720
+ setOpen,
1721
+ rootRef,
1722
+ triggerRef,
1723
+ theme,
1724
+ slot,
1725
+ label: value ? formatLabel(value) : "",
1726
+ hasValue: !!value,
1727
+ placeholder,
1728
+ disabled,
1729
+ className,
1730
+ style,
1731
+ ariaLabel: "Choose a date",
1732
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MonthList, {
1733
+ date: value ?? /* @__PURE__ */ new Date(),
1734
+ weekStartsOn,
1735
+ selectedDates: value ? [value] : [],
1736
+ minDate,
1737
+ maxDate,
1738
+ isDateDisabled,
1739
+ onPressDay: (day) => {
1740
+ onChange(day);
1741
+ setOpen(false);
1742
+ triggerRef.current?.focus();
1743
+ },
1744
+ height,
1745
+ classNames,
1746
+ styles
1747
+ })
1748
+ });
1749
+ }
1750
+ function defaultRangeLabel(range) {
1751
+ const start = (0, date_fns.format)(range.start, "d MMM yyyy");
1752
+ return range.end ? `${start} – ${(0, date_fns.format)(range.end, "d MMM yyyy")}` : `${start} – …`;
1753
+ }
1754
+ /**
1755
+ * A date-range input with a popover calendar. Controlled via `value` / `onChange`;
1756
+ * each pick advances the range through {@link nextDateRange} (start, then end), and
1757
+ * the popover closes once both ends are set.
1758
+ *
1759
+ * @example
1760
+ * ```tsx
1761
+ * const [range, setRange] = useState<DateRange | null>(null);
1762
+ * <DateRangePicker value={range} onChange={setRange} />
1763
+ * ```
1764
+ */
1765
+ function DateRangePicker({ value, onChange, formatLabel = defaultRangeLabel, weekStartsOn = 0, height = 320, placeholder = "Select dates", disabled, theme: themeOverrides, className, style, classNames, styles, minDate, maxDate, isDateDisabled }) {
1766
+ const theme = mergeDomTheme(themeOverrides);
1767
+ const slot = createSlots({
1768
+ classNames,
1769
+ styles
1770
+ });
1771
+ const { open, setOpen, rootRef, triggerRef } = usePopover();
1772
+ const constraints = {
1773
+ minDate,
1774
+ maxDate,
1775
+ isDateDisabled
1776
+ };
1777
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(PickerShell, {
1778
+ open,
1779
+ setOpen,
1780
+ rootRef,
1781
+ triggerRef,
1782
+ theme,
1783
+ slot,
1784
+ label: value ? formatLabel(value) : "",
1785
+ hasValue: !!value,
1786
+ placeholder,
1787
+ disabled,
1788
+ className,
1789
+ style,
1790
+ ariaLabel: "Choose a date range",
1791
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MonthList, {
1792
+ date: value?.start ?? /* @__PURE__ */ new Date(),
1793
+ weekStartsOn,
1794
+ selectedRange: value ?? void 0,
1795
+ minDate,
1796
+ maxDate,
1797
+ isDateDisabled,
1798
+ onPressDay: (day) => {
1799
+ const next = (0, _super_calendar_core.nextDateRange)(value, day, constraints);
1800
+ onChange(next);
1801
+ if (next?.end) {
1802
+ setOpen(false);
1803
+ triggerRef.current?.focus();
1804
+ }
1805
+ },
1806
+ height,
1807
+ classNames,
1808
+ styles
1809
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1810
+ ...slot("footer", {
1811
+ base: {
1812
+ display: "flex",
1813
+ justifyContent: "flex-end",
1814
+ padding: "8px 10px"
1815
+ },
1816
+ themed: { borderTop: `1px solid ${theme.gridLine}` }
1817
+ }),
1818
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1819
+ type: "button",
1820
+ onClick: () => onChange(null),
1821
+ ...slot("clear", {
1822
+ base: {
1823
+ border: "none",
1824
+ background: "transparent",
1825
+ cursor: "pointer",
1826
+ font: "inherit"
1827
+ },
1828
+ themed: { color: theme.textMuted }
1829
+ }),
1830
+ children: "Clear"
1339
1831
  })
1340
1832
  })]
1341
1833
  });
1342
1834
  }
1343
1835
  //#endregion
1836
+ //#region src/ResourceTimeline.tsx
1837
+ const clamp = (v, lo, hi) => Math.min(hi, Math.max(lo, v));
1838
+ function DefaultBar({ event, boxProps, theme }) {
1839
+ const time = (0, _super_calendar_core.eventTimeLabel)({
1840
+ mode: "day",
1841
+ isAllDay: false,
1842
+ start: event.start,
1843
+ end: event.end,
1844
+ ampm: false,
1845
+ showTime: true
1846
+ });
1847
+ const base = {
1848
+ height: "100%",
1849
+ boxSizing: "border-box",
1850
+ overflow: "hidden"
1851
+ };
1852
+ const themed = {
1853
+ padding: "2px 6px",
1854
+ borderRadius: 6,
1855
+ background: theme.eventBackground,
1856
+ color: theme.eventText,
1857
+ fontSize: 12
1858
+ };
1859
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1860
+ className: boxProps.className,
1861
+ "data-slot": boxProps["data-slot"],
1862
+ style: boxProps.className ? {
1863
+ ...base,
1864
+ ...boxProps.style
1865
+ } : {
1866
+ ...base,
1867
+ ...themed,
1868
+ ...boxProps.style
1869
+ },
1870
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1871
+ style: {
1872
+ fontWeight: 600,
1873
+ whiteSpace: "nowrap",
1874
+ overflow: "hidden",
1875
+ textOverflow: "ellipsis"
1876
+ },
1877
+ children: event.title
1878
+ }), time ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1879
+ style: {
1880
+ opacity: .75,
1881
+ fontSize: 11
1882
+ },
1883
+ children: time
1884
+ }) : null]
1885
+ });
1886
+ }
1887
+ /**
1888
+ * A horizontal resource timeline: rows are resources (rooms, people, machines)
1889
+ * and the x-axis is one day's hours. Events sit in their resource's row, and
1890
+ * overlapping events in the same row stack into sub-lanes (via the shared
1891
+ * `layoutDayEvents`). Pass `onDragEvent` to enable drag-to-move and edge-resize
1892
+ * along the axis.
1893
+ *
1894
+ * @example
1895
+ * ```tsx
1896
+ * <ResourceTimeline
1897
+ * date={new Date()}
1898
+ * resources={[{ id: "a", title: "Room A" }, { id: "b", title: "Room B" }]}
1899
+ * events={events} // each event carries a `resourceId`
1900
+ * />
1901
+ * ```
1902
+ */
1903
+ function ResourceTimeline({ date, resources, events, resourceId = (event) => event.resourceId, startHour = 0, endHour = 24, hourWidth = 80, rowHeight = 56, labelWidth = 140, ampm = false, theme: themeOverrides, renderEvent, onPressEvent, onDragEvent, dragStepMinutes = 15, className, style, classNames, styles }) {
1904
+ const theme = (0, react.useMemo)(() => mergeDomTheme(themeOverrides), [themeOverrides]);
1905
+ const slot = createSlots({
1906
+ classNames,
1907
+ styles
1908
+ });
1909
+ const Renderer = renderEvent;
1910
+ const snapHours = dragStepMinutes / 60;
1911
+ const [drag, setDrag] = (0, react.useState)(null);
1912
+ const dragRef = (0, react.useRef)(null);
1913
+ const origin = (0, react.useRef)(null);
1914
+ const applyDrag = (next) => {
1915
+ dragRef.current = next;
1916
+ setDrag(next);
1917
+ };
1918
+ const beginDrag = (e, key, kind, startHours, durationHours) => {
1919
+ if (!onDragEvent) return;
1920
+ e.stopPropagation();
1921
+ try {
1922
+ e.target.setPointerCapture?.(e.pointerId);
1923
+ } catch {}
1924
+ origin.current = {
1925
+ x: e.clientX,
1926
+ startHours,
1927
+ durationHours
1928
+ };
1929
+ applyDrag({
1930
+ key,
1931
+ kind,
1932
+ startHours,
1933
+ durationHours,
1934
+ moved: false
1935
+ });
1936
+ };
1937
+ const moveDrag = (e) => {
1938
+ const d = dragRef.current;
1939
+ if (!d || !origin.current) return;
1940
+ const dHours = (e.clientX - origin.current.x) / hourWidth;
1941
+ const snap = (v) => Math.round(v / snapHours) * snapHours;
1942
+ if (d.kind === "move") {
1943
+ const startHours = clamp(snap(origin.current.startHours + dHours), startHour, endHour - d.durationHours);
1944
+ applyDrag({
1945
+ ...d,
1946
+ startHours,
1947
+ moved: true
1948
+ });
1949
+ } else {
1950
+ const durationHours = clamp(snap(origin.current.durationHours + dHours), snapHours, endHour - d.startHours);
1951
+ applyDrag({
1952
+ ...d,
1953
+ durationHours,
1954
+ moved: true
1955
+ });
1956
+ }
1957
+ };
1958
+ const endDrag = (e, event, onPress) => {
1959
+ const d = dragRef.current;
1960
+ if (!d) return;
1961
+ try {
1962
+ e.target.releasePointerCapture?.(e.pointerId);
1963
+ } catch {}
1964
+ if (!d.moved) {
1965
+ applyDrag(null);
1966
+ onPress();
1967
+ return;
1968
+ }
1969
+ const base = (0, date_fns.startOfDay)(date);
1970
+ const start = (0, date_fns.addMinutes)(base, Math.round(d.startHours * 60));
1971
+ const end = (0, date_fns.addMinutes)(base, Math.round((d.startHours + d.durationHours) * 60));
1972
+ onDragEvent?.(event, start, end);
1973
+ applyDrag(null);
1974
+ origin.current = null;
1975
+ };
1976
+ const cancelDrag = () => {
1977
+ applyDrag(null);
1978
+ origin.current = null;
1979
+ };
1980
+ const hours = (0, react.useMemo)(() => Array.from({ length: Math.max(0, endHour - startHour) }, (_, i) => startHour + i), [startHour, endHour]);
1981
+ const trackWidth = (endHour - startHour) * hourWidth;
1982
+ const byResource = (0, react.useMemo)(() => {
1983
+ const map = /* @__PURE__ */ new Map();
1984
+ for (const resource of resources) {
1985
+ const own = events.filter((e) => resourceId(e) === resource.id);
1986
+ map.set(resource.id, (0, _super_calendar_core.layoutDayEvents)(own, date));
1987
+ }
1988
+ return map;
1989
+ }, [
1990
+ resources,
1991
+ events,
1992
+ resourceId,
1993
+ date
1994
+ ]);
1995
+ const gridLines = `repeating-linear-gradient(to right, transparent 0, transparent ${hourWidth - 1}px, ${theme.gridLine} ${hourWidth - 1}px, ${theme.gridLine} ${hourWidth}px)`;
1996
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1997
+ className,
1998
+ style: {
1999
+ fontFamily: theme.fontFamily,
2000
+ color: theme.text,
2001
+ overflowX: "auto",
2002
+ ...style
2003
+ },
2004
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2005
+ style: { minWidth: labelWidth + trackWidth },
2006
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2007
+ ...slot("header", {
2008
+ base: { display: "flex" },
2009
+ themed: { borderBottom: `1px solid ${theme.gridLine}` }
2010
+ }),
2011
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { ...slot("corner", { base: {
2012
+ width: labelWidth,
2013
+ flex: "none"
2014
+ } }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2015
+ ...slot("timeAxis", { base: {
2016
+ position: "relative",
2017
+ height: 24,
2018
+ width: trackWidth
2019
+ } }),
2020
+ children: hours.map((h) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2021
+ ...slot("hourTick", {
2022
+ base: {
2023
+ position: "absolute",
2024
+ left: (h - startHour) * hourWidth,
2025
+ top: 4
2026
+ },
2027
+ themed: {
2028
+ fontSize: 10,
2029
+ color: theme.textMuted
2030
+ }
2031
+ }),
2032
+ children: (0, _super_calendar_core.formatHour)(h, { ampm })
2033
+ }, h))
2034
+ })]
2035
+ }), resources.map((resource) => {
2036
+ const positioned = byResource.get(resource.id) ?? [];
2037
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2038
+ ...slot("row", {
2039
+ base: {
2040
+ display: "flex",
2041
+ height: rowHeight
2042
+ },
2043
+ themed: { borderBottom: `1px solid ${theme.gridLine}` }
2044
+ }),
2045
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2046
+ ...slot("resourceLabel", {
2047
+ base: {
2048
+ width: labelWidth,
2049
+ flex: "none",
2050
+ display: "flex",
2051
+ alignItems: "center",
2052
+ padding: "0 10px",
2053
+ boxSizing: "border-box"
2054
+ },
2055
+ themed: {
2056
+ fontSize: 13,
2057
+ fontWeight: 600,
2058
+ borderRight: `1px solid ${theme.gridLine}`
2059
+ }
2060
+ }),
2061
+ children: resource.title ?? resource.id
2062
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2063
+ ...slot("track", { base: {
2064
+ position: "relative",
2065
+ width: trackWidth
2066
+ } }),
2067
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2068
+ "aria-hidden": true,
2069
+ ...slot("gridLines", {
2070
+ base: {
2071
+ position: "absolute",
2072
+ inset: 0,
2073
+ pointerEvents: "none"
2074
+ },
2075
+ themed: { backgroundImage: gridLines }
2076
+ })
2077
+ }), positioned.map((pe, idx) => {
2078
+ const key = `${resource.id}:${idx}`;
2079
+ const active = drag?.key === key ? drag : null;
2080
+ const startH = active ? active.startHours : pe.startHours;
2081
+ const durH = active ? active.durationHours : pe.durationHours;
2082
+ const left = clamp(startH - startHour, 0, endHour - startHour) * hourWidth;
2083
+ const right = clamp(startH + durH - startHour, 0, endHour - startHour) * hourWidth;
2084
+ const width = Math.max(right - left, 2);
2085
+ const laneHeight = rowHeight / pe.columns;
2086
+ const onPress = () => onPressEvent?.(pe.event);
2087
+ const args = {
2088
+ event: pe.event,
2089
+ width,
2090
+ onPress
2091
+ };
2092
+ const draggable = !!onDragEvent;
2093
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
2094
+ type: "button",
2095
+ onClick: draggable ? void 0 : onPress,
2096
+ "aria-label": pe.event.title,
2097
+ ...dataState({ "data-dragging": !!active }),
2098
+ onPointerDown: draggable ? (e) => beginDrag(e, key, "move", pe.startHours, pe.durationHours) : void 0,
2099
+ onPointerMove: draggable ? moveDrag : void 0,
2100
+ onPointerUp: draggable ? (e) => endDrag(e, pe.event, onPress) : void 0,
2101
+ onPointerCancel: draggable ? cancelDrag : void 0,
2102
+ ...slot("event", { base: {
2103
+ position: "absolute",
2104
+ left,
2105
+ width,
2106
+ top: pe.column * laneHeight,
2107
+ height: laneHeight,
2108
+ padding: 1,
2109
+ border: "none",
2110
+ background: "transparent",
2111
+ cursor: draggable ? "grab" : "pointer",
2112
+ touchAction: draggable ? "none" : "auto",
2113
+ font: "inherit",
2114
+ textAlign: "left",
2115
+ boxSizing: "border-box",
2116
+ zIndex: active ? 3 : 1,
2117
+ opacity: active ? .85 : 1
2118
+ } }),
2119
+ children: [Renderer ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Renderer, { ...args }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultBar, {
2120
+ ...args,
2121
+ theme,
2122
+ boxProps: slot("eventBox")
2123
+ }), draggable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2124
+ "aria-hidden": true,
2125
+ onPointerDown: (e) => {
2126
+ e.stopPropagation();
2127
+ beginDrag(e, key, "resize", pe.startHours, pe.durationHours);
2128
+ },
2129
+ style: {
2130
+ position: "absolute",
2131
+ top: 0,
2132
+ bottom: 0,
2133
+ right: 0,
2134
+ width: 8,
2135
+ cursor: "ew-resize",
2136
+ touchAction: "none"
2137
+ }
2138
+ }) : null]
2139
+ }, idx);
2140
+ })]
2141
+ })]
2142
+ }, resource.id);
2143
+ })]
2144
+ })
2145
+ });
2146
+ }
2147
+ //#endregion
1344
2148
  exports.Agenda = Agenda;
1345
2149
  exports.Calendar = Calendar;
2150
+ exports.DatePicker = DatePicker;
2151
+ exports.DateRangePicker = DateRangePicker;
1346
2152
  exports.MonthList = MonthList;
1347
2153
  exports.MonthView = MonthView;
2154
+ exports.ResourceTimeline = ResourceTimeline;
1348
2155
  exports.TimeGrid = TimeGrid;
1349
2156
  Object.defineProperty(exports, "buildMonthGrid", {
1350
2157
  enumerable: true,
@@ -1360,6 +2167,12 @@ Object.defineProperty(exports, "daySelectionState", {
1360
2167
  }
1361
2168
  });
1362
2169
  exports.defaultDomTheme = defaultDomTheme;
2170
+ Object.defineProperty(exports, "expandRecurringEvents", {
2171
+ enumerable: true,
2172
+ get: function() {
2173
+ return _super_calendar_core.expandRecurringEvents;
2174
+ }
2175
+ });
1363
2176
  Object.defineProperty(exports, "getViewDays", {
1364
2177
  enumerable: true,
1365
2178
  get: function() {
@@ -1403,6 +2216,18 @@ Object.defineProperty(exports, "nextDateRange", {
1403
2216
  return _super_calendar_core.nextDateRange;
1404
2217
  }
1405
2218
  });
2219
+ Object.defineProperty(exports, "parseICalendar", {
2220
+ enumerable: true,
2221
+ get: function() {
2222
+ return _super_calendar_core.parseICalendar;
2223
+ }
2224
+ });
2225
+ Object.defineProperty(exports, "toICalendar", {
2226
+ enumerable: true,
2227
+ get: function() {
2228
+ return _super_calendar_core.toICalendar;
2229
+ }
2230
+ });
1406
2231
  Object.defineProperty(exports, "useDateRange", {
1407
2232
  enumerable: true,
1408
2233
  get: function() {
@@ -1415,3 +2240,9 @@ Object.defineProperty(exports, "useMonthGrid", {
1415
2240
  return _super_calendar_core.useMonthGrid;
1416
2241
  }
1417
2242
  });
2243
+ Object.defineProperty(exports, "weekdayFormatToken", {
2244
+ enumerable: true,
2245
+ get: function() {
2246
+ return _super_calendar_core.weekdayFormatToken;
2247
+ }
2248
+ });