@dxos/react-ui-calendar 0.8.4-main.fffef41 → 0.8.4-staging.60fe92afc8

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.
@@ -1,29 +1,19 @@
1
1
  import { createRequire } from 'node:module';const require = createRequire(import.meta.url);
2
2
 
3
3
  // src/components/Calendar/Calendar.tsx
4
- import { useSignals as _useSignals } from "@preact-signals/safe-react/tracking";
5
4
  import { createContext } from "@radix-ui/react-context";
6
- import { addDays, differenceInWeeks, format, startOfWeek } from "date-fns";
5
+ import { addDays, format, startOfDay, startOfWeek } from "date-fns";
7
6
  import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
8
7
  import { useResizeDetector } from "react-resize-detector";
9
8
  import { List } from "react-virtualized";
10
9
  import { Event } from "@dxos/async";
11
- import { Icon, IconButton, useTranslation } from "@dxos/react-ui";
12
- import { mx } from "@dxos/react-ui-theme";
13
-
14
- // src/translations.ts
15
- var translationKey = "react-ui-calendar";
16
- var translations = [
17
- {
18
- "en-US": {
19
- [translationKey]: {
20
- "today button": "Today"
21
- }
22
- }
23
- }
24
- ];
10
+ import { IconButton, useTranslation } from "@dxos/react-ui";
11
+ import { composable, composableProps } from "@dxos/react-ui";
12
+ import { mx } from "@dxos/ui-theme";
13
+ import { translationKey } from "#translations";
25
14
 
26
15
  // src/components/Calendar/util.ts
16
+ import { differenceInCalendarDays } from "date-fns";
27
17
  var getDate = (start2, weekNumber, dayOfWeek, weekStartsOn) => {
28
18
  const result = new Date(start2);
29
19
  const startDayOfWeek = start2.getDay();
@@ -31,6 +21,13 @@ var getDate = (start2, weekNumber, dayOfWeek, weekStartsOn) => {
31
21
  result.setDate(start2.getDate() - adjustedStartDay + weekNumber * 7 + dayOfWeek);
32
22
  return result;
33
23
  };
24
+ var getRowIndex = (start2, date, weekStartsOn) => {
25
+ const startDayOfWeek = start2.getDay();
26
+ const adjustedStartDay = (startDayOfWeek === 0 ? 7 : startDayOfWeek) - weekStartsOn;
27
+ const row0Start = new Date(start2);
28
+ row0Start.setDate(start2.getDate() - adjustedStartDay);
29
+ return Math.floor(differenceInCalendarDays(date, row0Start) / 7);
30
+ };
34
31
  var isSameDay = (date1, date2) => {
35
32
  return !!date2 && date1.getFullYear() === date2.getFullYear() && date1.getMonth() === date2.getMonth() && date1.getDate() === date2.getDate();
36
33
  };
@@ -38,240 +35,515 @@ var isSameDay = (date1, date2) => {
38
35
  // src/components/Calendar/Calendar.tsx
39
36
  var maxRows = 50 * 100;
40
37
  var start = /* @__PURE__ */ new Date("1970-01-01");
41
- var size = 48;
38
+ var size = 40;
42
39
  var defaultWidth = 7 * size;
43
- var [CalendarContextProvider, useCalendarContext] = createContext("Calendar");
44
- var CalendarRoot = /* @__PURE__ */ forwardRef(({ children, weekStartsOn = 1 }, forwardedRef) => {
45
- var _effect = _useSignals();
46
- try {
47
- const event = useMemo(() => new Event(), []);
48
- const [selected, setSelected] = useState();
49
- const [index, setIndex] = useState();
50
- useImperativeHandle(forwardedRef, () => ({
51
- scrollTo: (date) => {
52
- event.emit({
53
- type: "scroll",
54
- date
55
- });
56
- }
57
- }), [
58
- event
59
- ]);
60
- return /* @__PURE__ */ React.createElement(CalendarContextProvider, {
61
- weekStartsOn,
62
- event,
63
- index,
64
- setIndex,
65
- selected,
66
- setSelected
67
- }, children);
68
- } finally {
69
- _effect.f();
40
+ var EDGE_SCROLL_ZONE = 32;
41
+ var EDGE_SCROLL_MAX_SPEED = 12;
42
+ var makeRange = (a, b) => {
43
+ const dayA = startOfDay(a);
44
+ const dayB = startOfDay(b);
45
+ return dayA <= dayB ? {
46
+ from: dayA,
47
+ to: dayB
48
+ } : {
49
+ from: dayB,
50
+ to: dayA
51
+ };
52
+ };
53
+ var isInRange = (date, range) => {
54
+ if (!range) {
55
+ return false;
70
56
  }
71
- });
72
- var CalendarViewport = ({ children, classNames }) => {
73
- var _effect = _useSignals();
74
- try {
75
- return /* @__PURE__ */ React.createElement("div", {
76
- role: "none",
77
- className: mx("flex flex-col items-center overflow-hidden bg-inputSurface", classNames)
78
- }, children);
79
- } finally {
80
- _effect.f();
57
+ const day = startOfDay(date).getTime();
58
+ return day >= range.from.getTime() && day <= range.to.getTime();
59
+ };
60
+ var cellDate = (el) => {
61
+ let current = el;
62
+ while (current && current !== document.body) {
63
+ const iso = current.getAttribute?.("data-date");
64
+ if (iso) {
65
+ return new Date(iso);
66
+ }
67
+ current = current.parentElement;
81
68
  }
69
+ return void 0;
82
70
  };
83
- CalendarViewport.displayName = "CalendarContent";
84
- var CalendarToolbar = ({ classNames }) => {
85
- var _effect = _useSignals();
86
- try {
87
- const { t } = useTranslation(translationKey);
88
- const { weekStartsOn, event, index, selected } = useCalendarContext(CalendarToolbar.displayName);
89
- const top = useMemo(() => getDate(start, index ?? 0, 6, weekStartsOn), [
90
- index,
91
- weekStartsOn
92
- ]);
93
- const today = useMemo(() => /* @__PURE__ */ new Date(), []);
94
- const handleToday = useCallback(() => {
71
+ var [CalendarContextProvider, useCalendarContext] = createContext("Calendar");
72
+ var CalendarRoot = /* @__PURE__ */ forwardRef(({ children, weekStartsOn = 1 }, forwardedRef) => {
73
+ const event = useMemo(() => new Event(), []);
74
+ const [selected, setSelected] = useState();
75
+ const [index, setIndex] = useState();
76
+ const [range, setRange] = useState();
77
+ const [pendingRange, setPendingRange] = useState();
78
+ useImperativeHandle(forwardedRef, () => ({
79
+ scrollTo: (date) => {
95
80
  event.emit({
96
81
  type: "scroll",
97
- date: today
82
+ date
98
83
  });
99
- }, [
100
- event,
101
- start,
102
- today
103
- ]);
104
- return /* @__PURE__ */ React.createElement("div", {
84
+ }
85
+ }), [
86
+ event
87
+ ]);
88
+ return /* @__PURE__ */ React.createElement(CalendarContextProvider, {
89
+ weekStartsOn,
90
+ event,
91
+ index,
92
+ setIndex,
93
+ selected,
94
+ setSelected,
95
+ range,
96
+ setRange,
97
+ pendingRange,
98
+ setPendingRange
99
+ }, children);
100
+ });
101
+ var CALENDAR_TOOLBAR_NAME = "CalendarHeader";
102
+ var CalendarToolbar = composable(({ classNames, ...props }, forwardedRef) => {
103
+ const { t } = useTranslation(translationKey);
104
+ const { weekStartsOn, event, index, selected } = useCalendarContext(CALENDAR_TOOLBAR_NAME);
105
+ const top = useMemo(() => getDate(start, index ?? 0, 6, weekStartsOn), [
106
+ index,
107
+ weekStartsOn
108
+ ]);
109
+ const today = useMemo(() => /* @__PURE__ */ new Date(), []);
110
+ const handleToday = useCallback(() => {
111
+ event.emit({
112
+ type: "scroll",
113
+ date: today
114
+ });
115
+ }, [
116
+ event,
117
+ start,
118
+ today
119
+ ]);
120
+ return /* @__PURE__ */ React.createElement("div", {
121
+ ...composableProps(props, {
105
122
  role: "none",
106
- className: mx("shink-0 is-full grid grid-cols-3 items-center bg-barSurface", classNames),
107
- style: {
108
- width: defaultWidth
109
- }
110
- }, /* @__PURE__ */ React.createElement("div", {
111
- className: "flex justify-start"
112
- }, /* @__PURE__ */ React.createElement(IconButton, {
113
- variant: "ghost",
114
- size: 5,
115
- icon: "ph--calendar--regular",
116
- iconOnly: true,
117
- classNames: "aspect-square",
118
- label: t("today button"),
119
- onClick: handleToday
120
- })), /* @__PURE__ */ React.createElement("div", {
121
- className: "flex justify-center p-2 text-description"
122
- }, format(selected ?? top, "MMMM")), /* @__PURE__ */ React.createElement("div", {
123
- className: "flex justify-end p-2 text-description"
124
- }, (selected ?? top).getFullYear()));
125
- } finally {
126
- _effect.f();
127
- }
128
- };
129
- CalendarToolbar.displayName = "CalendarHeader";
130
- var CalendarGrid = ({ classNames, rows, onSelect }) => {
131
- var _effect = _useSignals();
132
- try {
133
- const { weekStartsOn, event, setIndex, selected, setSelected } = useCalendarContext(CalendarGrid.displayName);
134
- const { ref: containerRef, width = 0, height = 0 } = useResizeDetector();
135
- const maxHeight = rows ? rows * size : void 0;
136
- const listRef = useRef(null);
137
- const today = useMemo(() => /* @__PURE__ */ new Date(), []);
138
- const [initialized, setInitialized] = useState(false);
139
- useEffect(() => {
140
- const index = differenceInWeeks(today, start);
141
- listRef.current?.scrollToRow(index);
142
- }, [
143
- initialized,
144
- start,
145
- today
146
- ]);
147
- useEffect(() => {
148
- return event.on((event2) => {
149
- switch (event2.type) {
150
- case "scroll": {
151
- const index = differenceInWeeks(event2.date, start);
152
- listRef.current?.scrollToRow(index);
153
- break;
154
- }
123
+ classNames: [
124
+ "shrink-0 grid! grid-cols-3 items-center bg-toolbar-surface",
125
+ classNames
126
+ ]
127
+ }),
128
+ ref: forwardedRef,
129
+ style: {
130
+ width: defaultWidth
131
+ }
132
+ }, /* @__PURE__ */ React.createElement("div", {
133
+ className: "flex justify-start"
134
+ }, /* @__PURE__ */ React.createElement(IconButton, {
135
+ variant: "ghost",
136
+ icon: "ph--calendar--regular",
137
+ iconOnly: true,
138
+ classNames: "aspect-square",
139
+ label: t("today.button"),
140
+ onClick: handleToday
141
+ })), /* @__PURE__ */ React.createElement("div", {
142
+ className: "flex justify-center p-2 text-description"
143
+ }, format(selected ?? top, "MMMM")), /* @__PURE__ */ React.createElement("div", {
144
+ className: "flex justify-end p-2 text-description"
145
+ }, (selected ?? top).getFullYear()));
146
+ });
147
+ CalendarToolbar.displayName = CALENDAR_TOOLBAR_NAME;
148
+ var CALENDAR_GRID_NAME = "CalendarGrid";
149
+ var CalendarGrid = composable(({ classNames, rows, dates = [], initialDate, onSelect, onSelectRange, ...props }, forwardedRef) => {
150
+ const { weekStartsOn, event, setIndex, selected, setSelected, range, setRange, pendingRange, setPendingRange } = useCalendarContext(CALENDAR_GRID_NAME);
151
+ const { ref: containerRef, width = 0, height = 0 } = useResizeDetector();
152
+ const maxHeight = rows ? rows * size : void 0;
153
+ const listRef = useRef(null);
154
+ const gridRef = useRef(null);
155
+ const today = useMemo(() => /* @__PURE__ */ new Date(), []);
156
+ const dateSet = useMemo(() => new Set(dates.map((date) => startOfDay(date).toISOString())), [
157
+ dates
158
+ ]);
159
+ const hasDate = useCallback((date) => dateSet.has(startOfDay(date).toISOString()), [
160
+ dateSet
161
+ ]);
162
+ const [initialized, setInitialized] = useState(false);
163
+ useEffect(() => {
164
+ const index = getRowIndex(start, initialDate ?? today, weekStartsOn);
165
+ listRef.current?.scrollToRow(index);
166
+ }, [
167
+ initialized,
168
+ start,
169
+ today,
170
+ initialDate,
171
+ weekStartsOn
172
+ ]);
173
+ useEffect(() => {
174
+ return event.on((event2) => {
175
+ switch (event2.type) {
176
+ case "scroll": {
177
+ const index = getRowIndex(start, event2.date, weekStartsOn);
178
+ listRef.current?.scrollToRow(index);
179
+ break;
155
180
  }
156
- });
157
- }, [
158
- event
159
- ]);
160
- const days = useMemo(() => {
161
- const weekStart = startOfWeek(/* @__PURE__ */ new Date(), {
162
- weekStartsOn
163
- });
164
- return Array.from({
165
- length: 7
166
- }, (_, i) => {
167
- const day = addDays(weekStart, i);
168
- return format(day, "EEE");
169
- });
170
- }, []);
171
- const getNumAppointments = useCallback((_date) => {
172
- return 0;
173
- }, []);
174
- const handleDaySelect = useCallback((date) => {
175
- setSelected((current) => isSameDay(date, current) ? void 0 : date);
181
+ }
182
+ });
183
+ }, [
184
+ event
185
+ ]);
186
+ const days = useMemo(() => {
187
+ const weekStart = startOfWeek(/* @__PURE__ */ new Date(), {
188
+ weekStartsOn
189
+ });
190
+ return Array.from({
191
+ length: 7
192
+ }, (_, i) => {
193
+ const day = addDays(weekStart, i);
194
+ return format(day, "EEE");
195
+ });
196
+ }, []);
197
+ const anchorRef = useRef(void 0);
198
+ const focusRef = useRef(void 0);
199
+ const draggingRef = useRef(false);
200
+ const pointerXRef = useRef(0);
201
+ const pointerYRef = useRef(0);
202
+ const scrollTopRef = useRef(0);
203
+ const scrollRafRef = useRef(void 0);
204
+ const scrollIntoView = useCallback((date) => {
205
+ const targetRow = getRowIndex(start, date, weekStartsOn);
206
+ const visibleHeight = maxHeight ?? height;
207
+ if (!visibleHeight) {
208
+ return;
209
+ }
210
+ const firstFullyVisibleRow = Math.ceil(scrollTopRef.current / size);
211
+ const lastFullyVisibleRow = Math.floor((scrollTopRef.current + visibleHeight) / size) - 1;
212
+ if (targetRow < firstFullyVisibleRow) {
213
+ listRef.current?.scrollToPosition(targetRow * size);
214
+ } else if (targetRow > lastFullyVisibleRow) {
215
+ listRef.current?.scrollToPosition(Math.max(0, (targetRow + 1) * size - visibleHeight));
216
+ }
217
+ }, [
218
+ height,
219
+ maxHeight,
220
+ weekStartsOn
221
+ ]);
222
+ const updateRangeFromAnchor = useCallback((focus, fireRange = false) => {
223
+ const anchor = anchorRef.current;
224
+ if (!anchor) {
225
+ return;
226
+ }
227
+ focusRef.current = focus;
228
+ if (isSameDay(anchor, focus)) {
229
+ setRange(void 0);
230
+ setSelected(anchor);
231
+ } else {
232
+ setSelected(void 0);
233
+ const committed = makeRange(anchor, focus);
234
+ setRange(committed);
235
+ if (fireRange) {
236
+ onSelectRange?.({
237
+ range: committed
238
+ });
239
+ }
240
+ }
241
+ }, [
242
+ onSelectRange,
243
+ setRange,
244
+ setSelected
245
+ ]);
246
+ const prevSelectedRef = useRef(void 0);
247
+ const handleDayPointerDown = useCallback((date, ev) => {
248
+ ev.preventDefault();
249
+ prevSelectedRef.current = selected;
250
+ anchorRef.current = date;
251
+ focusRef.current = date;
252
+ draggingRef.current = true;
253
+ setRange(void 0);
254
+ setPendingRange(void 0);
255
+ setSelected(date);
256
+ gridRef.current?.focus({
257
+ preventScroll: true
258
+ });
259
+ }, [
260
+ selected,
261
+ setPendingRange,
262
+ setRange,
263
+ setSelected
264
+ ]);
265
+ const handleDayPointerEnter = useCallback((date) => {
266
+ if (!draggingRef.current) {
267
+ return;
268
+ }
269
+ const anchor = anchorRef.current;
270
+ if (!anchor) {
271
+ return;
272
+ }
273
+ focusRef.current = date;
274
+ setSelected(void 0);
275
+ setPendingRange(makeRange(anchor, date));
276
+ }, [
277
+ setPendingRange,
278
+ setSelected
279
+ ]);
280
+ const handleDayPointerUp = useCallback((date) => {
281
+ const anchor = anchorRef.current;
282
+ const wasDragging = draggingRef.current;
283
+ draggingRef.current = false;
284
+ setPendingRange(void 0);
285
+ if (!wasDragging || !anchor) {
286
+ return;
287
+ }
288
+ focusRef.current = date;
289
+ if (isSameDay(anchor, date)) {
290
+ if (prevSelectedRef.current && isSameDay(prevSelectedRef.current, date)) {
291
+ setSelected(void 0);
292
+ anchorRef.current = void 0;
293
+ focusRef.current = void 0;
294
+ return;
295
+ }
296
+ setSelected(anchor);
176
297
  onSelect?.({
177
298
  date
178
299
  });
179
- }, [
180
- onSelect
181
- ]);
182
- const handleScroll = useCallback((info) => {
183
- setIndex(Math.round(info.scrollTop / size));
184
- }, []);
185
- const rowRenderer = useCallback(({ key, index, style }) => {
186
- const getBgColor = (date) => date.getMonth() % 2 === 0 && "bg-modalSurface";
187
- return /* @__PURE__ */ React.createElement("div", {
188
- key,
189
- role: "none",
190
- style,
191
- className: "is-full grid grid-cols-[1fr_max-content_1fr] snap-center"
192
- }, /* @__PURE__ */ React.createElement("div", {
193
- role: "none",
194
- className: mx(getBgColor(getDate(start, index, 0, weekStartsOn)))
195
- }), /* @__PURE__ */ React.createElement("div", {
196
- role: "none",
197
- className: "grid grid-cols-7",
198
- style: {
199
- gridTemplateColumns: `repeat(7, ${size}px)`
300
+ return;
301
+ }
302
+ const committed = makeRange(anchor, date);
303
+ setRange(committed);
304
+ onSelectRange?.({
305
+ range: committed
306
+ });
307
+ }, [
308
+ onSelect,
309
+ onSelectRange,
310
+ setPendingRange,
311
+ setRange,
312
+ setSelected
313
+ ]);
314
+ useEffect(() => {
315
+ const cancel = () => {
316
+ if (draggingRef.current) {
317
+ draggingRef.current = false;
318
+ setPendingRange(void 0);
319
+ }
320
+ };
321
+ window.addEventListener("pointerup", cancel);
322
+ window.addEventListener("pointercancel", cancel);
323
+ return () => {
324
+ window.removeEventListener("pointerup", cancel);
325
+ window.removeEventListener("pointercancel", cancel);
326
+ };
327
+ }, [
328
+ setPendingRange
329
+ ]);
330
+ const tickEdgeScroll = useCallback(() => {
331
+ scrollRafRef.current = void 0;
332
+ if (!draggingRef.current) {
333
+ return;
334
+ }
335
+ const rect = containerRef.current?.getBoundingClientRect();
336
+ if (!rect) {
337
+ return;
338
+ }
339
+ const y = pointerYRef.current;
340
+ let delta = 0;
341
+ if (y < rect.top + EDGE_SCROLL_ZONE) {
342
+ delta = -EDGE_SCROLL_MAX_SPEED * Math.min(1, Math.max(0, (rect.top + EDGE_SCROLL_ZONE - y) / EDGE_SCROLL_ZONE));
343
+ } else if (y > rect.bottom - EDGE_SCROLL_ZONE) {
344
+ delta = EDGE_SCROLL_MAX_SPEED * Math.min(1, Math.max(0, (y - (rect.bottom - EDGE_SCROLL_ZONE)) / EDGE_SCROLL_ZONE));
345
+ }
346
+ if (delta !== 0) {
347
+ const newScroll = Math.max(0, scrollTopRef.current + delta);
348
+ listRef.current?.scrollToPosition(newScroll);
349
+ const date = cellDate(document.elementFromPoint(pointerXRef.current, y));
350
+ const anchor = anchorRef.current;
351
+ if (date && anchor) {
352
+ focusRef.current = date;
353
+ if (isSameDay(anchor, date)) {
354
+ setPendingRange(void 0);
355
+ setSelected(anchor);
356
+ } else {
357
+ setSelected(void 0);
358
+ setPendingRange(makeRange(anchor, date));
200
359
  }
201
- }, Array.from({
202
- length: 7
203
- }).map((_, i) => {
204
- const date = getDate(start, index, i, weekStartsOn);
205
- const num = getNumAppointments(date);
206
- const border = isSameDay(date, selected) ? "border-primary-500" : isSameDay(date, today) ? "border-amber-500" : void 0;
207
- return /* @__PURE__ */ React.createElement("div", {
208
- key: i,
209
- role: "none",
210
- className: mx("relative flex justify-center items-center cursor-pointer", getBgColor(date)),
211
- onClick: () => handleDaySelect(date)
212
- }, /* @__PURE__ */ React.createElement("span", {
213
- className: "text-description"
214
- }, date.getDate()), !border && date.getDate() === 1 && /* @__PURE__ */ React.createElement("span", {
215
- className: "absolute top-0 text-xs text-description"
216
- }, format(date, "MMM")), border && /* @__PURE__ */ React.createElement("div", {
217
- role: "none",
218
- className: mx("absolute top-0 left-0 is-full bs-full border-2 rounded-full", border)
219
- }), num > 0 && /* @__PURE__ */ React.createElement(Icon, {
220
- classNames: "absolute bottom-0",
221
- icon: num > 3 ? "ph--dots-three--regular" : "ph--dot--regular",
222
- size: 5
223
- }));
224
- })), /* @__PURE__ */ React.createElement("div", {
225
- className: mx(getBgColor(getDate(start, index, 6, weekStartsOn)))
226
- }));
227
- }, [
228
- handleDaySelect,
229
- getNumAppointments,
230
- selected,
231
- weekStartsOn
232
- ]);
360
+ }
361
+ scrollRafRef.current = requestAnimationFrame(tickEdgeScroll);
362
+ }
363
+ }, [
364
+ containerRef,
365
+ setPendingRange,
366
+ setSelected
367
+ ]);
368
+ useEffect(() => {
369
+ const handleMove = (ev) => {
370
+ if (!draggingRef.current) {
371
+ return;
372
+ }
373
+ pointerXRef.current = ev.clientX;
374
+ pointerYRef.current = ev.clientY;
375
+ if (scrollRafRef.current === void 0) {
376
+ scrollRafRef.current = requestAnimationFrame(tickEdgeScroll);
377
+ }
378
+ };
379
+ window.addEventListener("pointermove", handleMove);
380
+ return () => {
381
+ window.removeEventListener("pointermove", handleMove);
382
+ if (scrollRafRef.current !== void 0) {
383
+ cancelAnimationFrame(scrollRafRef.current);
384
+ scrollRafRef.current = void 0;
385
+ }
386
+ };
387
+ }, [
388
+ tickEdgeScroll
389
+ ]);
390
+ const handleKeyDown = useCallback((ev) => {
391
+ let dx = 0;
392
+ switch (ev.key) {
393
+ case "ArrowLeft":
394
+ dx = -1;
395
+ break;
396
+ case "ArrowRight":
397
+ dx = 1;
398
+ break;
399
+ case "ArrowUp":
400
+ dx = -7;
401
+ break;
402
+ case "ArrowDown":
403
+ dx = 7;
404
+ break;
405
+ default:
406
+ return;
407
+ }
408
+ ev.preventDefault();
409
+ if (ev.shiftKey) {
410
+ let anchor = anchorRef.current;
411
+ let focus = focusRef.current;
412
+ if (!anchor) {
413
+ if (selected) {
414
+ anchor = startOfDay(selected);
415
+ focus = anchor;
416
+ } else if (range) {
417
+ anchor = range.from;
418
+ focus = range.to;
419
+ } else {
420
+ anchor = startOfDay(today);
421
+ focus = anchor;
422
+ }
423
+ anchorRef.current = anchor;
424
+ focusRef.current = focus;
425
+ }
426
+ const newFocus = addDays(focus ?? anchor, dx);
427
+ updateRangeFromAnchor(newFocus, true);
428
+ scrollIntoView(newFocus);
429
+ } else {
430
+ const current = selected ?? focusRef.current ?? anchorRef.current ?? today;
431
+ const next = addDays(startOfDay(current), dx);
432
+ anchorRef.current = next;
433
+ focusRef.current = next;
434
+ setRange(void 0);
435
+ setPendingRange(void 0);
436
+ setSelected(next);
437
+ onSelect?.({
438
+ date: next
439
+ });
440
+ scrollIntoView(next);
441
+ }
442
+ }, [
443
+ onSelect,
444
+ range,
445
+ scrollIntoView,
446
+ selected,
447
+ setPendingRange,
448
+ setRange,
449
+ setSelected,
450
+ today,
451
+ updateRangeFromAnchor
452
+ ]);
453
+ const activeRange = pendingRange ?? range;
454
+ const handleScroll = useCallback((info) => {
455
+ scrollTopRef.current = info.scrollTop;
456
+ setIndex(Math.round(info.scrollTop / size));
457
+ }, []);
458
+ const rowRenderer = useCallback(({ key, index, style }) => {
459
+ const getBgColor = (date) => date.getMonth() % 2 === 0 ? "bg-group-surface" : "bg-group-alt-surface";
233
460
  return /* @__PURE__ */ React.createElement("div", {
234
- role: "none",
235
- className: mx("flex flex-col bs-full is-full justify-center overflow-hidden", classNames)
236
- }, /* @__PURE__ */ React.createElement("div", {
237
- role: "none",
238
- className: "flex justify-center bg-groupSurface"
461
+ key,
462
+ style,
463
+ className: "grid"
239
464
  }, /* @__PURE__ */ React.createElement("div", {
240
- role: "none",
241
- className: "flex is-full grid grid-cols-7",
465
+ className: "grid grid-cols-7 bg-input-surface",
242
466
  style: {
243
- width: defaultWidth
467
+ gridTemplateColumns: `repeat(7, ${size}px)`
244
468
  }
245
- }, days.map((date, i) => /* @__PURE__ */ React.createElement("div", {
246
- key: i,
247
- role: "none",
248
- className: "flex justify-center p-2 text-sm font-thin"
249
- }, date)))), /* @__PURE__ */ React.createElement("div", {
250
- role: "none",
251
- className: "flex flex-col bs-full is-full justify-center overflow-hidden",
252
- ref: containerRef
253
- }, /* @__PURE__ */ React.createElement(List, {
254
- ref: listRef,
255
- role: "none",
256
- // TODO(burdon): Snap isn't working.
257
- className: "[&>div]:snap-y scrollbar-none outline-none",
258
- width,
259
- height: maxHeight ?? height,
260
- rowCount: maxRows,
261
- rowHeight: size,
262
- rowRenderer,
263
- scrollToAlignment: "start",
264
- onScroll: handleScroll,
265
- onRowsRendered: () => setInitialized(true)
469
+ }, Array.from({
470
+ length: 7
471
+ }).map((_, i) => {
472
+ const date = getDate(start, index, i, weekStartsOn);
473
+ const inRange = isInRange(date, activeRange);
474
+ const border = isSameDay(date, selected) ? "border-primary-500" : isSameDay(date, today) ? "border-amber-500" : hasDate(date) ? "border-neutral-700 border-dashed" : void 0;
475
+ return /* @__PURE__ */ React.createElement("div", {
476
+ key: i,
477
+ "data-date": startOfDay(date).toISOString(),
478
+ className: mx("relative flex justify-center items-center cursor-pointer select-none", getBgColor(date)),
479
+ onPointerDown: (ev) => handleDayPointerDown(date, ev),
480
+ onPointerEnter: () => handleDayPointerEnter(date),
481
+ onPointerUp: () => handleDayPointerUp(date)
482
+ }, inRange && /* @__PURE__ */ React.createElement("div", {
483
+ className: "absolute inset-0 bg-primary-500/20"
484
+ }), /* @__PURE__ */ React.createElement("span", {
485
+ className: "relative text-description text-sm"
486
+ }, date.getDate()), !border && date.getDate() === 1 && /* @__PURE__ */ React.createElement("span", {
487
+ className: "absolute top-0 text-xs text-description"
488
+ }, format(date, "MMM")), border && /* @__PURE__ */ React.createElement("div", {
489
+ className: mx("absolute inset-1 border-2 rounded-full", border)
490
+ }));
266
491
  })));
267
- } finally {
268
- _effect.f();
269
- }
270
- };
271
- CalendarGrid.displayName = "CalendarGrid";
492
+ }, [
493
+ activeRange,
494
+ handleDayPointerDown,
495
+ handleDayPointerEnter,
496
+ handleDayPointerUp,
497
+ hasDate,
498
+ selected,
499
+ weekStartsOn
500
+ ]);
501
+ return /* @__PURE__ */ React.createElement("div", {
502
+ ...composableProps(props, {
503
+ role: "none",
504
+ classNames: [
505
+ "flex flex-col h-full w-full justify-center overflow-hidden outline-hidden",
506
+ classNames
507
+ ]
508
+ }),
509
+ ref: (node) => {
510
+ gridRef.current = node;
511
+ if (typeof forwardedRef === "function") {
512
+ forwardedRef(node);
513
+ } else if (forwardedRef) {
514
+ forwardedRef.current = node;
515
+ }
516
+ },
517
+ tabIndex: 0,
518
+ onKeyDown: handleKeyDown
519
+ }, /* @__PURE__ */ React.createElement("div", {
520
+ className: "grid w-full grid-cols-7",
521
+ style: {
522
+ width: defaultWidth
523
+ }
524
+ }, days.map((date, i) => /* @__PURE__ */ React.createElement("div", {
525
+ key: i,
526
+ className: "flex justify-center p-2 text-sm font-thin"
527
+ }, date))), /* @__PURE__ */ React.createElement("div", {
528
+ className: "flex flex-col h-full w-full justify-center overflow-hidden",
529
+ ref: containerRef
530
+ }, /* @__PURE__ */ React.createElement(List, {
531
+ ref: listRef,
532
+ role: "none",
533
+ className: "scrollbar-none outline-hidden",
534
+ width,
535
+ height: maxHeight ?? height,
536
+ rowCount: maxRows,
537
+ rowHeight: size,
538
+ rowRenderer,
539
+ scrollToAlignment: "start",
540
+ onScroll: handleScroll,
541
+ onRowsRendered: () => setInitialized(true)
542
+ })));
543
+ });
544
+ CalendarGrid.displayName = CALENDAR_GRID_NAME;
272
545
  var Calendar = {
273
546
  Root: CalendarRoot,
274
- Viewport: CalendarViewport,
275
547
  Toolbar: CalendarToolbar,
276
548
  Grid: CalendarGrid
277
549
  };