@tensor_1/react 2.33.0 → 2.35.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 +48 -0
- package/dist/{SchedulingCalendar-C4XVbqZr.d.ts → SchedulingCalendar-B6TakO2h.d.ts} +52 -3
- package/dist/components/List.d.ts +8 -1
- package/dist/components/List.js +3 -1
- package/dist/components/ListRoving.d.ts +12 -0
- package/dist/components/ListRoving.js +51 -0
- package/dist/components/SchedulingAgenda.d.ts +31 -0
- package/dist/components/SchedulingAgenda.js +258 -0
- package/dist/components/SchedulingCalendar.d.ts +1 -1
- package/dist/components/SchedulingCalendar.js +131 -18
- package/dist/components/SchedulingTimeGrid.d.ts +1 -1
- package/dist/components/SchedulingTimeGrid.js +101 -4
- package/dist/components/SchedulingTimeline.d.ts +1 -1
- package/dist/index.cjs +1977 -1454
- package/dist/index.d.cts +60 -4
- package/dist/index.d.ts +1 -1
- package/dist/labels-context.d.ts +1 -1
- package/dist/roving.d.ts +15 -0
- package/dist/roving.js +17 -0
- package/package.json +1 -1
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
4
|
+
import { rovingStep, syncRovingStop } from "../roving.js";
|
|
4
5
|
import { addCalendarDays, addCalendarMonths, buildMonthGrid, cachedDateTimeFormat, startOfMonth, zonedDateKey, zonedTimeOfDay } from "../calendar-model.js";
|
|
5
6
|
import { TOP_LANE_CROSS_MIDNIGHT_MINUTES, itemDaySpan, layoutMonthSpans } from "../scheduling-calendar-model.js";
|
|
7
|
+
import { SchedulingAgenda } from "./SchedulingAgenda.js";
|
|
6
8
|
import { SchedulingTimeGrid } from "./SchedulingTimeGrid.js";
|
|
7
9
|
import { cx } from "../cx.js";
|
|
8
10
|
import { useUixLabels } from "../labels-context.js";
|
|
@@ -36,6 +38,7 @@ const DEFAULT_SCHEDULING_CALENDAR_LABELS = {
|
|
|
36
38
|
fewerEntries: "Show fewer",
|
|
37
39
|
moreSpans: "+{count}",
|
|
38
40
|
moreSpansLabel: "{count} more spanning this week, from {date}",
|
|
41
|
+
dayName: "{date}, {label}",
|
|
39
42
|
dayCount: "{count} entries",
|
|
40
43
|
states: { scheduled: "Scheduled", conflicted: "Conflicted", "in-progress": "In progress", "blackout-violation": "Blackout violation" },
|
|
41
44
|
statuses: { tentative: "Tentative", committed: "Scheduled", live: "In progress", done: "Done", dead: "Cancelled" },
|
|
@@ -45,6 +48,9 @@ const DEFAULT_SCHEDULING_CALENDAR_LABELS = {
|
|
|
45
48
|
moreWindowsLabel: "{count} more windows, from {date}",
|
|
46
49
|
topLane: "All-day and longer entries",
|
|
47
50
|
continuesFrom: "from {time}",
|
|
51
|
+
allDay: "All day",
|
|
52
|
+
hiddenInDay: "{count} not shown \u2014 open day",
|
|
53
|
+
continuesInDay: "Continues: {count} listed under an earlier day",
|
|
48
54
|
moveHint: "Hold Shift and press an arrow key to move it. Enter confirms, Escape cancels.",
|
|
49
55
|
moveProposed: "Move to {start} \u2013 {end}. Enter confirms, Escape cancels.",
|
|
50
56
|
moveCancelled: "Move cancelled.",
|
|
@@ -60,6 +66,7 @@ const INTL_PARTS = {
|
|
|
60
66
|
};
|
|
61
67
|
const EMPTY_OVERLAYS = [];
|
|
62
68
|
const EMPTY_ENTRIES = [];
|
|
69
|
+
const EMPTY_IDS = [];
|
|
63
70
|
const DEFAULT_LANE_CAP = 2;
|
|
64
71
|
const DEFAULT_CHIP_ROWS = 3;
|
|
65
72
|
function coveredDays(start, end, timeZone) {
|
|
@@ -107,6 +114,10 @@ function SchedulingCalendar({
|
|
|
107
114
|
notice,
|
|
108
115
|
emptyNote,
|
|
109
116
|
onSelectDate,
|
|
117
|
+
agendaGroups,
|
|
118
|
+
agendaHeadingLevel = 3,
|
|
119
|
+
virtualizeAbove = 200,
|
|
120
|
+
monthDensity = "full",
|
|
110
121
|
timeGrid = false,
|
|
111
122
|
now,
|
|
112
123
|
canMove = false,
|
|
@@ -140,7 +151,8 @@ function SchedulingCalendar({
|
|
|
140
151
|
const gridRef = useRef(null);
|
|
141
152
|
const picksGiven = dayEntries !== void 0;
|
|
142
153
|
const controlled = picksGiven || dayInfo !== void 0;
|
|
143
|
-
const
|
|
154
|
+
const countsOnly = monthDensity === "counts" && view === "month";
|
|
155
|
+
const fixed = controlled || maxEntriesPerDay !== void 0 && onShowMore !== void 0 || countsOnly;
|
|
144
156
|
const windowCap = Math.max(0, windowLaneCap ?? (fixed ? DEFAULT_LANE_CAP : Infinity));
|
|
145
157
|
const spanCap = Math.max(0, spanLaneCap ?? (fixed ? DEFAULT_LANE_CAP : Infinity));
|
|
146
158
|
const visibleEntries = useMemo(() => entries.filter((entry) => filter?.(entry) ?? true), [entries, filter]);
|
|
@@ -178,13 +190,13 @@ function SchedulingCalendar({
|
|
|
178
190
|
windowIds.add(overlay.id);
|
|
179
191
|
inputs.push({ id: overlay.id, start: overlay.start, end: overlay.end, group: "window" });
|
|
180
192
|
}
|
|
181
|
-
for (const { entry } of multiDay) {
|
|
193
|
+
for (const { entry } of countsOnly ? [] : multiDay) {
|
|
182
194
|
if (itemIds.has(entry.id)) continue;
|
|
183
195
|
itemIds.add(entry.id);
|
|
184
196
|
inputs.push({ id: entry.id, start: entry.start, end: entry.end, group: "item" });
|
|
185
197
|
}
|
|
186
198
|
return layoutMonthSpans(inputs, days, { timeZone, laneCap: { window: windowCap, item: spanCap } });
|
|
187
|
-
}, [spanLayout, view, timeGridView, overlays, multiDay, days, timeZone, windowCap, spanCap]);
|
|
199
|
+
}, [spanLayout, view, timeGridView, overlays, multiDay, days, timeZone, windowCap, spanCap, countsOnly]);
|
|
188
200
|
const placedByRow = useMemo(() => {
|
|
189
201
|
const rows = /* @__PURE__ */ new Map();
|
|
190
202
|
for (const placed of layout.placed) {
|
|
@@ -212,6 +224,31 @@ function SchedulingCalendar({
|
|
|
212
224
|
setActiveDate(date);
|
|
213
225
|
requestAnimationFrame(() => gridRef.current?.querySelector(`[data-calendar-date="${date}"]`)?.focus());
|
|
214
226
|
};
|
|
227
|
+
const enteredFrom = useRef(null);
|
|
228
|
+
const cellOf = (date) => gridRef.current?.querySelector(`[data-calendar-date="${date}"]`)?.closest(".uix-scheduling-calendar__day") ?? null;
|
|
229
|
+
const dayRing = (date) => {
|
|
230
|
+
const cell = cellOf(date);
|
|
231
|
+
const week = cell?.parentElement;
|
|
232
|
+
if (!cell || !week) return [];
|
|
233
|
+
const column = Array.from(week.children).indexOf(cell) + 1;
|
|
234
|
+
const covers = (element) => {
|
|
235
|
+
const [from, to] = element.style.gridColumn.split("/").map((part) => Number(part.trim()));
|
|
236
|
+
return from !== void 0 && to !== void 0 && from <= column && column < to;
|
|
237
|
+
};
|
|
238
|
+
const direct = Array.from(week.children);
|
|
239
|
+
return [
|
|
240
|
+
...direct.filter((element) => element.matches("button.uix-scheduling-calendar__window, button.uix-scheduling-calendar__span") && covers(element)),
|
|
241
|
+
...Array.from(cell.querySelectorAll(".uix-scheduling-calendar__entries button")),
|
|
242
|
+
// The row's "+N" opens one day; it is reached from that day.
|
|
243
|
+
...direct.filter((element) => element.matches("button.uix-scheduling-calendar__rowmore") && element.getAttribute("data-more-date") === date)
|
|
244
|
+
];
|
|
245
|
+
};
|
|
246
|
+
const dayOfItem = (item) => {
|
|
247
|
+
if (enteredFrom.current && dayRing(enteredFrom.current).includes(item)) return enteredFrom.current;
|
|
248
|
+
const week = item.closest(".uix-scheduling-calendar__week");
|
|
249
|
+
const cell = item.closest(".uix-scheduling-calendar__day") ?? (week ? Array.from(week.children)[Number(item.style.gridColumn.split("/")[0]) - 1] ?? null : null);
|
|
250
|
+
return item.getAttribute("data-more-date") ?? cell?.querySelector("[data-calendar-date]")?.getAttribute("data-calendar-date") ?? null;
|
|
251
|
+
};
|
|
215
252
|
const onDayKeyDown = (event, date) => {
|
|
216
253
|
const offset = { ArrowLeft: -1, ArrowRight: 1, ArrowUp: -7, ArrowDown: 7 }[event.key];
|
|
217
254
|
if (offset) {
|
|
@@ -223,8 +260,57 @@ function SchedulingCalendar({
|
|
|
223
260
|
} else if (event.key === "End") {
|
|
224
261
|
event.preventDefault();
|
|
225
262
|
focusDate(days[days.length - 1]);
|
|
263
|
+
} else if (event.key === "Enter") {
|
|
264
|
+
const [first] = dayRing(date);
|
|
265
|
+
if (!first) return;
|
|
266
|
+
event.preventDefault();
|
|
267
|
+
enteredFrom.current = date;
|
|
268
|
+
first.focus();
|
|
226
269
|
}
|
|
227
270
|
};
|
|
271
|
+
const onGridKeyDown = (event) => {
|
|
272
|
+
const item = event.target;
|
|
273
|
+
if (event.defaultPrevented || item.hasAttribute("data-calendar-date") || !["ArrowDown", "ArrowUp", "Home", "End", "Escape"].includes(event.key)) return;
|
|
274
|
+
const date = dayOfItem(item);
|
|
275
|
+
if (!date) return;
|
|
276
|
+
const ring = dayRing(date);
|
|
277
|
+
const index = ring.indexOf(item);
|
|
278
|
+
if (index === -1) return;
|
|
279
|
+
event.preventDefault();
|
|
280
|
+
if (event.key === "Escape") {
|
|
281
|
+
event.stopPropagation();
|
|
282
|
+
enteredFrom.current = null;
|
|
283
|
+
setActiveDate(date);
|
|
284
|
+
gridRef.current?.querySelector(`[data-calendar-date="${date}"]`)?.focus();
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
enteredFrom.current = date;
|
|
288
|
+
const next = event.key === "Home" ? 0 : event.key === "End" ? ring.length - 1 : index + (event.key === "ArrowDown" ? 1 : -1);
|
|
289
|
+
ring[Math.max(0, Math.min(ring.length - 1, next))]?.focus();
|
|
290
|
+
};
|
|
291
|
+
const onGridFocus = (event) => {
|
|
292
|
+
const item = event.target;
|
|
293
|
+
if (item.hasAttribute("data-calendar-date")) return;
|
|
294
|
+
const date = dayOfItem(item);
|
|
295
|
+
if (date && dayRing(date).includes(item)) setActiveDate(date);
|
|
296
|
+
};
|
|
297
|
+
const flatAgendaRef = useRef(null);
|
|
298
|
+
const flatAgendaStop = useRef({ node: null, index: 0 });
|
|
299
|
+
const flatAgendaRows = () => Array.from(flatAgendaRef.current?.querySelectorAll(":scope > ol > li > button") ?? []);
|
|
300
|
+
useEffect(() => {
|
|
301
|
+
flatAgendaStop.current = syncRovingStop(flatAgendaRows(), flatAgendaStop.current);
|
|
302
|
+
});
|
|
303
|
+
const onFlatAgendaFocus = (event) => {
|
|
304
|
+
flatAgendaStop.current = syncRovingStop(flatAgendaRows(), flatAgendaStop.current, event.target);
|
|
305
|
+
};
|
|
306
|
+
const onFlatAgendaKeyDown = (event) => {
|
|
307
|
+
if (event.defaultPrevented) return;
|
|
308
|
+
const next = rovingStep(flatAgendaRows(), event.target, event.key);
|
|
309
|
+
if (!next) return;
|
|
310
|
+
event.preventDefault();
|
|
311
|
+
flatAgendaStop.current = syncRovingStop(flatAgendaRows(), flatAgendaStop.current, next);
|
|
312
|
+
next.focus();
|
|
313
|
+
};
|
|
228
314
|
const entriesOn = (date) => coverage.filter((covered) => date >= covered.start && date < covered.end).map((covered) => covered.entry);
|
|
229
315
|
const toggleMore = (date, all) => {
|
|
230
316
|
if (onShowMore) {
|
|
@@ -246,12 +332,16 @@ function SchedulingCalendar({
|
|
|
246
332
|
const stateText = (entry) => entry.state ? stateLabel[entry.state] : entry.status ? labels.statuses[entry.status] : stateLabel.scheduled;
|
|
247
333
|
const entryName = (entry) => entry.accessibleName ?? [fillLabel(labels.entry, { title: entry.title, state: stateText(entry), start: formatInstant(entry.start), end: formatInstant(entry.end) }), ...(entry.markers ?? []).map((marker) => marker.label)].join(", ");
|
|
248
334
|
const overlayName = (overlay) => overlay.accessibleName ?? fillLabel(labels.overlay, { title: [overlay.kindLabel, overlay.label, overlay.scopeLabel].filter(Boolean).join(", "), start: formatInstant(overlay.start), end: formatInstant(overlay.end) });
|
|
335
|
+
const emphasisOf = (entry) => ({ "data-highlight": entry.emphasis === "highlight" || void 0, "data-dim": entry.emphasis === "dim" || void 0 });
|
|
249
336
|
const entryProps = (entry) => ({
|
|
250
337
|
type: "button",
|
|
338
|
+
// Reached from its day with Enter, never with Tab.
|
|
339
|
+
tabIndex: -1,
|
|
251
340
|
"data-item-id": entry.id,
|
|
252
341
|
"data-band": entry.band ?? "none",
|
|
253
342
|
"data-status": statusOf(entry),
|
|
254
343
|
"data-state": entry.state,
|
|
344
|
+
...emphasisOf(entry),
|
|
255
345
|
onClick: () => onSelectEntry?.(entry),
|
|
256
346
|
"aria-label": entryName(entry)
|
|
257
347
|
});
|
|
@@ -273,24 +363,24 @@ function SchedulingCalendar({
|
|
|
273
363
|
["--uix-scheduling-calendar-lane"]: placed.group === "window" ? placed.lane : windowLanes + placed.lane
|
|
274
364
|
});
|
|
275
365
|
const nothingToShow = visibleEntries.length === 0 && !Object.values(dayEntries ?? {}).some((list) => list.length > 0) && !Object.values(dayInfo ?? {}).some((day) => day.count > 0);
|
|
276
|
-
const chipRows = Math.max(0, maxEntriesPerDay ?? DEFAULT_CHIP_ROWS, ...Object.values(dayEntries ?? {}).map((list) => list.length));
|
|
366
|
+
const chipRows = countsOnly ? -1 : Math.max(0, maxEntriesPerDay ?? DEFAULT_CHIP_ROWS, ...Object.values(dayEntries ?? {}).map((list) => list.length));
|
|
277
367
|
const renderDay = (date, index) => {
|
|
278
368
|
const info = dayInfo?.[date];
|
|
279
369
|
const all = picksGiven ? dayEntries[date] ?? EMPTY_ENTRIES : singles.get(date) ?? EMPTY_ENTRIES;
|
|
280
370
|
const overflowing = !picksGiven && maxEntriesPerDay !== void 0 && all.length > maxEntriesPerDay;
|
|
281
371
|
const collapsed = overflowing && (controlled || onShowMore !== void 0 || !expandedDates.has(date));
|
|
282
|
-
const shown = collapsed ? all.slice(0, Math.max(0, maxEntriesPerDay)) : all;
|
|
372
|
+
const shown = countsOnly ? EMPTY_ENTRIES : collapsed ? all.slice(0, Math.max(0, maxEntriesPerDay)) : all;
|
|
283
373
|
const hiddenCount = info ? info.overflowCount : picksGiven ? 0 : all.length - shown.length;
|
|
284
374
|
const onDay = picksGiven ? all : entriesOn(date);
|
|
285
375
|
const badge = renderDayBadge?.(date, onDay);
|
|
286
376
|
const hasBadge = badge != null && badge !== false;
|
|
287
|
-
const dateButton = /* @__PURE__ */ jsx("button", { type: "button", className: "uix-scheduling-calendar__date", "data-calendar-date": date, tabIndex: activeDate === date ? 0 : -1, onFocus: () => setActiveDate(date), onKeyDown: (event) => onDayKeyDown(event, date), onClick: onSelectDate ? () => onSelectDate(date) : void 0, "aria-label": dateText(date, "day"), children: Number(date.slice(-2)) });
|
|
377
|
+
const dateButton = /* @__PURE__ */ jsx("button", { type: "button", className: "uix-scheduling-calendar__date", "data-calendar-date": date, tabIndex: activeDate === date ? 0 : -1, onFocus: () => setActiveDate(date), onKeyDown: (event) => onDayKeyDown(event, date), onClick: onSelectDate ? () => onSelectDate(date) : void 0, "aria-label": info?.label ? fillLabel(labels.dayName, { date: dateText(date, "day"), label: info.label }) : dateText(date, "day"), children: Number(date.slice(-2)) });
|
|
288
378
|
const moreText = fillLabel(labels.moreEntries, { count: hiddenCount });
|
|
289
379
|
const moreName = fillLabel(labels.moreEntriesLabel, { count: hiddenCount, date: dateText(date, "day") });
|
|
290
380
|
return /* @__PURE__ */ jsxs("div", { className: "uix-scheduling-calendar__day", "data-outside": view === "month" && !monthDays[index].inMonth || void 0, children: [
|
|
291
381
|
hasBadge || info ? /* @__PURE__ */ jsxs("div", { className: "uix-scheduling-calendar__dayhead", children: [
|
|
292
382
|
hasBadge && /* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__badge", children: badge }),
|
|
293
|
-
info && /* @__PURE__ */ jsxs("span", { className: "uix-scheduling-calendar__count", children: [
|
|
383
|
+
info && /* @__PURE__ */ jsxs("span", { className: "uix-scheduling-calendar__count", "aria-hidden": info.label ? true : void 0, children: [
|
|
294
384
|
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: info.count }),
|
|
295
385
|
/* @__PURE__ */ jsx("span", { className: "uix-visually-hidden", children: info.label ?? fillLabel(labels.dayCount, { count: info.count }) })
|
|
296
386
|
] }),
|
|
@@ -299,18 +389,20 @@ function SchedulingCalendar({
|
|
|
299
389
|
] }) : dateButton,
|
|
300
390
|
/* @__PURE__ */ jsxs("div", { className: "uix-scheduling-calendar__entries", children: [
|
|
301
391
|
shown.map((entry) => /* @__PURE__ */ jsx("button", { className: "uix-scheduling-calendar__entry", ...entryProps(entry), children: entryContent(entry) }, entry.id)),
|
|
302
|
-
controlled ? hiddenCount > 0 && (onShowMore ? /* @__PURE__ */ jsx("button", { type: "button", className: "uix-scheduling-calendar__more", "aria-label": moreName, onClick: () => onShowMore(date, onDay), children: moreText }) : /* @__PURE__ */ jsxs("span", { className: "uix-scheduling-calendar__more", children: [
|
|
392
|
+
countsOnly ? null : controlled ? hiddenCount > 0 && (onShowMore ? /* @__PURE__ */ jsx("button", { type: "button", tabIndex: -1, className: "uix-scheduling-calendar__more", "aria-label": moreName, onClick: () => onShowMore(date, onDay), children: moreText }) : /* @__PURE__ */ jsxs("span", { className: "uix-scheduling-calendar__more", children: [
|
|
303
393
|
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: moreText }),
|
|
304
394
|
/* @__PURE__ */ jsx("span", { className: "uix-visually-hidden", children: moreName })
|
|
305
|
-
] })) : overflowing && /* @__PURE__ */ jsx("button", { type: "button", className: "uix-scheduling-calendar__more", "aria-expanded": onShowMore ? void 0 : !collapsed, "aria-label": collapsed ? moreName : void 0, onClick: () => toggleMore(date, onDay), children: collapsed ? moreText : labels.fewerEntries })
|
|
395
|
+
] })) : overflowing && /* @__PURE__ */ jsx("button", { type: "button", tabIndex: -1, className: "uix-scheduling-calendar__more", "aria-expanded": onShowMore ? void 0 : !collapsed, "aria-label": collapsed ? moreName : void 0, onClick: () => toggleMore(date, onDay), children: collapsed ? moreText : labels.fewerEntries })
|
|
306
396
|
] })
|
|
307
397
|
] }, date);
|
|
308
398
|
};
|
|
309
399
|
const renderWeek = (row) => {
|
|
310
400
|
const rowDays = days.slice(row * 7, row * 7 + 7);
|
|
311
401
|
const windowLanes = lanesUsed(row, "window");
|
|
312
|
-
const spanLanes = lanesUsed(row, "item");
|
|
313
|
-
const
|
|
402
|
+
const spanLanes = countsOnly ? 0 : lanesUsed(row, "item");
|
|
403
|
+
const hiddenIds = layout.hiddenByRow?.[row] ?? EMPTY_IDS;
|
|
404
|
+
const drawnWindows = countsOnly ? new Set((placedByRow.get(row) ?? []).filter((placed) => placed.group === "window").map((placed) => placed.id)) : void 0;
|
|
405
|
+
const hidden = countsOnly ? hiddenIds.filter((id) => overlayById.has(id) && !drawnWindows.has(id)).length : hiddenIds.length;
|
|
314
406
|
const firstHiddenDay = layout.firstHiddenDayByRow?.[row] ?? rowDays[0];
|
|
315
407
|
const moreText = fillLabel(labels.moreSpans, { count: hidden });
|
|
316
408
|
const moreName = fillLabel(labels.moreSpansLabel, { count: hidden, date: dateText(firstHiddenDay, "day") });
|
|
@@ -322,17 +414,17 @@ function SchedulingCalendar({
|
|
|
322
414
|
if (placed.group === "window") {
|
|
323
415
|
const overlay = overlayById.get(placed.id);
|
|
324
416
|
if (!overlay) return null;
|
|
325
|
-
return /* @__PURE__ */ jsx("button", { type: "button", className: "uix-scheduling-calendar__window", "data-overlay-id": overlay.id, "data-pattern": overlay.pattern ?? "solid", "data-global": overlay.global || void 0, "data-kind": overlay.kind, ...continues, style: laneStyle(placed, windowLanes), onClick: () => onSelectOverlay?.(overlay), "aria-label": overlayName(overlay), children: /* @__PURE__ */ jsxs("span", { className: "uix-scheduling-calendar__window-text", children: [
|
|
417
|
+
return /* @__PURE__ */ jsx("button", { type: "button", tabIndex: -1, className: "uix-scheduling-calendar__window", "data-overlay-id": overlay.id, "data-pattern": overlay.pattern ?? "solid", "data-global": overlay.global || void 0, "data-kind": overlay.kind, ...continues, style: laneStyle(placed, windowLanes), onClick: () => onSelectOverlay?.(overlay), "aria-label": overlayName(overlay), children: /* @__PURE__ */ jsxs("span", { className: "uix-scheduling-calendar__window-text", children: [
|
|
326
418
|
overlay.kindLabel && /* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__window-kind", children: overlay.kindLabel }),
|
|
327
419
|
/* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__window-name", children: overlay.label }),
|
|
328
420
|
overlay.scopeLabel && /* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__window-scope", children: overlay.scopeLabel })
|
|
329
421
|
] }) }, `window-${placed.id}`);
|
|
330
422
|
}
|
|
331
423
|
const entry = entryById.get(placed.id);
|
|
332
|
-
if (!entry) return null;
|
|
424
|
+
if (!entry || countsOnly) return null;
|
|
333
425
|
return /* @__PURE__ */ jsx("button", { className: "uix-scheduling-calendar__entry uix-scheduling-calendar__span", ...entryProps(entry), ...continues, style: laneStyle(placed, windowLanes), children: entryContent(entry, !placed.continuesBefore) }, `span-${placed.id}`);
|
|
334
426
|
}),
|
|
335
|
-
hidden > 0 && (onShowMore ? /* @__PURE__ */ jsx("button", { type: "button", className: "uix-scheduling-calendar__rowmore", "aria-label": moreName, onClick: () => onShowMore(firstHiddenDay, picksGiven ? dayEntries[firstHiddenDay] ?? EMPTY_ENTRIES : entriesOn(firstHiddenDay)), children: moreText }) : /* @__PURE__ */ jsxs("span", { className: "uix-scheduling-calendar__rowmore", children: [
|
|
427
|
+
hidden > 0 && (onShowMore ? /* @__PURE__ */ jsx("button", { type: "button", tabIndex: -1, className: "uix-scheduling-calendar__rowmore", "data-more-date": rowDays.includes(firstHiddenDay) ? firstHiddenDay : rowDays[0], "aria-label": moreName, onClick: () => onShowMore(firstHiddenDay, picksGiven ? dayEntries[firstHiddenDay] ?? EMPTY_ENTRIES : entriesOn(firstHiddenDay)), children: moreText }) : /* @__PURE__ */ jsxs("span", { className: "uix-scheduling-calendar__rowmore", children: [
|
|
336
428
|
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: moreText }),
|
|
337
429
|
/* @__PURE__ */ jsx("span", { className: "uix-visually-hidden", children: moreName })
|
|
338
430
|
] }))
|
|
@@ -341,7 +433,7 @@ function SchedulingCalendar({
|
|
|
341
433
|
const gridStyle = fixed ? {
|
|
342
434
|
["--uix-scheduling-calendar-chips"]: chipRows,
|
|
343
435
|
["--uix-scheduling-calendar-lane-cap"]: Math.max(
|
|
344
|
-
Math.max(Number.isFinite(windowCap) ? windowCap : 0, lanesUsedAnywhere("window")) + Math.max(Number.isFinite(spanCap) ? spanCap : 0, lanesUsedAnywhere("item")),
|
|
436
|
+
Math.max(Number.isFinite(windowCap) ? windowCap : 0, lanesUsedAnywhere("window")) + (countsOnly ? 0 : Math.max(Number.isFinite(spanCap) ? spanCap : 0, lanesUsedAnywhere("item"))),
|
|
345
437
|
Object.keys(layout.hiddenByRow ?? {}).length > 0 ? 1 : 0
|
|
346
438
|
)
|
|
347
439
|
} : void 0;
|
|
@@ -358,9 +450,30 @@ function SchedulingCalendar({
|
|
|
358
450
|
loading ? /* @__PURE__ */ jsx("div", { className: "uix-scheduling-calendar__state", role: "status", children: labels.loading }) : error ? /* @__PURE__ */ jsxs("div", { className: "uix-scheduling-calendar__state", role: "alert", children: [
|
|
359
451
|
/* @__PURE__ */ jsx("p", { children: error }),
|
|
360
452
|
onRetry && /* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--secondary", onClick: onRetry, children: labels.retry })
|
|
361
|
-
] }) : view === "agenda"
|
|
453
|
+
] }) : view === "agenda" && agendaGroups ? /* @__PURE__ */ jsx(
|
|
454
|
+
SchedulingAgenda,
|
|
455
|
+
{
|
|
456
|
+
groups: agendaGroups,
|
|
457
|
+
timeZone,
|
|
458
|
+
labels,
|
|
459
|
+
headingLevel: agendaHeadingLevel,
|
|
460
|
+
virtualizeAbove,
|
|
461
|
+
dateText,
|
|
462
|
+
formatInstant,
|
|
463
|
+
entryName,
|
|
464
|
+
entryStatus: statusOf,
|
|
465
|
+
entryStatusText: stateText,
|
|
466
|
+
entryMarkers: markersOf,
|
|
467
|
+
overlayName,
|
|
468
|
+
renderEntry,
|
|
469
|
+
renderMarker,
|
|
470
|
+
onSelectEntry,
|
|
471
|
+
onSelectOverlay,
|
|
472
|
+
onShowMore
|
|
473
|
+
}
|
|
474
|
+
) : view === "agenda" ? /* @__PURE__ */ jsx("div", { ref: flatAgendaRef, className: "uix-scheduling-calendar__agenda", role: "region", "aria-label": labels.agenda, onKeyDown: onFlatAgendaKeyDown, onFocus: onFlatAgendaFocus, children: visibleEntries.length === 0 ? /* @__PURE__ */ jsx("p", { children: labels.agendaEmpty }) : /* @__PURE__ */ jsx("ol", { children: [...visibleEntries].sort((a, b) => a.start.localeCompare(b.start)).map((entry) => {
|
|
362
475
|
const markers = markersOf(entry);
|
|
363
|
-
return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("button", { type: "button", "data-item-id": entry.id, "data-band": entry.band ?? "none", "data-status": statusOf(entry), "data-state": entry.state, onClick: () => onSelectEntry?.(entry), children: [
|
|
476
|
+
return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("button", { type: "button", "data-item-id": entry.id, "data-highlight": entry.emphasis === "highlight" || void 0, "data-dim": entry.emphasis === "dim" || void 0, "data-band": entry.band ?? "none", "data-status": statusOf(entry), "data-state": entry.state, onClick: () => onSelectEntry?.(entry), children: [
|
|
364
477
|
/* @__PURE__ */ jsxs("span", { children: [
|
|
365
478
|
/* @__PURE__ */ jsx("strong", { children: renderEntry?.(entry) ?? entry.title }),
|
|
366
479
|
/* @__PURE__ */ jsxs("span", { children: [
|
|
@@ -407,7 +520,7 @@ function SchedulingCalendar({
|
|
|
407
520
|
emptyNote,
|
|
408
521
|
nothingToShow
|
|
409
522
|
}
|
|
410
|
-
) : /* @__PURE__ */ jsxs("div", { ref: gridRef, className: cx("uix-scheduling-calendar__grid", view === "week" && "uix-scheduling-calendar__grid--week"), "data-fixed": fixed || void 0, style: gridStyle, role: "group", "aria-label": fillLabel(labels.grid, { view: viewLabel[view] }), children: [
|
|
523
|
+
) : /* @__PURE__ */ jsxs("div", { ref: gridRef, className: cx("uix-scheduling-calendar__grid", view === "week" && "uix-scheduling-calendar__grid--week"), "data-fixed": fixed || void 0, "data-density": countsOnly ? "counts" : void 0, style: gridStyle, onKeyDown: onGridKeyDown, onFocus: onGridFocus, role: "group", "aria-label": fillLabel(labels.grid, { view: viewLabel[view] }), children: [
|
|
411
524
|
days.slice(0, 7).map((date) => /* @__PURE__ */ jsx("div", { className: "uix-scheduling-calendar__weekday", "aria-hidden": "true", children: dateText(date, "weekday") }, `weekday-${date}`)),
|
|
412
525
|
Array.from({ length: days.length / 7 }, (_, row) => renderWeek(row)),
|
|
413
526
|
emptyNote != null && emptyNote !== false && nothingToShow && /* @__PURE__ */ jsx("div", { className: "uix-scheduling-calendar__empty", children: emptyNote })
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import 'react';
|
|
2
2
|
import '../scheduling-calendar-model.js';
|
|
3
|
-
export {
|
|
3
|
+
export { q as SchedulingTimeGrid, r as SchedulingTimeGridProps, T as TimeGridEntryContext } from '../SchedulingCalendar-B6TakO2h.js';
|
|
4
4
|
import '../calendar-model.js';
|
|
@@ -39,6 +39,8 @@ const GridItem = memo(function GridItem2({ entry, segment, column, alone, name,
|
|
|
39
39
|
] });
|
|
40
40
|
const shared = {
|
|
41
41
|
className: "uix-scheduling-calendar__entry uix-scheduling-calendar__tg-item",
|
|
42
|
+
"data-highlight": entry.emphasis === "highlight" || void 0,
|
|
43
|
+
"data-dim": entry.emphasis === "dim" || void 0,
|
|
42
44
|
"data-band": entry.band ?? "none",
|
|
43
45
|
"data-status": status,
|
|
44
46
|
"data-state": entry.state,
|
|
@@ -54,6 +56,7 @@ const GridItem = memo(function GridItem2({ entry, segment, column, alone, name,
|
|
|
54
56
|
"button",
|
|
55
57
|
{
|
|
56
58
|
type: "button",
|
|
59
|
+
tabIndex: -1,
|
|
57
60
|
...shared,
|
|
58
61
|
"data-item-id": entry.id,
|
|
59
62
|
"aria-label": name,
|
|
@@ -101,6 +104,9 @@ function SchedulingTimeGrid({
|
|
|
101
104
|
const [widthCap, setWidthCap] = useState(Infinity);
|
|
102
105
|
const [ghost, setGhost] = useState(null);
|
|
103
106
|
const [announcement, setAnnouncement] = useState("");
|
|
107
|
+
const [activeDay, setActiveDay] = useState(days[0]);
|
|
108
|
+
const enteredFrom = useRef(null);
|
|
109
|
+
const tabStop = days.includes(activeDay) ? activeDay : days[0];
|
|
104
110
|
const moving = canMove && onProposeMove !== void 0;
|
|
105
111
|
const step = Number.isFinite(givenStep) && givenStep > 0 ? givenStep : DEFAULT_STEP;
|
|
106
112
|
const laneOptions = useMemo(() => ({ crossMidnightMinutes: topLaneCrossMidnightMinutes }), [topLaneCrossMidnightMinutes]);
|
|
@@ -334,6 +340,78 @@ function SchedulingTimeGrid({
|
|
|
334
340
|
dragRef.current = null;
|
|
335
341
|
}, []);
|
|
336
342
|
const gridColumns = (span) => `${span.startCol + 2} / ${span.endCol + 3}`;
|
|
343
|
+
const headButton = (date) => rootRef.current?.querySelector(`.uix-scheduling-calendar__tg-dayhead[data-date="${date}"] [data-calendar-date]`) ?? null;
|
|
344
|
+
const dayRing = (date) => {
|
|
345
|
+
const root = rootRef.current;
|
|
346
|
+
const column = days.indexOf(date);
|
|
347
|
+
if (!root || column === -1) return [];
|
|
348
|
+
const covers = (element) => {
|
|
349
|
+
const [from, to] = element.style.gridColumn.split("/").map((part) => Number(part.trim()));
|
|
350
|
+
return from !== void 0 && to !== void 0 && from <= column + 2 && column + 2 < to;
|
|
351
|
+
};
|
|
352
|
+
const all = (selector) => Array.from(root.querySelectorAll(selector));
|
|
353
|
+
return [
|
|
354
|
+
...all(".uix-scheduling-calendar__tg-strip > button.uix-scheduling-calendar__window").filter(covers),
|
|
355
|
+
...all(".uix-scheduling-calendar__tg-top > button.uix-scheduling-calendar__tg-span").filter(covers),
|
|
356
|
+
...all(`[data-tg-column="${date}"] > button[data-item-id]`),
|
|
357
|
+
...all(`.uix-scheduling-calendar__tg-dayhead[data-date="${date}"] > button.uix-scheduling-calendar__more`),
|
|
358
|
+
// "+N windows" opens one day; it is reached from that day.
|
|
359
|
+
...all(`.uix-scheduling-calendar__tg-strip > button.uix-scheduling-calendar__rowmore[data-more-date="${date}"]`)
|
|
360
|
+
];
|
|
361
|
+
};
|
|
362
|
+
const dayOfItem = (item) => {
|
|
363
|
+
if (enteredFrom.current && dayRing(enteredFrom.current).includes(item)) return enteredFrom.current;
|
|
364
|
+
const inColumn = item.getAttribute("data-more-date") ?? item.closest("[data-tg-column]")?.getAttribute("data-tg-column") ?? item.closest("[data-date]")?.getAttribute("data-date");
|
|
365
|
+
if (inColumn) return inColumn;
|
|
366
|
+
const from = Number(item.style.gridColumn.split("/")[0]);
|
|
367
|
+
return Number.isFinite(from) ? days[Math.max(0, Math.min(days.length - 1, from - 2))] ?? null : days[days.length - 1] ?? null;
|
|
368
|
+
};
|
|
369
|
+
const onHeadKeyDown = (event, date) => {
|
|
370
|
+
const index = days.indexOf(date);
|
|
371
|
+
const target = event.key === "ArrowLeft" ? index - 1 : event.key === "ArrowRight" ? index + 1 : event.key === "Home" ? 0 : event.key === "End" ? days.length - 1 : null;
|
|
372
|
+
if (target !== null) {
|
|
373
|
+
event.preventDefault();
|
|
374
|
+
const next = days[Math.max(0, Math.min(days.length - 1, target))];
|
|
375
|
+
setActiveDay(next);
|
|
376
|
+
headButton(next)?.focus();
|
|
377
|
+
} else if (event.key === "Enter") {
|
|
378
|
+
const [first] = dayRing(date);
|
|
379
|
+
if (!first) return;
|
|
380
|
+
event.preventDefault();
|
|
381
|
+
enteredFrom.current = date;
|
|
382
|
+
first.focus();
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
const onGridKeyDown = (event) => {
|
|
386
|
+
const item = event.target;
|
|
387
|
+
if (event.defaultPrevented || event.shiftKey || item.hasAttribute("data-calendar-date") || !["ArrowDown", "ArrowUp", "Home", "End", "Escape"].includes(event.key)) return;
|
|
388
|
+
const date = dayOfItem(item);
|
|
389
|
+
if (!date) return;
|
|
390
|
+
const ring = dayRing(date);
|
|
391
|
+
const index = ring.indexOf(item);
|
|
392
|
+
if (index === -1) return;
|
|
393
|
+
event.preventDefault();
|
|
394
|
+
const to = event.key === "Escape" ? null : ring[Math.max(0, Math.min(ring.length - 1, event.key === "Home" ? 0 : event.key === "End" ? ring.length - 1 : index + (event.key === "ArrowDown" ? 1 : -1)))];
|
|
395
|
+
if (to !== item && ghost?.source === "keys") {
|
|
396
|
+
setGhost(null);
|
|
397
|
+
setAnnouncement(labels.moveCancelled);
|
|
398
|
+
}
|
|
399
|
+
if (event.key === "Escape") {
|
|
400
|
+
event.stopPropagation();
|
|
401
|
+
enteredFrom.current = null;
|
|
402
|
+
setActiveDay(date);
|
|
403
|
+
headButton(date)?.focus();
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
enteredFrom.current = date;
|
|
407
|
+
to?.focus();
|
|
408
|
+
};
|
|
409
|
+
const onGridFocus = (event) => {
|
|
410
|
+
const item = event.target;
|
|
411
|
+
if (item.hasAttribute("data-calendar-date")) return;
|
|
412
|
+
const date = dayOfItem(item);
|
|
413
|
+
if (date && dayRing(date).includes(item)) setActiveDay(date);
|
|
414
|
+
};
|
|
337
415
|
const nowAt = now === void 0 ? null : new Date(now).getTime();
|
|
338
416
|
const nowColumn = nowAt === null || Number.isNaN(nowAt) ? -1 : days.indexOf(zonedDateKey(new Date(nowAt), timeZone));
|
|
339
417
|
const topHiddenOn = (date) => top.hidden.filter((id) => {
|
|
@@ -358,6 +436,8 @@ function SchedulingTimeGrid({
|
|
|
358
436
|
role: "group",
|
|
359
437
|
"aria-label": gridLabel,
|
|
360
438
|
style: vars({ "--uix-scheduling-calendar-days": days.length, "--uix-scheduling-calendar-hours": maxHours }),
|
|
439
|
+
onKeyDown: onGridKeyDown,
|
|
440
|
+
onFocus: onGridFocus,
|
|
361
441
|
onClickCapture: (event) => {
|
|
362
442
|
if (suppressClick.current) {
|
|
363
443
|
suppressClick.current = false;
|
|
@@ -374,12 +454,25 @@ function SchedulingTimeGrid({
|
|
|
374
454
|
const more = info ? info.overflowCount : hiddenIds.length;
|
|
375
455
|
const moreText = fillLabel(labels.moreSpans, { count: more });
|
|
376
456
|
const moreName = fillLabel(labels.moreEntriesLabel, { count: more, date: dateText(date, "day") });
|
|
377
|
-
const headName = info ? info.label
|
|
457
|
+
const headName = !info ? dateText(date, "day") : info.label ? fillLabel(labels.dayName, { date: dateText(date, "day"), label: info.label }) : `${dateText(date, "day")}, ${fillLabel(labels.dayCount, { count: info.count })}`;
|
|
378
458
|
return /* @__PURE__ */ jsxs("div", { className: "uix-scheduling-calendar__tg-dayhead", role: "group", "aria-label": headName, "data-date": date, children: [
|
|
379
|
-
/* @__PURE__ */ jsx(
|
|
459
|
+
/* @__PURE__ */ jsx(
|
|
460
|
+
"button",
|
|
461
|
+
{
|
|
462
|
+
type: "button",
|
|
463
|
+
className: "uix-scheduling-calendar__date",
|
|
464
|
+
"data-calendar-date": date,
|
|
465
|
+
tabIndex: tabStop === date ? 0 : -1,
|
|
466
|
+
"aria-label": info?.label ? fillLabel(labels.dayName, { date: dateText(date, "day"), label: info.label }) : dateText(date, "day"),
|
|
467
|
+
onFocus: () => setActiveDay(date),
|
|
468
|
+
onKeyDown: (event) => onHeadKeyDown(event, date),
|
|
469
|
+
onClick: onSelectDate ? () => onSelectDate(date) : void 0,
|
|
470
|
+
children: dateText(date, "column")
|
|
471
|
+
}
|
|
472
|
+
),
|
|
380
473
|
info && /* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__count", "aria-hidden": "true", children: info.count }),
|
|
381
474
|
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: [
|
|
475
|
+
more > 0 && (onShowMore ? /* @__PURE__ */ jsx("button", { type: "button", tabIndex: -1, 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
476
|
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: moreText }),
|
|
384
477
|
/* @__PURE__ */ jsx("span", { className: "uix-visually-hidden", children: moreName })
|
|
385
478
|
] }))
|
|
@@ -394,6 +487,7 @@ function SchedulingTimeGrid({
|
|
|
394
487
|
"button",
|
|
395
488
|
{
|
|
396
489
|
type: "button",
|
|
490
|
+
tabIndex: -1,
|
|
397
491
|
className: "uix-scheduling-calendar__window",
|
|
398
492
|
"data-overlay-id": overlay.id,
|
|
399
493
|
"data-pattern": overlay.pattern ?? "solid",
|
|
@@ -413,7 +507,7 @@ function SchedulingTimeGrid({
|
|
|
413
507
|
placed.id
|
|
414
508
|
);
|
|
415
509
|
}),
|
|
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 }) }))
|
|
510
|
+
moreWindows > 0 && (onShowMore ? /* @__PURE__ */ jsx("button", { type: "button", tabIndex: -1, className: "uix-scheduling-calendar__rowmore", "data-more-date": firstHiddenWindowDay, "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
511
|
] }),
|
|
418
512
|
(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
513
|
top.placed.map((placed) => {
|
|
@@ -425,8 +519,11 @@ function SchedulingTimeGrid({
|
|
|
425
519
|
"button",
|
|
426
520
|
{
|
|
427
521
|
type: "button",
|
|
522
|
+
tabIndex: -1,
|
|
428
523
|
className: "uix-scheduling-calendar__entry uix-scheduling-calendar__tg-span",
|
|
429
524
|
"data-item-id": entry.id,
|
|
525
|
+
"data-highlight": entry.emphasis === "highlight" || void 0,
|
|
526
|
+
"data-dim": entry.emphasis === "dim" || void 0,
|
|
430
527
|
"data-band": entry.band ?? "none",
|
|
431
528
|
"data-status": entryStatus(entry),
|
|
432
529
|
"data-state": entry.state,
|
|
@@ -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 {
|
|
4
|
+
import { j as SchedulingEntryState } from '../SchedulingCalendar-B6TakO2h.js';
|
|
5
5
|
import '../calendar-model.js';
|
|
6
6
|
import '../scheduling-calendar-model.js';
|
|
7
7
|
|