@dxos/react-ui-calendar 0.8.4-staging.ac66bdf99f → 0.9.1-main.c7dcc2e112

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.
Files changed (36) hide show
  1. package/LICENSE +102 -5
  2. package/dist/lib/browser/index.mjs +881 -107
  3. package/dist/lib/browser/index.mjs.map +4 -4
  4. package/dist/lib/browser/meta.json +1 -1
  5. package/dist/lib/browser/translations.mjs +16 -0
  6. package/dist/lib/browser/translations.mjs.map +7 -0
  7. package/dist/lib/node-esm/index.mjs +881 -107
  8. package/dist/lib/node-esm/index.mjs.map +4 -4
  9. package/dist/lib/node-esm/meta.json +1 -1
  10. package/dist/lib/node-esm/translations.mjs +18 -0
  11. package/dist/lib/node-esm/translations.mjs.map +7 -0
  12. package/dist/types/src/components/Calendar/Calendar.d.ts +40 -21
  13. package/dist/types/src/components/Calendar/Calendar.d.ts.map +1 -1
  14. package/dist/types/src/components/Calendar/Calendar.stories.d.ts +4 -1
  15. package/dist/types/src/components/Calendar/Calendar.stories.d.ts.map +1 -1
  16. package/dist/types/src/components/Calendar/Week.d.ts +30 -0
  17. package/dist/types/src/components/Calendar/Week.d.ts.map +1 -0
  18. package/dist/types/src/components/Calendar/Weekdays.d.ts +18 -0
  19. package/dist/types/src/components/Calendar/Weekdays.d.ts.map +1 -0
  20. package/dist/types/src/components/Calendar/context.d.ts +40 -0
  21. package/dist/types/src/components/Calendar/context.d.ts.map +1 -0
  22. package/dist/types/src/components/Calendar/util.d.ts +47 -0
  23. package/dist/types/src/components/Calendar/util.d.ts.map +1 -1
  24. package/dist/types/src/components/Calendar/util.test.d.ts +2 -0
  25. package/dist/types/src/components/Calendar/util.test.d.ts.map +1 -0
  26. package/dist/types/src/translations.d.ts +1 -1
  27. package/dist/types/src/translations.d.ts.map +1 -1
  28. package/dist/types/tsconfig.tsbuildinfo +1 -1
  29. package/package.json +25 -19
  30. package/src/components/Calendar/Calendar.stories.tsx +63 -3
  31. package/src/components/Calendar/Calendar.tsx +483 -92
  32. package/src/components/Calendar/Week.tsx +488 -0
  33. package/src/components/Calendar/Weekdays.tsx +57 -0
  34. package/src/components/Calendar/context.ts +60 -0
  35. package/src/components/Calendar/util.test.ts +90 -0
  36. package/src/components/Calendar/util.ts +110 -1
@@ -1,26 +1,21 @@
1
1
  // src/components/Calendar/Calendar.tsx
2
- import { createContext } from "@radix-ui/react-context";
3
- import { addDays, differenceInWeeks, format, startOfDay, startOfWeek } from "date-fns";
4
- import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
2
+ import { addDays as addDays3, format as format2, startOfDay as startOfDay3 } from "date-fns";
3
+ import React3, { forwardRef, useCallback as useCallback2, useEffect as useEffect2, useImperativeHandle, useMemo as useMemo3, useRef as useRef2, useState as useState2 } from "react";
5
4
  import { useResizeDetector } from "react-resize-detector";
6
5
  import { List } from "react-virtualized";
7
6
  import { Event } from "@dxos/async";
8
7
  import { IconButton, useTranslation } from "@dxos/react-ui";
9
- import { composable, composableProps, mx } from "@dxos/ui-theme";
8
+ import { composable as composable2, composableProps as composableProps2 } from "@dxos/react-ui";
9
+ import { mx as mx3 } from "@dxos/ui-theme";
10
+ import { translationKey } from "#translations";
10
11
 
11
- // src/translations.ts
12
- var translationKey = "@dxos/react-ui-calendar";
13
- var translations = [
14
- {
15
- "en-US": {
16
- [translationKey]: {
17
- "today.button": "Today"
18
- }
19
- }
20
- }
21
- ];
12
+ // src/components/Calendar/context.ts
13
+ import { createContext } from "@radix-ui/react-context";
14
+ var [CalendarContextProvider, useCalendarContext] = createContext("Calendar");
22
15
 
23
16
  // src/components/Calendar/util.ts
17
+ import { differenceInCalendarDays, startOfDay } from "date-fns";
18
+ var gridEpoch = /* @__PURE__ */ new Date("1970-01-01");
24
19
  var getDate = (start2, weekNumber, dayOfWeek, weekStartsOn) => {
25
20
  const result = new Date(start2);
26
21
  const startDayOfWeek = start2.getDay();
@@ -28,49 +23,554 @@ var getDate = (start2, weekNumber, dayOfWeek, weekStartsOn) => {
28
23
  result.setDate(start2.getDate() - adjustedStartDay + weekNumber * 7 + dayOfWeek);
29
24
  return result;
30
25
  };
26
+ var getRowIndex = (start2, date, weekStartsOn) => {
27
+ const startDayOfWeek = start2.getDay();
28
+ const adjustedStartDay = (startDayOfWeek === 0 ? 7 : startDayOfWeek) - weekStartsOn;
29
+ const row0Start = new Date(start2);
30
+ row0Start.setDate(start2.getDate() - adjustedStartDay);
31
+ return Math.floor(differenceInCalendarDays(date, row0Start) / 7);
32
+ };
31
33
  var isSameDay = (date1, date2) => {
32
34
  return !!date2 && date1.getFullYear() === date2.getFullYear() && date1.getMonth() === date2.getMonth() && date1.getDate() === date2.getDate();
33
35
  };
36
+ var MINUTES_PER_DAY = 24 * 60;
37
+ var SNAP_MINUTES = 15;
38
+ var minutesOfDay = (date) => (date.getTime() - startOfDay(date).getTime()) / 6e4;
39
+ var setMinutesOfDay = (date, minutes) => new Date(startOfDay(date).getTime() + minutes * 6e4);
40
+ var yToMinutes = (y, hourHeight) => y / hourHeight * 60;
41
+ var minutesToY = (minutes, hourHeight) => minutes / 60 * hourHeight;
42
+ var snapMinutes = (minutes, step = SNAP_MINUTES) => {
43
+ const snapped = Math.round(minutes / step) * step;
44
+ return Math.max(0, Math.min(MINUTES_PER_DAY, snapped));
45
+ };
46
+ var layoutDayEvents = (events) => {
47
+ const layout = /* @__PURE__ */ new Map();
48
+ const ordered = events.map((event, index) => ({
49
+ index,
50
+ start: event.start.getTime(),
51
+ end: event.end.getTime()
52
+ })).sort((a, b) => a.start - b.start);
53
+ let cluster = [];
54
+ let clusterMax = -Infinity;
55
+ const flush = () => {
56
+ if (cluster.length === 0) {
57
+ return;
58
+ }
59
+ const columnEnds = [];
60
+ const assigned = cluster.map(({ index, start: start2, end }) => {
61
+ let column = columnEnds.findIndex((columnEnd) => columnEnd <= start2);
62
+ if (column === -1) {
63
+ column = columnEnds.length;
64
+ }
65
+ columnEnds[column] = end;
66
+ return {
67
+ index,
68
+ column
69
+ };
70
+ });
71
+ const columnCount = columnEnds.length;
72
+ for (const { index, column } of assigned) {
73
+ layout.set(index, {
74
+ columnIndex: column,
75
+ columnCount
76
+ });
77
+ }
78
+ cluster = [];
79
+ clusterMax = -Infinity;
80
+ };
81
+ for (const entry of ordered) {
82
+ if (cluster.length > 0 && entry.start >= clusterMax) {
83
+ flush();
84
+ }
85
+ cluster.push(entry);
86
+ clusterMax = Math.max(clusterMax, entry.end);
87
+ }
88
+ flush();
89
+ return layout;
90
+ };
91
+
92
+ // src/components/Calendar/Week.tsx
93
+ import { addDays as addDays2, startOfDay as startOfDay2, startOfWeek as startOfWeek2 } from "date-fns";
94
+ import React2, { useCallback, useEffect, useLayoutEffect, useMemo as useMemo2, useRef, useState } from "react";
95
+ import { composable, composableProps } from "@dxos/react-ui";
96
+ import { mx as mx2 } from "@dxos/ui-theme";
97
+
98
+ // src/components/Calendar/Weekdays.tsx
99
+ import { addDays, format, startOfWeek } from "date-fns";
100
+ import React, { useMemo } from "react";
101
+ import { mx } from "@dxos/ui-theme";
102
+ var Weekdays = ({ weekStartsOn, columnWidth, gutter, dates }) => {
103
+ const labels = useMemo(() => {
104
+ const weekStart = startOfWeek(/* @__PURE__ */ new Date(), {
105
+ weekStartsOn
106
+ });
107
+ return Array.from({
108
+ length: 7
109
+ }, (_, index) => format(addDays(weekStart, index), "EEE"));
110
+ }, [
111
+ weekStartsOn
112
+ ]);
113
+ const today = useMemo(() => /* @__PURE__ */ new Date(), []);
114
+ const columnTemplate = columnWidth ? `repeat(7, ${columnWidth}px)` : "repeat(7, 1fr)";
115
+ return /* @__PURE__ */ React.createElement("div", {
116
+ className: "grid w-full shrink-0",
117
+ style: {
118
+ gridTemplateColumns: gutter ? `${gutter}px ${columnTemplate}` : columnTemplate
119
+ }
120
+ }, gutter != null && /* @__PURE__ */ React.createElement("div", {
121
+ "aria-hidden": true
122
+ }), labels.map((label, index) => {
123
+ const date = dates?.[index];
124
+ const isToday = !!date && isSameDay(date, today);
125
+ return /* @__PURE__ */ React.createElement("div", {
126
+ key: index,
127
+ className: mx("flex flex-col items-center p-2 text-sm font-thin", isToday && "text-accent-text")
128
+ }, /* @__PURE__ */ React.createElement("span", null, label), date && /* @__PURE__ */ React.createElement("span", {
129
+ className: "text-lg font-normal tabular-nums"
130
+ }, date.getDate()));
131
+ }));
132
+ };
133
+
134
+ // src/components/Calendar/Week.tsx
135
+ var CALENDAR_WEEK_NAME = "CalendarWeek";
136
+ var HOUR_HEIGHT = 48;
137
+ var GUTTER_WIDTH = 56;
138
+ var RESIZE_HANDLE = 6;
139
+ var MIN_DURATION = SNAP_MINUTES;
140
+ var INITIAL_HOUR = 8;
141
+ var CalendarWeek = composable(({ classNames, date, events = [], onEventCreate, onEventUpdate, ...props }, forwardedRef) => {
142
+ const { weekStartsOn, event: scrollEvent, setIndex } = useCalendarContext(CALENDAR_WEEK_NAME);
143
+ const today = useMemo2(() => /* @__PURE__ */ new Date(), []);
144
+ const [viewDate, setViewDate] = useState(() => date ?? today);
145
+ useEffect(() => {
146
+ if (date) {
147
+ setViewDate(date);
148
+ }
149
+ }, [
150
+ date
151
+ ]);
152
+ useEffect(() => {
153
+ return scrollEvent.on(({ date: date2 }) => setViewDate(date2));
154
+ }, [
155
+ scrollEvent
156
+ ]);
157
+ const weekDays = useMemo2(() => {
158
+ const weekStart = startOfWeek2(viewDate, {
159
+ weekStartsOn
160
+ });
161
+ return Array.from({
162
+ length: 7
163
+ }, (_, index) => startOfDay2(addDays2(weekStart, index)));
164
+ }, [
165
+ viewDate,
166
+ weekStartsOn
167
+ ]);
168
+ useEffect(() => {
169
+ setIndex(getRowIndex(gridEpoch, weekDays[0], weekStartsOn));
170
+ }, [
171
+ weekDays,
172
+ weekStartsOn,
173
+ setIndex
174
+ ]);
175
+ const eventsByDay = useMemo2(() => {
176
+ const byDay = weekDays.map(() => []);
177
+ for (const event of events) {
178
+ const dayIndex = weekDays.findIndex((day) => isSameDay(day, event.start));
179
+ if (dayIndex >= 0) {
180
+ byDay[dayIndex].push(event);
181
+ }
182
+ }
183
+ return byDay;
184
+ }, [
185
+ events,
186
+ weekDays
187
+ ]);
188
+ const scrollRef = useRef(null);
189
+ useLayoutEffect(() => {
190
+ scrollRef.current?.scrollTo({
191
+ top: minutesToY(INITIAL_HOUR * 60, HOUR_HEIGHT)
192
+ });
193
+ }, []);
194
+ const gestureRef = useRef(void 0);
195
+ const columnsRef = useRef([]);
196
+ const [draft, setDraft] = useState(void 0);
197
+ const pointerMinutes = useCallback((clientY) => {
198
+ const rect = columnsRef.current.find(Boolean)?.getBoundingClientRect();
199
+ if (!rect) {
200
+ return 0;
201
+ }
202
+ return Math.max(0, Math.min(MINUTES_PER_DAY, yToMinutes(clientY - rect.top, HOUR_HEIGHT)));
203
+ }, []);
204
+ const dayFromX = useCallback((clientX) => {
205
+ const index = columnsRef.current.findIndex((node) => {
206
+ const rect = node?.getBoundingClientRect();
207
+ return rect && clientX >= rect.left && clientX < rect.right;
208
+ });
209
+ return index >= 0 ? weekDays[index] : void 0;
210
+ }, [
211
+ weekDays
212
+ ]);
213
+ const applyGesture = useCallback((clientX, clientY) => {
214
+ const gesture = gestureRef.current;
215
+ if (!gesture) {
216
+ return void 0;
217
+ }
218
+ const { kind, day, eventId, anchorMinutes, grabOffset, durationMinutes } = gesture;
219
+ const raw = pointerMinutes(clientY);
220
+ switch (kind) {
221
+ case "create": {
222
+ const focus = snapMinutes(raw);
223
+ const from = Math.min(anchorMinutes, focus);
224
+ const to = Math.max(anchorMinutes, focus);
225
+ const end = Math.max(to, from + MIN_DURATION);
226
+ return {
227
+ eventId,
228
+ start: setMinutesOfDay(day, from),
229
+ end: setMinutesOfDay(day, end)
230
+ };
231
+ }
232
+ case "move": {
233
+ const targetDay = dayFromX(clientX) ?? day;
234
+ let start2 = snapMinutes(raw - grabOffset);
235
+ start2 = Math.max(0, Math.min(MINUTES_PER_DAY - durationMinutes, start2));
236
+ return {
237
+ eventId,
238
+ start: setMinutesOfDay(targetDay, start2),
239
+ end: setMinutesOfDay(targetDay, start2 + durationMinutes)
240
+ };
241
+ }
242
+ case "resize-start": {
243
+ const start2 = Math.min(snapMinutes(raw), anchorMinutes - MIN_DURATION);
244
+ return {
245
+ eventId,
246
+ start: setMinutesOfDay(day, start2),
247
+ end: setMinutesOfDay(day, anchorMinutes)
248
+ };
249
+ }
250
+ case "resize-end": {
251
+ const end = Math.max(snapMinutes(raw), anchorMinutes + MIN_DURATION);
252
+ return {
253
+ eventId,
254
+ start: setMinutesOfDay(day, anchorMinutes),
255
+ end: setMinutesOfDay(day, end)
256
+ };
257
+ }
258
+ }
259
+ }, [
260
+ dayFromX,
261
+ pointerMinutes
262
+ ]);
263
+ const callbacksRef = useRef({
264
+ onEventCreate,
265
+ onEventUpdate
266
+ });
267
+ callbacksRef.current = {
268
+ onEventCreate,
269
+ onEventUpdate
270
+ };
271
+ const detachRef = useRef(() => {
272
+ });
273
+ const beginGesture = useCallback((gesture, ev) => {
274
+ ev.preventDefault();
275
+ ev.stopPropagation();
276
+ gestureRef.current = gesture;
277
+ setDraft(applyGesture(ev.clientX, ev.clientY));
278
+ const handleMove = (moveEv) => {
279
+ if (gestureRef.current) {
280
+ setDraft(applyGesture(moveEv.clientX, moveEv.clientY));
281
+ }
282
+ };
283
+ const handleUp = (upEv) => {
284
+ const active = gestureRef.current;
285
+ const result = applyGesture(upEv.clientX, upEv.clientY);
286
+ detachRef.current();
287
+ gestureRef.current = void 0;
288
+ setDraft(void 0);
289
+ if (active && result) {
290
+ if (active.kind === "create") {
291
+ callbacksRef.current.onEventCreate?.({
292
+ start: result.start,
293
+ end: result.end
294
+ });
295
+ } else if (result.eventId) {
296
+ callbacksRef.current.onEventUpdate?.({
297
+ id: result.eventId,
298
+ start: result.start,
299
+ end: result.end
300
+ });
301
+ }
302
+ }
303
+ };
304
+ detachRef.current = () => {
305
+ window.removeEventListener("pointermove", handleMove);
306
+ window.removeEventListener("pointerup", handleUp);
307
+ window.removeEventListener("pointercancel", handleUp);
308
+ };
309
+ window.addEventListener("pointermove", handleMove);
310
+ window.addEventListener("pointerup", handleUp);
311
+ window.addEventListener("pointercancel", handleUp);
312
+ }, [
313
+ applyGesture
314
+ ]);
315
+ useEffect(() => () => detachRef.current(), []);
316
+ const handleColumnPointerDown = useCallback((day, event) => {
317
+ const rect = event.currentTarget.getBoundingClientRect();
318
+ const anchor = snapMinutes(yToMinutes(event.clientY - rect.top, HOUR_HEIGHT));
319
+ beginGesture({
320
+ kind: "create",
321
+ day,
322
+ anchorMinutes: anchor,
323
+ grabOffset: 0,
324
+ durationMinutes: 0
325
+ }, event);
326
+ }, [
327
+ beginGesture
328
+ ]);
329
+ const renderEvent = useCallback((event, day, start2, end, columnIndex, columnCount) => /* @__PURE__ */ React2.createElement(EventBlock, {
330
+ key: event.id,
331
+ event,
332
+ start: start2,
333
+ end,
334
+ columnIndex,
335
+ columnCount,
336
+ onMoveStart: (ev) => beginGesture({
337
+ kind: "move",
338
+ day,
339
+ eventId: event.id,
340
+ anchorMinutes: 0,
341
+ grabOffset: pointerMinutes(ev.clientY) - minutesOfDay(event.start),
342
+ durationMinutes: minutesOfDay(event.end) - minutesOfDay(event.start)
343
+ }, ev),
344
+ onResizeStart: (ev) => beginGesture({
345
+ kind: "resize-start",
346
+ day,
347
+ eventId: event.id,
348
+ anchorMinutes: minutesOfDay(event.end),
349
+ grabOffset: 0,
350
+ durationMinutes: 0
351
+ }, ev),
352
+ onResizeEnd: (ev) => beginGesture({
353
+ kind: "resize-end",
354
+ day,
355
+ eventId: event.id,
356
+ anchorMinutes: minutesOfDay(event.start),
357
+ grabOffset: 0,
358
+ durationMinutes: 0
359
+ }, ev)
360
+ }), [
361
+ beginGesture,
362
+ pointerMinutes
363
+ ]);
364
+ const draggedEvent = draft?.eventId ? events.find((event) => event.id === draft.eventId) : void 0;
365
+ return /* @__PURE__ */ React2.createElement("div", {
366
+ ...composableProps(props, {
367
+ classNames: [
368
+ "flex flex-col h-full w-full overflow-hidden outline-hidden",
369
+ classNames
370
+ ]
371
+ }),
372
+ ref: forwardedRef
373
+ }, /* @__PURE__ */ React2.createElement(Weekdays, {
374
+ weekStartsOn,
375
+ gutter: GUTTER_WIDTH,
376
+ dates: weekDays
377
+ }), /* @__PURE__ */ React2.createElement("div", {
378
+ ref: scrollRef,
379
+ className: "flex-1 overflow-y-auto _scrollbar-thin"
380
+ }, /* @__PURE__ */ React2.createElement("div", {
381
+ className: "grid relative",
382
+ style: {
383
+ height: minutesToY(MINUTES_PER_DAY, HOUR_HEIGHT),
384
+ gridTemplateColumns: `${GUTTER_WIDTH}px repeat(7, 1fr)`
385
+ }
386
+ }, /* @__PURE__ */ React2.createElement("div", {
387
+ className: "relative"
388
+ }, Array.from({
389
+ length: 24
390
+ }, (_, hour) => /* @__PURE__ */ React2.createElement("div", {
391
+ key: hour,
392
+ className: "absolute right-1 -translate-y-1/2 text-xs text-description tabular-nums",
393
+ style: {
394
+ top: minutesToY(hour * 60, HOUR_HEIGHT)
395
+ }
396
+ }, hour === 0 ? "" : `${hour.toString().padStart(2, "0")}:00`))), weekDays.map((day, dayIndex) => {
397
+ const dayEvents = eventsByDay[dayIndex];
398
+ const layout = layoutDayEvents(dayEvents);
399
+ const isToday = isSameDay(day, today);
400
+ const draftHere = draft && isSameDay(day, draft.start) ? draft : void 0;
401
+ return /* @__PURE__ */ React2.createElement("div", {
402
+ key: day.toISOString(),
403
+ ref: (node) => {
404
+ columnsRef.current[dayIndex] = node;
405
+ },
406
+ "data-date": day.toISOString(),
407
+ className: mx2("relative border-l border-separator cursor-cell select-none", dayIndex === 6 && "border-r", isToday && "bg-primary-500/5"),
408
+ onPointerDown: (ev) => handleColumnPointerDown(day, ev)
409
+ }, Array.from({
410
+ length: 24
411
+ }, (_, hour) => /* @__PURE__ */ React2.createElement("div", {
412
+ key: hour,
413
+ className: "absolute inset-x-0 border-t border-separator/60",
414
+ style: {
415
+ top: minutesToY(hour * 60, HOUR_HEIGHT)
416
+ }
417
+ })), dayEvents.map((event, index) => {
418
+ const slot = layout.get(index) ?? {
419
+ columnIndex: 0,
420
+ columnCount: 1
421
+ };
422
+ const editing = draft && draft.eventId === event.id ? draft : void 0;
423
+ if (editing && !isSameDay(editing.start, day)) {
424
+ return null;
425
+ }
426
+ const start2 = editing ? editing.start : event.start;
427
+ const end = editing ? editing.end : event.end;
428
+ return renderEvent(event, day, start2, end, slot.columnIndex, slot.columnCount);
429
+ }), draftHere && draggedEvent && !dayEvents.some((event) => event.id === draggedEvent.id) && renderEvent(draggedEvent, day, draftHere.start, draftHere.end, 0, 1), draftHere && !draftHere.eventId && /* @__PURE__ */ React2.createElement(PendingBlock, {
430
+ start: draftHere.start,
431
+ end: draftHere.end
432
+ }));
433
+ }))));
434
+ });
435
+ CalendarWeek.displayName = CALENDAR_WEEK_NAME;
436
+ var formatTime = (date) => {
437
+ const minutes = minutesOfDay(date);
438
+ const hour = Math.floor(minutes / 60);
439
+ const minute = Math.round(minutes % 60);
440
+ return `${hour.toString().padStart(2, "0")}:${minute.toString().padStart(2, "0")}`;
441
+ };
442
+ var EventBlock = ({ event, start: start2, end, columnIndex, columnCount, onMoveStart, onResizeStart, onResizeEnd }) => {
443
+ const top = minutesToY(minutesOfDay(start2), HOUR_HEIGHT);
444
+ const height = Math.max(minutesToY(minutesOfDay(end) - minutesOfDay(start2), HOUR_HEIGHT), RESIZE_HANDLE * 2);
445
+ const widthPct = 100 / columnCount;
446
+ return /* @__PURE__ */ React2.createElement("div", {
447
+ className: "absolute rounded-sm bg-primary-500/80 text-inverse-fg overflow-hidden cursor-move shadow-sm",
448
+ style: {
449
+ top,
450
+ height,
451
+ left: `calc(${columnIndex * widthPct}% + 1px)`,
452
+ width: `calc(${widthPct}% - 2px)`
453
+ },
454
+ onPointerDown: onMoveStart
455
+ }, /* @__PURE__ */ React2.createElement("div", {
456
+ className: "absolute inset-x-0 top-0 cursor-ns-resize",
457
+ style: {
458
+ height: RESIZE_HANDLE
459
+ },
460
+ onPointerDown: onResizeStart
461
+ }), /* @__PURE__ */ React2.createElement("div", {
462
+ className: "px-1 py-0.5 text-xs leading-tight"
463
+ }, /* @__PURE__ */ React2.createElement("div", {
464
+ className: "font-medium truncate"
465
+ }, event.title ?? "(untitled)")), /* @__PURE__ */ React2.createElement("div", {
466
+ className: "absolute inset-x-0 bottom-0 cursor-ns-resize",
467
+ style: {
468
+ height: RESIZE_HANDLE
469
+ },
470
+ onPointerDown: onResizeEnd
471
+ }));
472
+ };
473
+ var PendingBlock = ({ start: start2, end }) => {
474
+ const top = minutesToY(minutesOfDay(start2), HOUR_HEIGHT);
475
+ const height = minutesToY(minutesOfDay(end) - minutesOfDay(start2), HOUR_HEIGHT);
476
+ return /* @__PURE__ */ React2.createElement("div", {
477
+ className: "absolute inset-x-0 rounded bg-primary-500/40 border border-primary-500 pointer-events-none",
478
+ style: {
479
+ top,
480
+ height
481
+ }
482
+ }, /* @__PURE__ */ React2.createElement("div", {
483
+ className: "px-1 py-0.5 text-xs tabular-nums text-inverse-fg"
484
+ }, formatTime(start2), "\u2013", formatTime(end)));
485
+ };
34
486
 
35
487
  // src/components/Calendar/Calendar.tsx
36
488
  var maxRows = 50 * 100;
37
- var start = /* @__PURE__ */ new Date("1970-01-01");
38
- var size = 48;
489
+ var start = gridEpoch;
490
+ var size = 40;
39
491
  var defaultWidth = 7 * size;
40
- var [CalendarContextProvider, useCalendarContext] = createContext("Calendar");
492
+ var EDGE_SCROLL_ZONE = 32;
493
+ var EDGE_SCROLL_MAX_SPEED = 12;
494
+ var DATE_CLASS_NAMES = {
495
+ current: "ring-2 ring-primary-500",
496
+ today: "border-2 border-amber-500 bg-amber-500/50 text-inverse-fg",
497
+ busy: "border border-green-700",
498
+ starred: "border-2 border-dashed border-amber-500"
499
+ };
500
+ var makeRange = (a, b) => {
501
+ const dayA = startOfDay3(a);
502
+ const dayB = startOfDay3(b);
503
+ return dayA <= dayB ? {
504
+ from: dayA,
505
+ to: dayB
506
+ } : {
507
+ from: dayB,
508
+ to: dayA
509
+ };
510
+ };
511
+ var isInRange = (date, range) => {
512
+ if (!range) {
513
+ return false;
514
+ }
515
+ const day = startOfDay3(date).getTime();
516
+ return day >= range.from.getTime() && day <= range.to.getTime();
517
+ };
518
+ var cellDate = (el) => {
519
+ let current = el;
520
+ while (current && current !== document.body) {
521
+ const iso = current.getAttribute?.("data-date");
522
+ if (iso) {
523
+ return new Date(iso);
524
+ }
525
+ current = current.parentElement;
526
+ }
527
+ return void 0;
528
+ };
41
529
  var CalendarRoot = /* @__PURE__ */ forwardRef(({ children, weekStartsOn = 1 }, forwardedRef) => {
42
- const event = useMemo(() => new Event(), []);
43
- const [selected, setSelected] = useState();
44
- const [index, setIndex] = useState();
530
+ const event = useMemo3(() => new Event(), []);
531
+ const [selected, setSelected] = useState2();
532
+ const [index, setIndex] = useState2();
533
+ const [range, setRange] = useState2();
534
+ const [pendingRange, setPendingRange] = useState2();
45
535
  useImperativeHandle(forwardedRef, () => ({
46
536
  scrollTo: (date) => {
47
537
  event.emit({
48
538
  type: "scroll",
49
539
  date
50
540
  });
541
+ },
542
+ select: (date) => {
543
+ event.emit({
544
+ type: "select",
545
+ date
546
+ });
51
547
  }
52
548
  }), [
53
549
  event
54
550
  ]);
55
- return /* @__PURE__ */ React.createElement(CalendarContextProvider, {
551
+ return /* @__PURE__ */ React3.createElement(CalendarContextProvider, {
56
552
  weekStartsOn,
57
553
  event,
58
554
  index,
59
555
  setIndex,
60
556
  selected,
61
- setSelected
557
+ setSelected,
558
+ range,
559
+ setRange,
560
+ pendingRange,
561
+ setPendingRange
62
562
  }, children);
63
563
  });
64
564
  var CALENDAR_TOOLBAR_NAME = "CalendarHeader";
65
- var CalendarToolbar = composable(({ classNames, ...props }, forwardedRef) => {
565
+ var CalendarToolbar = composable2(({ classNames, ...props }, forwardedRef) => {
66
566
  const { t } = useTranslation(translationKey);
67
567
  const { weekStartsOn, event, index, selected } = useCalendarContext(CALENDAR_TOOLBAR_NAME);
68
- const top = useMemo(() => getDate(start, index ?? 0, 6, weekStartsOn), [
568
+ const top = useMemo3(() => getDate(start, index ?? 0, 6, weekStartsOn), [
69
569
  index,
70
570
  weekStartsOn
71
571
  ]);
72
- const today = useMemo(() => /* @__PURE__ */ new Date(), []);
73
- const handleToday = useCallback(() => {
572
+ const today = useMemo3(() => /* @__PURE__ */ new Date(), []);
573
+ const handleToday = useCallback2(() => {
74
574
  event.emit({
75
575
  type: "scroll",
76
576
  date: today
@@ -80,100 +580,358 @@ var CalendarToolbar = composable(({ classNames, ...props }, forwardedRef) => {
80
580
  start,
81
581
  today
82
582
  ]);
83
- return /* @__PURE__ */ React.createElement("div", {
84
- ...composableProps(props, {
583
+ return /* @__PURE__ */ React3.createElement("div", {
584
+ ...composableProps2(props, {
85
585
  role: "none",
86
586
  classNames: [
87
587
  "shrink-0 grid! grid-cols-3 items-center bg-toolbar-surface",
88
588
  classNames
89
589
  ]
90
590
  }),
91
- ref: forwardedRef,
92
- style: {
93
- width: defaultWidth
94
- }
95
- }, /* @__PURE__ */ React.createElement("div", {
591
+ ref: forwardedRef
592
+ }, /* @__PURE__ */ React3.createElement("div", {
96
593
  className: "flex justify-start"
97
- }, /* @__PURE__ */ React.createElement(IconButton, {
594
+ }, /* @__PURE__ */ React3.createElement(IconButton, {
98
595
  variant: "ghost",
99
596
  icon: "ph--calendar--regular",
100
597
  iconOnly: true,
101
598
  classNames: "aspect-square",
102
599
  label: t("today.button"),
103
600
  onClick: handleToday
104
- })), /* @__PURE__ */ React.createElement("div", {
601
+ })), /* @__PURE__ */ React3.createElement("div", {
105
602
  className: "flex justify-center p-2 text-description"
106
- }, format(selected ?? top, "MMMM")), /* @__PURE__ */ React.createElement("div", {
603
+ }, format2(selected ?? top, "MMMM")), /* @__PURE__ */ React3.createElement("div", {
107
604
  className: "flex justify-end p-2 text-description"
108
605
  }, (selected ?? top).getFullYear()));
109
606
  });
110
607
  CalendarToolbar.displayName = CALENDAR_TOOLBAR_NAME;
111
608
  var CALENDAR_GRID_NAME = "CalendarGrid";
112
- var CalendarGrid = composable(({ classNames, rows, dates = [], onSelect, ...props }, forwardedRef) => {
113
- const { weekStartsOn, event, setIndex, selected, setSelected } = useCalendarContext(CALENDAR_GRID_NAME);
609
+ var CalendarGrid = composable2(({ classNames, rows, dates = [], initialDate, scrollMargin = 2, onSelect, onSelectRange, ...props }, forwardedRef) => {
610
+ const { weekStartsOn, event, setIndex, selected, setSelected, range, setRange, pendingRange, setPendingRange } = useCalendarContext(CALENDAR_GRID_NAME);
114
611
  const { ref: containerRef, width = 0, height = 0 } = useResizeDetector();
115
612
  const maxHeight = rows ? rows * size : void 0;
116
- const listRef = useRef(null);
117
- const today = useMemo(() => /* @__PURE__ */ new Date(), []);
118
- const dateSet = useMemo(() => new Set(dates.map((date) => startOfDay(date).toISOString())), [
613
+ const listRef = useRef2(null);
614
+ const gridRef = useRef2(null);
615
+ const today = useMemo3(() => /* @__PURE__ */ new Date(), []);
616
+ const dateMarkers = useMemo3(() => {
617
+ const markers = /* @__PURE__ */ new Map();
618
+ for (const { startDate, endDate, tag = "busy" } of dates) {
619
+ const end = endDate ? startOfDay3(endDate) : startOfDay3(startDate);
620
+ for (let date = startOfDay3(startDate); date <= end; date = addDays3(date, 1)) {
621
+ const iso = date.toISOString();
622
+ if (markers.get(iso) !== "star") {
623
+ markers.set(iso, tag);
624
+ }
625
+ }
626
+ }
627
+ return markers;
628
+ }, [
119
629
  dates
120
630
  ]);
121
- const hasDate = useCallback((date) => dateSet.has(startOfDay(date).toISOString()), [
122
- dateSet
631
+ const getMarker = useCallback2((date) => {
632
+ const iso = startOfDay3(date).toISOString();
633
+ const tag = dateMarkers.get(iso);
634
+ return tag ? {
635
+ tag
636
+ } : void 0;
637
+ }, [
638
+ dateMarkers
123
639
  ]);
124
- const [initialized, setInitialized] = useState(false);
125
- useEffect(() => {
126
- const index = differenceInWeeks(today, start);
127
- listRef.current?.scrollToRow(index);
640
+ const [initialized, setInitialized] = useState2(false);
641
+ useEffect2(() => {
642
+ const index = getRowIndex(start, initialDate ?? today, weekStartsOn);
643
+ listRef.current?.scrollToRow(Math.max(0, index - scrollMargin));
128
644
  }, [
129
645
  initialized,
130
646
  start,
131
- today
647
+ today,
648
+ initialDate,
649
+ weekStartsOn,
650
+ scrollMargin
132
651
  ]);
133
- useEffect(() => {
652
+ useEffect2(() => {
134
653
  return event.on((event2) => {
135
- switch (event2.type) {
136
- case "scroll": {
137
- const index = differenceInWeeks(event2.date, start);
138
- listRef.current?.scrollToRow(index);
139
- break;
140
- }
654
+ if (event2.type === "select") {
655
+ setSelected(event2.date);
141
656
  }
657
+ const index = getRowIndex(start, event2.date, weekStartsOn);
658
+ listRef.current?.scrollToRow(Math.max(0, index - scrollMargin));
142
659
  });
143
660
  }, [
144
- event
661
+ event,
662
+ start,
663
+ weekStartsOn,
664
+ scrollMargin,
665
+ setSelected
145
666
  ]);
146
- const days = useMemo(() => {
147
- const weekStart = startOfWeek(/* @__PURE__ */ new Date(), {
148
- weekStartsOn
149
- });
150
- return Array.from({
151
- length: 7
152
- }, (_, i) => {
153
- const day = addDays(weekStart, i);
154
- return format(day, "EEE");
667
+ const anchorRef = useRef2(void 0);
668
+ const focusRef = useRef2(void 0);
669
+ const draggingRef = useRef2(false);
670
+ const pointerXRef = useRef2(0);
671
+ const pointerYRef = useRef2(0);
672
+ const scrollTopRef = useRef2(0);
673
+ const scrollRafRef = useRef2(void 0);
674
+ const scrollIntoView = useCallback2((date) => {
675
+ const targetRow = getRowIndex(start, date, weekStartsOn);
676
+ const visibleHeight = maxHeight ?? height;
677
+ if (!visibleHeight) {
678
+ return;
679
+ }
680
+ const firstFullyVisibleRow = Math.ceil(scrollTopRef.current / size);
681
+ const lastFullyVisibleRow = Math.floor((scrollTopRef.current + visibleHeight) / size) - 1;
682
+ if (targetRow < firstFullyVisibleRow) {
683
+ listRef.current?.scrollToPosition(targetRow * size);
684
+ } else if (targetRow > lastFullyVisibleRow) {
685
+ listRef.current?.scrollToPosition(Math.max(0, (targetRow + 1) * size - visibleHeight));
686
+ }
687
+ }, [
688
+ height,
689
+ maxHeight,
690
+ weekStartsOn
691
+ ]);
692
+ const updateRangeFromAnchor = useCallback2((focus, fireRange = false) => {
693
+ const anchor = anchorRef.current;
694
+ if (!anchor) {
695
+ return;
696
+ }
697
+ focusRef.current = focus;
698
+ if (isSameDay(anchor, focus)) {
699
+ setRange(void 0);
700
+ setSelected(anchor);
701
+ } else {
702
+ setSelected(void 0);
703
+ const committed = makeRange(anchor, focus);
704
+ setRange(committed);
705
+ if (fireRange) {
706
+ onSelectRange?.({
707
+ range: committed
708
+ });
709
+ }
710
+ }
711
+ }, [
712
+ onSelectRange,
713
+ setRange,
714
+ setSelected
715
+ ]);
716
+ const prevSelectedRef = useRef2(void 0);
717
+ const handleDayPointerDown = useCallback2((date, ev) => {
718
+ ev.preventDefault();
719
+ prevSelectedRef.current = selected;
720
+ anchorRef.current = date;
721
+ focusRef.current = date;
722
+ draggingRef.current = true;
723
+ setRange(void 0);
724
+ setPendingRange(void 0);
725
+ setSelected(date);
726
+ gridRef.current?.focus({
727
+ preventScroll: true
155
728
  });
156
- }, []);
157
- const handleDaySelect = useCallback((date) => {
158
- setSelected((current) => isSameDay(date, current) ? void 0 : date);
159
- onSelect?.({
160
- date
729
+ }, [
730
+ selected,
731
+ setPendingRange,
732
+ setRange,
733
+ setSelected
734
+ ]);
735
+ const handleDayPointerEnter = useCallback2((date) => {
736
+ if (!draggingRef.current) {
737
+ return;
738
+ }
739
+ const anchor = anchorRef.current;
740
+ if (!anchor) {
741
+ return;
742
+ }
743
+ focusRef.current = date;
744
+ setSelected(void 0);
745
+ setPendingRange(makeRange(anchor, date));
746
+ }, [
747
+ setPendingRange,
748
+ setSelected
749
+ ]);
750
+ const handleDayPointerUp = useCallback2((date) => {
751
+ const anchor = anchorRef.current;
752
+ const wasDragging = draggingRef.current;
753
+ draggingRef.current = false;
754
+ setPendingRange(void 0);
755
+ if (!wasDragging || !anchor) {
756
+ return;
757
+ }
758
+ focusRef.current = date;
759
+ if (isSameDay(anchor, date)) {
760
+ if (prevSelectedRef.current && isSameDay(prevSelectedRef.current, date)) {
761
+ setSelected(void 0);
762
+ anchorRef.current = void 0;
763
+ focusRef.current = void 0;
764
+ return;
765
+ }
766
+ setSelected(anchor);
767
+ onSelect?.({
768
+ date
769
+ });
770
+ return;
771
+ }
772
+ const committed = makeRange(anchor, date);
773
+ setRange(committed);
774
+ onSelectRange?.({
775
+ range: committed
161
776
  });
162
777
  }, [
163
- onSelect
778
+ onSelect,
779
+ onSelectRange,
780
+ setPendingRange,
781
+ setRange,
782
+ setSelected
783
+ ]);
784
+ useEffect2(() => {
785
+ const cancel = () => {
786
+ if (draggingRef.current) {
787
+ draggingRef.current = false;
788
+ setPendingRange(void 0);
789
+ }
790
+ };
791
+ window.addEventListener("pointerup", cancel);
792
+ window.addEventListener("pointercancel", cancel);
793
+ return () => {
794
+ window.removeEventListener("pointerup", cancel);
795
+ window.removeEventListener("pointercancel", cancel);
796
+ };
797
+ }, [
798
+ setPendingRange
799
+ ]);
800
+ const tickEdgeScroll = useCallback2(() => {
801
+ scrollRafRef.current = void 0;
802
+ if (!draggingRef.current) {
803
+ return;
804
+ }
805
+ const rect = containerRef.current?.getBoundingClientRect();
806
+ if (!rect) {
807
+ return;
808
+ }
809
+ const y = pointerYRef.current;
810
+ let delta = 0;
811
+ if (y < rect.top + EDGE_SCROLL_ZONE) {
812
+ delta = -EDGE_SCROLL_MAX_SPEED * Math.min(1, Math.max(0, (rect.top + EDGE_SCROLL_ZONE - y) / EDGE_SCROLL_ZONE));
813
+ } else if (y > rect.bottom - EDGE_SCROLL_ZONE) {
814
+ delta = EDGE_SCROLL_MAX_SPEED * Math.min(1, Math.max(0, (y - (rect.bottom - EDGE_SCROLL_ZONE)) / EDGE_SCROLL_ZONE));
815
+ }
816
+ if (delta !== 0) {
817
+ const newScroll = Math.max(0, scrollTopRef.current + delta);
818
+ listRef.current?.scrollToPosition(newScroll);
819
+ const date = cellDate(document.elementFromPoint(pointerXRef.current, y));
820
+ const anchor = anchorRef.current;
821
+ if (date && anchor) {
822
+ focusRef.current = date;
823
+ if (isSameDay(anchor, date)) {
824
+ setPendingRange(void 0);
825
+ setSelected(anchor);
826
+ } else {
827
+ setSelected(void 0);
828
+ setPendingRange(makeRange(anchor, date));
829
+ }
830
+ }
831
+ scrollRafRef.current = requestAnimationFrame(tickEdgeScroll);
832
+ }
833
+ }, [
834
+ containerRef,
835
+ setPendingRange,
836
+ setSelected
837
+ ]);
838
+ useEffect2(() => {
839
+ const handleMove = (ev) => {
840
+ if (!draggingRef.current) {
841
+ return;
842
+ }
843
+ pointerXRef.current = ev.clientX;
844
+ pointerYRef.current = ev.clientY;
845
+ if (scrollRafRef.current === void 0) {
846
+ scrollRafRef.current = requestAnimationFrame(tickEdgeScroll);
847
+ }
848
+ };
849
+ window.addEventListener("pointermove", handleMove);
850
+ return () => {
851
+ window.removeEventListener("pointermove", handleMove);
852
+ if (scrollRafRef.current !== void 0) {
853
+ cancelAnimationFrame(scrollRafRef.current);
854
+ scrollRafRef.current = void 0;
855
+ }
856
+ };
857
+ }, [
858
+ tickEdgeScroll
859
+ ]);
860
+ const handleKeyDown = useCallback2((ev) => {
861
+ let dx = 0;
862
+ switch (ev.key) {
863
+ case "ArrowLeft":
864
+ dx = -1;
865
+ break;
866
+ case "ArrowRight":
867
+ dx = 1;
868
+ break;
869
+ case "ArrowUp":
870
+ dx = -7;
871
+ break;
872
+ case "ArrowDown":
873
+ dx = 7;
874
+ break;
875
+ default:
876
+ return;
877
+ }
878
+ ev.preventDefault();
879
+ if (ev.shiftKey) {
880
+ let anchor = anchorRef.current;
881
+ let focus = focusRef.current;
882
+ if (!anchor) {
883
+ if (selected) {
884
+ anchor = startOfDay3(selected);
885
+ focus = anchor;
886
+ } else if (range) {
887
+ anchor = range.from;
888
+ focus = range.to;
889
+ } else {
890
+ anchor = startOfDay3(today);
891
+ focus = anchor;
892
+ }
893
+ anchorRef.current = anchor;
894
+ focusRef.current = focus;
895
+ }
896
+ const newFocus = addDays3(focus ?? anchor, dx);
897
+ updateRangeFromAnchor(newFocus, true);
898
+ scrollIntoView(newFocus);
899
+ } else {
900
+ const current = selected ?? focusRef.current ?? anchorRef.current ?? today;
901
+ const next = addDays3(startOfDay3(current), dx);
902
+ anchorRef.current = next;
903
+ focusRef.current = next;
904
+ setRange(void 0);
905
+ setPendingRange(void 0);
906
+ setSelected(next);
907
+ onSelect?.({
908
+ date: next
909
+ });
910
+ scrollIntoView(next);
911
+ }
912
+ }, [
913
+ onSelect,
914
+ range,
915
+ scrollIntoView,
916
+ selected,
917
+ setPendingRange,
918
+ setRange,
919
+ setSelected,
920
+ today,
921
+ updateRangeFromAnchor
164
922
  ]);
165
- const handleScroll = useCallback((info) => {
923
+ const activeRange = pendingRange ?? range;
924
+ const handleScroll = useCallback2((info) => {
925
+ scrollTopRef.current = info.scrollTop;
166
926
  setIndex(Math.round(info.scrollTop / size));
167
927
  }, []);
168
- const rowRenderer = useCallback(({ key, index, style }) => {
169
- const getBgColor = (date) => date.getMonth() % 2 === 0 && "bg-modal-surface";
170
- return /* @__PURE__ */ React.createElement("div", {
928
+ const rowRenderer = useCallback2(({ key, index, style }) => {
929
+ const getBgColor = (date) => date.getMonth() % 2 === 0 ? "bg-group-surface" : "bg-group-alt-surface";
930
+ return /* @__PURE__ */ React3.createElement("div", {
171
931
  key,
172
- role: "none",
173
932
  style,
174
933
  className: "grid"
175
- }, /* @__PURE__ */ React.createElement("div", {
176
- role: "none",
934
+ }, /* @__PURE__ */ React3.createElement("div", {
177
935
  className: "grid grid-cols-7 bg-input-surface",
178
936
  style: {
179
937
  gridTemplateColumns: `repeat(7, ${size}px)`
@@ -182,51 +940,66 @@ var CalendarGrid = composable(({ classNames, rows, dates = [], onSelect, ...prop
182
940
  length: 7
183
941
  }).map((_, i) => {
184
942
  const date = getDate(start, index, i, weekStartsOn);
185
- const border = isSameDay(date, selected) ? "border-primary-500" : isSameDay(date, today) ? "border-amber-500" : hasDate(date) ? "border-neutral-700 border-dashed" : void 0;
186
- return /* @__PURE__ */ React.createElement("div", {
943
+ const marker = getMarker(date);
944
+ const isToday = isSameDay(date, today);
945
+ const isCurrent = isSameDay(date, selected);
946
+ const dateClassNames = isToday ? DATE_CLASS_NAMES.today : marker?.tag === "star" ? DATE_CLASS_NAMES.starred : marker ? DATE_CLASS_NAMES.busy : void 0;
947
+ const inRange = isInRange(date, activeRange);
948
+ return /* @__PURE__ */ React3.createElement("div", {
187
949
  key: i,
188
- role: "none",
189
- className: mx("relative flex justify-center items-center cursor-pointer", getBgColor(date)),
190
- onClick: () => handleDaySelect(date)
191
- }, /* @__PURE__ */ React.createElement("span", {
192
- className: "text-description"
193
- }, date.getDate()), !border && date.getDate() === 1 && /* @__PURE__ */ React.createElement("span", {
950
+ "data-date": startOfDay3(date).toISOString(),
951
+ className: mx3("relative flex justify-center cursor-pointer select-none", getBgColor(date)),
952
+ onPointerDown: (ev) => handleDayPointerDown(date, ev),
953
+ onPointerEnter: () => handleDayPointerEnter(date),
954
+ onPointerUp: () => handleDayPointerUp(date)
955
+ }, inRange && /* @__PURE__ */ React3.createElement("div", {
956
+ className: "absolute inset-0 bg-primary-500/20"
957
+ }), !dateClassNames && date.getDate() === 1 && /* @__PURE__ */ React3.createElement("span", {
194
958
  className: "absolute top-0 text-xs text-description"
195
- }, format(date, "MMM")), border && /* @__PURE__ */ React.createElement("div", {
196
- role: "none",
197
- className: mx("absolute inset-1 border-2 rounded-full", border)
959
+ }, format2(date, "MMM")), /* @__PURE__ */ React3.createElement("div", {
960
+ className: mx3("absolute inset-1 rounded-full flex justify-center items-center text-sm text-description", dateClassNames)
961
+ }, date.getDate()), isCurrent && /* @__PURE__ */ React3.createElement("div", {
962
+ className: mx3("absolute inset-0.5 rounded-full", DATE_CLASS_NAMES.current)
198
963
  }));
199
964
  })));
200
965
  }, [
201
- handleDaySelect,
202
- hasDate,
966
+ activeRange,
967
+ handleDayPointerDown,
968
+ handleDayPointerEnter,
969
+ handleDayPointerUp,
970
+ getMarker,
203
971
  selected,
204
972
  weekStartsOn
205
973
  ]);
206
- return /* @__PURE__ */ React.createElement("div", {
207
- ...composableProps(props, {
974
+ return /* @__PURE__ */ React3.createElement("div", {
975
+ ...composableProps2(props, {
208
976
  role: "none",
209
977
  classNames: [
210
- "flex flex-col h-full w-full justify-center overflow-hidden",
978
+ "flex flex-col h-full w-full justify-center overflow-hidden outline-hidden",
211
979
  classNames
212
980
  ]
213
981
  }),
214
- ref: forwardedRef
215
- }, /* @__PURE__ */ React.createElement("div", {
216
- role: "none",
217
- className: "grid w-full grid-cols-7",
982
+ ref: (node) => {
983
+ gridRef.current = node;
984
+ if (typeof forwardedRef === "function") {
985
+ forwardedRef(node);
986
+ } else if (forwardedRef) {
987
+ forwardedRef.current = node;
988
+ }
989
+ },
990
+ tabIndex: 0,
991
+ onKeyDown: handleKeyDown
992
+ }, /* @__PURE__ */ React3.createElement("div", {
218
993
  style: {
219
994
  width: defaultWidth
220
995
  }
221
- }, days.map((date, i) => /* @__PURE__ */ React.createElement("div", {
222
- key: i,
223
- role: "none",
224
- className: "flex justify-center p-2 text-sm font-thin"
225
- }, date))), /* @__PURE__ */ React.createElement("div", {
226
- role: "none",
996
+ }, /* @__PURE__ */ React3.createElement(Weekdays, {
997
+ weekStartsOn,
998
+ columnWidth: size
999
+ })), /* @__PURE__ */ React3.createElement("div", {
227
1000
  className: "flex flex-col h-full w-full justify-center overflow-hidden",
228
1001
  ref: containerRef
229
- }, /* @__PURE__ */ React.createElement(List, {
1002
+ }, /* @__PURE__ */ React3.createElement(List, {
230
1003
  ref: listRef,
231
1004
  role: "none",
232
1005
  className: "scrollbar-none outline-hidden",
@@ -244,7 +1017,8 @@ CalendarGrid.displayName = CALENDAR_GRID_NAME;
244
1017
  var Calendar = {
245
1018
  Root: CalendarRoot,
246
1019
  Toolbar: CalendarToolbar,
247
- Grid: CalendarGrid
1020
+ Grid: CalendarGrid,
1021
+ Week: CalendarWeek
248
1022
  };
249
1023
  export {
250
1024
  Calendar