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