@marco.colia/react-fast-scheduler 2.1.0 → 2.1.1
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/README.md +32 -4
- package/dist/index.cjs +244 -85
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -2
- package/dist/index.d.ts +10 -2
- package/dist/index.js +245 -86
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { format } from 'date-fns';
|
|
3
|
-
import * as
|
|
1
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
+
import { format, startOfMonth, startOfWeek, endOfWeek, endOfMonth, addDays, addMonths, isSameDay, isToday, isSameMonth } from 'date-fns';
|
|
3
|
+
import * as React5 from 'react';
|
|
4
4
|
|
|
5
5
|
// src/components/defaultRenderAppointment/index.tsx
|
|
6
6
|
function defaultRenderAppointment({
|
|
@@ -33,6 +33,206 @@ function defaultRenderAppointment({
|
|
|
33
33
|
] });
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
// src/lib/cn.ts
|
|
37
|
+
function cn(...inputs) {
|
|
38
|
+
return inputs.filter(Boolean).join(" ");
|
|
39
|
+
}
|
|
40
|
+
var variantClasses = {
|
|
41
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
42
|
+
outline: "border border-border bg-card text-foreground hover:bg-muted",
|
|
43
|
+
ghost: "text-foreground hover:bg-muted"
|
|
44
|
+
};
|
|
45
|
+
var sizeClasses = {
|
|
46
|
+
default: "h-9 px-4 py-2",
|
|
47
|
+
sm: "h-8 px-3 text-xs",
|
|
48
|
+
icon: "h-8 w-8"
|
|
49
|
+
};
|
|
50
|
+
var Button = React5.forwardRef(function Button2({ className, variant = "default", size = "default", type = "button", ...props }, ref) {
|
|
51
|
+
return /* @__PURE__ */ jsx(
|
|
52
|
+
"button",
|
|
53
|
+
{
|
|
54
|
+
ref,
|
|
55
|
+
type,
|
|
56
|
+
className: cn(
|
|
57
|
+
"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
|
58
|
+
variantClasses[variant],
|
|
59
|
+
sizeClasses[size],
|
|
60
|
+
className
|
|
61
|
+
),
|
|
62
|
+
...props
|
|
63
|
+
}
|
|
64
|
+
);
|
|
65
|
+
});
|
|
66
|
+
function Calendar({
|
|
67
|
+
selected,
|
|
68
|
+
onSelect
|
|
69
|
+
}) {
|
|
70
|
+
const [month, setMonth] = React5.useState(selected ?? /* @__PURE__ */ new Date());
|
|
71
|
+
React5.useEffect(() => {
|
|
72
|
+
if (selected) {
|
|
73
|
+
setMonth(selected);
|
|
74
|
+
}
|
|
75
|
+
}, [selected]);
|
|
76
|
+
const monthStart = startOfMonth(month);
|
|
77
|
+
const calendarStart = startOfWeek(monthStart, { weekStartsOn: 1 });
|
|
78
|
+
const calendarEnd = endOfWeek(endOfMonth(month), { weekStartsOn: 1 });
|
|
79
|
+
const days = [];
|
|
80
|
+
for (let day = calendarStart; day <= calendarEnd; day = addDays(day, 1)) {
|
|
81
|
+
days.push(day);
|
|
82
|
+
}
|
|
83
|
+
return /* @__PURE__ */ jsxs("div", { className: "w-72 rounded-md bg-popover", children: [
|
|
84
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between", children: [
|
|
85
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", "aria-label": "Previous month", onClick: () => setMonth(addMonths(month, -1)), children: "<" }),
|
|
86
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm font-medium", children: format(month, "MMMM yyyy") }),
|
|
87
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", "aria-label": "Next month", onClick: () => setMonth(addMonths(month, 1)), children: ">" })
|
|
88
|
+
] }),
|
|
89
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 gap-1 text-center text-xs text-muted-foreground", children: ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"].map((label) => /* @__PURE__ */ jsx("div", { className: "py-1", children: label }, label)) }),
|
|
90
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 gap-1", children: days.map((day) => {
|
|
91
|
+
const isSelected = selected ? isSameDay(day, selected) : false;
|
|
92
|
+
return /* @__PURE__ */ jsx(
|
|
93
|
+
Button,
|
|
94
|
+
{
|
|
95
|
+
variant: isSelected ? "default" : "ghost",
|
|
96
|
+
size: "icon",
|
|
97
|
+
className: cn(
|
|
98
|
+
"text-xs",
|
|
99
|
+
!isSameMonth(day, month) && "text-muted-foreground opacity-60",
|
|
100
|
+
isToday(day) && !isSelected && "border border-border"
|
|
101
|
+
),
|
|
102
|
+
onClick: () => onSelect?.(day),
|
|
103
|
+
children: format(day, "d")
|
|
104
|
+
},
|
|
105
|
+
day.toISOString()
|
|
106
|
+
);
|
|
107
|
+
}) })
|
|
108
|
+
] });
|
|
109
|
+
}
|
|
110
|
+
var PopoverContext = React5.createContext(null);
|
|
111
|
+
function usePopoverContext() {
|
|
112
|
+
const context = React5.useContext(PopoverContext);
|
|
113
|
+
if (!context) {
|
|
114
|
+
throw new Error("Popover components must be used within Popover.");
|
|
115
|
+
}
|
|
116
|
+
return context;
|
|
117
|
+
}
|
|
118
|
+
function Popover({
|
|
119
|
+
children,
|
|
120
|
+
open: controlledOpen,
|
|
121
|
+
onOpenChange
|
|
122
|
+
}) {
|
|
123
|
+
const [uncontrolledOpen, setUncontrolledOpen] = React5.useState(false);
|
|
124
|
+
const triggerRef = React5.useRef(null);
|
|
125
|
+
const contentRef = React5.useRef(null);
|
|
126
|
+
const open = controlledOpen ?? uncontrolledOpen;
|
|
127
|
+
const setOpen = React5.useCallback(
|
|
128
|
+
(value) => {
|
|
129
|
+
const nextValue = typeof value === "function" ? value(open) : value;
|
|
130
|
+
if (controlledOpen === void 0) {
|
|
131
|
+
setUncontrolledOpen(nextValue);
|
|
132
|
+
}
|
|
133
|
+
onOpenChange?.(nextValue);
|
|
134
|
+
},
|
|
135
|
+
[controlledOpen, onOpenChange, open]
|
|
136
|
+
);
|
|
137
|
+
React5.useEffect(() => {
|
|
138
|
+
if (!open) {
|
|
139
|
+
return void 0;
|
|
140
|
+
}
|
|
141
|
+
const handlePointerDown = (event) => {
|
|
142
|
+
const target = event.target;
|
|
143
|
+
if (triggerRef.current?.contains(target) || contentRef.current?.contains(target)) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
setOpen(false);
|
|
147
|
+
};
|
|
148
|
+
const handleKeyDown = (event) => {
|
|
149
|
+
if (event.key === "Escape") {
|
|
150
|
+
setOpen(false);
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
document.addEventListener("mousedown", handlePointerDown);
|
|
154
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
155
|
+
return () => {
|
|
156
|
+
document.removeEventListener("mousedown", handlePointerDown);
|
|
157
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
158
|
+
};
|
|
159
|
+
}, [open, setOpen]);
|
|
160
|
+
return /* @__PURE__ */ jsx(PopoverContext.Provider, { value: { open, setOpen, triggerRef, contentRef }, children: /* @__PURE__ */ jsx("div", { className: "relative inline-flex", children }) });
|
|
161
|
+
}
|
|
162
|
+
function PopoverTrigger({
|
|
163
|
+
children,
|
|
164
|
+
asChild = false
|
|
165
|
+
}) {
|
|
166
|
+
const { open, setOpen, triggerRef } = usePopoverContext();
|
|
167
|
+
if (asChild && React5.isValidElement(children)) {
|
|
168
|
+
const child = children;
|
|
169
|
+
return React5.cloneElement(child, {
|
|
170
|
+
ref: triggerRef,
|
|
171
|
+
"aria-expanded": open,
|
|
172
|
+
onClick: (event) => {
|
|
173
|
+
child.props.onClick?.(event);
|
|
174
|
+
setOpen((current) => !current);
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
return /* @__PURE__ */ jsx(
|
|
179
|
+
"button",
|
|
180
|
+
{
|
|
181
|
+
ref: triggerRef,
|
|
182
|
+
type: "button",
|
|
183
|
+
"aria-expanded": open,
|
|
184
|
+
onClick: () => setOpen((current) => !current),
|
|
185
|
+
children
|
|
186
|
+
}
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
function PopoverContent({
|
|
190
|
+
children,
|
|
191
|
+
className,
|
|
192
|
+
align = "start"
|
|
193
|
+
}) {
|
|
194
|
+
const { open, contentRef } = usePopoverContext();
|
|
195
|
+
if (!open) {
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
return /* @__PURE__ */ jsx(
|
|
199
|
+
"div",
|
|
200
|
+
{
|
|
201
|
+
ref: contentRef,
|
|
202
|
+
className: cn(
|
|
203
|
+
"absolute top-full z-50 mt-2 min-w-72 rounded-md border border-border bg-popover p-2 text-popover-foreground shadow-md outline-none",
|
|
204
|
+
align === "start" && "left-0",
|
|
205
|
+
align === "center" && "left-1/2 -translate-x-1/2",
|
|
206
|
+
align === "end" && "right-0",
|
|
207
|
+
className
|
|
208
|
+
),
|
|
209
|
+
children
|
|
210
|
+
}
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
function defaultRenderDatePicker({ selectedDate, onSelectedDateChange }) {
|
|
214
|
+
return /* @__PURE__ */ jsxs(Popover, { children: [
|
|
215
|
+
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", className: "min-w-40 justify-start text-left font-normal", children: format(selectedDate, "PPP") }) }),
|
|
216
|
+
/* @__PURE__ */ jsx(PopoverContent, { className: "w-auto p-2", align: "center", children: /* @__PURE__ */ jsx(Calendar, { selected: selectedDate, onSelect: onSelectedDateChange }) })
|
|
217
|
+
] });
|
|
218
|
+
}
|
|
219
|
+
function defaultRenderResourceHeader({ resource }) {
|
|
220
|
+
return /* @__PURE__ */ jsx("strong", { children: resource.label });
|
|
221
|
+
}
|
|
222
|
+
function defaultRenderToolbar({
|
|
223
|
+
goToPreviousDay,
|
|
224
|
+
goToNextDay,
|
|
225
|
+
defaultDatePicker,
|
|
226
|
+
prevButtonLabel = "Prev",
|
|
227
|
+
nextButtonLabel = "Next"
|
|
228
|
+
}) {
|
|
229
|
+
return /* @__PURE__ */ jsx("div", { className: "rfs-toolbar", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-lg border border-border bg-card p-1 shadow-xs", children: [
|
|
230
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: goToPreviousDay, children: prevButtonLabel }),
|
|
231
|
+
defaultDatePicker,
|
|
232
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: goToNextDay, children: nextButtonLabel })
|
|
233
|
+
] }) });
|
|
234
|
+
}
|
|
235
|
+
|
|
36
236
|
// src/components/constants.ts
|
|
37
237
|
var STEP_MIN = 15;
|
|
38
238
|
var MIN_EVENT_MIN = 15;
|
|
@@ -41,8 +241,6 @@ var GUTTER_W = 72;
|
|
|
41
241
|
var TOP_PAD = 12;
|
|
42
242
|
var BOTTOM_PAD = 24;
|
|
43
243
|
var RESOURCE_MIN_W = 420;
|
|
44
|
-
|
|
45
|
-
// src/utils/scheduler-core.utils.ts
|
|
46
244
|
function isNameLike(resource) {
|
|
47
245
|
if (!resource || typeof resource !== "object") {
|
|
48
246
|
return false;
|
|
@@ -50,22 +248,6 @@ function isNameLike(resource) {
|
|
|
50
248
|
const maybe = resource;
|
|
51
249
|
return typeof maybe.firstName === "string" || typeof maybe.lastName === "string";
|
|
52
250
|
}
|
|
53
|
-
function toInputDateValue(date) {
|
|
54
|
-
const year = date.getFullYear();
|
|
55
|
-
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
56
|
-
const day = String(date.getDate()).padStart(2, "0");
|
|
57
|
-
return `${year}-${month}-${day}`;
|
|
58
|
-
}
|
|
59
|
-
function fromInputDateValue(value) {
|
|
60
|
-
const [yearRaw, monthRaw, dayRaw] = value.split("-");
|
|
61
|
-
const year = Number(yearRaw);
|
|
62
|
-
const month = Number(monthRaw);
|
|
63
|
-
const day = Number(dayRaw);
|
|
64
|
-
if (!Number.isInteger(year) || !Number.isInteger(month) || !Number.isInteger(day)) {
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
67
|
-
return new Date(year, month - 1, day);
|
|
68
|
-
}
|
|
69
251
|
var DEFAULT_APPOINTMENT_COLOR_TOKEN_CLASS_MAP = {
|
|
70
252
|
slate: "bg-slate-100 dark:bg-slate-900/40",
|
|
71
253
|
gray: "bg-gray-100 dark:bg-gray-900/40",
|
|
@@ -176,26 +358,6 @@ function layoutOverlaps(blocks) {
|
|
|
176
358
|
}
|
|
177
359
|
return output;
|
|
178
360
|
}
|
|
179
|
-
function defaultRenderDatePicker({ selectedDate, onSelectedDateChange }) {
|
|
180
|
-
return /* @__PURE__ */ jsx(
|
|
181
|
-
"input",
|
|
182
|
-
{
|
|
183
|
-
type: "date",
|
|
184
|
-
value: toInputDateValue(selectedDate),
|
|
185
|
-
onChange: (event) => {
|
|
186
|
-
const nextDate = fromInputDateValue(event.target.value);
|
|
187
|
-
if (nextDate) {
|
|
188
|
-
onSelectedDateChange(nextDate);
|
|
189
|
-
}
|
|
190
|
-
},
|
|
191
|
-
className: "h-7 rounded-md border border-border bg-card px-2 text-xs text-foreground",
|
|
192
|
-
"aria-label": "Select date"
|
|
193
|
-
}
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
function defaultRenderResourceHeader({ resource }) {
|
|
197
|
-
return /* @__PURE__ */ jsx("strong", { children: resource.label });
|
|
198
|
-
}
|
|
199
361
|
|
|
200
362
|
// src/utils/scheduler-data.utils.ts
|
|
201
363
|
function buildResourceMap({
|
|
@@ -299,13 +461,13 @@ function useSchedulerBaseData({
|
|
|
299
461
|
resources,
|
|
300
462
|
selectedDate
|
|
301
463
|
}) {
|
|
302
|
-
const dayStartAbs =
|
|
303
|
-
const dayEndAbs =
|
|
304
|
-
const dayMinutes =
|
|
305
|
-
const gridHeight =
|
|
306
|
-
const gridMinWidth =
|
|
307
|
-
const selectedISO =
|
|
308
|
-
const renderAppts =
|
|
464
|
+
const dayStartAbs = React5.useMemo(() => parseHHMM(dayStart), [dayStart]);
|
|
465
|
+
const dayEndAbs = React5.useMemo(() => parseHHMM(dayEnd), [dayEnd]);
|
|
466
|
+
const dayMinutes = React5.useMemo(() => Math.max(0, dayEndAbs - dayStartAbs), [dayEndAbs, dayStartAbs]);
|
|
467
|
+
const gridHeight = React5.useMemo(() => TOP_PAD + dayMinutes * PX_PER_MIN + BOTTOM_PAD, [dayMinutes]);
|
|
468
|
+
const gridMinWidth = React5.useMemo(() => GUTTER_W + Math.max(resources.length, 1) * RESOURCE_MIN_W, [resources.length]);
|
|
469
|
+
const selectedISO = React5.useMemo(() => dayISO(selectedDate), [selectedDate]);
|
|
470
|
+
const renderAppts = React5.useMemo(() => {
|
|
309
471
|
return normalizeAppointments({
|
|
310
472
|
adapter,
|
|
311
473
|
appointments,
|
|
@@ -314,7 +476,7 @@ function useSchedulerBaseData({
|
|
|
314
476
|
selectedISO
|
|
315
477
|
});
|
|
316
478
|
}, [adapter, appointments, dayMinutes, dayStartAbs, selectedISO]);
|
|
317
|
-
const appointmentMap =
|
|
479
|
+
const appointmentMap = React5.useMemo(() => buildAppointmentMap(renderAppts), [renderAppts]);
|
|
318
480
|
return {
|
|
319
481
|
appointmentMap,
|
|
320
482
|
dayEndAbs,
|
|
@@ -335,10 +497,10 @@ function useSchedulerInteractions({
|
|
|
335
497
|
resources,
|
|
336
498
|
selectedDate
|
|
337
499
|
}) {
|
|
338
|
-
const [drag, setDrag] =
|
|
339
|
-
const colRefs =
|
|
340
|
-
const suppressClickRef =
|
|
341
|
-
const pointerToMin =
|
|
500
|
+
const [drag, setDrag] = React5.useState({ kind: "none" });
|
|
501
|
+
const colRefs = React5.useRef({});
|
|
502
|
+
const suppressClickRef = React5.useRef(false);
|
|
503
|
+
const pointerToMin = React5.useCallback(
|
|
342
504
|
(resourceId, clientY) => {
|
|
343
505
|
const col = colRefs.current[String(resourceId)];
|
|
344
506
|
if (!col) {
|
|
@@ -349,7 +511,7 @@ function useSchedulerInteractions({
|
|
|
349
511
|
},
|
|
350
512
|
[dayMinutes]
|
|
351
513
|
);
|
|
352
|
-
const pointerToResourceId =
|
|
514
|
+
const pointerToResourceId = React5.useCallback(
|
|
353
515
|
(clientX, fallbackResourceId) => {
|
|
354
516
|
for (const resource of resources) {
|
|
355
517
|
const col = colRefs.current[String(resource.id)];
|
|
@@ -365,14 +527,14 @@ function useSchedulerInteractions({
|
|
|
365
527
|
},
|
|
366
528
|
[resources]
|
|
367
529
|
);
|
|
368
|
-
const hasOverlap =
|
|
530
|
+
const hasOverlap = React5.useCallback(
|
|
369
531
|
(appointmentId, resourceId, startMin, endMin) => {
|
|
370
532
|
const events = renderAppts.filter((appointment) => appointment.resourceId === resourceId && appointment.id !== appointmentId);
|
|
371
533
|
return events.some((appointment) => overlaps(appointment, { startMin, endMin }));
|
|
372
534
|
},
|
|
373
535
|
[renderAppts]
|
|
374
536
|
);
|
|
375
|
-
const persistDrag =
|
|
537
|
+
const persistDrag = React5.useCallback(
|
|
376
538
|
async (state) => {
|
|
377
539
|
const target = appointmentMap.get(state.appointmentId);
|
|
378
540
|
if (!target) {
|
|
@@ -410,7 +572,7 @@ function useSchedulerInteractions({
|
|
|
410
572
|
},
|
|
411
573
|
[appointmentMap, dayStartAbs, hasOverlap, onAppointmentChange, onPersistMoveResize, selectedDate]
|
|
412
574
|
);
|
|
413
|
-
const onApptPointerDown =
|
|
575
|
+
const onApptPointerDown = React5.useCallback(
|
|
414
576
|
(event, appointment) => {
|
|
415
577
|
if (event.button !== 0) {
|
|
416
578
|
return;
|
|
@@ -431,7 +593,7 @@ function useSchedulerInteractions({
|
|
|
431
593
|
},
|
|
432
594
|
[pointerToMin]
|
|
433
595
|
);
|
|
434
|
-
const onResizePointerDown =
|
|
596
|
+
const onResizePointerDown = React5.useCallback((event, appointment) => {
|
|
435
597
|
if (event.button !== 0) {
|
|
436
598
|
return;
|
|
437
599
|
}
|
|
@@ -446,7 +608,7 @@ function useSchedulerInteractions({
|
|
|
446
608
|
endMin: appointment.endMin
|
|
447
609
|
});
|
|
448
610
|
}, []);
|
|
449
|
-
const onGlobalPointerMove =
|
|
611
|
+
const onGlobalPointerMove = React5.useCallback(
|
|
450
612
|
(event) => {
|
|
451
613
|
if (drag.kind === "none") {
|
|
452
614
|
return;
|
|
@@ -480,7 +642,7 @@ function useSchedulerInteractions({
|
|
|
480
642
|
},
|
|
481
643
|
[dayMinutes, drag, pointerToMin, pointerToResourceId]
|
|
482
644
|
);
|
|
483
|
-
const onGlobalPointerUp =
|
|
645
|
+
const onGlobalPointerUp = React5.useCallback(() => {
|
|
484
646
|
if (drag.kind === "none") {
|
|
485
647
|
return;
|
|
486
648
|
}
|
|
@@ -512,13 +674,13 @@ function useSchedulerPresentationData({
|
|
|
512
674
|
resources,
|
|
513
675
|
selectedDate
|
|
514
676
|
}) {
|
|
515
|
-
const effectiveAppts =
|
|
677
|
+
const effectiveAppts = React5.useMemo(() => {
|
|
516
678
|
return applyDragToAppointments(renderAppts, drag, selectedDate, dayStartAbs);
|
|
517
679
|
}, [dayStartAbs, drag, renderAppts, selectedDate]);
|
|
518
|
-
const laidOutByResource =
|
|
680
|
+
const laidOutByResource = React5.useMemo(() => {
|
|
519
681
|
return buildLaidOutByResource(resources, effectiveAppts);
|
|
520
682
|
}, [effectiveAppts, resources]);
|
|
521
|
-
const appointmentBgByResource =
|
|
683
|
+
const appointmentBgByResource = React5.useMemo(() => {
|
|
522
684
|
if (!getResourceAppointmentBackground) {
|
|
523
685
|
return /* @__PURE__ */ new Map();
|
|
524
686
|
}
|
|
@@ -529,7 +691,7 @@ function useSchedulerPresentationData({
|
|
|
529
691
|
toKey: (resource) => resource.id
|
|
530
692
|
});
|
|
531
693
|
}, [getResourceAppointmentBackground, resources]);
|
|
532
|
-
const appointmentColorTokenClassByResource =
|
|
694
|
+
const appointmentColorTokenClassByResource = React5.useMemo(() => {
|
|
533
695
|
if (!getResourceAppointmentColorToken) {
|
|
534
696
|
return /* @__PURE__ */ new Map();
|
|
535
697
|
}
|
|
@@ -547,7 +709,7 @@ function useSchedulerPresentationData({
|
|
|
547
709
|
toKey: (resource) => resource.id
|
|
548
710
|
});
|
|
549
711
|
}, [appointmentColorTokenClassMap, getResourceAppointmentColorToken, resources]);
|
|
550
|
-
const appointmentAppearanceByResource =
|
|
712
|
+
const appointmentAppearanceByResource = React5.useMemo(() => {
|
|
551
713
|
return buildAppointmentAppearanceByResource(
|
|
552
714
|
resources,
|
|
553
715
|
resourceAppointmentClassMap,
|
|
@@ -584,6 +746,7 @@ function Scheduler({
|
|
|
584
746
|
getResourceAppointmentColorToken,
|
|
585
747
|
appointmentColorTokenClassMap,
|
|
586
748
|
getResourceAppointmentBackground,
|
|
749
|
+
renderToolbar,
|
|
587
750
|
renderDatePicker,
|
|
588
751
|
prevButtonLabel = "Prev",
|
|
589
752
|
nextButtonLabel = "Next",
|
|
@@ -640,6 +803,22 @@ function Scheduler({
|
|
|
640
803
|
selectedDate
|
|
641
804
|
});
|
|
642
805
|
const defaultResourceHeaderRenderer = (resource) => defaultRenderResourceHeader({ resource });
|
|
806
|
+
const goToPreviousDay = () => onSelectedDateChange(shiftDays(selectedDate, -1));
|
|
807
|
+
const goToNextDay = () => onSelectedDateChange(shiftDays(selectedDate, 1));
|
|
808
|
+
const defaultDatePicker = (renderDatePicker ?? defaultRenderDatePicker)({ selectedDate, onSelectedDateChange });
|
|
809
|
+
const toolbar = renderToolbar ? renderToolbar({
|
|
810
|
+
selectedDate,
|
|
811
|
+
onSelectedDateChange,
|
|
812
|
+
goToPreviousDay,
|
|
813
|
+
goToNextDay,
|
|
814
|
+
defaultDatePicker
|
|
815
|
+
}) : defaultRenderToolbar({
|
|
816
|
+
goToPreviousDay,
|
|
817
|
+
goToNextDay,
|
|
818
|
+
defaultDatePicker,
|
|
819
|
+
prevButtonLabel,
|
|
820
|
+
nextButtonLabel
|
|
821
|
+
});
|
|
643
822
|
return /* @__PURE__ */ jsxs(
|
|
644
823
|
"section",
|
|
645
824
|
{
|
|
@@ -648,27 +827,7 @@ function Scheduler({
|
|
|
648
827
|
onPointerUp: onGlobalPointerUp,
|
|
649
828
|
onPointerCancel: onGlobalPointerUp,
|
|
650
829
|
children: [
|
|
651
|
-
|
|
652
|
-
/* @__PURE__ */ jsx(
|
|
653
|
-
"button",
|
|
654
|
-
{
|
|
655
|
-
className: "rfs-btn border-border bg-card text-foreground hover:bg-muted",
|
|
656
|
-
type: "button",
|
|
657
|
-
onClick: () => onSelectedDateChange(shiftDays(selectedDate, -1)),
|
|
658
|
-
children: prevButtonLabel
|
|
659
|
-
}
|
|
660
|
-
),
|
|
661
|
-
(renderDatePicker ?? defaultRenderDatePicker)({ selectedDate, onSelectedDateChange }),
|
|
662
|
-
/* @__PURE__ */ jsx(
|
|
663
|
-
"button",
|
|
664
|
-
{
|
|
665
|
-
className: "rfs-btn border-border bg-card text-foreground hover:bg-muted",
|
|
666
|
-
type: "button",
|
|
667
|
-
onClick: () => onSelectedDateChange(shiftDays(selectedDate, 1)),
|
|
668
|
-
children: nextButtonLabel
|
|
669
|
-
}
|
|
670
|
-
)
|
|
671
|
-
] }),
|
|
830
|
+
toolbar,
|
|
672
831
|
/* @__PURE__ */ jsx("div", { className: "rfs-shell border-border bg-card", children: /* @__PURE__ */ jsxs("div", { style: { minWidth: gridMinWidth }, children: [
|
|
673
832
|
/* @__PURE__ */ jsxs("div", { className: "rfs-header border-border bg-card", children: [
|
|
674
833
|
/* @__PURE__ */ jsx("div", { className: "rfs-gutter border-border", style: { width: GUTTER_W } }),
|