@marco.colia/react-fast-scheduler 2.0.0 → 2.1.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/LICENSE +22 -22
- package/README.md +251 -251
- package/dist/index.cjs +543 -357
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +542 -356
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +86 -86
package/dist/index.cjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var React = require('react');
|
|
4
|
-
var dateFns = require('date-fns');
|
|
5
3
|
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var dateFns = require('date-fns');
|
|
5
|
+
var React2 = require('react');
|
|
6
6
|
|
|
7
7
|
function _interopNamespace(e) {
|
|
8
8
|
if (e && e.__esModule) return e;
|
|
@@ -22,9 +22,38 @@ function _interopNamespace(e) {
|
|
|
22
22
|
return Object.freeze(n);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
var
|
|
25
|
+
var React2__namespace = /*#__PURE__*/_interopNamespace(React2);
|
|
26
26
|
|
|
27
|
-
// src/components/
|
|
27
|
+
// src/components/defaultRenderAppointment/index.tsx
|
|
28
|
+
function defaultRenderAppointment({
|
|
29
|
+
appointment,
|
|
30
|
+
onPointerDown,
|
|
31
|
+
onResizePointerDown,
|
|
32
|
+
appointmentAppearance,
|
|
33
|
+
appointmentBackgroundColor
|
|
34
|
+
}) {
|
|
35
|
+
const rawMaybe = appointment.raw;
|
|
36
|
+
const description = typeof rawMaybe.description === "string" ? rawMaybe.description : void 0;
|
|
37
|
+
const legacyClassName = typeof appointmentBackgroundColor === "string" ? appointmentBackgroundColor : void 0;
|
|
38
|
+
const backgroundClassName = appointmentAppearance?.className ?? legacyClassName ?? "bg-muted/40 dark:bg-muted/30";
|
|
39
|
+
const className = `relative h-full cursor-grab overflow-hidden rounded-md border border-border p-2 pb-5 text-foreground ${backgroundClassName}`;
|
|
40
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { onPointerDown, className, children: [
|
|
41
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs font-semibold", children: appointment.title }),
|
|
42
|
+
description ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1 text-[10px] font-medium text-muted-foreground", children: description }) : null,
|
|
43
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44
|
+
"div",
|
|
45
|
+
{
|
|
46
|
+
role: "button",
|
|
47
|
+
"aria-label": "Resize appointment",
|
|
48
|
+
onPointerDown: (event) => {
|
|
49
|
+
event.stopPropagation();
|
|
50
|
+
onResizePointerDown(event);
|
|
51
|
+
},
|
|
52
|
+
className: "absolute inset-x-1 bottom-1 h-2 cursor-ns-resize rounded-full bg-border/90 transition-colors hover:bg-border"
|
|
53
|
+
}
|
|
54
|
+
)
|
|
55
|
+
] });
|
|
56
|
+
}
|
|
28
57
|
|
|
29
58
|
// src/components/constants.ts
|
|
30
59
|
var STEP_MIN = 15;
|
|
@@ -34,26 +63,75 @@ var GUTTER_W = 72;
|
|
|
34
63
|
var TOP_PAD = 12;
|
|
35
64
|
var BOTTOM_PAD = 24;
|
|
36
65
|
var RESOURCE_MIN_W = 420;
|
|
37
|
-
|
|
38
|
-
|
|
66
|
+
|
|
67
|
+
// src/utils/scheduler-core.utils.ts
|
|
68
|
+
function isNameLike(resource) {
|
|
69
|
+
if (!resource || typeof resource !== "object") {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
const maybe = resource;
|
|
73
|
+
return typeof maybe.firstName === "string" || typeof maybe.lastName === "string";
|
|
74
|
+
}
|
|
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
|
+
var DEFAULT_APPOINTMENT_COLOR_TOKEN_CLASS_MAP = {
|
|
92
|
+
slate: "bg-slate-100 dark:bg-slate-900/40",
|
|
93
|
+
gray: "bg-gray-100 dark:bg-gray-900/40",
|
|
94
|
+
zinc: "bg-zinc-100 dark:bg-zinc-900/40",
|
|
95
|
+
neutral: "bg-neutral-100 dark:bg-neutral-900/40",
|
|
96
|
+
stone: "bg-stone-100 dark:bg-stone-900/40",
|
|
97
|
+
red: "bg-red-100 dark:bg-red-950/40",
|
|
98
|
+
orange: "bg-orange-100 dark:bg-orange-950/40",
|
|
99
|
+
amber: "bg-amber-100 dark:bg-amber-950/40",
|
|
100
|
+
yellow: "bg-yellow-100 dark:bg-yellow-950/40",
|
|
101
|
+
lime: "bg-lime-100 dark:bg-lime-950/40",
|
|
102
|
+
green: "bg-green-100 dark:bg-green-950/40",
|
|
103
|
+
emerald: "bg-emerald-100 dark:bg-emerald-950/40",
|
|
104
|
+
teal: "bg-teal-100 dark:bg-teal-950/40",
|
|
105
|
+
cyan: "bg-cyan-100 dark:bg-cyan-950/40",
|
|
106
|
+
sky: "bg-sky-100 dark:bg-sky-950/40",
|
|
107
|
+
blue: "bg-blue-100 dark:bg-blue-950/40",
|
|
108
|
+
indigo: "bg-indigo-100 dark:bg-indigo-950/40",
|
|
109
|
+
violet: "bg-violet-100 dark:bg-violet-950/40",
|
|
110
|
+
purple: "bg-purple-100 dark:bg-purple-950/40",
|
|
111
|
+
fuchsia: "bg-fuchsia-100 dark:bg-fuchsia-950/40",
|
|
112
|
+
pink: "bg-pink-100 dark:bg-pink-950/40",
|
|
113
|
+
rose: "bg-rose-100 dark:bg-rose-950/40"
|
|
39
114
|
};
|
|
40
|
-
|
|
115
|
+
function clamp(n, min, max) {
|
|
116
|
+
return Math.max(min, Math.min(max, n));
|
|
117
|
+
}
|
|
118
|
+
function snap(mins, stepMin = STEP_MIN) {
|
|
41
119
|
return Math.round(mins / stepMin) * stepMin;
|
|
42
|
-
}
|
|
43
|
-
|
|
120
|
+
}
|
|
121
|
+
function parseHHMM(hhmm) {
|
|
44
122
|
const [h, m] = hhmm.split(":").map(Number);
|
|
45
123
|
return h * 60 + m;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return `${
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return dateFns.format(
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return new Date(
|
|
55
|
-
}
|
|
56
|
-
|
|
124
|
+
}
|
|
125
|
+
function fullName(resource) {
|
|
126
|
+
return `${resource.firstName ?? ""} ${resource.lastName ?? ""}`.trim();
|
|
127
|
+
}
|
|
128
|
+
function dayISO(date) {
|
|
129
|
+
return dateFns.format(date, "yyyy-MM-dd");
|
|
130
|
+
}
|
|
131
|
+
function shiftDays(date, delta) {
|
|
132
|
+
return new Date(date.getFullYear(), date.getMonth(), date.getDate() + delta);
|
|
133
|
+
}
|
|
134
|
+
function dateAtMinute(selectedDate, dayStartAbs, minuteFromStart) {
|
|
57
135
|
const base = new Date(
|
|
58
136
|
selectedDate.getFullYear(),
|
|
59
137
|
selectedDate.getMonth(),
|
|
@@ -68,311 +146,314 @@ var dateAtMinute = (selectedDate, dayStartAbs, minuteFromStart) => {
|
|
|
68
146
|
const mm = abs % 60;
|
|
69
147
|
base.setHours(hh, mm, 0, 0);
|
|
70
148
|
return base;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return
|
|
74
|
-
}
|
|
75
|
-
|
|
149
|
+
}
|
|
150
|
+
function minutesFromDayStart(dayStartAbs, date) {
|
|
151
|
+
return date.getHours() * 60 + date.getMinutes() - dayStartAbs;
|
|
152
|
+
}
|
|
153
|
+
function overlaps(a, b) {
|
|
76
154
|
return a.startMin < b.endMin && b.startMin < a.endMin;
|
|
77
|
-
}
|
|
78
|
-
|
|
155
|
+
}
|
|
156
|
+
function layoutOverlaps(blocks) {
|
|
79
157
|
const sorted = [...blocks].sort((a, b) => a.startMin - b.startMin || a.endMin - b.endMin);
|
|
80
158
|
const groups = [];
|
|
81
159
|
let active = [];
|
|
82
160
|
let current = [];
|
|
83
|
-
for (const
|
|
84
|
-
active = active.filter((
|
|
85
|
-
const conflicts = active.some((
|
|
161
|
+
for (const block of sorted) {
|
|
162
|
+
active = active.filter((item) => item.endMin > block.startMin);
|
|
163
|
+
const conflicts = active.some((item) => overlaps(item, block));
|
|
86
164
|
if (!conflicts && current.length > 0) {
|
|
87
165
|
groups.push(current);
|
|
88
166
|
current = [];
|
|
89
167
|
}
|
|
90
|
-
current.push(
|
|
91
|
-
active.push(
|
|
168
|
+
current.push(block);
|
|
169
|
+
active.push(block);
|
|
92
170
|
}
|
|
93
171
|
if (current.length) {
|
|
94
172
|
groups.push(current);
|
|
95
173
|
}
|
|
96
|
-
const
|
|
174
|
+
const output = [];
|
|
97
175
|
for (const group of groups) {
|
|
98
|
-
const
|
|
176
|
+
const sortedGroup = [...group].sort((a, b) => a.startMin - b.startMin || a.endMin - b.endMin);
|
|
99
177
|
const laneEnd = [];
|
|
100
178
|
const laneOf = /* @__PURE__ */ new Map();
|
|
101
|
-
for (const
|
|
179
|
+
for (const block of sortedGroup) {
|
|
102
180
|
let placed = false;
|
|
103
181
|
for (let i = 0; i < laneEnd.length; i++) {
|
|
104
|
-
if (laneEnd[i] <=
|
|
105
|
-
laneEnd[i] =
|
|
106
|
-
laneOf.set(
|
|
182
|
+
if (laneEnd[i] <= block.startMin) {
|
|
183
|
+
laneEnd[i] = block.endMin;
|
|
184
|
+
laneOf.set(block.id, i);
|
|
107
185
|
placed = true;
|
|
108
186
|
break;
|
|
109
187
|
}
|
|
110
188
|
}
|
|
111
189
|
if (!placed) {
|
|
112
|
-
laneEnd.push(
|
|
113
|
-
laneOf.set(
|
|
190
|
+
laneEnd.push(block.endMin);
|
|
191
|
+
laneOf.set(block.id, laneEnd.length - 1);
|
|
114
192
|
}
|
|
115
193
|
}
|
|
116
194
|
const lanes = laneEnd.length;
|
|
117
|
-
for (const
|
|
118
|
-
|
|
195
|
+
for (const block of sortedGroup) {
|
|
196
|
+
output.push({ ...block, lane: laneOf.get(block.id) ?? 0, lanes });
|
|
119
197
|
}
|
|
120
198
|
}
|
|
121
|
-
return
|
|
122
|
-
};
|
|
123
|
-
function isNameLike(resource) {
|
|
124
|
-
if (!resource || typeof resource !== "object") {
|
|
125
|
-
return false;
|
|
126
|
-
}
|
|
127
|
-
const maybe = resource;
|
|
128
|
-
return typeof maybe.firstName === "string" || typeof maybe.lastName === "string";
|
|
199
|
+
return output;
|
|
129
200
|
}
|
|
130
|
-
function
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
+
);
|
|
135
217
|
}
|
|
136
|
-
function
|
|
137
|
-
|
|
138
|
-
const year = Number(yearRaw);
|
|
139
|
-
const month = Number(monthRaw);
|
|
140
|
-
const day = Number(dayRaw);
|
|
141
|
-
if (!Number.isInteger(year) || !Number.isInteger(month) || !Number.isInteger(day)) {
|
|
142
|
-
return null;
|
|
143
|
-
}
|
|
144
|
-
return new Date(year, month - 1, day);
|
|
218
|
+
function defaultRenderResourceHeader({ resource }) {
|
|
219
|
+
return /* @__PURE__ */ jsxRuntime.jsx("strong", { children: resource.label });
|
|
145
220
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
stone: "bg-stone-100 dark:bg-stone-900/40",
|
|
152
|
-
red: "bg-red-100 dark:bg-red-950/40",
|
|
153
|
-
orange: "bg-orange-100 dark:bg-orange-950/40",
|
|
154
|
-
amber: "bg-amber-100 dark:bg-amber-950/40",
|
|
155
|
-
yellow: "bg-yellow-100 dark:bg-yellow-950/40",
|
|
156
|
-
lime: "bg-lime-100 dark:bg-lime-950/40",
|
|
157
|
-
green: "bg-green-100 dark:bg-green-950/40",
|
|
158
|
-
emerald: "bg-emerald-100 dark:bg-emerald-950/40",
|
|
159
|
-
teal: "bg-teal-100 dark:bg-teal-950/40",
|
|
160
|
-
cyan: "bg-cyan-100 dark:bg-cyan-950/40",
|
|
161
|
-
sky: "bg-sky-100 dark:bg-sky-950/40",
|
|
162
|
-
blue: "bg-blue-100 dark:bg-blue-950/40",
|
|
163
|
-
indigo: "bg-indigo-100 dark:bg-indigo-950/40",
|
|
164
|
-
violet: "bg-violet-100 dark:bg-violet-950/40",
|
|
165
|
-
purple: "bg-purple-100 dark:bg-purple-950/40",
|
|
166
|
-
fuchsia: "bg-fuchsia-100 dark:bg-fuchsia-950/40",
|
|
167
|
-
pink: "bg-pink-100 dark:bg-pink-950/40",
|
|
168
|
-
rose: "bg-rose-100 dark:bg-rose-950/40"
|
|
169
|
-
};
|
|
170
|
-
function Scheduler({
|
|
221
|
+
|
|
222
|
+
// src/utils/scheduler-data.utils.ts
|
|
223
|
+
function buildResourceMap({
|
|
224
|
+
fallback,
|
|
225
|
+
getValue,
|
|
171
226
|
resources,
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
227
|
+
toKey
|
|
228
|
+
}) {
|
|
229
|
+
const map = /* @__PURE__ */ new Map();
|
|
230
|
+
for (const resource of resources) {
|
|
231
|
+
map.set(toKey(resource), getValue(resource) ?? fallback);
|
|
232
|
+
}
|
|
233
|
+
return map;
|
|
234
|
+
}
|
|
235
|
+
function normalizeAppointments({
|
|
175
236
|
adapter,
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
resourceAppointmentClassMap,
|
|
181
|
-
getResourceAppointmentAppearance,
|
|
182
|
-
getResourceAppointmentColorToken,
|
|
183
|
-
appointmentColorTokenClassMap,
|
|
184
|
-
getResourceAppointmentBackground,
|
|
185
|
-
renderDatePicker,
|
|
186
|
-
prevButtonLabel = "Prev",
|
|
187
|
-
nextButtonLabel = "Next",
|
|
188
|
-
dayStart = "09:00",
|
|
189
|
-
dayEnd = "18:00"
|
|
237
|
+
appointments,
|
|
238
|
+
dayMinutes,
|
|
239
|
+
dayStartAbs,
|
|
240
|
+
selectedISO
|
|
190
241
|
}) {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
const appointmentMap = React__namespace.useMemo(() => {
|
|
217
|
-
return new Map(renderAppts.map((appointment) => [appointment.id, appointment]));
|
|
218
|
-
}, [renderAppts]);
|
|
219
|
-
const effectiveAppts = React__namespace.useMemo(() => {
|
|
220
|
-
if (drag.kind === "none") {
|
|
221
|
-
return renderAppts;
|
|
222
|
-
}
|
|
223
|
-
return renderAppts.map((appointment) => {
|
|
224
|
-
if (appointment.id !== drag.appointmentId) {
|
|
225
|
-
return appointment;
|
|
226
|
-
}
|
|
227
|
-
return {
|
|
228
|
-
...appointment,
|
|
229
|
-
resourceId: drag.resourceId,
|
|
230
|
-
startMin: drag.startMin,
|
|
231
|
-
endMin: drag.endMin,
|
|
232
|
-
start: dateAtMinute(selectedDate, dayStartAbs, drag.startMin),
|
|
233
|
-
end: dateAtMinute(selectedDate, dayStartAbs, drag.endMin)
|
|
234
|
-
};
|
|
235
|
-
});
|
|
236
|
-
}, [drag, renderAppts, selectedDate, dayStartAbs]);
|
|
237
|
-
const laidOutByResource = React__namespace.useMemo(() => {
|
|
238
|
-
const byResource = /* @__PURE__ */ new Map();
|
|
239
|
-
for (const resource of resources) {
|
|
240
|
-
const events = effectiveAppts.filter((appointment) => appointment.resourceId === resource.id);
|
|
241
|
-
byResource.set(resource.id, layoutOverlaps(events));
|
|
242
|
-
}
|
|
243
|
-
return byResource;
|
|
244
|
-
}, [effectiveAppts, resources]);
|
|
245
|
-
const appointmentBgByResource = React__namespace.useMemo(() => {
|
|
246
|
-
const map = /* @__PURE__ */ new Map();
|
|
247
|
-
if (!getResourceAppointmentBackground) {
|
|
248
|
-
return map;
|
|
249
|
-
}
|
|
250
|
-
for (const resource of resources) {
|
|
251
|
-
map.set(resource.id, getResourceAppointmentBackground(resource));
|
|
242
|
+
return appointments.map((appointment) => {
|
|
243
|
+
const start = new Date(adapter.getStart(appointment));
|
|
244
|
+
const end = new Date(adapter.getEnd(appointment));
|
|
245
|
+
return {
|
|
246
|
+
raw: appointment,
|
|
247
|
+
id: adapter.getId(appointment),
|
|
248
|
+
resourceId: adapter.getResourceId(appointment),
|
|
249
|
+
start,
|
|
250
|
+
end,
|
|
251
|
+
startMin: clamp(minutesFromDayStart(dayStartAbs, start), 0, dayMinutes),
|
|
252
|
+
endMin: clamp(minutesFromDayStart(dayStartAbs, end), 0, dayMinutes),
|
|
253
|
+
title: adapter.getTitle(appointment)
|
|
254
|
+
};
|
|
255
|
+
}).filter((appointment) => dayISO(appointment.start) === selectedISO);
|
|
256
|
+
}
|
|
257
|
+
function buildAppointmentMap(appointments) {
|
|
258
|
+
return new Map(appointments.map((appointment) => [appointment.id, appointment]));
|
|
259
|
+
}
|
|
260
|
+
function applyDragToAppointments(appointments, drag, selectedDate, dayStartAbs) {
|
|
261
|
+
if (drag.kind === "none") {
|
|
262
|
+
return appointments;
|
|
263
|
+
}
|
|
264
|
+
return appointments.map((appointment) => {
|
|
265
|
+
if (appointment.id !== drag.appointmentId) {
|
|
266
|
+
return appointment;
|
|
252
267
|
}
|
|
253
|
-
return
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
268
|
+
return {
|
|
269
|
+
...appointment,
|
|
270
|
+
resourceId: drag.resourceId,
|
|
271
|
+
startMin: drag.startMin,
|
|
272
|
+
endMin: drag.endMin,
|
|
273
|
+
start: dateAtMinute(selectedDate, dayStartAbs, drag.startMin),
|
|
274
|
+
end: dateAtMinute(selectedDate, dayStartAbs, drag.endMin)
|
|
260
275
|
};
|
|
261
|
-
|
|
262
|
-
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
function buildLaidOutByResource(resources, appointments) {
|
|
279
|
+
const appointmentsByResource = /* @__PURE__ */ new Map();
|
|
280
|
+
for (const appointment of appointments) {
|
|
281
|
+
const current = appointmentsByResource.get(appointment.resourceId) ?? [];
|
|
282
|
+
current.push(appointment);
|
|
283
|
+
appointmentsByResource.set(appointment.resourceId, current);
|
|
284
|
+
}
|
|
285
|
+
const result = /* @__PURE__ */ new Map();
|
|
286
|
+
for (const resource of resources) {
|
|
287
|
+
result.set(resource.id, layoutOverlaps(appointmentsByResource.get(resource.id) ?? []));
|
|
288
|
+
}
|
|
289
|
+
return result;
|
|
290
|
+
}
|
|
291
|
+
function buildAppointmentAppearanceByResource(resources, resourceAppointmentClassMap, getResourceAppointmentAppearance, appointmentColorTokenClassByResource, appointmentBgByResource) {
|
|
292
|
+
const map = /* @__PURE__ */ new Map();
|
|
293
|
+
for (const resource of resources) {
|
|
294
|
+
const classFromIdMap = resourceAppointmentClassMap?.[String(resource.id)];
|
|
295
|
+
if (classFromIdMap) {
|
|
296
|
+
map.set(resource.id, { className: classFromIdMap });
|
|
297
|
+
continue;
|
|
263
298
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
map.set(resource.id,
|
|
299
|
+
const appearance = getResourceAppointmentAppearance?.(resource);
|
|
300
|
+
if (appearance) {
|
|
301
|
+
map.set(resource.id, appearance);
|
|
302
|
+
continue;
|
|
267
303
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
304
|
+
const tokenClass = appointmentColorTokenClassByResource.get(resource.id);
|
|
305
|
+
if (tokenClass) {
|
|
306
|
+
map.set(resource.id, { className: tokenClass });
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
const legacyBackground = appointmentBgByResource.get(resource.id);
|
|
310
|
+
map.set(resource.id, legacyBackground ? { className: legacyBackground } : void 0);
|
|
311
|
+
}
|
|
312
|
+
return map;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// src/hooks/useSchedulerBaseData.ts
|
|
316
|
+
function useSchedulerBaseData({
|
|
317
|
+
adapter,
|
|
318
|
+
appointments,
|
|
319
|
+
dayEnd = "18:00",
|
|
320
|
+
dayStart = "09:00",
|
|
321
|
+
resources,
|
|
322
|
+
selectedDate
|
|
323
|
+
}) {
|
|
324
|
+
const dayStartAbs = React2__namespace.useMemo(() => parseHHMM(dayStart), [dayStart]);
|
|
325
|
+
const dayEndAbs = React2__namespace.useMemo(() => parseHHMM(dayEnd), [dayEnd]);
|
|
326
|
+
const dayMinutes = React2__namespace.useMemo(() => Math.max(0, dayEndAbs - dayStartAbs), [dayEndAbs, dayStartAbs]);
|
|
327
|
+
const gridHeight = React2__namespace.useMemo(() => TOP_PAD + dayMinutes * PX_PER_MIN + BOTTOM_PAD, [dayMinutes]);
|
|
328
|
+
const gridMinWidth = React2__namespace.useMemo(() => GUTTER_W + Math.max(resources.length, 1) * RESOURCE_MIN_W, [resources.length]);
|
|
329
|
+
const selectedISO = React2__namespace.useMemo(() => dayISO(selectedDate), [selectedDate]);
|
|
330
|
+
const renderAppts = React2__namespace.useMemo(() => {
|
|
331
|
+
return normalizeAppointments({
|
|
332
|
+
adapter,
|
|
333
|
+
appointments,
|
|
334
|
+
dayMinutes,
|
|
335
|
+
dayStartAbs,
|
|
336
|
+
selectedISO
|
|
337
|
+
});
|
|
338
|
+
}, [adapter, appointments, dayMinutes, dayStartAbs, selectedISO]);
|
|
339
|
+
const appointmentMap = React2__namespace.useMemo(() => buildAppointmentMap(renderAppts), [renderAppts]);
|
|
340
|
+
return {
|
|
341
|
+
appointmentMap,
|
|
342
|
+
dayEndAbs,
|
|
343
|
+
dayMinutes,
|
|
344
|
+
dayStartAbs,
|
|
345
|
+
gridHeight,
|
|
346
|
+
gridMinWidth,
|
|
347
|
+
renderAppts
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
function useSchedulerInteractions({
|
|
351
|
+
appointmentMap,
|
|
352
|
+
dayMinutes,
|
|
353
|
+
dayStartAbs,
|
|
354
|
+
onAppointmentChange,
|
|
355
|
+
onPersistMoveResize,
|
|
356
|
+
renderAppts,
|
|
357
|
+
resources,
|
|
358
|
+
selectedDate
|
|
359
|
+
}) {
|
|
360
|
+
const [drag, setDrag] = React2__namespace.useState({ kind: "none" });
|
|
361
|
+
const colRefs = React2__namespace.useRef({});
|
|
362
|
+
const suppressClickRef = React2__namespace.useRef(false);
|
|
363
|
+
const pointerToMin = React2__namespace.useCallback(
|
|
364
|
+
(resourceId, clientY) => {
|
|
365
|
+
const col = colRefs.current[String(resourceId)];
|
|
366
|
+
if (!col) {
|
|
367
|
+
return 0;
|
|
368
|
+
}
|
|
369
|
+
const y = clientY - col.getBoundingClientRect().top - TOP_PAD;
|
|
370
|
+
return clamp(snap(y / PX_PER_MIN), 0, dayMinutes);
|
|
371
|
+
},
|
|
372
|
+
[dayMinutes]
|
|
373
|
+
);
|
|
374
|
+
const pointerToResourceId = React2__namespace.useCallback(
|
|
375
|
+
(clientX, fallbackResourceId) => {
|
|
376
|
+
for (const resource of resources) {
|
|
377
|
+
const col = colRefs.current[String(resource.id)];
|
|
378
|
+
if (!col) {
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
const rect = col.getBoundingClientRect();
|
|
382
|
+
if (clientX >= rect.left && clientX <= rect.right) {
|
|
383
|
+
return resource.id;
|
|
384
|
+
}
|
|
277
385
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
386
|
+
return fallbackResourceId;
|
|
387
|
+
},
|
|
388
|
+
[resources]
|
|
389
|
+
);
|
|
390
|
+
const hasOverlap = React2__namespace.useCallback(
|
|
391
|
+
(appointmentId, resourceId, startMin, endMin) => {
|
|
392
|
+
const events = renderAppts.filter((appointment) => appointment.resourceId === resourceId && appointment.id !== appointmentId);
|
|
393
|
+
return events.some((appointment) => overlaps(appointment, { startMin, endMin }));
|
|
394
|
+
},
|
|
395
|
+
[renderAppts]
|
|
396
|
+
);
|
|
397
|
+
const persistDrag = React2__namespace.useCallback(
|
|
398
|
+
async (state) => {
|
|
399
|
+
const target = appointmentMap.get(state.appointmentId);
|
|
400
|
+
if (!target) {
|
|
401
|
+
return;
|
|
282
402
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
map.set(resource.id, { className: tokenClass });
|
|
286
|
-
continue;
|
|
403
|
+
if (hasOverlap(state.appointmentId, state.resourceId, state.startMin, state.endMin)) {
|
|
404
|
+
return;
|
|
287
405
|
}
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
406
|
+
const next = {
|
|
407
|
+
resourceId: state.resourceId,
|
|
408
|
+
start: dateAtMinute(selectedDate, dayStartAbs, state.startMin),
|
|
409
|
+
end: dateAtMinute(selectedDate, dayStartAbs, state.endMin)
|
|
410
|
+
};
|
|
411
|
+
if (onAppointmentChange) {
|
|
412
|
+
await onAppointmentChange({
|
|
413
|
+
kind: state.kind,
|
|
414
|
+
appointment: target.raw,
|
|
415
|
+
previous: {
|
|
416
|
+
resourceId: target.resourceId,
|
|
417
|
+
start: target.start,
|
|
418
|
+
end: target.end
|
|
419
|
+
},
|
|
420
|
+
next
|
|
421
|
+
});
|
|
422
|
+
return;
|
|
292
423
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
return 0;
|
|
301
|
-
}
|
|
302
|
-
const y = clientY - col.getBoundingClientRect().top - TOP_PAD;
|
|
303
|
-
return clamp(snap(y / PX_PER_MIN), 0, dayMinutes);
|
|
304
|
-
};
|
|
305
|
-
const pointerToResourceId = (clientX, fallbackResourceId) => {
|
|
306
|
-
for (const resource of resources) {
|
|
307
|
-
const col = colRefs.current[String(resource.id)];
|
|
308
|
-
if (!col) {
|
|
309
|
-
continue;
|
|
424
|
+
if (onPersistMoveResize) {
|
|
425
|
+
await onPersistMoveResize({
|
|
426
|
+
appointment: target.raw,
|
|
427
|
+
resourceId: next.resourceId,
|
|
428
|
+
start: next.start,
|
|
429
|
+
end: next.end
|
|
430
|
+
});
|
|
310
431
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
432
|
+
},
|
|
433
|
+
[appointmentMap, dayStartAbs, hasOverlap, onAppointmentChange, onPersistMoveResize, selectedDate]
|
|
434
|
+
);
|
|
435
|
+
const onApptPointerDown = React2__namespace.useCallback(
|
|
436
|
+
(event, appointment) => {
|
|
437
|
+
if (event.button !== 0) {
|
|
438
|
+
return;
|
|
314
439
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
if (hasOverlap(state.appointmentId, state.resourceId, state.startMin, state.endMin)) {
|
|
328
|
-
return;
|
|
329
|
-
}
|
|
330
|
-
const next = {
|
|
331
|
-
resourceId: state.resourceId,
|
|
332
|
-
start: dateAtMinute(selectedDate, dayStartAbs, state.startMin),
|
|
333
|
-
end: dateAtMinute(selectedDate, dayStartAbs, state.endMin)
|
|
334
|
-
};
|
|
335
|
-
if (onAppointmentChange) {
|
|
336
|
-
await onAppointmentChange({
|
|
337
|
-
kind: state.kind,
|
|
338
|
-
appointment: target.raw,
|
|
339
|
-
previous: {
|
|
340
|
-
resourceId: target.resourceId,
|
|
341
|
-
start: target.start,
|
|
342
|
-
end: target.end
|
|
343
|
-
},
|
|
344
|
-
next
|
|
345
|
-
});
|
|
346
|
-
return;
|
|
347
|
-
}
|
|
348
|
-
if (onPersistMoveResize) {
|
|
349
|
-
await onPersistMoveResize({
|
|
350
|
-
appointment: target.raw,
|
|
351
|
-
resourceId: next.resourceId,
|
|
352
|
-
start: next.start,
|
|
353
|
-
end: next.end
|
|
440
|
+
event.stopPropagation();
|
|
441
|
+
suppressClickRef.current = false;
|
|
442
|
+
const pointerMin = pointerToMin(appointment.resourceId, event.clientY);
|
|
443
|
+
setDrag({
|
|
444
|
+
kind: "move",
|
|
445
|
+
appointmentId: appointment.id,
|
|
446
|
+
pointerId: event.pointerId,
|
|
447
|
+
resourceId: appointment.resourceId,
|
|
448
|
+
durationMin: Math.max(MIN_EVENT_MIN, appointment.endMin - appointment.startMin),
|
|
449
|
+
offsetMin: pointerMin - appointment.startMin,
|
|
450
|
+
startMin: appointment.startMin,
|
|
451
|
+
endMin: appointment.endMin
|
|
354
452
|
});
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
return;
|
|
360
|
-
}
|
|
361
|
-
event.stopPropagation();
|
|
362
|
-
suppressClickRef.current = false;
|
|
363
|
-
const pointerMin = pointerToMin(appointment.resourceId, event.clientY);
|
|
364
|
-
setDrag({
|
|
365
|
-
kind: "move",
|
|
366
|
-
appointmentId: appointment.id,
|
|
367
|
-
pointerId: event.pointerId,
|
|
368
|
-
resourceId: appointment.resourceId,
|
|
369
|
-
durationMin: Math.max(MIN_EVENT_MIN, appointment.endMin - appointment.startMin),
|
|
370
|
-
offsetMin: pointerMin - appointment.startMin,
|
|
371
|
-
startMin: appointment.startMin,
|
|
372
|
-
endMin: appointment.endMin
|
|
373
|
-
});
|
|
374
|
-
};
|
|
375
|
-
const onResizePointerDown = (event, appointment) => {
|
|
453
|
+
},
|
|
454
|
+
[pointerToMin]
|
|
455
|
+
);
|
|
456
|
+
const onResizePointerDown = React2__namespace.useCallback((event, appointment) => {
|
|
376
457
|
if (event.button !== 0) {
|
|
377
458
|
return;
|
|
378
459
|
}
|
|
@@ -386,39 +467,42 @@ function Scheduler({
|
|
|
386
467
|
startMin: appointment.startMin,
|
|
387
468
|
endMin: appointment.endMin
|
|
388
469
|
});
|
|
389
|
-
};
|
|
390
|
-
const onGlobalPointerMove = (
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
470
|
+
}, []);
|
|
471
|
+
const onGlobalPointerMove = React2__namespace.useCallback(
|
|
472
|
+
(event) => {
|
|
473
|
+
if (drag.kind === "none") {
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
if (drag.kind === "move") {
|
|
477
|
+
const nextResourceId = pointerToResourceId(event.clientX, drag.resourceId);
|
|
478
|
+
const pointerMin2 = pointerToMin(nextResourceId, event.clientY);
|
|
479
|
+
const rawStart = pointerMin2 - drag.offsetMin;
|
|
480
|
+
const nextStartMin = clamp(snap(rawStart), 0, dayMinutes - drag.durationMin);
|
|
481
|
+
const nextEndMin2 = nextStartMin + drag.durationMin;
|
|
482
|
+
if (nextStartMin !== drag.startMin || nextEndMin2 !== drag.endMin || nextResourceId !== drag.resourceId) {
|
|
483
|
+
suppressClickRef.current = true;
|
|
484
|
+
setDrag({
|
|
485
|
+
...drag,
|
|
486
|
+
resourceId: nextResourceId,
|
|
487
|
+
startMin: nextStartMin,
|
|
488
|
+
endMin: nextEndMin2
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
const pointerMin = pointerToMin(drag.resourceId, event.clientY);
|
|
494
|
+
const nextEndMin = clamp(snap(pointerMin), drag.startMin + MIN_EVENT_MIN, dayMinutes);
|
|
495
|
+
if (nextEndMin !== drag.endMin) {
|
|
401
496
|
suppressClickRef.current = true;
|
|
402
497
|
setDrag({
|
|
403
498
|
...drag,
|
|
404
|
-
|
|
405
|
-
startMin: nextStartMin,
|
|
406
|
-
endMin: nextEndMin2
|
|
499
|
+
endMin: nextEndMin
|
|
407
500
|
});
|
|
408
501
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
if (nextEndMin !== drag.endMin) {
|
|
414
|
-
suppressClickRef.current = true;
|
|
415
|
-
setDrag({
|
|
416
|
-
...drag,
|
|
417
|
-
endMin: nextEndMin
|
|
418
|
-
});
|
|
419
|
-
}
|
|
420
|
-
};
|
|
421
|
-
const onGlobalPointerUp = () => {
|
|
502
|
+
},
|
|
503
|
+
[dayMinutes, drag, pointerToMin, pointerToResourceId]
|
|
504
|
+
);
|
|
505
|
+
const onGlobalPointerUp = React2__namespace.useCallback(() => {
|
|
422
506
|
if (drag.kind === "none") {
|
|
423
507
|
return;
|
|
424
508
|
}
|
|
@@ -427,55 +511,157 @@ function Scheduler({
|
|
|
427
511
|
if (suppressClickRef.current) {
|
|
428
512
|
void persistDrag(current);
|
|
429
513
|
}
|
|
514
|
+
}, [drag, persistDrag]);
|
|
515
|
+
return {
|
|
516
|
+
colRefs,
|
|
517
|
+
drag,
|
|
518
|
+
onApptPointerDown,
|
|
519
|
+
onGlobalPointerMove,
|
|
520
|
+
onGlobalPointerUp,
|
|
521
|
+
onResizePointerDown,
|
|
522
|
+
suppressClickRef
|
|
430
523
|
};
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
onResizePointerDown2(event);
|
|
454
|
-
},
|
|
455
|
-
className: "absolute inset-x-1 bottom-1 h-2 cursor-ns-resize rounded-full bg-border/90 transition-colors hover:bg-border"
|
|
456
|
-
}
|
|
457
|
-
)
|
|
458
|
-
] });
|
|
459
|
-
};
|
|
460
|
-
const defaultRenderDatePicker = ({
|
|
461
|
-
selectedDate: value,
|
|
462
|
-
onSelectedDateChange: onDateChange
|
|
463
|
-
}) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
464
|
-
"input",
|
|
465
|
-
{
|
|
466
|
-
type: "date",
|
|
467
|
-
value: toInputDateValue(value),
|
|
468
|
-
onChange: (event) => {
|
|
469
|
-
const nextDate = fromInputDateValue(event.target.value);
|
|
470
|
-
if (nextDate) {
|
|
471
|
-
onDateChange(nextDate);
|
|
472
|
-
}
|
|
473
|
-
},
|
|
474
|
-
className: "h-7 rounded-md border border-border bg-card px-2 text-xs text-foreground",
|
|
475
|
-
"aria-label": "Select date"
|
|
524
|
+
}
|
|
525
|
+
function useSchedulerPresentationData({
|
|
526
|
+
appointmentColorTokenClassMap,
|
|
527
|
+
dayStartAbs,
|
|
528
|
+
drag,
|
|
529
|
+
getResourceAppointmentAppearance,
|
|
530
|
+
getResourceAppointmentBackground,
|
|
531
|
+
getResourceAppointmentColorToken,
|
|
532
|
+
renderAppts,
|
|
533
|
+
resourceAppointmentClassMap,
|
|
534
|
+
resources,
|
|
535
|
+
selectedDate
|
|
536
|
+
}) {
|
|
537
|
+
const effectiveAppts = React2__namespace.useMemo(() => {
|
|
538
|
+
return applyDragToAppointments(renderAppts, drag, selectedDate, dayStartAbs);
|
|
539
|
+
}, [dayStartAbs, drag, renderAppts, selectedDate]);
|
|
540
|
+
const laidOutByResource = React2__namespace.useMemo(() => {
|
|
541
|
+
return buildLaidOutByResource(resources, effectiveAppts);
|
|
542
|
+
}, [effectiveAppts, resources]);
|
|
543
|
+
const appointmentBgByResource = React2__namespace.useMemo(() => {
|
|
544
|
+
if (!getResourceAppointmentBackground) {
|
|
545
|
+
return /* @__PURE__ */ new Map();
|
|
476
546
|
}
|
|
477
|
-
|
|
478
|
-
|
|
547
|
+
return buildResourceMap({
|
|
548
|
+
fallback: void 0,
|
|
549
|
+
getValue: getResourceAppointmentBackground,
|
|
550
|
+
resources,
|
|
551
|
+
toKey: (resource) => resource.id
|
|
552
|
+
});
|
|
553
|
+
}, [getResourceAppointmentBackground, resources]);
|
|
554
|
+
const appointmentColorTokenClassByResource = React2__namespace.useMemo(() => {
|
|
555
|
+
if (!getResourceAppointmentColorToken) {
|
|
556
|
+
return /* @__PURE__ */ new Map();
|
|
557
|
+
}
|
|
558
|
+
const mergedClassMap = {
|
|
559
|
+
...DEFAULT_APPOINTMENT_COLOR_TOKEN_CLASS_MAP,
|
|
560
|
+
...appointmentColorTokenClassMap ?? {}
|
|
561
|
+
};
|
|
562
|
+
return buildResourceMap({
|
|
563
|
+
fallback: void 0,
|
|
564
|
+
getValue: (resource) => {
|
|
565
|
+
const token = getResourceAppointmentColorToken(resource);
|
|
566
|
+
return token ? mergedClassMap[token] : void 0;
|
|
567
|
+
},
|
|
568
|
+
resources,
|
|
569
|
+
toKey: (resource) => resource.id
|
|
570
|
+
});
|
|
571
|
+
}, [appointmentColorTokenClassMap, getResourceAppointmentColorToken, resources]);
|
|
572
|
+
const appointmentAppearanceByResource = React2__namespace.useMemo(() => {
|
|
573
|
+
return buildAppointmentAppearanceByResource(
|
|
574
|
+
resources,
|
|
575
|
+
resourceAppointmentClassMap,
|
|
576
|
+
getResourceAppointmentAppearance,
|
|
577
|
+
appointmentColorTokenClassByResource,
|
|
578
|
+
appointmentBgByResource
|
|
579
|
+
);
|
|
580
|
+
}, [
|
|
581
|
+
appointmentBgByResource,
|
|
582
|
+
appointmentColorTokenClassByResource,
|
|
583
|
+
getResourceAppointmentAppearance,
|
|
584
|
+
resourceAppointmentClassMap,
|
|
585
|
+
resources
|
|
586
|
+
]);
|
|
587
|
+
return {
|
|
588
|
+
appointmentAppearanceByResource,
|
|
589
|
+
appointmentBgByResource,
|
|
590
|
+
effectiveAppts,
|
|
591
|
+
laidOutByResource
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
function Scheduler({
|
|
595
|
+
resources,
|
|
596
|
+
appointments,
|
|
597
|
+
selectedDate,
|
|
598
|
+
onSelectedDateChange,
|
|
599
|
+
adapter,
|
|
600
|
+
onPersistMoveResize,
|
|
601
|
+
onAppointmentChange,
|
|
602
|
+
renderResourceHeader,
|
|
603
|
+
renderAppointment,
|
|
604
|
+
resourceAppointmentClassMap,
|
|
605
|
+
getResourceAppointmentAppearance,
|
|
606
|
+
getResourceAppointmentColorToken,
|
|
607
|
+
appointmentColorTokenClassMap,
|
|
608
|
+
getResourceAppointmentBackground,
|
|
609
|
+
renderDatePicker,
|
|
610
|
+
prevButtonLabel = "Prev",
|
|
611
|
+
nextButtonLabel = "Next",
|
|
612
|
+
dayStart = "09:00",
|
|
613
|
+
dayEnd = "18:00"
|
|
614
|
+
}) {
|
|
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
|
+
});
|
|
630
|
+
const {
|
|
631
|
+
colRefs,
|
|
632
|
+
drag,
|
|
633
|
+
onApptPointerDown,
|
|
634
|
+
onGlobalPointerMove,
|
|
635
|
+
onGlobalPointerUp,
|
|
636
|
+
onResizePointerDown,
|
|
637
|
+
suppressClickRef
|
|
638
|
+
} = useSchedulerInteractions({
|
|
639
|
+
appointmentMap,
|
|
640
|
+
dayMinutes,
|
|
641
|
+
dayStartAbs,
|
|
642
|
+
onAppointmentChange,
|
|
643
|
+
onPersistMoveResize,
|
|
644
|
+
renderAppts,
|
|
645
|
+
resources,
|
|
646
|
+
selectedDate
|
|
647
|
+
});
|
|
648
|
+
const {
|
|
649
|
+
appointmentAppearanceByResource,
|
|
650
|
+
appointmentBgByResource,
|
|
651
|
+
laidOutByResource
|
|
652
|
+
} = useSchedulerPresentationData({
|
|
653
|
+
appointmentColorTokenClassMap,
|
|
654
|
+
dayStartAbs,
|
|
655
|
+
drag,
|
|
656
|
+
getResourceAppointmentAppearance,
|
|
657
|
+
getResourceAppointmentBackground,
|
|
658
|
+
getResourceAppointmentColorToken,
|
|
659
|
+
renderAppts,
|
|
660
|
+
resourceAppointmentClassMap,
|
|
661
|
+
resources,
|
|
662
|
+
selectedDate
|
|
663
|
+
});
|
|
664
|
+
const defaultResourceHeaderRenderer = (resource) => defaultRenderResourceHeader({ resource });
|
|
479
665
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
480
666
|
"section",
|
|
481
667
|
{
|
|
@@ -514,7 +700,7 @@ function Scheduler({
|
|
|
514
700
|
className: "rfs-resource-header border-border bg-card text-foreground",
|
|
515
701
|
style: { minWidth: RESOURCE_MIN_W, flex: 1 },
|
|
516
702
|
children: [
|
|
517
|
-
(renderResourceHeader ??
|
|
703
|
+
(renderResourceHeader ?? defaultResourceHeaderRenderer)(resource),
|
|
518
704
|
isNameLike(resource) ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rfs-resource-subtitle text-muted-foreground", children: fullName(resource) }) : null
|
|
519
705
|
]
|
|
520
706
|
},
|
|
@@ -607,7 +793,7 @@ function Scheduler({
|
|
|
607
793
|
);
|
|
608
794
|
}
|
|
609
795
|
|
|
610
|
-
// src/appointment-change.ts
|
|
796
|
+
// src/utils/scheduler-appointment-change.utils.ts
|
|
611
797
|
function applySchedulerAppointmentChange(appointments, change, options) {
|
|
612
798
|
return appointments.map((item) => {
|
|
613
799
|
if (options.getId(item) !== options.getId(change.appointment)) {
|