@tensor_1/react 2.32.0 → 2.34.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.
@@ -0,0 +1,521 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import { memo, useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
4
+ import { zonedDateKey, zonedHourSlots, zonedTimeOfDay } from "../calendar-model.js";
5
+ import { layoutDaySpans, layoutTimeGridDay, placesInTopLane, proposeMove } from "../scheduling-calendar-model.js";
6
+ import { fillLabel } from "../fill-label.js";
7
+ const HOUR_MS = 36e5;
8
+ const MINUTES_PER_HOUR = 60;
9
+ const LINES_PER_HOUR = 3;
10
+ const DRAG_THRESHOLD = 4;
11
+ const DEFAULT_STEP = 15;
12
+ const vars = (values) => values;
13
+ const linesFor = (segment) => Math.max(0, Math.floor(segment.lengthHours * LINES_PER_HOUR - 0.25));
14
+ const slotSignature = (slots) => slots.map((slot) => `${slot.label}${slot.offsetLabel ?? ""}`).join(" ");
15
+ function uniqueById(items) {
16
+ const seen = /* @__PURE__ */ new Set();
17
+ return items.filter((item) => seen.has(item.id) ? false : (seen.add(item.id), true));
18
+ }
19
+ const GridItem = memo(function GridItem2({ entry, segment, column, alone, name, status, movable, hintId, timeZone, continuesFrom, markers, renderEntry, renderMarker, handlers }) {
20
+ const lines = linesFor(segment);
21
+ const continuation = segment.part === "continuation";
22
+ const style = vars({
23
+ "--uix-scheduling-calendar-offset": segment.offsetHours,
24
+ "--uix-scheduling-calendar-length": segment.lengthHours,
25
+ "--uix-scheduling-calendar-lane": segment.lane,
26
+ "--uix-scheduling-calendar-lanes": segment.laneCount
27
+ });
28
+ const text = renderEntry?.(entry, { availableLines: lines }) ?? (lines > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
29
+ !continuation && /* @__PURE__ */ jsxs(Fragment, { children: [
30
+ /* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__time", children: zonedTimeOfDay(entry.start, timeZone) }),
31
+ " "
32
+ ] }),
33
+ /* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__title", children: entry.title })
34
+ ] }) : null);
35
+ const body = /* @__PURE__ */ jsxs(Fragment, { children: [
36
+ continuation && lines > 0 && /* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__tg-from", children: continuesFrom }),
37
+ (!continuation || alone) && text != null && text !== false && /* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__entry-text", children: text }),
38
+ markers.length > 0 && /* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__markers", children: markers.map((marker) => /* @__PURE__ */ jsx("span", { children: renderMarker(marker) }, marker.id)) })
39
+ ] });
40
+ const shared = {
41
+ className: "uix-scheduling-calendar__entry uix-scheduling-calendar__tg-item",
42
+ "data-band": entry.band ?? "none",
43
+ "data-status": status,
44
+ "data-state": entry.state,
45
+ "data-part": segment.part,
46
+ "data-lines": Math.min(lines, 3),
47
+ "data-movable": movable || void 0,
48
+ style,
49
+ onClick: () => handlers.click(entry),
50
+ onPointerDown: movable ? (event) => handlers.pointerDown(event, entry, column, false) : void 0
51
+ };
52
+ if (continuation && !alone) return /* @__PURE__ */ jsx("div", { ...shared, "data-continuation-of": entry.id, "aria-hidden": "true", children: body });
53
+ return /* @__PURE__ */ jsx(
54
+ "button",
55
+ {
56
+ type: "button",
57
+ ...shared,
58
+ "data-item-id": entry.id,
59
+ "aria-label": name,
60
+ "aria-describedby": movable ? hintId : void 0,
61
+ onKeyDown: movable ? (event) => handlers.keyDown(event, entry) : void 0,
62
+ onBlur: movable ? () => handlers.blur(entry) : void 0,
63
+ children: body
64
+ }
65
+ );
66
+ }, (previous, next) => previous.entry === next.entry && previous.column === next.column && previous.alone === next.alone && previous.name === next.name && previous.status === next.status && previous.movable === next.movable && previous.hintId === next.hintId && previous.timeZone === next.timeZone && previous.continuesFrom === next.continuesFrom && previous.renderEntry === next.renderEntry && previous.segment.part === next.segment.part && previous.segment.offsetHours === next.segment.offsetHours && previous.segment.lengthHours === next.segment.lengthHours && previous.segment.lane === next.segment.lane && previous.segment.laneCount === next.segment.laneCount);
67
+ function SchedulingTimeGrid({
68
+ view,
69
+ days,
70
+ timeZone,
71
+ entries: givenEntries,
72
+ overlays: givenOverlays,
73
+ dayInfo,
74
+ labels,
75
+ gridLabel,
76
+ dateText,
77
+ formatInstant,
78
+ entryName,
79
+ entryStatus,
80
+ entryMarkers,
81
+ overlayName,
82
+ renderEntry,
83
+ renderMarker,
84
+ onSelectEntry,
85
+ onSelectOverlay,
86
+ onSelectDate,
87
+ onShowMore,
88
+ now,
89
+ canMove,
90
+ step: givenStep,
91
+ onProposeMove,
92
+ topLaneCrossMidnightMinutes,
93
+ topLaneCap,
94
+ windowLaneCap,
95
+ maxLanes,
96
+ emptyNote,
97
+ nothingToShow
98
+ }) {
99
+ const rootRef = useRef(null);
100
+ const hintId = useId();
101
+ const [widthCap, setWidthCap] = useState(Infinity);
102
+ const [ghost, setGhost] = useState(null);
103
+ const [announcement, setAnnouncement] = useState("");
104
+ const moving = canMove && onProposeMove !== void 0;
105
+ const step = Number.isFinite(givenStep) && givenStep > 0 ? givenStep : DEFAULT_STEP;
106
+ const laneOptions = useMemo(() => ({ crossMidnightMinutes: topLaneCrossMidnightMinutes }), [topLaneCrossMidnightMinutes]);
107
+ const entries = useMemo(() => uniqueById(givenEntries), [givenEntries]);
108
+ const overlays = useMemo(() => uniqueById(givenOverlays), [givenOverlays]);
109
+ useEffect(() => {
110
+ const root = rootRef.current;
111
+ if (!root || typeof ResizeObserver === "undefined") return void 0;
112
+ const column = root.querySelector("[data-tg-column]");
113
+ const probe = root.querySelector(".uix-scheduling-calendar__tg-probe");
114
+ if (!column || !probe) return void 0;
115
+ let frame = 0;
116
+ const measure = () => {
117
+ frame = 0;
118
+ if (!column.isConnected) return;
119
+ const lane = probe.getBoundingClientRect().width;
120
+ const width = column.getBoundingClientRect().width;
121
+ setWidthCap(lane > 0 && width > 0 ? Math.max(1, Math.floor(width / lane)) : Infinity);
122
+ };
123
+ const observer = new ResizeObserver(() => {
124
+ if (!frame) frame = requestAnimationFrame(measure);
125
+ });
126
+ observer.observe(column);
127
+ measure();
128
+ return () => {
129
+ observer.disconnect();
130
+ if (frame) cancelAnimationFrame(frame);
131
+ };
132
+ }, [days]);
133
+ const lanes = Math.max(1, Math.min(maxLanes, widthCap));
134
+ const entryById = useMemo(() => new Map(entries.map((entry) => [entry.id, entry])), [entries]);
135
+ const overlayById = useMemo(() => new Map(overlays.map((overlay) => [overlay.id, overlay])), [overlays]);
136
+ const columns = useMemo(() => days.map((date) => layoutTimeGridDay(entries, date, timeZone, { ...laneOptions, maxLanes: lanes })), [days, entries, timeZone, laneOptions, lanes]);
137
+ const slots = useMemo(() => days.map((date) => zonedHourSlots(date, timeZone)), [days, timeZone]);
138
+ const top = useMemo(() => layoutDaySpans(entries.filter((entry) => placesInTopLane(entry, timeZone, laneOptions)), days, { timeZone, laneCap: Math.max(0, topLaneCap) }), [entries, days, timeZone, laneOptions, topLaneCap]);
139
+ const drawable = useMemo(() => overlays.filter((overlay) => new Date(overlay.end).getTime() >= new Date(overlay.start).getTime()), [overlays]);
140
+ const strip = useMemo(() => layoutDaySpans(drawable, days, { timeZone, laneCap: Math.max(0, windowLaneCap) }), [drawable, days, timeZone, windowLaneCap]);
141
+ const shades = useMemo(() => layoutDaySpans(drawable.filter((overlay) => overlay.global), days, { timeZone }).placed, [drawable, days, timeZone]);
142
+ const lanesIn = (placed) => placed.reduce((max, span) => Math.max(max, span.lane + 1), 0);
143
+ const maxHours = Math.max(...columns.map((column) => column.dayHours));
144
+ const gutterIndex = useMemo(() => {
145
+ const signatures = slots.map(slotSignature);
146
+ const count = /* @__PURE__ */ new Map();
147
+ for (const signature of signatures) count.set(signature, (count.get(signature) ?? 0) + 1);
148
+ let best = 0;
149
+ signatures.forEach((signature, index) => {
150
+ if ((count.get(signature) ?? 0) > (count.get(signatures[best]) ?? 0)) best = index;
151
+ });
152
+ return best;
153
+ }, [slots]);
154
+ const gutterSignature = slotSignature(slots[gutterIndex]);
155
+ const hourLabels = (index, inline) => slots[index].map((slot) => /* @__PURE__ */ jsxs(
156
+ "div",
157
+ {
158
+ className: inline ? "uix-scheduling-calendar__tg-hour uix-scheduling-calendar__tg-hour--inline" : "uix-scheduling-calendar__tg-hour",
159
+ "data-hour": slot.label,
160
+ style: vars({ "--uix-scheduling-calendar-offset": (slot.instant.getTime() - columns[index].dayStart) / HOUR_MS }),
161
+ children: [
162
+ slot.label,
163
+ slot.offsetLabel && /* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__tg-offset", children: slot.offsetLabel })
164
+ ]
165
+ },
166
+ slot.instant.getTime()
167
+ ));
168
+ const movable = (entry) => moving && entry.movable !== false;
169
+ const live = useRef({ ghost, entryById, timeZone, step, onProposeMove, onSelectEntry, labels, formatInstant, days, columns, movable });
170
+ live.current = { ghost, entryById, timeZone, step, onProposeMove, onSelectEntry, labels, formatInstant, days, columns, movable };
171
+ const dragRef = useRef(null);
172
+ const suppressClick = useRef(false);
173
+ const refocus = useRef(null);
174
+ useEffect(() => {
175
+ if (ghost && !entryById.has(ghost.id)) setGhost(null);
176
+ }, [ghost, entryById]);
177
+ useEffect(() => {
178
+ const id = refocus.current;
179
+ const root = rootRef.current;
180
+ if (!id || !root) return;
181
+ const active = document.activeElement;
182
+ const lost = !active || active === document.body;
183
+ if (!lost) {
184
+ if (active.getAttribute("data-item-id") !== id) refocus.current = null;
185
+ return;
186
+ }
187
+ for (const candidate of Array.from(root.querySelectorAll("[data-item-id]"))) {
188
+ if (candidate.getAttribute("data-item-id") === id) {
189
+ candidate.focus();
190
+ break;
191
+ }
192
+ }
193
+ });
194
+ const send = useCallback((id, delta) => {
195
+ const state = live.current;
196
+ const entry = state.entryById.get(id);
197
+ if (!entry || !state.onProposeMove || !state.movable(entry) || delta.days === 0 && delta.minutes === 0) {
198
+ setGhost(null);
199
+ return;
200
+ }
201
+ const proposal = proposeMove(entry, delta, state.timeZone);
202
+ const result = state.onProposeMove(id, proposal);
203
+ if (result && typeof result.then === "function") {
204
+ setGhost({ id, delta, source: "sent", proposal });
205
+ const clear = () => setGhost((current) => current && current.source === "sent" && current.proposal === proposal ? null : current);
206
+ result.then(clear, clear);
207
+ } else setGhost(null);
208
+ }, []);
209
+ const handlers = useMemo(() => ({
210
+ click: (entry) => {
211
+ live.current.onSelectEntry?.(entry);
212
+ },
213
+ keyDown: (event, entry) => {
214
+ const state = live.current;
215
+ const pending = state.ghost && state.ghost.id === entry.id && state.ghost.source === "keys" ? state.ghost.delta : null;
216
+ if (event.key === "Enter" && pending) {
217
+ event.preventDefault();
218
+ refocus.current = entry.id;
219
+ send(entry.id, pending);
220
+ return;
221
+ }
222
+ if (event.key === "Escape" && pending) {
223
+ event.preventDefault();
224
+ event.stopPropagation();
225
+ setGhost(null);
226
+ setAnnouncement(state.labels.moveCancelled);
227
+ return;
228
+ }
229
+ if (!event.shiftKey) return;
230
+ const base = pending ?? { days: 0, minutes: 0 };
231
+ const next = event.key === "ArrowUp" ? { ...base, minutes: base.minutes - state.step } : event.key === "ArrowDown" ? { ...base, minutes: base.minutes + state.step } : event.key === "ArrowLeft" ? { ...base, days: base.days - 1 } : event.key === "ArrowRight" ? { ...base, days: base.days + 1 } : null;
232
+ if (!next) return;
233
+ event.preventDefault();
234
+ if (next.days === 0 && next.minutes === 0) {
235
+ setGhost(null);
236
+ setAnnouncement(state.labels.moveCancelled);
237
+ return;
238
+ }
239
+ const proposal = proposeMove(entry, next, state.timeZone);
240
+ setGhost({ id: entry.id, delta: next, source: "keys", proposal });
241
+ const proposed = fillLabel(state.labels.moveProposed, { start: state.formatInstant(proposal.start), end: state.formatInstant(proposal.end) });
242
+ setAnnouncement(proposal.adjusted ? `${fillLabel(state.labels.gapForward, { time: zonedTimeOfDay(proposal.start, state.timeZone) })} ${proposed}` : proposed);
243
+ },
244
+ blur: (entry) => {
245
+ refocus.current = null;
246
+ setGhost((current) => current && current.id === entry.id && current.source === "keys" ? null : current);
247
+ },
248
+ pointerDown: (event, entry, column, top2) => {
249
+ if (event.button !== 0 || dragRef.current) return;
250
+ const root = rootRef.current;
251
+ if (!root) return;
252
+ const elements = Array.from(root.querySelectorAll("[data-tg-column]"));
253
+ const rects = elements.map((element) => element.getBoundingClientRect());
254
+ const under = rects.findIndex((rect) => event.clientX >= rect.left && event.clientX < rect.right);
255
+ const from = under === -1 ? column : under;
256
+ const hours = live.current.columns[from]?.dayHours ?? 1;
257
+ const finish = (commit) => {
258
+ const drag = dragRef.current;
259
+ if (!drag) return;
260
+ drag.stop();
261
+ dragRef.current = null;
262
+ if (!drag.moved) return;
263
+ suppressClick.current = true;
264
+ setTimeout(() => {
265
+ suppressClick.current = false;
266
+ }, 0);
267
+ if (commit && !drag.dropped) send(drag.id, drag.delta);
268
+ else setGhost(null);
269
+ };
270
+ const onMove = (move) => {
271
+ const drag = dragRef.current;
272
+ if (!drag || move.pointerId !== drag.pointerId) return;
273
+ if (move.buttons === 0 && move.pointerType === "mouse") {
274
+ finish(false);
275
+ return;
276
+ }
277
+ if (drag.dropped) return;
278
+ const dx = move.clientX - drag.x;
279
+ const dy = move.clientY - drag.y;
280
+ if (!drag.moved && Math.hypot(dx, dy) < DRAG_THRESHOLD) return;
281
+ drag.moved = true;
282
+ const target = drag.rects.findIndex((rect) => move.clientX >= rect.left && move.clientX < rect.right);
283
+ const stepMinutes = live.current.step;
284
+ const delta = {
285
+ days: target === -1 ? drag.delta.days : target - drag.column,
286
+ minutes: drag.top || drag.hourPx <= 0 ? 0 : Math.round(dy / drag.hourPx * MINUTES_PER_HOUR / stepMinutes) * stepMinutes
287
+ };
288
+ if (delta.days === drag.delta.days && delta.minutes === drag.delta.minutes) return;
289
+ drag.delta = delta;
290
+ const dragged = live.current.entryById.get(drag.id);
291
+ if (dragged) setGhost({ id: drag.id, delta, source: "pointer", proposal: proposeMove(dragged, delta, live.current.timeZone) });
292
+ };
293
+ const onUp = (up) => {
294
+ if (up.pointerId === dragRef.current?.pointerId) finish(true);
295
+ };
296
+ const onCancel = (cancel) => {
297
+ if (cancel.pointerId === dragRef.current?.pointerId) finish(false);
298
+ };
299
+ const onKey = (key) => {
300
+ const drag = dragRef.current;
301
+ if (key.key !== "Escape" || !drag?.moved || drag.dropped) return;
302
+ key.preventDefault();
303
+ key.stopPropagation();
304
+ drag.dropped = true;
305
+ setGhost(null);
306
+ };
307
+ window.addEventListener("pointermove", onMove);
308
+ window.addEventListener("pointerup", onUp);
309
+ window.addEventListener("pointercancel", onCancel);
310
+ window.addEventListener("keydown", onKey, true);
311
+ dragRef.current = {
312
+ id: entry.id,
313
+ pointerId: event.pointerId,
314
+ x: event.clientX,
315
+ y: event.clientY,
316
+ column: from,
317
+ top: top2,
318
+ moved: false,
319
+ dropped: false,
320
+ rects,
321
+ hourPx: (rects[from]?.height ?? 0) / hours,
322
+ delta: { days: 0, minutes: 0 },
323
+ stop: () => {
324
+ window.removeEventListener("pointermove", onMove);
325
+ window.removeEventListener("pointerup", onUp);
326
+ window.removeEventListener("pointercancel", onCancel);
327
+ window.removeEventListener("keydown", onKey, true);
328
+ }
329
+ };
330
+ }
331
+ }), [send]);
332
+ useEffect(() => () => {
333
+ dragRef.current?.stop();
334
+ dragRef.current = null;
335
+ }, []);
336
+ const gridColumns = (span) => `${span.startCol + 2} / ${span.endCol + 3}`;
337
+ const nowAt = now === void 0 ? null : new Date(now).getTime();
338
+ const nowColumn = nowAt === null || Number.isNaN(nowAt) ? -1 : days.indexOf(zonedDateKey(new Date(nowAt), timeZone));
339
+ const topHiddenOn = (date) => top.hidden.filter((id) => {
340
+ const entry = entryById.get(id);
341
+ return entry ? layoutDaySpans([entry], [date], { timeZone }).placed.length > 0 : false;
342
+ });
343
+ const windowLanes = lanesIn(strip.placed);
344
+ const topLanes = lanesIn(top.placed);
345
+ const moreWindows = strip.hidden.length;
346
+ const firstHiddenWindowDay = strip.firstHiddenDay ?? days[0];
347
+ const ghostEntry = ghost ? entryById.get(ghost.id) : void 0;
348
+ const ghostTop = ghostEntry ? placesInTopLane(ghostEntry, timeZone, laneOptions) : false;
349
+ const ghostParts = ghost && ghostEntry && !ghostTop ? days.map((date) => layoutTimeGridDay([{ id: "ghost", start: ghost.proposal.start, end: ghost.proposal.end }], date, timeZone, { crossMidnightMinutes: Infinity }).segments) : null;
350
+ const ghostSpan = ghost && ghostEntry && ghostTop ? layoutDaySpans([{ id: "ghost", start: ghost.proposal.start, end: ghost.proposal.end }], days, { timeZone }).placed[0] : void 0;
351
+ const ghostLane = ghost ? top.placed.find((span) => span.id === ghost.id)?.lane ?? 0 : 0;
352
+ return /* @__PURE__ */ jsxs(
353
+ "div",
354
+ {
355
+ ref: rootRef,
356
+ className: "uix-scheduling-calendar__timegrid",
357
+ "data-view": view,
358
+ role: "group",
359
+ "aria-label": gridLabel,
360
+ style: vars({ "--uix-scheduling-calendar-days": days.length, "--uix-scheduling-calendar-hours": maxHours }),
361
+ onClickCapture: (event) => {
362
+ if (suppressClick.current) {
363
+ suppressClick.current = false;
364
+ event.preventDefault();
365
+ event.stopPropagation();
366
+ }
367
+ },
368
+ children: [
369
+ /* @__PURE__ */ jsxs("div", { className: "uix-scheduling-calendar__tg-head", children: [
370
+ /* @__PURE__ */ jsx("div", { className: "uix-scheduling-calendar__tg-zone", children: fillLabel(labels.timeZone, { timeZone }) }),
371
+ days.map((date, index) => {
372
+ const info = dayInfo?.[date];
373
+ const hiddenIds = dayInfo ? [] : [...columns[index].hidden, ...topHiddenOn(date)];
374
+ const more = info ? info.overflowCount : hiddenIds.length;
375
+ const moreText = fillLabel(labels.moreSpans, { count: more });
376
+ const moreName = fillLabel(labels.moreEntriesLabel, { count: more, date: dateText(date, "day") });
377
+ const headName = info ? info.label ?? `${dateText(date, "day")}, ${fillLabel(labels.dayCount, { count: info.count })}` : dateText(date, "day");
378
+ return /* @__PURE__ */ jsxs("div", { className: "uix-scheduling-calendar__tg-dayhead", role: "group", "aria-label": headName, "data-date": date, children: [
379
+ /* @__PURE__ */ jsx("button", { type: "button", className: "uix-scheduling-calendar__date", "data-calendar-date": date, "aria-label": dateText(date, "day"), onClick: onSelectDate ? () => onSelectDate(date) : void 0, children: dateText(date, "column") }),
380
+ info && /* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__count", "aria-hidden": "true", children: info.count }),
381
+ info?.markers && info.markers.length > 0 && /* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__markers", children: info.markers.map((marker) => /* @__PURE__ */ jsx("span", { children: renderMarker(marker, true) }, marker.id)) }),
382
+ more > 0 && (onShowMore ? /* @__PURE__ */ jsx("button", { type: "button", className: "uix-scheduling-calendar__more", "aria-label": moreName, onClick: () => onShowMore(date, hiddenIds.map((id) => entryById.get(id)).filter(Boolean)), children: moreText }) : /* @__PURE__ */ jsxs("span", { className: "uix-scheduling-calendar__more", children: [
383
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: moreText }),
384
+ /* @__PURE__ */ jsx("span", { className: "uix-visually-hidden", children: moreName })
385
+ ] }))
386
+ ] }, date);
387
+ })
388
+ ] }),
389
+ (windowLanes > 0 || moreWindows > 0) && /* @__PURE__ */ jsxs("div", { className: "uix-scheduling-calendar__tg-strip", role: "group", "aria-label": labels.windows, style: vars({ "--uix-scheduling-calendar-lanes": Math.max(windowLanes, 1) }), children: [
390
+ strip.placed.map((placed) => {
391
+ const overlay = overlayById.get(placed.id);
392
+ if (!overlay) return null;
393
+ return /* @__PURE__ */ jsx(
394
+ "button",
395
+ {
396
+ type: "button",
397
+ className: "uix-scheduling-calendar__window",
398
+ "data-overlay-id": overlay.id,
399
+ "data-pattern": overlay.pattern ?? "solid",
400
+ "data-global": overlay.global || void 0,
401
+ "data-kind": overlay.kind,
402
+ "data-continues-before": placed.continuesBefore || void 0,
403
+ "data-continues-after": placed.continuesAfter || void 0,
404
+ style: { gridColumn: gridColumns(placed), ...vars({ "--uix-scheduling-calendar-lane": placed.lane }) },
405
+ onClick: () => onSelectOverlay?.(overlay),
406
+ "aria-label": overlayName(overlay),
407
+ children: /* @__PURE__ */ jsxs("span", { className: "uix-scheduling-calendar__window-text", children: [
408
+ overlay.kindLabel && /* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__window-kind", children: overlay.kindLabel }),
409
+ /* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__window-name", children: overlay.label }),
410
+ overlay.scopeLabel && /* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__window-scope", children: overlay.scopeLabel })
411
+ ] })
412
+ },
413
+ placed.id
414
+ );
415
+ }),
416
+ moreWindows > 0 && (onShowMore ? /* @__PURE__ */ jsx("button", { type: "button", className: "uix-scheduling-calendar__rowmore", "aria-label": fillLabel(labels.moreWindowsLabel, { count: moreWindows, date: dateText(firstHiddenWindowDay, "day") }), onClick: () => onShowMore(firstHiddenWindowDay, []), children: fillLabel(labels.moreWindows, { count: moreWindows }) }) : /* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__rowmore", children: fillLabel(labels.moreWindows, { count: moreWindows }) }))
417
+ ] }),
418
+ (topLanes > 0 || ghostSpan) && /* @__PURE__ */ jsxs("div", { className: "uix-scheduling-calendar__tg-strip uix-scheduling-calendar__tg-top", role: "group", "aria-label": labels.topLane, style: vars({ "--uix-scheduling-calendar-lanes": Math.max(topLanes, 1) }), children: [
419
+ top.placed.map((placed) => {
420
+ const entry = entryById.get(placed.id);
421
+ if (!entry) return null;
422
+ const canDrag = movable(entry);
423
+ const markers = entryMarkers(entry);
424
+ return /* @__PURE__ */ jsxs(
425
+ "button",
426
+ {
427
+ type: "button",
428
+ className: "uix-scheduling-calendar__entry uix-scheduling-calendar__tg-span",
429
+ "data-item-id": entry.id,
430
+ "data-band": entry.band ?? "none",
431
+ "data-status": entryStatus(entry),
432
+ "data-state": entry.state,
433
+ "data-movable": canDrag || void 0,
434
+ "data-continues-before": placed.continuesBefore || void 0,
435
+ "data-continues-after": placed.continuesAfter || void 0,
436
+ style: { gridColumn: gridColumns(placed), ...vars({ "--uix-scheduling-calendar-lane": placed.lane }) },
437
+ "aria-label": entryName(entry),
438
+ "aria-describedby": canDrag ? hintId : void 0,
439
+ onClick: () => handlers.click(entry),
440
+ onKeyDown: canDrag ? (event) => handlers.keyDown(event, entry) : void 0,
441
+ onBlur: canDrag ? () => handlers.blur(entry) : void 0,
442
+ onPointerDown: canDrag ? (event) => handlers.pointerDown(event, entry, placed.startCol, true) : void 0,
443
+ children: [
444
+ /* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__entry-text", children: renderEntry?.(entry, { availableLines: 1 }) ?? /* @__PURE__ */ jsxs(Fragment, { children: [
445
+ !placed.continuesBefore && !entry.allDay && /* @__PURE__ */ jsxs(Fragment, { children: [
446
+ /* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__time", children: zonedTimeOfDay(entry.start, timeZone) }),
447
+ " "
448
+ ] }),
449
+ /* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__title", children: entry.title })
450
+ ] }) }),
451
+ markers.length > 0 && /* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__markers", children: markers.map((marker) => /* @__PURE__ */ jsx("span", { children: renderMarker(marker) }, marker.id)) })
452
+ ]
453
+ },
454
+ placed.id
455
+ );
456
+ }),
457
+ ghostSpan && /* @__PURE__ */ jsx("div", { className: "uix-scheduling-calendar__tg-ghost", "aria-hidden": "true", "data-sent": ghost?.source === "sent" || void 0, style: { gridColumn: gridColumns(ghostSpan), ...vars({ "--uix-scheduling-calendar-lane": ghostLane }) } })
458
+ ] }),
459
+ /* @__PURE__ */ jsxs("div", { className: "uix-scheduling-calendar__tg-body", children: [
460
+ /* @__PURE__ */ jsx("div", { className: "uix-scheduling-calendar__tg-gutter", "aria-hidden": "true", children: hourLabels(gutterIndex, false) }),
461
+ shades.map((placed) => {
462
+ const overlay = overlayById.get(placed.id);
463
+ const first = columns[placed.startCol];
464
+ const last = columns[placed.endCol];
465
+ const from = placed.continuesBefore ? 0 : Math.max(0, (new Date(overlay.start).getTime() - first.dayStart) / HOUR_MS) / maxHours;
466
+ const to = (placed.continuesAfter ? last.dayHours : Math.min(last.dayHours, (new Date(overlay.end).getTime() - last.dayStart) / HOUR_MS)) / maxHours;
467
+ const width = 100 / (placed.endCol - placed.startCol + 1);
468
+ const percent = (value) => `${Math.round(value * 1e4) / 100}%`;
469
+ const clipPath = placed.startCol === placed.endCol ? `polygon(0 ${percent(from)}, 100% ${percent(from)}, 100% ${percent(to)}, 0 ${percent(to)})` : `polygon(0 ${percent(from)}, ${width}% ${percent(from)}, ${width}% 0, 100% 0, 100% ${percent(to)}, ${100 - width}% ${percent(to)}, ${100 - width}% 100%, 0 100%)`;
470
+ return /* @__PURE__ */ jsx("div", { className: "uix-scheduling-calendar__tg-shade", "aria-hidden": "true", "data-pattern": overlay.pattern ?? "solid", style: { gridColumn: gridColumns(placed), clipPath } }, `shade-${placed.id}`);
471
+ }),
472
+ columns.map((column, index) => /* @__PURE__ */ jsxs("div", { className: "uix-scheduling-calendar__tg-column", "data-tg-column": column.date, style: { gridColumn: `${index + 2}`, ...vars({ "--uix-scheduling-calendar-hours": column.dayHours }) }, children: [
473
+ slotSignature(slots[index]) !== gutterSignature && hourLabels(index, true),
474
+ column.segments.map((segment) => {
475
+ const entry = entryById.get(segment.id);
476
+ const alone = segment.part === "continuation" && index === 0;
477
+ return /* @__PURE__ */ jsx(
478
+ GridItem,
479
+ {
480
+ entry,
481
+ segment,
482
+ column: index,
483
+ alone,
484
+ name: entryName(entry),
485
+ status: entryStatus(entry),
486
+ movable: movable(entry),
487
+ hintId,
488
+ timeZone,
489
+ continuesFrom: fillLabel(labels.continuesFrom, { time: zonedTimeOfDay(entry.start, timeZone) }),
490
+ markers: entryMarkers(entry),
491
+ renderEntry,
492
+ renderMarker,
493
+ handlers
494
+ },
495
+ `${segment.id}-${segment.part}`
496
+ );
497
+ }),
498
+ ghostParts?.[index].map((segment) => /* @__PURE__ */ jsx(
499
+ "div",
500
+ {
501
+ className: "uix-scheduling-calendar__tg-ghost",
502
+ "aria-hidden": "true",
503
+ "data-sent": ghost?.source === "sent" || void 0,
504
+ style: vars({ "--uix-scheduling-calendar-offset": segment.offsetHours, "--uix-scheduling-calendar-length": segment.lengthHours })
505
+ },
506
+ `ghost-${segment.part}`
507
+ )),
508
+ index === nowColumn && nowAt !== null && /* @__PURE__ */ jsx("div", { className: "uix-scheduling-calendar__tg-now", "aria-hidden": "true", style: vars({ "--uix-scheduling-calendar-offset": (nowAt - column.dayStart) / HOUR_MS }) })
509
+ ] }, column.date))
510
+ ] }),
511
+ emptyNote != null && emptyNote !== false && nothingToShow && /* @__PURE__ */ jsx("div", { className: "uix-scheduling-calendar__empty", children: emptyNote }),
512
+ moving && /* @__PURE__ */ jsx("p", { id: hintId, className: "uix-visually-hidden", children: labels.moveHint }),
513
+ /* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__tg-probe", "aria-hidden": "true", children: "00:00 abcdef" }),
514
+ /* @__PURE__ */ jsx("div", { className: "uix-visually-hidden", "aria-live": "polite", children: announcement })
515
+ ]
516
+ }
517
+ );
518
+ }
519
+ export {
520
+ SchedulingTimeGrid
521
+ };
@@ -1,7 +1,7 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
3
  import { TimelineRange, TimelineScale } from '../scheduling-timeline-model.js';
4
- import { SchedulingEntryState } from './SchedulingCalendar.js';
4
+ import { j as SchedulingEntryState } from '../SchedulingCalendar-DSLQ6AY5.js';
5
5
  import '../calendar-model.js';
6
6
  import '../scheduling-calendar-model.js';
7
7