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