@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,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,
|
|
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 {
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
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 =
|
|
38
|
+
var size = 40;
|
|
42
39
|
var defaultWidth = 7 * size;
|
|
43
|
-
var
|
|
44
|
-
var
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
84
|
-
var
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
|
82
|
+
date
|
|
98
83
|
});
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
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
|
-
|
|
153
|
-
|
|
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
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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
|
-
}
|
|
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
|
-
|
|
231
|
-
|
|
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
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
role: "none",
|
|
238
|
-
className: "flex justify-center bg-groupSurface"
|
|
461
|
+
key,
|
|
462
|
+
style,
|
|
463
|
+
className: "grid"
|
|
239
464
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
240
|
-
|
|
241
|
-
className: "flex is-full grid grid-cols-7",
|
|
465
|
+
className: "grid grid-cols-7 bg-input-surface",
|
|
242
466
|
style: {
|
|
243
|
-
|
|
467
|
+
gridTemplateColumns: `repeat(7, ${size}px)`
|
|
244
468
|
}
|
|
245
|
-
},
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
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
|
-
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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
|
};
|