@marco.colia/react-fast-scheduler 2.1.0 → 2.2.0
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 +144 -67
- package/dist/index.cjs +883 -261
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -3
- package/dist/index.d.ts +17 -3
- package/dist/index.js +885 -263
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +11 -4
package/dist/index.cjs
CHANGED
|
@@ -27,6 +27,7 @@ var React2__namespace = /*#__PURE__*/_interopNamespace(React2);
|
|
|
27
27
|
// src/components/defaultRenderAppointment/index.tsx
|
|
28
28
|
function defaultRenderAppointment({
|
|
29
29
|
appointment,
|
|
30
|
+
isDropInvalid,
|
|
30
31
|
onPointerDown,
|
|
31
32
|
onResizePointerDown,
|
|
32
33
|
appointmentAppearance,
|
|
@@ -36,23 +37,36 @@ function defaultRenderAppointment({
|
|
|
36
37
|
const description = typeof rawMaybe.description === "string" ? rawMaybe.description : void 0;
|
|
37
38
|
const legacyClassName = typeof appointmentBackgroundColor === "string" ? appointmentBackgroundColor : void 0;
|
|
38
39
|
const backgroundClassName = appointmentAppearance?.className ?? legacyClassName ?? "bg-muted/40 dark:bg-muted/30";
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
40
|
+
const invalidStateClassName = isDropInvalid ? "border-red-500 bg-red-100/80 text-red-950 ring-1 ring-red-500/60 dark:bg-red-950/40 dark:text-red-100" : "";
|
|
41
|
+
const visualStateClassName = appointment.visualState === "ghost" ? "cursor-grab border-dashed opacity-35" : appointment.visualState === "dragging" ? "cursor-grabbing opacity-55 shadow-xl ring-1 ring-border/70" : "cursor-grab";
|
|
42
|
+
const className = `relative h-full overflow-hidden rounded-md border border-border p-2 pb-5 text-foreground ${backgroundClassName} ${invalidStateClassName} ${visualStateClassName}`;
|
|
43
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
44
|
+
"div",
|
|
45
|
+
{
|
|
46
|
+
onPointerDown,
|
|
47
|
+
className,
|
|
48
|
+
children: [
|
|
49
|
+
isDropInvalid ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-1 top-1 flex h-4 w-4 items-center justify-center rounded-full bg-red-600 text-[10px] font-bold text-white", children: "X" }) : null,
|
|
50
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs font-semibold", children: appointment.title }),
|
|
51
|
+
description ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1 text-[10px] font-medium text-muted-foreground", children: description }) : null,
|
|
52
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
53
|
+
"div",
|
|
54
|
+
{
|
|
55
|
+
role: "button",
|
|
56
|
+
"aria-label": "Resize appointment",
|
|
57
|
+
onPointerDown: (event) => {
|
|
58
|
+
if (appointment.visualState === "ghost") {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
event.stopPropagation();
|
|
62
|
+
onResizePointerDown(event);
|
|
63
|
+
},
|
|
64
|
+
className: "absolute inset-x-1 bottom-1 h-2 cursor-ns-resize rounded-full bg-border/90 transition-colors hover:bg-border"
|
|
65
|
+
}
|
|
66
|
+
)
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
);
|
|
56
70
|
}
|
|
57
71
|
|
|
58
72
|
// src/components/constants.ts
|
|
@@ -63,8 +77,6 @@ var GUTTER_W = 72;
|
|
|
63
77
|
var TOP_PAD = 12;
|
|
64
78
|
var BOTTOM_PAD = 24;
|
|
65
79
|
var RESOURCE_MIN_W = 420;
|
|
66
|
-
|
|
67
|
-
// src/utils/scheduler-core.utils.ts
|
|
68
80
|
function isNameLike(resource) {
|
|
69
81
|
if (!resource || typeof resource !== "object") {
|
|
70
82
|
return false;
|
|
@@ -72,22 +84,6 @@ function isNameLike(resource) {
|
|
|
72
84
|
const maybe = resource;
|
|
73
85
|
return typeof maybe.firstName === "string" || typeof maybe.lastName === "string";
|
|
74
86
|
}
|
|
75
|
-
function toInputDateValue(date) {
|
|
76
|
-
const year = date.getFullYear();
|
|
77
|
-
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
78
|
-
const day = String(date.getDate()).padStart(2, "0");
|
|
79
|
-
return `${year}-${month}-${day}`;
|
|
80
|
-
}
|
|
81
|
-
function fromInputDateValue(value) {
|
|
82
|
-
const [yearRaw, monthRaw, dayRaw] = value.split("-");
|
|
83
|
-
const year = Number(yearRaw);
|
|
84
|
-
const month = Number(monthRaw);
|
|
85
|
-
const day = Number(dayRaw);
|
|
86
|
-
if (!Number.isInteger(year) || !Number.isInteger(month) || !Number.isInteger(day)) {
|
|
87
|
-
return null;
|
|
88
|
-
}
|
|
89
|
-
return new Date(year, month - 1, day);
|
|
90
|
-
}
|
|
91
87
|
var DEFAULT_APPOINTMENT_COLOR_TOKEN_CLASS_MAP = {
|
|
92
88
|
slate: "bg-slate-100 dark:bg-slate-900/40",
|
|
93
89
|
gray: "bg-gray-100 dark:bg-gray-900/40",
|
|
@@ -198,26 +194,6 @@ function layoutOverlaps(blocks) {
|
|
|
198
194
|
}
|
|
199
195
|
return output;
|
|
200
196
|
}
|
|
201
|
-
function defaultRenderDatePicker({ selectedDate, onSelectedDateChange }) {
|
|
202
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
203
|
-
"input",
|
|
204
|
-
{
|
|
205
|
-
type: "date",
|
|
206
|
-
value: toInputDateValue(selectedDate),
|
|
207
|
-
onChange: (event) => {
|
|
208
|
-
const nextDate = fromInputDateValue(event.target.value);
|
|
209
|
-
if (nextDate) {
|
|
210
|
-
onSelectedDateChange(nextDate);
|
|
211
|
-
}
|
|
212
|
-
},
|
|
213
|
-
className: "h-7 rounded-md border border-border bg-card px-2 text-xs text-foreground",
|
|
214
|
-
"aria-label": "Select date"
|
|
215
|
-
}
|
|
216
|
-
);
|
|
217
|
-
}
|
|
218
|
-
function defaultRenderResourceHeader({ resource }) {
|
|
219
|
-
return /* @__PURE__ */ jsxRuntime.jsx("strong", { children: resource.label });
|
|
220
|
-
}
|
|
221
197
|
|
|
222
198
|
// src/utils/scheduler-data.utils.ts
|
|
223
199
|
function buildResourceMap({
|
|
@@ -250,17 +226,22 @@ function normalizeAppointments({
|
|
|
250
226
|
end,
|
|
251
227
|
startMin: clamp(minutesFromDayStart(dayStartAbs, start), 0, dayMinutes),
|
|
252
228
|
endMin: clamp(minutesFromDayStart(dayStartAbs, end), 0, dayMinutes),
|
|
253
|
-
title: adapter.getTitle(appointment)
|
|
229
|
+
title: adapter.getTitle(appointment),
|
|
230
|
+
renderKey: String(adapter.getId(appointment)),
|
|
231
|
+
visualState: "normal"
|
|
254
232
|
};
|
|
255
233
|
}).filter((appointment) => dayISO(appointment.start) === selectedISO);
|
|
256
234
|
}
|
|
257
235
|
function buildAppointmentMap(appointments) {
|
|
258
236
|
return new Map(appointments.map((appointment) => [appointment.id, appointment]));
|
|
259
237
|
}
|
|
260
|
-
function
|
|
238
|
+
function buildPresentationAppointments(appointments, drag, selectedDate, dayStartAbs) {
|
|
261
239
|
if (drag.kind === "none") {
|
|
262
240
|
return appointments;
|
|
263
241
|
}
|
|
242
|
+
if (drag.kind === "move") {
|
|
243
|
+
return appointments;
|
|
244
|
+
}
|
|
264
245
|
return appointments.map((appointment) => {
|
|
265
246
|
if (appointment.id !== drag.appointmentId) {
|
|
266
247
|
return appointment;
|
|
@@ -271,7 +252,8 @@ function applyDragToAppointments(appointments, drag, selectedDate, dayStartAbs)
|
|
|
271
252
|
startMin: drag.startMin,
|
|
272
253
|
endMin: drag.endMin,
|
|
273
254
|
start: dateAtMinute(selectedDate, dayStartAbs, drag.startMin),
|
|
274
|
-
end: dateAtMinute(selectedDate, dayStartAbs, drag.endMin)
|
|
255
|
+
end: dateAtMinute(selectedDate, dayStartAbs, drag.endMin),
|
|
256
|
+
visualState: "dragging"
|
|
275
257
|
};
|
|
276
258
|
});
|
|
277
259
|
}
|
|
@@ -284,10 +266,22 @@ function buildLaidOutByResource(resources, appointments) {
|
|
|
284
266
|
}
|
|
285
267
|
const result = /* @__PURE__ */ new Map();
|
|
286
268
|
for (const resource of resources) {
|
|
287
|
-
result.set(resource.id,
|
|
269
|
+
result.set(resource.id, buildLaidOutAppointments(appointmentsByResource.get(resource.id) ?? []));
|
|
288
270
|
}
|
|
289
271
|
return result;
|
|
290
272
|
}
|
|
273
|
+
function buildLaidOutAppointments(appointments) {
|
|
274
|
+
return layoutOverlaps(
|
|
275
|
+
appointments.map((appointment) => ({
|
|
276
|
+
...appointment,
|
|
277
|
+
appointmentId: appointment.id,
|
|
278
|
+
id: appointment.renderKey
|
|
279
|
+
}))
|
|
280
|
+
).map(({ appointmentId, ...appointment }) => ({
|
|
281
|
+
...appointment,
|
|
282
|
+
id: appointmentId
|
|
283
|
+
}));
|
|
284
|
+
}
|
|
291
285
|
function buildAppointmentAppearanceByResource(resources, resourceAppointmentClassMap, getResourceAppointmentAppearance, appointmentColorTokenClassByResource, appointmentBgByResource) {
|
|
292
286
|
const map = /* @__PURE__ */ new Map();
|
|
293
287
|
for (const resource of resources) {
|
|
@@ -311,8 +305,6 @@ function buildAppointmentAppearanceByResource(resources, resourceAppointmentClas
|
|
|
311
305
|
}
|
|
312
306
|
return map;
|
|
313
307
|
}
|
|
314
|
-
|
|
315
|
-
// src/hooks/useSchedulerBaseData.ts
|
|
316
308
|
function useSchedulerBaseData({
|
|
317
309
|
adapter,
|
|
318
310
|
appointments,
|
|
@@ -323,9 +315,18 @@ function useSchedulerBaseData({
|
|
|
323
315
|
}) {
|
|
324
316
|
const dayStartAbs = React2__namespace.useMemo(() => parseHHMM(dayStart), [dayStart]);
|
|
325
317
|
const dayEndAbs = React2__namespace.useMemo(() => parseHHMM(dayEnd), [dayEnd]);
|
|
326
|
-
const dayMinutes = React2__namespace.useMemo(
|
|
327
|
-
|
|
328
|
-
|
|
318
|
+
const dayMinutes = React2__namespace.useMemo(
|
|
319
|
+
() => Math.max(0, dayEndAbs - dayStartAbs),
|
|
320
|
+
[dayEndAbs, dayStartAbs]
|
|
321
|
+
);
|
|
322
|
+
const gridHeight = React2__namespace.useMemo(
|
|
323
|
+
() => TOP_PAD + dayMinutes * PX_PER_MIN + BOTTOM_PAD,
|
|
324
|
+
[dayMinutes]
|
|
325
|
+
);
|
|
326
|
+
const gridMinWidth = React2__namespace.useMemo(
|
|
327
|
+
() => GUTTER_W + Math.max(resources.length, 1) * RESOURCE_MIN_W,
|
|
328
|
+
[resources.length]
|
|
329
|
+
);
|
|
329
330
|
const selectedISO = React2__namespace.useMemo(() => dayISO(selectedDate), [selectedDate]);
|
|
330
331
|
const renderAppts = React2__namespace.useMemo(() => {
|
|
331
332
|
return normalizeAppointments({
|
|
@@ -347,7 +348,157 @@ function useSchedulerBaseData({
|
|
|
347
348
|
renderAppts
|
|
348
349
|
};
|
|
349
350
|
}
|
|
350
|
-
|
|
351
|
+
|
|
352
|
+
// src/hooks/useSchedulerInteractions.helpers.ts
|
|
353
|
+
var DRAG_START_THRESHOLD_PX = 4;
|
|
354
|
+
var updateMovePreviewPosition = (setMovePreview, pointer) => {
|
|
355
|
+
setMovePreview(
|
|
356
|
+
(current) => current ? {
|
|
357
|
+
...current,
|
|
358
|
+
clientX: pointer.clientX,
|
|
359
|
+
clientY: pointer.clientY
|
|
360
|
+
} : current
|
|
361
|
+
);
|
|
362
|
+
};
|
|
363
|
+
var startPendingDragIfThresholdPassed = (args) => {
|
|
364
|
+
const { dragRef, pendingDragRef, pointer, setDrag, setMovePreview, suppressClickRef } = args;
|
|
365
|
+
const pending = pendingDragRef.current;
|
|
366
|
+
if (!pending || pending.drag.pointerId !== pointer.pointerId) {
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
const deltaX = pointer.clientX - pending.startClientX;
|
|
370
|
+
const deltaY = pointer.clientY - pending.startClientY;
|
|
371
|
+
if (Math.hypot(deltaX, deltaY) < DRAG_START_THRESHOLD_PX) {
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
suppressClickRef.current = true;
|
|
375
|
+
dragRef.current = pending.drag;
|
|
376
|
+
setDrag(pending.drag);
|
|
377
|
+
if (pending.drag.kind === "move") {
|
|
378
|
+
setMovePreview({
|
|
379
|
+
appointment: pending.appointment,
|
|
380
|
+
clientX: pointer.clientX,
|
|
381
|
+
clientY: pointer.clientY,
|
|
382
|
+
width: pending.width,
|
|
383
|
+
height: pending.height,
|
|
384
|
+
offsetX: pending.offsetX,
|
|
385
|
+
offsetY: pending.offsetY
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
pendingDragRef.current = null;
|
|
389
|
+
};
|
|
390
|
+
var buildNextMoveDrag = (drag, pointer, dayMinutes, pointerToResourceId, pointerToMin) => {
|
|
391
|
+
const nextResourceId = pointerToResourceId(pointer.clientX, drag.resourceId);
|
|
392
|
+
const pointerMin = pointerToMin(nextResourceId, pointer.clientY);
|
|
393
|
+
const rawStart = pointerMin - drag.offsetMin;
|
|
394
|
+
const nextStartMin = clamp(snap(rawStart), 0, dayMinutes - drag.durationMin);
|
|
395
|
+
const nextEndMin = nextStartMin + drag.durationMin;
|
|
396
|
+
if (nextStartMin === drag.startMin && nextEndMin === drag.endMin && nextResourceId === drag.resourceId) {
|
|
397
|
+
return null;
|
|
398
|
+
}
|
|
399
|
+
return {
|
|
400
|
+
...drag,
|
|
401
|
+
resourceId: nextResourceId,
|
|
402
|
+
startMin: nextStartMin,
|
|
403
|
+
endMin: nextEndMin
|
|
404
|
+
};
|
|
405
|
+
};
|
|
406
|
+
var buildNextResizeDrag = (drag, pointer, dayMinutes, pointerToMin) => {
|
|
407
|
+
const pointerMin = pointerToMin(drag.resourceId, pointer.clientY);
|
|
408
|
+
const nextEndMin = clamp(snap(pointerMin), drag.startMin + MIN_EVENT_MIN, dayMinutes);
|
|
409
|
+
if (nextEndMin === drag.endMin) {
|
|
410
|
+
return null;
|
|
411
|
+
}
|
|
412
|
+
return {
|
|
413
|
+
...drag,
|
|
414
|
+
endMin: nextEndMin
|
|
415
|
+
};
|
|
416
|
+
};
|
|
417
|
+
var processPointerFrame = (args) => {
|
|
418
|
+
const {
|
|
419
|
+
dayMinutes,
|
|
420
|
+
dragRef,
|
|
421
|
+
latestPointerRef,
|
|
422
|
+
pendingDragRef,
|
|
423
|
+
pointerToMin,
|
|
424
|
+
pointerToResourceId,
|
|
425
|
+
setDrag,
|
|
426
|
+
setMovePreview,
|
|
427
|
+
suppressClickRef
|
|
428
|
+
} = args;
|
|
429
|
+
const pointer = latestPointerRef.current;
|
|
430
|
+
if (!pointer) {
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
const currentDrag = dragRef.current;
|
|
434
|
+
if (currentDrag.kind === "none" && pendingDragRef.current) {
|
|
435
|
+
startPendingDragIfThresholdPassed({
|
|
436
|
+
pointer,
|
|
437
|
+
pendingDragRef,
|
|
438
|
+
setDrag,
|
|
439
|
+
setMovePreview,
|
|
440
|
+
dragRef,
|
|
441
|
+
suppressClickRef
|
|
442
|
+
});
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
if (currentDrag.kind === "none") {
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
if (currentDrag.kind === "move") {
|
|
449
|
+
updateMovePreviewPosition(setMovePreview, pointer);
|
|
450
|
+
const nextDrag2 = buildNextMoveDrag(
|
|
451
|
+
currentDrag,
|
|
452
|
+
pointer,
|
|
453
|
+
dayMinutes,
|
|
454
|
+
pointerToResourceId,
|
|
455
|
+
pointerToMin
|
|
456
|
+
);
|
|
457
|
+
if (nextDrag2) {
|
|
458
|
+
suppressClickRef.current = true;
|
|
459
|
+
dragRef.current = nextDrag2;
|
|
460
|
+
setDrag(nextDrag2);
|
|
461
|
+
}
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
const nextDrag = buildNextResizeDrag(currentDrag, pointer, dayMinutes, pointerToMin);
|
|
465
|
+
if (nextDrag) {
|
|
466
|
+
suppressClickRef.current = true;
|
|
467
|
+
dragRef.current = nextDrag;
|
|
468
|
+
setDrag(nextDrag);
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
var finishDrag = (args) => {
|
|
472
|
+
const {
|
|
473
|
+
dragRef,
|
|
474
|
+
frameRef,
|
|
475
|
+
latestPointerRef,
|
|
476
|
+
pendingDragRef,
|
|
477
|
+
persistDrag,
|
|
478
|
+
setDrag,
|
|
479
|
+
setMovePreview,
|
|
480
|
+
suppressClickRef
|
|
481
|
+
} = args;
|
|
482
|
+
if (frameRef.current !== null) {
|
|
483
|
+
window.cancelAnimationFrame(frameRef.current);
|
|
484
|
+
frameRef.current = null;
|
|
485
|
+
}
|
|
486
|
+
latestPointerRef.current = null;
|
|
487
|
+
pendingDragRef.current = null;
|
|
488
|
+
setMovePreview(null);
|
|
489
|
+
const current = dragRef.current;
|
|
490
|
+
if (current.kind === "none") {
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
setDrag({ kind: "none" });
|
|
494
|
+
dragRef.current = { kind: "none" };
|
|
495
|
+
if (suppressClickRef.current) {
|
|
496
|
+
void persistDrag(current);
|
|
497
|
+
}
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
// src/hooks/useSchedulerInteractions.ts
|
|
501
|
+
var useSchedulerInteractions = ({
|
|
351
502
|
appointmentMap,
|
|
352
503
|
dayMinutes,
|
|
353
504
|
dayStartAbs,
|
|
@@ -356,10 +507,27 @@ function useSchedulerInteractions({
|
|
|
356
507
|
renderAppts,
|
|
357
508
|
resources,
|
|
358
509
|
selectedDate
|
|
359
|
-
}) {
|
|
510
|
+
}) => {
|
|
360
511
|
const [drag, setDrag] = React2__namespace.useState({ kind: "none" });
|
|
512
|
+
const [movePreview, setMovePreview] = React2__namespace.useState(
|
|
513
|
+
null
|
|
514
|
+
);
|
|
361
515
|
const colRefs = React2__namespace.useRef({});
|
|
362
516
|
const suppressClickRef = React2__namespace.useRef(false);
|
|
517
|
+
const dragRef = React2__namespace.useRef({ kind: "none" });
|
|
518
|
+
const pendingDragRef = React2__namespace.useRef(null);
|
|
519
|
+
const latestPointerRef = React2__namespace.useRef(null);
|
|
520
|
+
const frameRef = React2__namespace.useRef(null);
|
|
521
|
+
React2__namespace.useEffect(() => {
|
|
522
|
+
dragRef.current = drag;
|
|
523
|
+
}, [drag]);
|
|
524
|
+
React2__namespace.useEffect(() => {
|
|
525
|
+
return () => {
|
|
526
|
+
if (frameRef.current !== null) {
|
|
527
|
+
window.cancelAnimationFrame(frameRef.current);
|
|
528
|
+
}
|
|
529
|
+
};
|
|
530
|
+
}, []);
|
|
363
531
|
const pointerToMin = React2__namespace.useCallback(
|
|
364
532
|
(resourceId, clientY) => {
|
|
365
533
|
const col = colRefs.current[String(resourceId)];
|
|
@@ -389,11 +557,21 @@ function useSchedulerInteractions({
|
|
|
389
557
|
);
|
|
390
558
|
const hasOverlap = React2__namespace.useCallback(
|
|
391
559
|
(appointmentId, resourceId, startMin, endMin) => {
|
|
392
|
-
const events = renderAppts.filter(
|
|
393
|
-
|
|
560
|
+
const events = renderAppts.filter(
|
|
561
|
+
(appointment) => appointment.resourceId === resourceId && appointment.id !== appointmentId
|
|
562
|
+
);
|
|
563
|
+
return events.some(
|
|
564
|
+
(appointment) => overlaps(appointment, { startMin, endMin })
|
|
565
|
+
);
|
|
394
566
|
},
|
|
395
567
|
[renderAppts]
|
|
396
568
|
);
|
|
569
|
+
const isDropInvalid = React2__namespace.useMemo(() => {
|
|
570
|
+
if (drag.kind === "none") {
|
|
571
|
+
return false;
|
|
572
|
+
}
|
|
573
|
+
return hasOverlap(drag.appointmentId, drag.resourceId, drag.startMin, drag.endMin);
|
|
574
|
+
}, [drag, hasOverlap]);
|
|
397
575
|
const persistDrag = React2__namespace.useCallback(
|
|
398
576
|
async (state) => {
|
|
399
577
|
const target = appointmentMap.get(state.appointmentId);
|
|
@@ -430,7 +608,14 @@ function useSchedulerInteractions({
|
|
|
430
608
|
});
|
|
431
609
|
}
|
|
432
610
|
},
|
|
433
|
-
[
|
|
611
|
+
[
|
|
612
|
+
appointmentMap,
|
|
613
|
+
dayStartAbs,
|
|
614
|
+
hasOverlap,
|
|
615
|
+
onAppointmentChange,
|
|
616
|
+
onPersistMoveResize,
|
|
617
|
+
selectedDate
|
|
618
|
+
]
|
|
434
619
|
);
|
|
435
620
|
const onApptPointerDown = React2__namespace.useCallback(
|
|
436
621
|
(event, appointment) => {
|
|
@@ -439,89 +624,108 @@ function useSchedulerInteractions({
|
|
|
439
624
|
}
|
|
440
625
|
event.stopPropagation();
|
|
441
626
|
suppressClickRef.current = false;
|
|
627
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
442
628
|
const pointerMin = pointerToMin(appointment.resourceId, event.clientY);
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
629
|
+
pendingDragRef.current = {
|
|
630
|
+
drag: {
|
|
631
|
+
kind: "move",
|
|
632
|
+
appointmentId: appointment.id,
|
|
633
|
+
pointerId: event.pointerId,
|
|
634
|
+
resourceId: appointment.resourceId,
|
|
635
|
+
durationMin: Math.max(MIN_EVENT_MIN, appointment.endMin - appointment.startMin),
|
|
636
|
+
offsetMin: pointerMin - appointment.startMin,
|
|
637
|
+
startMin: appointment.startMin,
|
|
638
|
+
endMin: appointment.endMin
|
|
639
|
+
},
|
|
640
|
+
appointment,
|
|
641
|
+
startClientX: event.clientX,
|
|
642
|
+
startClientY: event.clientY,
|
|
643
|
+
width: rect.width,
|
|
644
|
+
height: rect.height,
|
|
645
|
+
offsetX: event.clientX - rect.left,
|
|
646
|
+
offsetY: event.clientY - rect.top
|
|
647
|
+
};
|
|
453
648
|
},
|
|
454
649
|
[pointerToMin]
|
|
455
650
|
);
|
|
456
|
-
const onResizePointerDown = React2__namespace.useCallback(
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
}
|
|
460
|
-
event.stopPropagation();
|
|
461
|
-
suppressClickRef.current = false;
|
|
462
|
-
setDrag({
|
|
463
|
-
kind: "resize",
|
|
464
|
-
appointmentId: appointment.id,
|
|
465
|
-
pointerId: event.pointerId,
|
|
466
|
-
resourceId: appointment.resourceId,
|
|
467
|
-
startMin: appointment.startMin,
|
|
468
|
-
endMin: appointment.endMin
|
|
469
|
-
});
|
|
470
|
-
}, []);
|
|
471
|
-
const onGlobalPointerMove = React2__namespace.useCallback(
|
|
472
|
-
(event) => {
|
|
473
|
-
if (drag.kind === "none") {
|
|
651
|
+
const onResizePointerDown = React2__namespace.useCallback(
|
|
652
|
+
(event, appointment) => {
|
|
653
|
+
if (event.button !== 0) {
|
|
474
654
|
return;
|
|
475
655
|
}
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
656
|
+
event.stopPropagation();
|
|
657
|
+
suppressClickRef.current = false;
|
|
658
|
+
pendingDragRef.current = {
|
|
659
|
+
drag: {
|
|
660
|
+
kind: "resize",
|
|
661
|
+
appointmentId: appointment.id,
|
|
662
|
+
pointerId: event.pointerId,
|
|
663
|
+
resourceId: appointment.resourceId,
|
|
664
|
+
startMin: appointment.startMin,
|
|
665
|
+
endMin: appointment.endMin
|
|
666
|
+
},
|
|
667
|
+
appointment,
|
|
668
|
+
startClientX: event.clientX,
|
|
669
|
+
startClientY: event.clientY,
|
|
670
|
+
width: 0,
|
|
671
|
+
height: 0,
|
|
672
|
+
offsetX: 0,
|
|
673
|
+
offsetY: 0
|
|
674
|
+
};
|
|
675
|
+
},
|
|
676
|
+
[]
|
|
677
|
+
);
|
|
678
|
+
const onGlobalPointerMove = React2__namespace.useCallback(
|
|
679
|
+
(event) => {
|
|
680
|
+
latestPointerRef.current = {
|
|
681
|
+
clientX: event.clientX,
|
|
682
|
+
clientY: event.clientY,
|
|
683
|
+
pointerId: event.pointerId
|
|
684
|
+
};
|
|
685
|
+
if (frameRef.current !== null) {
|
|
491
686
|
return;
|
|
492
687
|
}
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
688
|
+
frameRef.current = window.requestAnimationFrame(() => {
|
|
689
|
+
frameRef.current = null;
|
|
690
|
+
processPointerFrame({
|
|
691
|
+
dayMinutes,
|
|
692
|
+
dragRef,
|
|
693
|
+
latestPointerRef,
|
|
694
|
+
pendingDragRef,
|
|
695
|
+
pointerToMin,
|
|
696
|
+
pointerToResourceId,
|
|
697
|
+
setDrag,
|
|
698
|
+
setMovePreview,
|
|
699
|
+
suppressClickRef
|
|
500
700
|
});
|
|
501
|
-
}
|
|
701
|
+
});
|
|
502
702
|
},
|
|
503
|
-
[dayMinutes,
|
|
703
|
+
[dayMinutes, pointerToMin, pointerToResourceId]
|
|
504
704
|
);
|
|
505
705
|
const onGlobalPointerUp = React2__namespace.useCallback(() => {
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
706
|
+
finishDrag({
|
|
707
|
+
dragRef,
|
|
708
|
+
frameRef,
|
|
709
|
+
latestPointerRef,
|
|
710
|
+
pendingDragRef,
|
|
711
|
+
persistDrag,
|
|
712
|
+
setDrag,
|
|
713
|
+
setMovePreview,
|
|
714
|
+
suppressClickRef
|
|
715
|
+
});
|
|
716
|
+
}, [persistDrag]);
|
|
515
717
|
return {
|
|
516
718
|
colRefs,
|
|
517
719
|
drag,
|
|
720
|
+
isDropInvalid,
|
|
721
|
+
movePreview,
|
|
518
722
|
onApptPointerDown,
|
|
519
723
|
onGlobalPointerMove,
|
|
520
724
|
onGlobalPointerUp,
|
|
521
725
|
onResizePointerDown,
|
|
522
726
|
suppressClickRef
|
|
523
727
|
};
|
|
524
|
-
}
|
|
728
|
+
};
|
|
525
729
|
function useSchedulerPresentationData({
|
|
526
730
|
appointmentColorTokenClassMap,
|
|
527
731
|
dayStartAbs,
|
|
@@ -535,11 +739,22 @@ function useSchedulerPresentationData({
|
|
|
535
739
|
selectedDate
|
|
536
740
|
}) {
|
|
537
741
|
const effectiveAppts = React2__namespace.useMemo(() => {
|
|
538
|
-
return
|
|
742
|
+
return buildPresentationAppointments(renderAppts, drag, selectedDate, dayStartAbs);
|
|
539
743
|
}, [dayStartAbs, drag, renderAppts, selectedDate]);
|
|
744
|
+
const baseLaidOutByResource = React2__namespace.useMemo(() => {
|
|
745
|
+
return buildLaidOutByResource(resources, renderAppts);
|
|
746
|
+
}, [renderAppts, resources]);
|
|
540
747
|
const laidOutByResource = React2__namespace.useMemo(() => {
|
|
541
|
-
|
|
542
|
-
|
|
748
|
+
if (drag.kind !== "resize") {
|
|
749
|
+
return baseLaidOutByResource;
|
|
750
|
+
}
|
|
751
|
+
const next = new Map(baseLaidOutByResource);
|
|
752
|
+
const nextAppointmentsForResource = effectiveAppts.filter(
|
|
753
|
+
(appointment) => appointment.resourceId === drag.resourceId
|
|
754
|
+
);
|
|
755
|
+
next.set(drag.resourceId, buildLaidOutAppointments(nextAppointmentsForResource));
|
|
756
|
+
return next;
|
|
757
|
+
}, [baseLaidOutByResource, drag, effectiveAppts]);
|
|
543
758
|
const appointmentBgByResource = React2__namespace.useMemo(() => {
|
|
544
759
|
if (!getResourceAppointmentBackground) {
|
|
545
760
|
return /* @__PURE__ */ new Map();
|
|
@@ -591,6 +806,453 @@ function useSchedulerPresentationData({
|
|
|
591
806
|
laidOutByResource
|
|
592
807
|
};
|
|
593
808
|
}
|
|
809
|
+
function Gutter({ dayMinutes, dayStartAbs, gridHeight }) {
|
|
810
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
811
|
+
"div",
|
|
812
|
+
{
|
|
813
|
+
className: "rfs-times border-border bg-card",
|
|
814
|
+
style: { width: GUTTER_W, height: gridHeight },
|
|
815
|
+
children: Array.from({ length: Math.floor(dayMinutes / 60) + 1 }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
816
|
+
"div",
|
|
817
|
+
{
|
|
818
|
+
className: "rfs-time-tick text-muted-foreground",
|
|
819
|
+
style: { top: TOP_PAD + i * 60 * PX_PER_MIN },
|
|
820
|
+
children: [
|
|
821
|
+
String(Math.floor((dayStartAbs + i * 60) / 60)).padStart(2, "0"),
|
|
822
|
+
":",
|
|
823
|
+
String((dayStartAbs + i * 60) % 60).padStart(2, "0")
|
|
824
|
+
]
|
|
825
|
+
},
|
|
826
|
+
`tick-${i}`
|
|
827
|
+
))
|
|
828
|
+
}
|
|
829
|
+
);
|
|
830
|
+
}
|
|
831
|
+
function defaultRenderResourceHeader({ resource }) {
|
|
832
|
+
return /* @__PURE__ */ jsxRuntime.jsx("strong", { children: resource.label });
|
|
833
|
+
}
|
|
834
|
+
function Header({ resources, renderResourceHeader }) {
|
|
835
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rfs-header border-border bg-card", children: [
|
|
836
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
837
|
+
"div",
|
|
838
|
+
{
|
|
839
|
+
className: "rfs-gutter border-border",
|
|
840
|
+
style: { width: GUTTER_W }
|
|
841
|
+
}
|
|
842
|
+
),
|
|
843
|
+
resources.map((resource) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
844
|
+
"div",
|
|
845
|
+
{
|
|
846
|
+
className: "rfs-resource-header border-border bg-card text-foreground",
|
|
847
|
+
style: { minWidth: RESOURCE_MIN_W, flex: 1 },
|
|
848
|
+
children: [
|
|
849
|
+
(renderResourceHeader ?? ((r) => defaultRenderResourceHeader({ resource: r })))(
|
|
850
|
+
resource
|
|
851
|
+
),
|
|
852
|
+
isNameLike(resource) ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rfs-resource-subtitle text-muted-foreground", children: fullName(resource) }) : null
|
|
853
|
+
]
|
|
854
|
+
},
|
|
855
|
+
String(resource.id)
|
|
856
|
+
))
|
|
857
|
+
] });
|
|
858
|
+
}
|
|
859
|
+
var ResourceColInner = ({
|
|
860
|
+
resource,
|
|
861
|
+
colRefs,
|
|
862
|
+
gridHeight,
|
|
863
|
+
dayMinutes,
|
|
864
|
+
appointments,
|
|
865
|
+
appointmentAppearance,
|
|
866
|
+
appointmentBg,
|
|
867
|
+
isDropInvalid,
|
|
868
|
+
renderAppointment,
|
|
869
|
+
onApptPointerDown,
|
|
870
|
+
onResizePointerDown,
|
|
871
|
+
drag,
|
|
872
|
+
suppressClickRef
|
|
873
|
+
}) => {
|
|
874
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
875
|
+
"div",
|
|
876
|
+
{
|
|
877
|
+
className: "rfs-col border-border bg-card",
|
|
878
|
+
style: { minWidth: RESOURCE_MIN_W, flex: 1 },
|
|
879
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
880
|
+
"div",
|
|
881
|
+
{
|
|
882
|
+
ref: (el) => {
|
|
883
|
+
colRefs.current[String(resource.id)] = el;
|
|
884
|
+
},
|
|
885
|
+
className: "rfs-col-inner bg-card",
|
|
886
|
+
style: { height: gridHeight },
|
|
887
|
+
children: [
|
|
888
|
+
Array.from({ length: Math.floor(dayMinutes / 60) + 1 }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
889
|
+
"div",
|
|
890
|
+
{
|
|
891
|
+
className: "rfs-hour-line border-border/70",
|
|
892
|
+
style: { top: TOP_PAD + i * 60 * PX_PER_MIN }
|
|
893
|
+
},
|
|
894
|
+
`major-${i}`
|
|
895
|
+
)),
|
|
896
|
+
Array.from({ length: Math.floor(dayMinutes / STEP_MIN) + 1 }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
897
|
+
"div",
|
|
898
|
+
{
|
|
899
|
+
className: "rfs-slot-line border-border/50",
|
|
900
|
+
style: { top: TOP_PAD + i * STEP_MIN * PX_PER_MIN }
|
|
901
|
+
},
|
|
902
|
+
`minor-${i}`
|
|
903
|
+
)),
|
|
904
|
+
appointments.map((appointment) => {
|
|
905
|
+
const laneWidthPct = 100 / appointment.lanes;
|
|
906
|
+
const top = TOP_PAD + appointment.startMin * PX_PER_MIN;
|
|
907
|
+
const height = Math.max(
|
|
908
|
+
MIN_EVENT_MIN * PX_PER_MIN,
|
|
909
|
+
(appointment.endMin - appointment.startMin) * PX_PER_MIN
|
|
910
|
+
);
|
|
911
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
912
|
+
"div",
|
|
913
|
+
{
|
|
914
|
+
className: `rfs-card-slot ${appointment.visualState === "ghost" ? "rfs-card-slot--ghost" : ""} ${appointment.visualState === "dragging" ? "rfs-card-slot--dragging" : ""}`,
|
|
915
|
+
style: {
|
|
916
|
+
top,
|
|
917
|
+
left: `${laneWidthPct * appointment.lane}%`,
|
|
918
|
+
width: `${laneWidthPct}%`,
|
|
919
|
+
height
|
|
920
|
+
},
|
|
921
|
+
children: (renderAppointment ?? defaultRenderAppointment)({
|
|
922
|
+
appointment,
|
|
923
|
+
isDropInvalid: isDropInvalid && drag.kind === "resize" && appointment.id === drag.appointmentId,
|
|
924
|
+
onPointerDown: (event) => onApptPointerDown(event, appointment),
|
|
925
|
+
onResizePointerDown: (event) => onResizePointerDown(event, appointment),
|
|
926
|
+
appointmentAppearance,
|
|
927
|
+
appointmentBackgroundColor: appointmentBg,
|
|
928
|
+
drag,
|
|
929
|
+
suppressClickRef
|
|
930
|
+
})
|
|
931
|
+
},
|
|
932
|
+
appointment.renderKey
|
|
933
|
+
);
|
|
934
|
+
})
|
|
935
|
+
]
|
|
936
|
+
}
|
|
937
|
+
)
|
|
938
|
+
}
|
|
939
|
+
);
|
|
940
|
+
};
|
|
941
|
+
|
|
942
|
+
// src/components/scheduler/parts/ResourceCol.tsx
|
|
943
|
+
var areResourceColsEqual = (prev, next) => [
|
|
944
|
+
prev.resource === next.resource,
|
|
945
|
+
prev.colRefs === next.colRefs,
|
|
946
|
+
prev.gridHeight === next.gridHeight,
|
|
947
|
+
prev.dayMinutes === next.dayMinutes,
|
|
948
|
+
prev.appointments === next.appointments,
|
|
949
|
+
prev.appointmentAppearance === next.appointmentAppearance,
|
|
950
|
+
prev.appointmentBg === next.appointmentBg,
|
|
951
|
+
prev.isDropInvalid === next.isDropInvalid,
|
|
952
|
+
prev.renderAppointment === next.renderAppointment,
|
|
953
|
+
prev.onApptPointerDown === next.onApptPointerDown,
|
|
954
|
+
prev.onResizePointerDown === next.onResizePointerDown,
|
|
955
|
+
prev.drag === next.drag,
|
|
956
|
+
prev.suppressClickRef === next.suppressClickRef
|
|
957
|
+
].every(Boolean);
|
|
958
|
+
var ResourceCol = React2__namespace.memo(
|
|
959
|
+
ResourceColInner,
|
|
960
|
+
areResourceColsEqual
|
|
961
|
+
);
|
|
962
|
+
|
|
963
|
+
// src/lib/cn.ts
|
|
964
|
+
function cn(...inputs) {
|
|
965
|
+
return inputs.filter(Boolean).join(" ");
|
|
966
|
+
}
|
|
967
|
+
var variantClasses = {
|
|
968
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
969
|
+
outline: "border border-border bg-card text-foreground hover:bg-muted",
|
|
970
|
+
ghost: "text-foreground hover:bg-muted"
|
|
971
|
+
};
|
|
972
|
+
var sizeClasses = {
|
|
973
|
+
default: "h-9 px-4 py-2",
|
|
974
|
+
sm: "h-8 px-3 text-xs",
|
|
975
|
+
icon: "h-8 w-8"
|
|
976
|
+
};
|
|
977
|
+
var Button = React2__namespace.forwardRef(function Button2({ className, variant = "default", size = "default", type = "button", ...props }, ref) {
|
|
978
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
979
|
+
"button",
|
|
980
|
+
{
|
|
981
|
+
ref,
|
|
982
|
+
type,
|
|
983
|
+
className: cn(
|
|
984
|
+
"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",
|
|
985
|
+
variantClasses[variant],
|
|
986
|
+
sizeClasses[size],
|
|
987
|
+
className
|
|
988
|
+
),
|
|
989
|
+
...props
|
|
990
|
+
}
|
|
991
|
+
);
|
|
992
|
+
});
|
|
993
|
+
function Calendar({
|
|
994
|
+
selected,
|
|
995
|
+
onSelect
|
|
996
|
+
}) {
|
|
997
|
+
const [month, setMonth] = React2__namespace.useState(selected ?? /* @__PURE__ */ new Date());
|
|
998
|
+
React2__namespace.useEffect(() => {
|
|
999
|
+
if (selected) {
|
|
1000
|
+
setMonth(selected);
|
|
1001
|
+
}
|
|
1002
|
+
}, [selected]);
|
|
1003
|
+
const monthStart = dateFns.startOfMonth(month);
|
|
1004
|
+
const calendarStart = dateFns.startOfWeek(monthStart, { weekStartsOn: 1 });
|
|
1005
|
+
const calendarEnd = dateFns.endOfWeek(dateFns.endOfMonth(month), { weekStartsOn: 1 });
|
|
1006
|
+
const days = [];
|
|
1007
|
+
for (let day = calendarStart; day <= calendarEnd; day = dateFns.addDays(day, 1)) {
|
|
1008
|
+
days.push(day);
|
|
1009
|
+
}
|
|
1010
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-72 rounded-md bg-popover", children: [
|
|
1011
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-2 flex items-center justify-between", children: [
|
|
1012
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1013
|
+
Button,
|
|
1014
|
+
{
|
|
1015
|
+
variant: "ghost",
|
|
1016
|
+
size: "icon",
|
|
1017
|
+
"aria-label": "Previous month",
|
|
1018
|
+
onClick: () => setMonth(dateFns.addMonths(month, -1)),
|
|
1019
|
+
children: "<"
|
|
1020
|
+
}
|
|
1021
|
+
),
|
|
1022
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-medium", children: dateFns.format(month, "MMMM yyyy") }),
|
|
1023
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1024
|
+
Button,
|
|
1025
|
+
{
|
|
1026
|
+
variant: "ghost",
|
|
1027
|
+
size: "icon",
|
|
1028
|
+
"aria-label": "Next month",
|
|
1029
|
+
onClick: () => setMonth(dateFns.addMonths(month, 1)),
|
|
1030
|
+
children: ">"
|
|
1031
|
+
}
|
|
1032
|
+
)
|
|
1033
|
+
] }),
|
|
1034
|
+
/* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsx(
|
|
1035
|
+
"div",
|
|
1036
|
+
{
|
|
1037
|
+
className: "py-1",
|
|
1038
|
+
children: label
|
|
1039
|
+
},
|
|
1040
|
+
label
|
|
1041
|
+
)) }),
|
|
1042
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-7 gap-1", children: days.map((day) => {
|
|
1043
|
+
const isSelected = selected ? dateFns.isSameDay(day, selected) : false;
|
|
1044
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1045
|
+
Button,
|
|
1046
|
+
{
|
|
1047
|
+
variant: isSelected ? "default" : "ghost",
|
|
1048
|
+
size: "icon",
|
|
1049
|
+
className: cn(
|
|
1050
|
+
"text-xs",
|
|
1051
|
+
!dateFns.isSameMonth(day, month) && "text-muted-foreground opacity-60",
|
|
1052
|
+
dateFns.isToday(day) && !isSelected && "border border-border"
|
|
1053
|
+
),
|
|
1054
|
+
onClick: () => onSelect?.(day),
|
|
1055
|
+
children: dateFns.format(day, "d")
|
|
1056
|
+
},
|
|
1057
|
+
day.toISOString()
|
|
1058
|
+
);
|
|
1059
|
+
}) })
|
|
1060
|
+
] });
|
|
1061
|
+
}
|
|
1062
|
+
var PopoverContext = React2__namespace.createContext(null);
|
|
1063
|
+
function usePopoverContext() {
|
|
1064
|
+
const context = React2__namespace.useContext(PopoverContext);
|
|
1065
|
+
if (!context) {
|
|
1066
|
+
throw new Error("Popover components must be used within Popover.");
|
|
1067
|
+
}
|
|
1068
|
+
return context;
|
|
1069
|
+
}
|
|
1070
|
+
function Popover({
|
|
1071
|
+
children,
|
|
1072
|
+
open: controlledOpen,
|
|
1073
|
+
onOpenChange
|
|
1074
|
+
}) {
|
|
1075
|
+
const [uncontrolledOpen, setUncontrolledOpen] = React2__namespace.useState(false);
|
|
1076
|
+
const triggerRef = React2__namespace.useRef(null);
|
|
1077
|
+
const contentRef = React2__namespace.useRef(null);
|
|
1078
|
+
const open = controlledOpen ?? uncontrolledOpen;
|
|
1079
|
+
const setOpen = React2__namespace.useCallback(
|
|
1080
|
+
(value) => {
|
|
1081
|
+
const nextValue = typeof value === "function" ? value(open) : value;
|
|
1082
|
+
if (controlledOpen === void 0) {
|
|
1083
|
+
setUncontrolledOpen(nextValue);
|
|
1084
|
+
}
|
|
1085
|
+
onOpenChange?.(nextValue);
|
|
1086
|
+
},
|
|
1087
|
+
[controlledOpen, onOpenChange, open]
|
|
1088
|
+
);
|
|
1089
|
+
React2__namespace.useEffect(() => {
|
|
1090
|
+
if (!open) {
|
|
1091
|
+
return void 0;
|
|
1092
|
+
}
|
|
1093
|
+
const handlePointerDown = (event) => {
|
|
1094
|
+
const target = event.target;
|
|
1095
|
+
if (triggerRef.current?.contains(target) || contentRef.current?.contains(target)) {
|
|
1096
|
+
return;
|
|
1097
|
+
}
|
|
1098
|
+
setOpen(false);
|
|
1099
|
+
};
|
|
1100
|
+
const handleKeyDown = (event) => {
|
|
1101
|
+
if (event.key === "Escape") {
|
|
1102
|
+
setOpen(false);
|
|
1103
|
+
}
|
|
1104
|
+
};
|
|
1105
|
+
document.addEventListener("mousedown", handlePointerDown);
|
|
1106
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
1107
|
+
return () => {
|
|
1108
|
+
document.removeEventListener("mousedown", handlePointerDown);
|
|
1109
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
1110
|
+
};
|
|
1111
|
+
}, [open, setOpen]);
|
|
1112
|
+
return /* @__PURE__ */ jsxRuntime.jsx(PopoverContext.Provider, { value: { open, setOpen, triggerRef, contentRef }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative inline-flex", children }) });
|
|
1113
|
+
}
|
|
1114
|
+
function PopoverTrigger({
|
|
1115
|
+
children,
|
|
1116
|
+
asChild = false
|
|
1117
|
+
}) {
|
|
1118
|
+
const { open, setOpen, triggerRef } = usePopoverContext();
|
|
1119
|
+
if (asChild && React2__namespace.isValidElement(children)) {
|
|
1120
|
+
const child = children;
|
|
1121
|
+
return React2__namespace.cloneElement(child, {
|
|
1122
|
+
ref: triggerRef,
|
|
1123
|
+
"aria-expanded": open,
|
|
1124
|
+
onClick: (event) => {
|
|
1125
|
+
child.props.onClick?.(event);
|
|
1126
|
+
setOpen((current) => !current);
|
|
1127
|
+
}
|
|
1128
|
+
});
|
|
1129
|
+
}
|
|
1130
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1131
|
+
"button",
|
|
1132
|
+
{
|
|
1133
|
+
ref: triggerRef,
|
|
1134
|
+
type: "button",
|
|
1135
|
+
"aria-expanded": open,
|
|
1136
|
+
onClick: () => setOpen((current) => !current),
|
|
1137
|
+
children
|
|
1138
|
+
}
|
|
1139
|
+
);
|
|
1140
|
+
}
|
|
1141
|
+
function PopoverContent({
|
|
1142
|
+
children,
|
|
1143
|
+
className,
|
|
1144
|
+
align = "start"
|
|
1145
|
+
}) {
|
|
1146
|
+
const { open, contentRef } = usePopoverContext();
|
|
1147
|
+
if (!open) {
|
|
1148
|
+
return null;
|
|
1149
|
+
}
|
|
1150
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1151
|
+
"div",
|
|
1152
|
+
{
|
|
1153
|
+
ref: contentRef,
|
|
1154
|
+
className: cn(
|
|
1155
|
+
"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",
|
|
1156
|
+
align === "start" && "left-0",
|
|
1157
|
+
align === "center" && "left-1/2 -translate-x-1/2",
|
|
1158
|
+
align === "end" && "right-0",
|
|
1159
|
+
className
|
|
1160
|
+
),
|
|
1161
|
+
children
|
|
1162
|
+
}
|
|
1163
|
+
);
|
|
1164
|
+
}
|
|
1165
|
+
function defaultRenderDatePicker({
|
|
1166
|
+
selectedDate,
|
|
1167
|
+
onSelectedDateChange
|
|
1168
|
+
}) {
|
|
1169
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Popover, { children: [
|
|
1170
|
+
/* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1171
|
+
Button,
|
|
1172
|
+
{
|
|
1173
|
+
variant: "outline",
|
|
1174
|
+
size: "sm",
|
|
1175
|
+
className: "min-w-40 justify-start text-left font-normal",
|
|
1176
|
+
children: dateFns.format(selectedDate, "PPP")
|
|
1177
|
+
}
|
|
1178
|
+
) }),
|
|
1179
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1180
|
+
PopoverContent,
|
|
1181
|
+
{
|
|
1182
|
+
className: "w-auto p-2",
|
|
1183
|
+
align: "center",
|
|
1184
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1185
|
+
Calendar,
|
|
1186
|
+
{
|
|
1187
|
+
selected: selectedDate,
|
|
1188
|
+
onSelect: onSelectedDateChange
|
|
1189
|
+
}
|
|
1190
|
+
)
|
|
1191
|
+
}
|
|
1192
|
+
)
|
|
1193
|
+
] });
|
|
1194
|
+
}
|
|
1195
|
+
function defaultRenderToolbar({
|
|
1196
|
+
goToPreviousDay,
|
|
1197
|
+
goToNextDay,
|
|
1198
|
+
defaultDatePicker,
|
|
1199
|
+
prevButtonLabel = "Prev",
|
|
1200
|
+
nextButtonLabel = "Next"
|
|
1201
|
+
}) {
|
|
1202
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rfs-toolbar", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 rounded-lg border border-border bg-card p-1 shadow-xs", children: [
|
|
1203
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1204
|
+
Button,
|
|
1205
|
+
{
|
|
1206
|
+
variant: "outline",
|
|
1207
|
+
size: "sm",
|
|
1208
|
+
onClick: goToPreviousDay,
|
|
1209
|
+
children: prevButtonLabel
|
|
1210
|
+
}
|
|
1211
|
+
),
|
|
1212
|
+
defaultDatePicker,
|
|
1213
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1214
|
+
Button,
|
|
1215
|
+
{
|
|
1216
|
+
variant: "outline",
|
|
1217
|
+
size: "sm",
|
|
1218
|
+
onClick: goToNextDay,
|
|
1219
|
+
children: nextButtonLabel
|
|
1220
|
+
}
|
|
1221
|
+
)
|
|
1222
|
+
] }) });
|
|
1223
|
+
}
|
|
1224
|
+
function Toolbar({
|
|
1225
|
+
selectedDate,
|
|
1226
|
+
onSelectedDateChange,
|
|
1227
|
+
prevButtonLabel,
|
|
1228
|
+
nextButtonLabel,
|
|
1229
|
+
renderToolbar,
|
|
1230
|
+
renderDatePicker
|
|
1231
|
+
}) {
|
|
1232
|
+
const goToPreviousDay = () => onSelectedDateChange(shiftDays(selectedDate, -1));
|
|
1233
|
+
const goToNextDay = () => onSelectedDateChange(shiftDays(selectedDate, 1));
|
|
1234
|
+
const defaultDatePicker = (renderDatePicker ?? defaultRenderDatePicker)({
|
|
1235
|
+
selectedDate,
|
|
1236
|
+
onSelectedDateChange
|
|
1237
|
+
});
|
|
1238
|
+
if (renderToolbar) {
|
|
1239
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: renderToolbar({
|
|
1240
|
+
selectedDate,
|
|
1241
|
+
onSelectedDateChange,
|
|
1242
|
+
goToPreviousDay,
|
|
1243
|
+
goToNextDay,
|
|
1244
|
+
defaultDatePicker
|
|
1245
|
+
}) });
|
|
1246
|
+
}
|
|
1247
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: defaultRenderToolbar({
|
|
1248
|
+
goToPreviousDay,
|
|
1249
|
+
goToNextDay,
|
|
1250
|
+
defaultDatePicker,
|
|
1251
|
+
prevButtonLabel,
|
|
1252
|
+
nextButtonLabel
|
|
1253
|
+
}) });
|
|
1254
|
+
}
|
|
1255
|
+
var IDLE_DRAG = { kind: "none" };
|
|
594
1256
|
function Scheduler({
|
|
595
1257
|
resources,
|
|
596
1258
|
appointments,
|
|
@@ -606,30 +1268,19 @@ function Scheduler({
|
|
|
606
1268
|
getResourceAppointmentColorToken,
|
|
607
1269
|
appointmentColorTokenClassMap,
|
|
608
1270
|
getResourceAppointmentBackground,
|
|
1271
|
+
renderToolbar,
|
|
609
1272
|
renderDatePicker,
|
|
610
1273
|
prevButtonLabel = "Prev",
|
|
611
1274
|
nextButtonLabel = "Next",
|
|
612
1275
|
dayStart = "09:00",
|
|
613
1276
|
dayEnd = "18:00"
|
|
614
1277
|
}) {
|
|
615
|
-
const {
|
|
616
|
-
appointmentMap,
|
|
617
|
-
dayMinutes,
|
|
618
|
-
dayStartAbs,
|
|
619
|
-
gridHeight,
|
|
620
|
-
gridMinWidth,
|
|
621
|
-
renderAppts
|
|
622
|
-
} = useSchedulerBaseData({
|
|
623
|
-
adapter,
|
|
624
|
-
appointments,
|
|
625
|
-
dayEnd,
|
|
626
|
-
dayStart,
|
|
627
|
-
resources,
|
|
628
|
-
selectedDate
|
|
629
|
-
});
|
|
1278
|
+
const { appointmentMap, dayMinutes, dayStartAbs, gridHeight, gridMinWidth, renderAppts } = useSchedulerBaseData({ adapter, appointments, dayEnd, dayStart, resources, selectedDate });
|
|
630
1279
|
const {
|
|
631
1280
|
colRefs,
|
|
632
1281
|
drag,
|
|
1282
|
+
isDropInvalid,
|
|
1283
|
+
movePreview,
|
|
633
1284
|
onApptPointerDown,
|
|
634
1285
|
onGlobalPointerMove,
|
|
635
1286
|
onGlobalPointerUp,
|
|
@@ -645,11 +1296,7 @@ function Scheduler({
|
|
|
645
1296
|
resources,
|
|
646
1297
|
selectedDate
|
|
647
1298
|
});
|
|
648
|
-
const {
|
|
649
|
-
appointmentAppearanceByResource,
|
|
650
|
-
appointmentBgByResource,
|
|
651
|
-
laidOutByResource
|
|
652
|
-
} = useSchedulerPresentationData({
|
|
1299
|
+
const { appointmentAppearanceByResource, appointmentBgByResource, laidOutByResource } = useSchedulerPresentationData({
|
|
653
1300
|
appointmentColorTokenClassMap,
|
|
654
1301
|
dayStartAbs,
|
|
655
1302
|
drag,
|
|
@@ -661,7 +1308,29 @@ function Scheduler({
|
|
|
661
1308
|
resources,
|
|
662
1309
|
selectedDate
|
|
663
1310
|
});
|
|
664
|
-
const
|
|
1311
|
+
const moveOverlay = React2__namespace.useMemo(() => {
|
|
1312
|
+
if (drag.kind !== "move" || !movePreview) {
|
|
1313
|
+
return null;
|
|
1314
|
+
}
|
|
1315
|
+
return {
|
|
1316
|
+
appointment: {
|
|
1317
|
+
...movePreview.appointment,
|
|
1318
|
+
resourceId: drag.resourceId,
|
|
1319
|
+
startMin: drag.startMin,
|
|
1320
|
+
endMin: drag.endMin,
|
|
1321
|
+
visualState: "ghost",
|
|
1322
|
+
renderKey: `${movePreview.appointment.renderKey}-overlay`,
|
|
1323
|
+
lane: 0,
|
|
1324
|
+
lanes: 1
|
|
1325
|
+
},
|
|
1326
|
+
appearance: appointmentAppearanceByResource.get(drag.resourceId),
|
|
1327
|
+
background: appointmentBgByResource.get(drag.resourceId),
|
|
1328
|
+
left: movePreview.clientX - movePreview.offsetX,
|
|
1329
|
+
top: movePreview.clientY - movePreview.offsetY,
|
|
1330
|
+
width: movePreview.width,
|
|
1331
|
+
height: movePreview.height
|
|
1332
|
+
};
|
|
1333
|
+
}, [appointmentAppearanceByResource, appointmentBgByResource, drag, movePreview]);
|
|
665
1334
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
666
1335
|
"section",
|
|
667
1336
|
{
|
|
@@ -670,124 +1339,77 @@ function Scheduler({
|
|
|
670
1339
|
onPointerUp: onGlobalPointerUp,
|
|
671
1340
|
onPointerCancel: onGlobalPointerUp,
|
|
672
1341
|
children: [
|
|
673
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1342
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1343
|
+
Toolbar,
|
|
1344
|
+
{
|
|
1345
|
+
selectedDate,
|
|
1346
|
+
onSelectedDateChange,
|
|
1347
|
+
prevButtonLabel,
|
|
1348
|
+
nextButtonLabel,
|
|
1349
|
+
renderToolbar,
|
|
1350
|
+
renderDatePicker
|
|
1351
|
+
}
|
|
1352
|
+
),
|
|
1353
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "rfs-shell border-border bg-card", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { minWidth: gridMinWidth }, children: [
|
|
674
1354
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
675
|
-
|
|
1355
|
+
Header,
|
|
676
1356
|
{
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
onClick: () => onSelectedDateChange(shiftDays(selectedDate, -1)),
|
|
680
|
-
children: prevButtonLabel
|
|
1357
|
+
resources,
|
|
1358
|
+
renderResourceHeader
|
|
681
1359
|
}
|
|
682
1360
|
),
|
|
683
|
-
(renderDatePicker ?? defaultRenderDatePicker)({ selectedDate, onSelectedDateChange }),
|
|
684
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
685
|
-
"button",
|
|
686
|
-
{
|
|
687
|
-
className: "rfs-btn border-border bg-card text-foreground hover:bg-muted",
|
|
688
|
-
type: "button",
|
|
689
|
-
onClick: () => onSelectedDateChange(shiftDays(selectedDate, 1)),
|
|
690
|
-
children: nextButtonLabel
|
|
691
|
-
}
|
|
692
|
-
)
|
|
693
|
-
] }),
|
|
694
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "rfs-shell border-border bg-card", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { minWidth: gridMinWidth }, children: [
|
|
695
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rfs-header border-border bg-card", children: [
|
|
696
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "rfs-gutter border-border", style: { width: GUTTER_W } }),
|
|
697
|
-
resources.map((resource) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
698
|
-
"div",
|
|
699
|
-
{
|
|
700
|
-
className: "rfs-resource-header border-border bg-card text-foreground",
|
|
701
|
-
style: { minWidth: RESOURCE_MIN_W, flex: 1 },
|
|
702
|
-
children: [
|
|
703
|
-
(renderResourceHeader ?? defaultResourceHeaderRenderer)(resource),
|
|
704
|
-
isNameLike(resource) ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rfs-resource-subtitle text-muted-foreground", children: fullName(resource) }) : null
|
|
705
|
-
]
|
|
706
|
-
},
|
|
707
|
-
String(resource.id)
|
|
708
|
-
))
|
|
709
|
-
] }),
|
|
710
1361
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rfs-body", children: [
|
|
711
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
712
|
-
|
|
1362
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1363
|
+
Gutter,
|
|
713
1364
|
{
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
String((dayStartAbs + i * 60) % 60).padStart(2, "0")
|
|
720
|
-
]
|
|
721
|
-
},
|
|
722
|
-
`tick-${i}`
|
|
723
|
-
)) }),
|
|
1365
|
+
dayMinutes,
|
|
1366
|
+
dayStartAbs,
|
|
1367
|
+
gridHeight
|
|
1368
|
+
}
|
|
1369
|
+
),
|
|
724
1370
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "rfs-grid", children: resources.map((resource) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
725
|
-
|
|
1371
|
+
ResourceCol,
|
|
726
1372
|
{
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
{
|
|
741
|
-
className: "rfs-hour-line border-border/70",
|
|
742
|
-
style: { top: TOP_PAD + i * 60 * PX_PER_MIN }
|
|
743
|
-
},
|
|
744
|
-
`major-${String(resource.id)}-${i}`
|
|
745
|
-
)),
|
|
746
|
-
Array.from({ length: Math.floor(dayMinutes / STEP_MIN) + 1 }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
747
|
-
"div",
|
|
748
|
-
{
|
|
749
|
-
className: "rfs-slot-line border-border/50",
|
|
750
|
-
style: { top: TOP_PAD + i * STEP_MIN * PX_PER_MIN }
|
|
751
|
-
},
|
|
752
|
-
`minor-${String(resource.id)}-${i}`
|
|
753
|
-
)),
|
|
754
|
-
(laidOutByResource.get(resource.id) ?? []).map((appointment) => {
|
|
755
|
-
const laneWidthPct = 100 / appointment.lanes;
|
|
756
|
-
const top = TOP_PAD + appointment.startMin * PX_PER_MIN;
|
|
757
|
-
const height = Math.max(MIN_EVENT_MIN * PX_PER_MIN, (appointment.endMin - appointment.startMin) * PX_PER_MIN);
|
|
758
|
-
const appointmentAppearance = appointmentAppearanceByResource.get(resource.id);
|
|
759
|
-
const appointmentBackgroundColor = appointmentBgByResource.get(resource.id);
|
|
760
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
761
|
-
"div",
|
|
762
|
-
{
|
|
763
|
-
className: "rfs-card-slot",
|
|
764
|
-
style: {
|
|
765
|
-
top,
|
|
766
|
-
left: `${laneWidthPct * appointment.lane}%`,
|
|
767
|
-
width: `${laneWidthPct}%`,
|
|
768
|
-
height
|
|
769
|
-
},
|
|
770
|
-
children: (renderAppointment ?? defaultRenderAppointment)({
|
|
771
|
-
appointment,
|
|
772
|
-
onPointerDown: (event) => onApptPointerDown(event, appointment),
|
|
773
|
-
onResizePointerDown: (event) => onResizePointerDown(event, appointment),
|
|
774
|
-
appointmentAppearance,
|
|
775
|
-
appointmentBackgroundColor,
|
|
776
|
-
drag,
|
|
777
|
-
suppressClickRef
|
|
778
|
-
})
|
|
779
|
-
},
|
|
780
|
-
String(appointment.id)
|
|
781
|
-
);
|
|
782
|
-
})
|
|
783
|
-
]
|
|
784
|
-
}
|
|
785
|
-
)
|
|
1373
|
+
resource,
|
|
1374
|
+
colRefs,
|
|
1375
|
+
gridHeight,
|
|
1376
|
+
dayMinutes,
|
|
1377
|
+
appointments: laidOutByResource.get(resource.id) ?? [],
|
|
1378
|
+
appointmentAppearance: appointmentAppearanceByResource.get(resource.id),
|
|
1379
|
+
appointmentBg: appointmentBgByResource.get(resource.id),
|
|
1380
|
+
isDropInvalid,
|
|
1381
|
+
renderAppointment,
|
|
1382
|
+
onApptPointerDown,
|
|
1383
|
+
onResizePointerDown,
|
|
1384
|
+
drag: drag.kind === "resize" && drag.resourceId === resource.id ? drag : IDLE_DRAG,
|
|
1385
|
+
suppressClickRef
|
|
786
1386
|
},
|
|
787
1387
|
String(resource.id)
|
|
788
1388
|
)) })
|
|
789
1389
|
] })
|
|
790
|
-
] }) })
|
|
1390
|
+
] }) }),
|
|
1391
|
+
moveOverlay ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1392
|
+
"div",
|
|
1393
|
+
{
|
|
1394
|
+
className: "rfs-drag-overlay",
|
|
1395
|
+
style: {
|
|
1396
|
+
left: moveOverlay.left,
|
|
1397
|
+
top: moveOverlay.top,
|
|
1398
|
+
width: moveOverlay.width,
|
|
1399
|
+
height: moveOverlay.height
|
|
1400
|
+
},
|
|
1401
|
+
children: (renderAppointment ?? defaultRenderAppointment)({
|
|
1402
|
+
appointment: moveOverlay.appointment,
|
|
1403
|
+
isDropInvalid,
|
|
1404
|
+
onPointerDown: () => void 0,
|
|
1405
|
+
onResizePointerDown: () => void 0,
|
|
1406
|
+
appointmentAppearance: moveOverlay.appearance,
|
|
1407
|
+
appointmentBackgroundColor: moveOverlay.background,
|
|
1408
|
+
drag,
|
|
1409
|
+
suppressClickRef
|
|
1410
|
+
})
|
|
1411
|
+
}
|
|
1412
|
+
) : null
|
|
791
1413
|
]
|
|
792
1414
|
}
|
|
793
1415
|
);
|