@tensor_1/react 2.33.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.
- package/README.md +25 -0
- package/dist/{SchedulingCalendar-C4XVbqZr.d.ts → SchedulingCalendar-DSLQ6AY5.d.ts} +39 -2
- package/dist/components/SchedulingAgenda.d.ts +31 -0
- package/dist/components/SchedulingAgenda.js +161 -0
- package/dist/components/SchedulingCalendar.d.ts +1 -1
- package/dist/components/SchedulingCalendar.js +45 -12
- package/dist/components/SchedulingTimeGrid.d.ts +1 -1
- package/dist/components/SchedulingTimeline.d.ts +1 -1
- package/dist/index.cjs +748 -562
- package/dist/index.d.cts +39 -2
- package/dist/index.d.ts +1 -1
- package/dist/labels-context.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -412,6 +412,31 @@ const spanLayout = layoutMonthSpans(spansInSalienceOrder, grid, { timeZone, week
|
|
|
412
412
|
change stays where it was, so a refused move snaps back by construction. `movable: false` pins an entry.
|
|
413
413
|
- Overlapping items share at most `maxLanes` lanes, fewer in a narrow column; the column "+N" is the consumer's
|
|
414
414
|
`days[date].overflowCount`. `renderEntry(entry, { availableLines })` says how many text lines fit.
|
|
415
|
+
- **Agenda grouped by day** (`agendaGroups`, HAR-1520): the consumer builds the day groups and the calendar renders
|
|
416
|
+
them in order, each under a heading.
|
|
417
|
+
|
|
418
|
+
```tsx
|
|
419
|
+
<SchedulingCalendar view="agenda" timeZone="Europe/Berlin" anchorDate="2026-10-07" entries={[]} showHeader={false}
|
|
420
|
+
agendaGroups={[
|
|
421
|
+
{ date: '2026-10-07', annotations: [hold], rows: firstTen, hiddenCount: 4 }, // "4 not shown — open day"
|
|
422
|
+
{ date: '2026-10-08', rows: [], hiddenCount: 12, continuesCount: 2 }, // the heading stays
|
|
423
|
+
]}
|
|
424
|
+
onSelectEntry={openItem} onSelectOverlay={openWindow} onShowMore={openDay}
|
|
425
|
+
notice={truncated && <p>Showing the first 500 items.</p>} />
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
Above `virtualizeAbove` rows (default 200) the agenda is one scroller of 44 px rows and only the rows near the
|
|
429
|
+
viewport are mounted, on the server too; each window note then has a row of its own under the heading. In a box
|
|
430
|
+
of 576 px or less those rows are 64 px with three lines: the title, the time, then markers and status. A row
|
|
431
|
+
there holds one line of title, so `renderEntry` content must fit one line. A row shows its status as words and
|
|
432
|
+
its markers with their text. Its time is two times of day when the entry starts on that day and ends within a
|
|
433
|
+
day by the clock; otherwise both ends go through `formatInstant` (pass a short form for narrow screens: an end
|
|
434
|
+
that does not fit its half of the line is cut), and an `allDay` entry reads `labels.allDay` or the days it
|
|
435
|
+
covers. Crossing `virtualizeAbove` swaps the two forms, which resets focus and scroll inside the agenda. Type
|
|
436
|
+
the groups with `SchedulingAgendaGroup`.
|
|
437
|
+
- **A narrow month** (`monthDensity="counts"`): a cell is the date, the consumer's `days[date].count` and its markers.
|
|
438
|
+
No entry is drawn and none is counted: a row's "+N" is for windows over `windowLaneCap` only. The setting does not
|
|
439
|
+
touch the week.
|
|
415
440
|
- **Colour.** Every calendar colour is named once at the top of `scheduling-calendar.css` (`--calendar-*`).
|
|
416
441
|
- **Deprecated, still working in 2.x:** `entry.state` / `SchedulingEntryState`, `overlay.kind` /
|
|
417
442
|
`SchedulingOverlayKind` and the `previous` / `next` labels. An entry with `state` is no longer tinted: the two
|
|
@@ -127,6 +127,20 @@ interface SchedulingCalendarDay {
|
|
|
127
127
|
label?: string;
|
|
128
128
|
markers?: SchedulingMarker[];
|
|
129
129
|
}
|
|
130
|
+
/** One day of the agenda, built by the consumer. The calendar renders the groups and their rows in the order given. */
|
|
131
|
+
interface SchedulingAgendaGroup {
|
|
132
|
+
/** The day the group is for (`YYYY-MM-DD`). */
|
|
133
|
+
date: string;
|
|
134
|
+
/** Replaces the heading text. Default: the date through `formatDate(date, 'day')`. */
|
|
135
|
+
heading?: ReactNode;
|
|
136
|
+
/** Windows that touch the day: one note each beside the heading, never one per row. */
|
|
137
|
+
annotations?: SchedulingCalendarOverlay[];
|
|
138
|
+
rows: SchedulingCalendarEntry[];
|
|
139
|
+
/** Entries of the day that are not in `rows`. Shows "N not shown — open day", which calls `onShowMore`. */
|
|
140
|
+
hiddenCount?: number;
|
|
141
|
+
/** Entries that go on into this day and are listed under an earlier one. */
|
|
142
|
+
continuesCount?: number;
|
|
143
|
+
}
|
|
130
144
|
interface SchedulingLegendItem {
|
|
131
145
|
id: string;
|
|
132
146
|
label: string;
|
|
@@ -196,6 +210,12 @@ interface SchedulingCalendarLabels {
|
|
|
196
210
|
topLane?: string;
|
|
197
211
|
/** The cue on the second-day part of an entry that crosses midnight. `{time}` is its start. */
|
|
198
212
|
continuesFrom?: string;
|
|
213
|
+
/** The time text of an agenda row whose entry has no time of day (`allDay`). */
|
|
214
|
+
allDay?: string;
|
|
215
|
+
/** Under an agenda day whose rows are not all listed. `{count}` placeholder. */
|
|
216
|
+
hiddenInDay?: string;
|
|
217
|
+
/** Under an agenda day that entries from an earlier day run into. `{count}` placeholder. */
|
|
218
|
+
continuesInDay?: string;
|
|
199
219
|
/** Read after an item's name when it can be moved. */
|
|
200
220
|
moveHint?: string;
|
|
201
221
|
/** Announced for a pending move. `{start}` and `{end}` placeholders. */
|
|
@@ -301,6 +321,23 @@ interface SchedulingCalendarProps {
|
|
|
301
321
|
emptyNote?: ReactNode;
|
|
302
322
|
/** Called by a click or Enter on a day number. */
|
|
303
323
|
onSelectDate?: (date: string) => void;
|
|
324
|
+
/**
|
|
325
|
+
* The agenda as day groups built by the consumer: which entry is listed under which day,
|
|
326
|
+
* in which order, and how many are left out. When set, `view="agenda"` renders exactly
|
|
327
|
+
* these, each day under a heading, and never sorts, groups or counts. Unset keeps the flat
|
|
328
|
+
* list sorted by start.
|
|
329
|
+
*/
|
|
330
|
+
agendaGroups?: SchedulingAgendaGroup[];
|
|
331
|
+
/** The heading level of an agenda day. Default 3. */
|
|
332
|
+
agendaHeadingLevel?: 2 | 3 | 4 | 5 | 6;
|
|
333
|
+
/** Above this many agenda rows only the rows near the viewport are mounted. Default 200. */
|
|
334
|
+
virtualizeAbove?: number;
|
|
335
|
+
/**
|
|
336
|
+
* `'counts'` draws each month cell with the consumer's `days[date].count` and markers
|
|
337
|
+
* only: no chips and no entry bars, and narrower columns, for a narrow screen. Windows keep
|
|
338
|
+
* their bar and name. Default `'full'`.
|
|
339
|
+
*/
|
|
340
|
+
monthDensity?: 'full' | 'counts';
|
|
304
341
|
/**
|
|
305
342
|
* Draws `view="week"` as a time grid: seven day columns on an hour axis. `view="day"` is
|
|
306
343
|
* always a time grid. Default `false`: the week stays seven day cells.
|
|
@@ -334,6 +371,6 @@ interface SchedulingCalendarProps {
|
|
|
334
371
|
labels?: Partial<SchedulingCalendarLabels>;
|
|
335
372
|
}
|
|
336
373
|
/** Month/week/agenda calendar for UTC ranges rendered in an explicit IANA time zone. */
|
|
337
|
-
declare function SchedulingCalendar({ entries, anchorDate, timeZone, view: controlledView, onViewChange, onAnchorDateChange, onSelectEntry, overlays, onSelectOverlay, filter, renderEntry, locale, weekStartsOn, formatDate, formatInstant: formatInstantProp, maxEntriesPerDay, onShowMore, renderDayBadge, days: dayInfo, dayEntries, spanLayout, windowLaneCap, spanLaneCap, legend, legendCaption, showHeader, notice, emptyNote, onSelectDate, timeGrid, now, canMove, step, onProposeMove, topLaneCrossMidnightMinutes, topLaneCap, maxLanes, loading, error, onRetry, className, labels: labelOverrides, }: SchedulingCalendarProps): react.JSX.Element;
|
|
374
|
+
declare function SchedulingCalendar({ entries, anchorDate, timeZone, view: controlledView, onViewChange, onAnchorDateChange, onSelectEntry, overlays, onSelectOverlay, filter, renderEntry, locale, weekStartsOn, formatDate, formatInstant: formatInstantProp, maxEntriesPerDay, onShowMore, renderDayBadge, days: dayInfo, dayEntries, spanLayout, windowLaneCap, spanLaneCap, legend, legendCaption, showHeader, notice, emptyNote, onSelectDate, agendaGroups, agendaHeadingLevel, virtualizeAbove, monthDensity, timeGrid, now, canMove, step, onProposeMove, topLaneCrossMidnightMinutes, topLaneCap, maxLanes, loading, error, onRetry, className, labels: labelOverrides, }: SchedulingCalendarProps): react.JSX.Element;
|
|
338
375
|
|
|
339
|
-
export { DEFAULT_SCHEDULING_CALENDAR_LABELS as D, type
|
|
376
|
+
export { DEFAULT_SCHEDULING_CALENDAR_LABELS as D, type SchedulingAgendaGroup as S, type TimeGridEntryContext as T, type SchedulingBand as a, SchedulingCalendar as b, type SchedulingCalendarDay as c, type SchedulingCalendarEntry as d, type SchedulingCalendarLabels as e, type SchedulingCalendarOverlay as f, type SchedulingCalendarProps as g, type SchedulingCalendarView as h, type SchedulingDatePart as i, type SchedulingEntryState as j, type SchedulingLegendItem as k, type SchedulingMarker as l, type SchedulingMarkerEmphasis as m, type SchedulingOverlayKind as n, type SchedulingOverlayPattern as o, type SchedulingStatus as p, SchedulingTimeGrid as q, type SchedulingTimeGridProps as r };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { S as SchedulingAgendaGroup, e as SchedulingCalendarLabels, i as SchedulingDatePart, d as SchedulingCalendarEntry, l as SchedulingMarker, f as SchedulingCalendarOverlay } from '../SchedulingCalendar-DSLQ6AY5.js';
|
|
4
|
+
import '../calendar-model.js';
|
|
5
|
+
import '../scheduling-calendar-model.js';
|
|
6
|
+
|
|
7
|
+
/** The height of one row of the virtualised agenda, in px. The stylesheet uses the same number. */
|
|
8
|
+
declare const AGENDA_VIRTUAL_ROW = 44;
|
|
9
|
+
interface SchedulingAgendaProps {
|
|
10
|
+
groups: SchedulingAgendaGroup[];
|
|
11
|
+
timeZone: string;
|
|
12
|
+
labels: Required<SchedulingCalendarLabels>;
|
|
13
|
+
headingLevel: 2 | 3 | 4 | 5 | 6;
|
|
14
|
+
virtualizeAbove: number;
|
|
15
|
+
dateText: (date: string, part: SchedulingDatePart) => string;
|
|
16
|
+
formatInstant: (instant: string) => string;
|
|
17
|
+
entryName: (entry: SchedulingCalendarEntry) => string;
|
|
18
|
+
entryStatus: (entry: SchedulingCalendarEntry) => string;
|
|
19
|
+
entryStatusText: (entry: SchedulingCalendarEntry) => string;
|
|
20
|
+
entryMarkers: (entry: SchedulingCalendarEntry) => SchedulingMarker[];
|
|
21
|
+
overlayName: (overlay: SchedulingCalendarOverlay) => string;
|
|
22
|
+
renderEntry?: (entry: SchedulingCalendarEntry) => ReactNode;
|
|
23
|
+
renderMarker: (marker: SchedulingMarker, showLabel?: boolean) => ReactNode;
|
|
24
|
+
onSelectEntry?: (entry: SchedulingCalendarEntry) => void;
|
|
25
|
+
onSelectOverlay?: (overlay: SchedulingCalendarOverlay) => void;
|
|
26
|
+
onShowMore?: (date: string, entries: SchedulingCalendarEntry[]) => void;
|
|
27
|
+
}
|
|
28
|
+
/** Day headings with their rows, in the consumer's order. */
|
|
29
|
+
declare function SchedulingAgenda(props: SchedulingAgendaProps): react.JSX.Element;
|
|
30
|
+
|
|
31
|
+
export { AGENDA_VIRTUAL_ROW, SchedulingAgenda, type SchedulingAgendaProps };
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { createElement, useLayoutEffect, useRef, useState } from "react";
|
|
4
|
+
import { addCalendarDays, zonedDateKey, zonedTimeOfDay } from "../calendar-model.js";
|
|
5
|
+
import { itemDaySpan } from "../scheduling-calendar-model.js";
|
|
6
|
+
import { useVirtualRows } from "../hooks/useVirtualRows.js";
|
|
7
|
+
import { fillLabel } from "../fill-label.js";
|
|
8
|
+
const AGENDA_VIRTUAL_ROW = 44;
|
|
9
|
+
const AGENDA_NARROW_WIDTH = 576;
|
|
10
|
+
const AGENDA_NARROW_ROW = 64;
|
|
11
|
+
const AGENDA_VIRTUAL_VIEWPORT = 640;
|
|
12
|
+
function uniqueKeys(ids) {
|
|
13
|
+
const used = /* @__PURE__ */ new Set();
|
|
14
|
+
return ids.map((id) => {
|
|
15
|
+
let key = id;
|
|
16
|
+
for (let times = 1; used.has(key); times++) key = `${id}#${times}`;
|
|
17
|
+
used.add(key);
|
|
18
|
+
return key;
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
function SchedulingAgenda(props) {
|
|
22
|
+
const rowCount = props.groups.reduce((sum, group) => sum + group.rows.length, 0);
|
|
23
|
+
const flat = rowCount > props.virtualizeAbove;
|
|
24
|
+
return /* @__PURE__ */ jsx(AgendaBody, { ...props, flat }, flat ? "flat" : "grouped");
|
|
25
|
+
}
|
|
26
|
+
function AgendaBody({
|
|
27
|
+
groups,
|
|
28
|
+
flat,
|
|
29
|
+
timeZone,
|
|
30
|
+
labels,
|
|
31
|
+
headingLevel,
|
|
32
|
+
dateText,
|
|
33
|
+
formatInstant,
|
|
34
|
+
entryName,
|
|
35
|
+
entryStatus,
|
|
36
|
+
entryStatusText,
|
|
37
|
+
entryMarkers,
|
|
38
|
+
overlayName,
|
|
39
|
+
renderEntry,
|
|
40
|
+
renderMarker,
|
|
41
|
+
onSelectEntry,
|
|
42
|
+
onSelectOverlay,
|
|
43
|
+
onShowMore
|
|
44
|
+
}) {
|
|
45
|
+
const groupKeys = uniqueKeys(groups.map((group) => group.date));
|
|
46
|
+
const flatRows = [];
|
|
47
|
+
if (flat) {
|
|
48
|
+
groups.forEach((group, index) => {
|
|
49
|
+
const groupKey = groupKeys[index];
|
|
50
|
+
flatRows.push({ kind: "heading", key: `h-${groupKey}`, group });
|
|
51
|
+
const noteKeys = uniqueKeys((group.annotations ?? []).map((overlay) => overlay.id));
|
|
52
|
+
(group.annotations ?? []).forEach((overlay, at) => flatRows.push({ kind: "note", key: `n-${groupKey}-${noteKeys[at]}`, group, overlay }));
|
|
53
|
+
const rowKeys = uniqueKeys(group.rows.map((entry) => entry.id));
|
|
54
|
+
group.rows.forEach((entry, at) => flatRows.push({ kind: "entry", key: `e-${groupKey}-${rowKeys[at]}`, group, entry }));
|
|
55
|
+
if ((group.hiddenCount ?? 0) > 0) flatRows.push({ kind: "hidden", key: `m-${groupKey}`, group });
|
|
56
|
+
if ((group.continuesCount ?? 0) > 0) flatRows.push({ kind: "continues", key: `c-${groupKey}`, group });
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
const [narrow, setNarrow] = useState(false);
|
|
60
|
+
const virtual = useVirtualRows(flatRows, { rowHeight: narrow ? AGENDA_NARROW_ROW : AGENDA_VIRTUAL_ROW, threshold: 0, estimatedViewportHeight: AGENDA_VIRTUAL_VIEWPORT });
|
|
61
|
+
const scroller = virtual.containerRef;
|
|
62
|
+
useLayoutEffect(() => {
|
|
63
|
+
const box = scroller.current;
|
|
64
|
+
if (!box) return;
|
|
65
|
+
const measure = () => setNarrow(box.clientWidth > 0 && box.clientWidth <= AGENDA_NARROW_WIDTH);
|
|
66
|
+
measure();
|
|
67
|
+
const observer = new ResizeObserver(measure);
|
|
68
|
+
observer.observe(box);
|
|
69
|
+
return () => observer.disconnect();
|
|
70
|
+
}, [scroller]);
|
|
71
|
+
const grouped = useRef(null);
|
|
72
|
+
useLayoutEffect(() => {
|
|
73
|
+
const box = grouped.current;
|
|
74
|
+
if (!box) return;
|
|
75
|
+
const observer = new ResizeObserver((entries) => {
|
|
76
|
+
for (const entry of entries) {
|
|
77
|
+
const head = entry.target;
|
|
78
|
+
head.parentElement?.style.setProperty("--uix-scheduling-calendar-agenda-head", `${Math.ceil(head.getBoundingClientRect().height)}px`);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
box.querySelectorAll(":scope > section > .uix-scheduling-calendar__agenda-head").forEach((head) => observer.observe(head));
|
|
82
|
+
return () => observer.disconnect();
|
|
83
|
+
}, [groups]);
|
|
84
|
+
const note = (overlay) => /* @__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, onClick: () => onSelectOverlay?.(overlay), "aria-label": overlayName(overlay), children: /* @__PURE__ */ jsxs("span", { className: "uix-scheduling-calendar__window-text", children: [
|
|
85
|
+
overlay.kindLabel && /* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__window-kind", children: overlay.kindLabel }),
|
|
86
|
+
/* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__window-name", children: overlay.label }),
|
|
87
|
+
overlay.scopeLabel && /* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__window-scope", children: overlay.scopeLabel })
|
|
88
|
+
] }) });
|
|
89
|
+
const heading = (group, withNotes) => /* @__PURE__ */ jsxs("div", { className: "uix-scheduling-calendar__agenda-head", children: [
|
|
90
|
+
createElement(`h${headingLevel}`, { className: "uix-scheduling-calendar__agenda-heading" }, group.heading ?? dateText(group.date, "day")),
|
|
91
|
+
withNotes && group.annotations && group.annotations.length > 0 && /* @__PURE__ */ jsx("ul", { className: "uix-scheduling-calendar__agenda-notes", children: uniqueKeys(group.annotations.map((overlay) => overlay.id)).map((key, at) => /* @__PURE__ */ jsx("li", { children: note(group.annotations[at]) }, key)) })
|
|
92
|
+
] });
|
|
93
|
+
const timeText = (entry, date) => {
|
|
94
|
+
const part = (text) => /* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__agenda-time-part", children: text });
|
|
95
|
+
const dash = /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: " \u2013 " });
|
|
96
|
+
if (entry.allDay) {
|
|
97
|
+
let span;
|
|
98
|
+
try {
|
|
99
|
+
span = itemDaySpan(entry.start, entry.end, timeZone);
|
|
100
|
+
} catch {
|
|
101
|
+
span = itemDaySpan(entry.start, entry.start, timeZone);
|
|
102
|
+
}
|
|
103
|
+
const last = addCalendarDays(span.end, -1);
|
|
104
|
+
if (span.start === date && last === date) return labels.allDay;
|
|
105
|
+
return span.start === last ? part(dateText(last, "day")) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
106
|
+
part(dateText(span.start, "day")),
|
|
107
|
+
dash,
|
|
108
|
+
part(dateText(last, "day"))
|
|
109
|
+
] });
|
|
110
|
+
}
|
|
111
|
+
const startTime = zonedTimeOfDay(entry.start, timeZone);
|
|
112
|
+
const endTime = zonedTimeOfDay(entry.end, timeZone);
|
|
113
|
+
const endDay = zonedDateKey(entry.end, timeZone);
|
|
114
|
+
const underItsDay = zonedDateKey(entry.start, timeZone) === date && (endDay === date || endDay === addCalendarDays(date, 1) && endTime < startTime);
|
|
115
|
+
return underItsDay ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
116
|
+
startTime,
|
|
117
|
+
dash,
|
|
118
|
+
endTime
|
|
119
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
120
|
+
part(formatInstant(entry.start)),
|
|
121
|
+
dash,
|
|
122
|
+
part(formatInstant(entry.end))
|
|
123
|
+
] });
|
|
124
|
+
};
|
|
125
|
+
const row = (entry, date) => {
|
|
126
|
+
const markers = entryMarkers(entry);
|
|
127
|
+
const name = entry.accessibleName ?? (entry.meta ? `${entryName(entry)}, ${entry.meta}` : entryName(entry));
|
|
128
|
+
return /* @__PURE__ */ jsxs("button", { type: "button", className: "uix-scheduling-calendar__agenda-row", "data-item-id": entry.id, "data-band": entry.band ?? "none", "data-status": entryStatus(entry), "data-state": entry.state, onClick: () => onSelectEntry?.(entry), "aria-label": name, children: [
|
|
129
|
+
/* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__swatch", "aria-hidden": "true", "data-band": entry.band ?? "none", "data-status": entryStatus(entry) }),
|
|
130
|
+
/* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__agenda-time", children: timeText(entry, date) }),
|
|
131
|
+
/* @__PURE__ */ jsxs("span", { className: "uix-scheduling-calendar__agenda-main", children: [
|
|
132
|
+
renderEntry?.(entry) ?? /* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__title", children: entry.title }),
|
|
133
|
+
entry.meta && /* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__agenda-meta", children: entry.meta })
|
|
134
|
+
] }),
|
|
135
|
+
markers.length > 0 && /* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__markers", children: markers.map((marker) => /* @__PURE__ */ jsx("span", { children: renderMarker(marker, true) }, marker.id)) }),
|
|
136
|
+
/* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__status", children: entryStatusText(entry) })
|
|
137
|
+
] });
|
|
138
|
+
};
|
|
139
|
+
const hidden = (group) => {
|
|
140
|
+
const text = fillLabel(labels.hiddenInDay, { count: group.hiddenCount ?? 0 });
|
|
141
|
+
return onShowMore ? /* @__PURE__ */ jsx("button", { type: "button", className: "uix-scheduling-calendar__agenda-more", onClick: () => onShowMore(group.date, group.rows), children: text }) : /* @__PURE__ */ jsx("p", { className: "uix-scheduling-calendar__agenda-more", children: text });
|
|
142
|
+
};
|
|
143
|
+
const continues = (group) => /* @__PURE__ */ jsx("p", { className: "uix-scheduling-calendar__agenda-continues", children: fillLabel(labels.continuesInDay, { count: group.continuesCount ?? 0 }) });
|
|
144
|
+
if (groups.length === 0) return /* @__PURE__ */ jsx("div", { className: "uix-scheduling-calendar__agenda", role: "region", "aria-label": labels.agenda, children: /* @__PURE__ */ jsx("p", { children: labels.agendaEmpty }) });
|
|
145
|
+
if (flat) {
|
|
146
|
+
return /* @__PURE__ */ jsx("div", { ref: virtual.containerRef, className: "uix-scheduling-calendar__agenda uix-scheduling-calendar__agenda--virtual", "data-narrow": narrow || void 0, role: "region", "aria-label": labels.agenda, children: /* @__PURE__ */ jsx("div", { className: "uix-scheduling-calendar__agenda-window", style: { paddingTop: virtual.padTop, paddingBottom: virtual.padBottom }, children: virtual.rows.map((item) => /* @__PURE__ */ jsx("div", { className: "uix-scheduling-calendar__agenda-vrow", "data-kind": item.kind, children: item.kind === "heading" ? heading(item.group, false) : item.kind === "note" ? note(item.overlay) : item.kind === "entry" ? row(item.entry, item.group.date) : item.kind === "hidden" ? hidden(item.group) : continues(item.group) }, item.key)) }) });
|
|
147
|
+
}
|
|
148
|
+
return /* @__PURE__ */ jsx("div", { ref: grouped, className: "uix-scheduling-calendar__agenda", role: "region", "aria-label": labels.agenda, children: groups.map((group, index) => {
|
|
149
|
+
const rowKeys = uniqueKeys(group.rows.map((entry) => entry.id));
|
|
150
|
+
return /* @__PURE__ */ jsxs("section", { className: "uix-scheduling-calendar__agenda-group", "data-date": group.date, children: [
|
|
151
|
+
heading(group, true),
|
|
152
|
+
group.rows.length > 0 && /* @__PURE__ */ jsx("ol", { className: "uix-scheduling-calendar__agenda-rows", children: group.rows.map((entry, at) => /* @__PURE__ */ jsx("li", { children: row(entry, group.date) }, rowKeys[at])) }),
|
|
153
|
+
(group.hiddenCount ?? 0) > 0 && hidden(group),
|
|
154
|
+
(group.continuesCount ?? 0) > 0 && continues(group)
|
|
155
|
+
] }, groupKeys[index]);
|
|
156
|
+
}) });
|
|
157
|
+
}
|
|
158
|
+
export {
|
|
159
|
+
AGENDA_VIRTUAL_ROW,
|
|
160
|
+
SchedulingAgenda
|
|
161
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import 'react';
|
|
2
2
|
import '../calendar-model.js';
|
|
3
3
|
import '../scheduling-calendar-model.js';
|
|
4
|
-
export { D as DEFAULT_SCHEDULING_CALENDAR_LABELS, S as
|
|
4
|
+
export { D as DEFAULT_SCHEDULING_CALENDAR_LABELS, S as SchedulingAgendaGroup, a as SchedulingBand, b as SchedulingCalendar, c as SchedulingCalendarDay, d as SchedulingCalendarEntry, e as SchedulingCalendarLabels, f as SchedulingCalendarOverlay, g as SchedulingCalendarProps, h as SchedulingCalendarView, i as SchedulingDatePart, j as SchedulingEntryState, k as SchedulingLegendItem, l as SchedulingMarker, m as SchedulingMarkerEmphasis, n as SchedulingOverlayKind, o as SchedulingOverlayPattern, p as SchedulingStatus } from '../SchedulingCalendar-DSLQ6AY5.js';
|
|
@@ -3,6 +3,7 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
4
4
|
import { addCalendarDays, addCalendarMonths, buildMonthGrid, cachedDateTimeFormat, startOfMonth, zonedDateKey, zonedTimeOfDay } from "../calendar-model.js";
|
|
5
5
|
import { TOP_LANE_CROSS_MIDNIGHT_MINUTES, itemDaySpan, layoutMonthSpans } from "../scheduling-calendar-model.js";
|
|
6
|
+
import { SchedulingAgenda } from "./SchedulingAgenda.js";
|
|
6
7
|
import { SchedulingTimeGrid } from "./SchedulingTimeGrid.js";
|
|
7
8
|
import { cx } from "../cx.js";
|
|
8
9
|
import { useUixLabels } from "../labels-context.js";
|
|
@@ -45,6 +46,9 @@ const DEFAULT_SCHEDULING_CALENDAR_LABELS = {
|
|
|
45
46
|
moreWindowsLabel: "{count} more windows, from {date}",
|
|
46
47
|
topLane: "All-day and longer entries",
|
|
47
48
|
continuesFrom: "from {time}",
|
|
49
|
+
allDay: "All day",
|
|
50
|
+
hiddenInDay: "{count} not shown \u2014 open day",
|
|
51
|
+
continuesInDay: "Continues: {count} listed under an earlier day",
|
|
48
52
|
moveHint: "Hold Shift and press an arrow key to move it. Enter confirms, Escape cancels.",
|
|
49
53
|
moveProposed: "Move to {start} \u2013 {end}. Enter confirms, Escape cancels.",
|
|
50
54
|
moveCancelled: "Move cancelled.",
|
|
@@ -60,6 +64,7 @@ const INTL_PARTS = {
|
|
|
60
64
|
};
|
|
61
65
|
const EMPTY_OVERLAYS = [];
|
|
62
66
|
const EMPTY_ENTRIES = [];
|
|
67
|
+
const EMPTY_IDS = [];
|
|
63
68
|
const DEFAULT_LANE_CAP = 2;
|
|
64
69
|
const DEFAULT_CHIP_ROWS = 3;
|
|
65
70
|
function coveredDays(start, end, timeZone) {
|
|
@@ -107,6 +112,10 @@ function SchedulingCalendar({
|
|
|
107
112
|
notice,
|
|
108
113
|
emptyNote,
|
|
109
114
|
onSelectDate,
|
|
115
|
+
agendaGroups,
|
|
116
|
+
agendaHeadingLevel = 3,
|
|
117
|
+
virtualizeAbove = 200,
|
|
118
|
+
monthDensity = "full",
|
|
110
119
|
timeGrid = false,
|
|
111
120
|
now,
|
|
112
121
|
canMove = false,
|
|
@@ -140,7 +149,8 @@ function SchedulingCalendar({
|
|
|
140
149
|
const gridRef = useRef(null);
|
|
141
150
|
const picksGiven = dayEntries !== void 0;
|
|
142
151
|
const controlled = picksGiven || dayInfo !== void 0;
|
|
143
|
-
const
|
|
152
|
+
const countsOnly = monthDensity === "counts" && view === "month";
|
|
153
|
+
const fixed = controlled || maxEntriesPerDay !== void 0 && onShowMore !== void 0 || countsOnly;
|
|
144
154
|
const windowCap = Math.max(0, windowLaneCap ?? (fixed ? DEFAULT_LANE_CAP : Infinity));
|
|
145
155
|
const spanCap = Math.max(0, spanLaneCap ?? (fixed ? DEFAULT_LANE_CAP : Infinity));
|
|
146
156
|
const visibleEntries = useMemo(() => entries.filter((entry) => filter?.(entry) ?? true), [entries, filter]);
|
|
@@ -178,13 +188,13 @@ function SchedulingCalendar({
|
|
|
178
188
|
windowIds.add(overlay.id);
|
|
179
189
|
inputs.push({ id: overlay.id, start: overlay.start, end: overlay.end, group: "window" });
|
|
180
190
|
}
|
|
181
|
-
for (const { entry } of multiDay) {
|
|
191
|
+
for (const { entry } of countsOnly ? [] : multiDay) {
|
|
182
192
|
if (itemIds.has(entry.id)) continue;
|
|
183
193
|
itemIds.add(entry.id);
|
|
184
194
|
inputs.push({ id: entry.id, start: entry.start, end: entry.end, group: "item" });
|
|
185
195
|
}
|
|
186
196
|
return layoutMonthSpans(inputs, days, { timeZone, laneCap: { window: windowCap, item: spanCap } });
|
|
187
|
-
}, [spanLayout, view, timeGridView, overlays, multiDay, days, timeZone, windowCap, spanCap]);
|
|
197
|
+
}, [spanLayout, view, timeGridView, overlays, multiDay, days, timeZone, windowCap, spanCap, countsOnly]);
|
|
188
198
|
const placedByRow = useMemo(() => {
|
|
189
199
|
const rows = /* @__PURE__ */ new Map();
|
|
190
200
|
for (const placed of layout.placed) {
|
|
@@ -273,13 +283,13 @@ function SchedulingCalendar({
|
|
|
273
283
|
["--uix-scheduling-calendar-lane"]: placed.group === "window" ? placed.lane : windowLanes + placed.lane
|
|
274
284
|
});
|
|
275
285
|
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));
|
|
286
|
+
const chipRows = countsOnly ? -1 : Math.max(0, maxEntriesPerDay ?? DEFAULT_CHIP_ROWS, ...Object.values(dayEntries ?? {}).map((list) => list.length));
|
|
277
287
|
const renderDay = (date, index) => {
|
|
278
288
|
const info = dayInfo?.[date];
|
|
279
289
|
const all = picksGiven ? dayEntries[date] ?? EMPTY_ENTRIES : singles.get(date) ?? EMPTY_ENTRIES;
|
|
280
290
|
const overflowing = !picksGiven && maxEntriesPerDay !== void 0 && all.length > maxEntriesPerDay;
|
|
281
291
|
const collapsed = overflowing && (controlled || onShowMore !== void 0 || !expandedDates.has(date));
|
|
282
|
-
const shown = collapsed ? all.slice(0, Math.max(0, maxEntriesPerDay)) : all;
|
|
292
|
+
const shown = countsOnly ? EMPTY_ENTRIES : collapsed ? all.slice(0, Math.max(0, maxEntriesPerDay)) : all;
|
|
283
293
|
const hiddenCount = info ? info.overflowCount : picksGiven ? 0 : all.length - shown.length;
|
|
284
294
|
const onDay = picksGiven ? all : entriesOn(date);
|
|
285
295
|
const badge = renderDayBadge?.(date, onDay);
|
|
@@ -299,7 +309,7 @@ function SchedulingCalendar({
|
|
|
299
309
|
] }) : dateButton,
|
|
300
310
|
/* @__PURE__ */ jsxs("div", { className: "uix-scheduling-calendar__entries", children: [
|
|
301
311
|
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: [
|
|
312
|
+
countsOnly ? null : 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: [
|
|
303
313
|
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: moreText }),
|
|
304
314
|
/* @__PURE__ */ jsx("span", { className: "uix-visually-hidden", children: moreName })
|
|
305
315
|
] })) : 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 })
|
|
@@ -309,8 +319,10 @@ function SchedulingCalendar({
|
|
|
309
319
|
const renderWeek = (row) => {
|
|
310
320
|
const rowDays = days.slice(row * 7, row * 7 + 7);
|
|
311
321
|
const windowLanes = lanesUsed(row, "window");
|
|
312
|
-
const spanLanes = lanesUsed(row, "item");
|
|
313
|
-
const
|
|
322
|
+
const spanLanes = countsOnly ? 0 : lanesUsed(row, "item");
|
|
323
|
+
const hiddenIds = layout.hiddenByRow?.[row] ?? EMPTY_IDS;
|
|
324
|
+
const drawnWindows = countsOnly ? new Set((placedByRow.get(row) ?? []).filter((placed) => placed.group === "window").map((placed) => placed.id)) : void 0;
|
|
325
|
+
const hidden = countsOnly ? hiddenIds.filter((id) => overlayById.has(id) && !drawnWindows.has(id)).length : hiddenIds.length;
|
|
314
326
|
const firstHiddenDay = layout.firstHiddenDayByRow?.[row] ?? rowDays[0];
|
|
315
327
|
const moreText = fillLabel(labels.moreSpans, { count: hidden });
|
|
316
328
|
const moreName = fillLabel(labels.moreSpansLabel, { count: hidden, date: dateText(firstHiddenDay, "day") });
|
|
@@ -329,7 +341,7 @@ function SchedulingCalendar({
|
|
|
329
341
|
] }) }, `window-${placed.id}`);
|
|
330
342
|
}
|
|
331
343
|
const entry = entryById.get(placed.id);
|
|
332
|
-
if (!entry) return null;
|
|
344
|
+
if (!entry || countsOnly) return null;
|
|
333
345
|
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
346
|
}),
|
|
335
347
|
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: [
|
|
@@ -341,7 +353,7 @@ function SchedulingCalendar({
|
|
|
341
353
|
const gridStyle = fixed ? {
|
|
342
354
|
["--uix-scheduling-calendar-chips"]: chipRows,
|
|
343
355
|
["--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")),
|
|
356
|
+
Math.max(Number.isFinite(windowCap) ? windowCap : 0, lanesUsedAnywhere("window")) + (countsOnly ? 0 : Math.max(Number.isFinite(spanCap) ? spanCap : 0, lanesUsedAnywhere("item"))),
|
|
345
357
|
Object.keys(layout.hiddenByRow ?? {}).length > 0 ? 1 : 0
|
|
346
358
|
)
|
|
347
359
|
} : void 0;
|
|
@@ -358,7 +370,28 @@ function SchedulingCalendar({
|
|
|
358
370
|
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
371
|
/* @__PURE__ */ jsx("p", { children: error }),
|
|
360
372
|
onRetry && /* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--secondary", onClick: onRetry, children: labels.retry })
|
|
361
|
-
] }) : view === "agenda"
|
|
373
|
+
] }) : view === "agenda" && agendaGroups ? /* @__PURE__ */ jsx(
|
|
374
|
+
SchedulingAgenda,
|
|
375
|
+
{
|
|
376
|
+
groups: agendaGroups,
|
|
377
|
+
timeZone,
|
|
378
|
+
labels,
|
|
379
|
+
headingLevel: agendaHeadingLevel,
|
|
380
|
+
virtualizeAbove,
|
|
381
|
+
dateText,
|
|
382
|
+
formatInstant,
|
|
383
|
+
entryName,
|
|
384
|
+
entryStatus: statusOf,
|
|
385
|
+
entryStatusText: stateText,
|
|
386
|
+
entryMarkers: markersOf,
|
|
387
|
+
overlayName,
|
|
388
|
+
renderEntry,
|
|
389
|
+
renderMarker,
|
|
390
|
+
onSelectEntry,
|
|
391
|
+
onSelectOverlay,
|
|
392
|
+
onShowMore
|
|
393
|
+
}
|
|
394
|
+
) : view === "agenda" ? /* @__PURE__ */ jsx("div", { className: "uix-scheduling-calendar__agenda", role: "region", "aria-label": labels.agenda, 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
395
|
const markers = markersOf(entry);
|
|
363
396
|
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: [
|
|
364
397
|
/* @__PURE__ */ jsxs("span", { children: [
|
|
@@ -407,7 +440,7 @@ function SchedulingCalendar({
|
|
|
407
440
|
emptyNote,
|
|
408
441
|
nothingToShow
|
|
409
442
|
}
|
|
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: [
|
|
443
|
+
) : /* @__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, role: "group", "aria-label": fillLabel(labels.grid, { view: viewLabel[view] }), children: [
|
|
411
444
|
days.slice(0, 7).map((date) => /* @__PURE__ */ jsx("div", { className: "uix-scheduling-calendar__weekday", "aria-hidden": "true", children: dateText(date, "weekday") }, `weekday-${date}`)),
|
|
412
445
|
Array.from({ length: days.length / 7 }, (_, row) => renderWeek(row)),
|
|
413
446
|
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-DSLQ6AY5.js';
|
|
4
4
|
import '../calendar-model.js';
|
|
@@ -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-DSLQ6AY5.js';
|
|
5
5
|
import '../calendar-model.js';
|
|
6
6
|
import '../scheduling-calendar-model.js';
|
|
7
7
|
|