@tensor_1/react 2.36.0 → 2.37.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 +13 -0
- package/dist/components/SchedulingAgenda.js +3 -1
- package/dist/components/SchedulingTimeGrid.js +6 -4
- package/dist/components/SchedulingTimeline.js +5 -2
- package/dist/hooks/usePrinting.d.ts +15 -0
- package/dist/hooks/usePrinting.js +29 -0
- package/dist/index.cjs +264 -230
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -460,6 +460,18 @@ const spanLayout = layoutMonthSpans(spansInSalienceOrder, grid, { timeZone, week
|
|
|
460
460
|
- **`<List roving>`**: the items are one tab stop (ArrowUp/ArrowDown, Home, End; Enter or Space activates the
|
|
461
461
|
focused item's `onClick`). A control inside an item keeps its own tab stop and keys. Items may be wrapped or
|
|
462
462
|
rendered later by a child component.
|
|
463
|
+
- **Print (HAR-1541).** The stylesheet has `@media print` rules; add no print CSS of your own. On paper the same
|
|
464
|
+
markup has no scroller, fits the page width, and keeps a week row or an agenda row on one page. The colours are
|
|
465
|
+
the `--uix-print-*` tokens (light, whatever theme the screen has). Nothing depends on a background: the `high`
|
|
466
|
+
band is a heavy edge on a paper surface (on paper `--calendar-band-high-fill` is the paper's and the edge reads
|
|
467
|
+
the new `--calendar-band-high-edge`; no screen rule reads it), each window pattern has an edge style of its own (`solid`, `diagonal` dashed, `cross`
|
|
468
|
+
double, `dotted` dotted), and the hour lines are drawn as lines. Month chips wrap to show the whole title, and
|
|
469
|
+
a time-grid item is at least as tall as its time and grows to hold its words (one too short for a line on a
|
|
470
|
+
screen prints them too). The header's buttons, the "now" line and a pending move are not printed; the period
|
|
471
|
+
title, the zone label and the legend are. A long agenda mounts every row while the page prints (`beforeprint`,
|
|
472
|
+
or print media) and is a window of rows again afterwards. One limit: `window.print()` called from inside a
|
|
473
|
+
React effect, on mount or later, runs before those rows are there. Such a page calls it from an event handler
|
|
474
|
+
or a timer, or passes `virtualizeAbove={Infinity}`.
|
|
463
475
|
- **Colour.** Every calendar colour is named once at the top of `scheduling-calendar.css` (`--calendar-*`).
|
|
464
476
|
- **Deprecated, still working in 2.x:** `entry.state` / `SchedulingEntryState`, `overlay.kind` /
|
|
465
477
|
`SchedulingOverlayKind` and the `previous` / `next` labels. An entry with `state` is no longer tinted: the two
|
|
@@ -502,6 +514,7 @@ time zone, the same proposal model for moves.
|
|
|
502
514
|
- **Notice and counts:** `notice` is shown above the axis; `columnNotes` puts a note on the column of a day (`YYYY-MM-DD` in `timeZone`).
|
|
503
515
|
- **Row heights:** with `groups`, `laneIds` or a virtual window every row has a fixed height (a lane is its sub-rows, a group head one line) and a lane label that does not fit is cut with an ellipsis. A timeline that uses none of them keeps rows that grow with their label.
|
|
504
516
|
- **Colour:** every colour is named once at the top of `scheduling-timeline.css` (`--timeline-*`). Forced colours are handled in `forced-colors.css`, as for the calendar.
|
|
517
|
+
- **Print (HAR-1541).** The stylesheet has `@media print` rules; add no print CSS of your own. On paper the timeline has no scroller and nothing sticks: the axis is fitted to the page width and the bars follow (every position is a share of the axis), and a lane stays on one page. The colours are the `--uix-print-*` tokens (light, whatever theme the screen has). Nothing depends on a background: the `high` band is a heavy edge on a paper surface (on paper `--timeline-band-high-fill` is the paper's and the edge reads the new `--timeline-band-high-edge`; no screen rule reads it), each window pattern has an edge style of its own, and the grid lines are drawn as lines. A note on the column of a day is not cut at its column (notes on neighbouring days can meet on a narrow page). The "now" line and a pending move are not printed. A timeline over `virtualizeAbove` rows mounts every row and bar while the page prints and windows again afterwards; a page that calls `window.print()` from inside a React effect calls it from an event handler or a timer, or passes `virtualizeAbove={Infinity}`. Limits: a bar keeps its share of the axis, so on a narrow page a short bar shows its time and not its title (the title is still the bar's name); and the bars are packed into sub-rows for the width the timeline had on the screen, so print from a window about as wide as the page when bars are short and close together.
|
|
505
518
|
- **Deprecated, still working in 2.x:** `item.state` / `SchedulingEntryState`, `overlay.kind` / `SchedulingTimelineOverlayKind` and the `states` / `overlays` labels. A bar with `state` is no longer tinted: the two problem states show a marker with the word for that state, and the kinds are told apart by pattern.
|
|
506
519
|
- Pure helpers are exported for tests and server code: `timelineTicks`, `timelineSubTicks`, `timelineRepeatedHourOffset`, `placeSpan`, `layoutLane`, `shiftSpan`, `snapToStep`, `pixelsToMs`, `defaultTimelineStep`, `timelineStepDelta`.
|
|
507
520
|
|
|
@@ -4,6 +4,7 @@ import { createElement, useEffect, useLayoutEffect, useRef, useState } from "rea
|
|
|
4
4
|
import { addCalendarDays, zonedDateKey, zonedTimeOfDay } from "../calendar-model.js";
|
|
5
5
|
import { itemDaySpan } from "../scheduling-calendar-model.js";
|
|
6
6
|
import { useVirtualRows } from "../hooks/useVirtualRows.js";
|
|
7
|
+
import { usePrinting } from "../hooks/usePrinting.js";
|
|
7
8
|
import { fillLabel } from "../fill-label.js";
|
|
8
9
|
import { syncRovingStop } from "../roving.js";
|
|
9
10
|
const AGENDA_VIRTUAL_ROW = 44;
|
|
@@ -58,7 +59,8 @@ function AgendaBody({
|
|
|
58
59
|
});
|
|
59
60
|
}
|
|
60
61
|
const [narrow, setNarrow] = useState(false);
|
|
61
|
-
const
|
|
62
|
+
const printing = usePrinting(flat);
|
|
63
|
+
const virtual = useVirtualRows(flatRows, { rowHeight: narrow ? AGENDA_NARROW_ROW : AGENDA_VIRTUAL_ROW, threshold: 0, estimatedViewportHeight: AGENDA_VIRTUAL_VIEWPORT, enabled: !printing });
|
|
62
64
|
const scroller = virtual.containerRef;
|
|
63
65
|
useLayoutEffect(() => {
|
|
64
66
|
const box = scroller.current;
|
|
@@ -25,16 +25,18 @@ const GridItem = memo(function GridItem2({ entry, segment, column, alone, name,
|
|
|
25
25
|
"--uix-scheduling-calendar-lane": segment.lane,
|
|
26
26
|
"--uix-scheduling-calendar-lanes": segment.laneCount
|
|
27
27
|
});
|
|
28
|
-
const
|
|
28
|
+
const custom = renderEntry?.(entry, { availableLines: lines });
|
|
29
|
+
const text = custom ?? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
29
30
|
!continuation && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
30
31
|
/* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__time", children: zonedTimeOfDay(entry.start, timeZone) }),
|
|
31
32
|
" "
|
|
32
33
|
] }),
|
|
33
34
|
/* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__title", children: entry.title })
|
|
34
|
-
] })
|
|
35
|
+
] });
|
|
36
|
+
const noRoom = lines === 0 && (custom === void 0 || custom === null) ? "none" : void 0;
|
|
35
37
|
const body = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
36
|
-
continuation &&
|
|
37
|
-
(!continuation || alone) && text != null && text !== false && /* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__entry-text", children: text }),
|
|
38
|
+
continuation && /* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__tg-from", "data-room": lines === 0 ? "none" : void 0, children: continuesFrom }),
|
|
39
|
+
(!continuation || alone) && text != null && text !== false && /* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__entry-text", "data-room": noRoom, children: text }),
|
|
38
40
|
markers.length > 0 && /* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__markers", children: markers.map((marker) => /* @__PURE__ */ jsx("span", { children: renderMarker(marker) }, marker.id)) })
|
|
39
41
|
] });
|
|
40
42
|
const shared = {
|
|
@@ -6,6 +6,7 @@ import { proposeMove } from "../scheduling-calendar-model.js";
|
|
|
6
6
|
import { cx } from "../cx.js";
|
|
7
7
|
import { fillLabel } from "../fill-label.js";
|
|
8
8
|
import { useUixLabels } from "../labels-context.js";
|
|
9
|
+
import { usePrinting } from "../hooks/usePrinting.js";
|
|
9
10
|
import { defaultTimelineStep, layoutLane, pixelsToMs, placeSpan, shiftSpan, timelineRepeatedHourOffset, timelineStepDelta, timelineSubTicks, timelineTicks } from "../scheduling-timeline-model.js";
|
|
10
11
|
import { timelineRowExtents, timelineWindow } from "../scheduling-timeline-rows.js";
|
|
11
12
|
const DEFAULT_SCHEDULING_TIMELINE_LABELS = {
|
|
@@ -253,8 +254,10 @@ function SchedulingTimeline({
|
|
|
253
254
|
return set;
|
|
254
255
|
}, [rows, overlays, range.start, range.end]);
|
|
255
256
|
const extents = useMemo(() => timelineRowExtents(rows.map((row, index) => row.kind === "head" ? { kind: "head", subRows: strips.has(index) ? 1 : 0 } : { kind: "lane", subRows: row.subRows })), [rows, strips]);
|
|
256
|
-
const
|
|
257
|
-
const
|
|
257
|
+
const windowed = lanes.length + (groups?.length ?? 0) > virtualizeAbove;
|
|
258
|
+
const printing = usePrinting(windowed);
|
|
259
|
+
const virtual = windowed && !printing;
|
|
260
|
+
const fixed = windowed || groups !== void 0 || overlays.some((overlay) => overlay.laneIds !== void 0) || markers.some((marker) => marker.laneIds !== void 0);
|
|
258
261
|
useEffect(() => {
|
|
259
262
|
if (!virtual || !scroller) return void 0;
|
|
260
263
|
let frame = 0;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `true` while the page is printed, or shown with print media.
|
|
3
|
+
*
|
|
4
|
+
* A view that mounts only the rows near its viewport uses this to mount all of them for the
|
|
5
|
+
* paper. The browser lays the page out for print as soon as the `beforeprint` handlers return,
|
|
6
|
+
* so the state is committed inside the event (`flushSync`), not in a later task.
|
|
7
|
+
*
|
|
8
|
+
* It cannot answer in time when `window.print()` is called from inside a React effect or
|
|
9
|
+
* lifecycle method, on mount or later: React commits the change after the effect, which is
|
|
10
|
+
* after the page was laid out. A page that prints itself that way turns windowing off through
|
|
11
|
+
* the component's own prop instead, or calls `print()` from an event handler or a timer.
|
|
12
|
+
*/
|
|
13
|
+
declare function usePrinting(enabled?: boolean): boolean;
|
|
14
|
+
|
|
15
|
+
export { usePrinting };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { flushSync } from "react-dom";
|
|
4
|
+
function usePrinting(enabled = true) {
|
|
5
|
+
const [printing, setPrinting] = useState(false);
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
if (!enabled || typeof window === "undefined") return void 0;
|
|
8
|
+
const set = (next) => {
|
|
9
|
+
flushSync(() => setPrinting(next));
|
|
10
|
+
};
|
|
11
|
+
const before = () => set(true);
|
|
12
|
+
const after = () => set(false);
|
|
13
|
+
const query = typeof window.matchMedia === "function" ? window.matchMedia("print") : null;
|
|
14
|
+
const changed = (event) => set(event.matches);
|
|
15
|
+
window.addEventListener("beforeprint", before);
|
|
16
|
+
window.addEventListener("afterprint", after);
|
|
17
|
+
query?.addEventListener?.("change", changed);
|
|
18
|
+
setPrinting(Boolean(query?.matches));
|
|
19
|
+
return () => {
|
|
20
|
+
window.removeEventListener("beforeprint", before);
|
|
21
|
+
window.removeEventListener("afterprint", after);
|
|
22
|
+
query?.removeEventListener?.("change", changed);
|
|
23
|
+
};
|
|
24
|
+
}, [enabled]);
|
|
25
|
+
return enabled && printing;
|
|
26
|
+
}
|
|
27
|
+
export {
|
|
28
|
+
usePrinting
|
|
29
|
+
};
|