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