@tensor_1/react 2.19.2 → 2.21.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 +14 -0
- package/dist/{ChartCore-Do6gRBN3.d.cts → ChartCore-DzWe10bN.d.cts} +16 -1
- package/dist/chart-preset.cjs +25 -13
- package/dist/chart-preset.d.cts +2 -2
- package/dist/chart-preset.d.ts +1 -1
- package/dist/chart-preset.js +2 -1
- package/dist/chart.cjs +25 -13
- package/dist/chart.d.cts +2 -2
- package/dist/chart.d.ts +1 -1
- package/dist/chart.js +2 -1
- package/dist/components/AppShell.d.ts +5 -1
- package/dist/components/AppShell.js +4 -2
- package/dist/components/Avatar.d.ts +3 -1
- package/dist/components/Avatar.js +2 -2
- package/dist/components/BrandProfiles.d.ts +29 -2
- package/dist/components/BrandProfiles.js +45 -20
- package/dist/components/BuilderCanvas.d.ts +27 -2
- package/dist/components/BuilderCanvas.js +45 -22
- package/dist/components/Chart.d.ts +1 -1
- package/dist/components/Chart.js +2 -1
- package/dist/components/ChartCore.d.ts +17 -2
- package/dist/components/ChartCore.js +24 -13
- package/dist/components/ColorPicker.d.ts +24 -2
- package/dist/components/ColorPicker.js +38 -20
- package/dist/components/CommandPalette.d.ts +3 -1
- package/dist/components/CommandPalette.js +2 -2
- package/dist/components/CopyButton.d.ts +25 -0
- package/dist/components/CopyButton.js +52 -0
- package/dist/components/DateRangePicker.d.ts +21 -2
- package/dist/components/DateRangePicker.js +26 -6
- package/dist/components/DetailPage.d.ts +22 -3
- package/dist/components/DetailPage.js +33 -3
- package/dist/components/DiffViewer.d.ts +34 -3
- package/dist/components/DiffViewer.js +60 -40
- package/dist/components/Drawer.d.ts +3 -1
- package/dist/components/Drawer.js +2 -2
- package/dist/components/Inbox.d.ts +3 -1
- package/dist/components/Inbox.js +2 -2
- package/dist/components/MatchReview.d.ts +49 -2
- package/dist/components/MatchReview.js +72 -30
- package/dist/components/Meter.d.ts +3 -1
- package/dist/components/Meter.js +3 -2
- package/dist/components/Pagination.d.ts +10 -2
- package/dist/components/Pagination.js +11 -4
- package/dist/components/Peek.d.ts +7 -1
- package/dist/components/Peek.js +4 -4
- package/dist/components/RelationshipGraph.js +6 -2
- package/dist/components/RuleBuilder.d.ts +42 -2
- package/dist/components/RuleBuilder.js +71 -37
- package/dist/components/SchedulingCalendar.d.ts +25 -2
- package/dist/components/SchedulingCalendar.js +38 -15
- package/dist/components/Stat.d.ts +24 -3
- package/dist/components/Stat.js +51 -10
- package/dist/components/Table.d.ts +11 -3
- package/dist/components/Table.js +5 -8
- package/dist/components/Tabs.d.ts +14 -1
- package/dist/components/Tabs.js +88 -19
- package/dist/components/Toast.d.ts +6 -2
- package/dist/components/Toast.js +4 -4
- package/dist/components/Tree.d.ts +9 -1
- package/dist/components/Tree.js +55 -8
- package/dist/copy-model.d.ts +12 -0
- package/dist/copy-model.js +12 -0
- package/dist/fill-label.d.ts +7 -0
- package/dist/fill-label.js +9 -0
- package/dist/index.cjs +1480 -1004
- package/dist/index.d.cts +387 -30
- package/dist/index.d.ts +11 -10
- package/dist/index.js +20 -9
- package/package.json +1 -1
|
@@ -19,6 +19,28 @@ interface SchedulingCalendarOverlay {
|
|
|
19
19
|
end: string;
|
|
20
20
|
kind: SchedulingOverlayKind;
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Every word the calendar renders (TENSOR RX-125, UIX-12). `{timeZone}`, `{view}`,
|
|
24
|
+
* `{title}`, `{state}`, `{start}`, `{end}` are placeholders.
|
|
25
|
+
*/
|
|
26
|
+
interface SchedulingCalendarLabels {
|
|
27
|
+
region: string;
|
|
28
|
+
previous: string;
|
|
29
|
+
next: string;
|
|
30
|
+
viewGroup: string;
|
|
31
|
+
viewMonth: string;
|
|
32
|
+
viewWeek: string;
|
|
33
|
+
viewAgenda: string;
|
|
34
|
+
loading: string;
|
|
35
|
+
retry: string;
|
|
36
|
+
agenda: string;
|
|
37
|
+
agendaEmpty: string;
|
|
38
|
+
grid: string;
|
|
39
|
+
entry: string;
|
|
40
|
+
legend: string;
|
|
41
|
+
states: Record<SchedulingEntryState, string>;
|
|
42
|
+
}
|
|
43
|
+
declare const DEFAULT_SCHEDULING_CALENDAR_LABELS: SchedulingCalendarLabels;
|
|
22
44
|
interface SchedulingCalendarProps {
|
|
23
45
|
entries: SchedulingCalendarEntry[];
|
|
24
46
|
anchorDate: string;
|
|
@@ -35,8 +57,9 @@ interface SchedulingCalendarProps {
|
|
|
35
57
|
error?: string;
|
|
36
58
|
onRetry?: () => void;
|
|
37
59
|
className?: string;
|
|
60
|
+
labels?: Partial<SchedulingCalendarLabels>;
|
|
38
61
|
}
|
|
39
62
|
/** Month/week/agenda calendar for UTC ranges rendered in an explicit IANA time zone. */
|
|
40
|
-
declare function SchedulingCalendar({ entries, anchorDate, timeZone, view: controlledView, onViewChange, onAnchorDateChange, onSelectEntry, overlays, filter, renderEntry, locale, loading, error, onRetry, className, }: SchedulingCalendarProps): react.JSX.Element;
|
|
63
|
+
declare function SchedulingCalendar({ entries, anchorDate, timeZone, view: controlledView, onViewChange, onAnchorDateChange, onSelectEntry, overlays, filter, renderEntry, locale, loading, error, onRetry, className, labels: labelOverrides, }: SchedulingCalendarProps): react.JSX.Element;
|
|
41
64
|
|
|
42
|
-
export { SchedulingCalendar, type SchedulingCalendarEntry, type SchedulingCalendarOverlay, type SchedulingCalendarProps, type SchedulingCalendarView, type SchedulingEntryState, type SchedulingOverlayKind };
|
|
65
|
+
export { DEFAULT_SCHEDULING_CALENDAR_LABELS, SchedulingCalendar, type SchedulingCalendarEntry, type SchedulingCalendarLabels, type SchedulingCalendarOverlay, type SchedulingCalendarProps, type SchedulingCalendarView, type SchedulingEntryState, type SchedulingOverlayKind };
|
|
@@ -4,7 +4,24 @@ import { useEffect, useMemo, useRef, useState } from "react";
|
|
|
4
4
|
import { addCalendarDays, addCalendarMonths, buildMonthGrid, startOfMonth, zonedDateSpan } from "../calendar-model.js";
|
|
5
5
|
import { cx } from "../cx.js";
|
|
6
6
|
import { StatusPill } from "./StatusPill.js";
|
|
7
|
-
|
|
7
|
+
import { fillLabel } from "../fill-label.js";
|
|
8
|
+
const DEFAULT_SCHEDULING_CALENDAR_LABELS = {
|
|
9
|
+
region: "Scheduling calendar in {timeZone}",
|
|
10
|
+
previous: "Previous",
|
|
11
|
+
next: "Next",
|
|
12
|
+
viewGroup: "Calendar view",
|
|
13
|
+
viewMonth: "Month",
|
|
14
|
+
viewWeek: "Week",
|
|
15
|
+
viewAgenda: "Agenda",
|
|
16
|
+
loading: "Loading schedule\u2026",
|
|
17
|
+
retry: "Try again",
|
|
18
|
+
agenda: "Schedule agenda",
|
|
19
|
+
agendaEmpty: "No scheduled entries match the current filters.",
|
|
20
|
+
grid: "{view} schedule",
|
|
21
|
+
entry: "{title}, {state}, {start} to {end}",
|
|
22
|
+
legend: "Schedule state legend",
|
|
23
|
+
states: { scheduled: "Scheduled", conflicted: "Conflicted", "in-progress": "In progress", "blackout-violation": "Blackout violation" }
|
|
24
|
+
};
|
|
8
25
|
const stateTone = (state) => state === "scheduled" ? "info" : state === "in-progress" ? "success" : "danger";
|
|
9
26
|
const dateLabel = (date, locale) => new Intl.DateTimeFormat(locale, { dateStyle: "full", timeZone: "UTC" }).format(/* @__PURE__ */ new Date(`${date}T00:00:00Z`));
|
|
10
27
|
const EMPTY_OVERLAYS = [];
|
|
@@ -23,8 +40,16 @@ function SchedulingCalendar({
|
|
|
23
40
|
loading,
|
|
24
41
|
error,
|
|
25
42
|
onRetry,
|
|
26
|
-
className
|
|
43
|
+
className,
|
|
44
|
+
labels: labelOverrides
|
|
27
45
|
}) {
|
|
46
|
+
const labels = {
|
|
47
|
+
...DEFAULT_SCHEDULING_CALENDAR_LABELS,
|
|
48
|
+
...labelOverrides,
|
|
49
|
+
states: { ...DEFAULT_SCHEDULING_CALENDAR_LABELS.states, ...labelOverrides?.states }
|
|
50
|
+
};
|
|
51
|
+
const stateLabel = labels.states;
|
|
52
|
+
const viewLabel = { month: labels.viewMonth, week: labels.viewWeek, agenda: labels.viewAgenda };
|
|
28
53
|
const [internalView, setInternalView] = useState("month");
|
|
29
54
|
const view = controlledView ?? internalView;
|
|
30
55
|
const [activeDate, setActiveDate] = useState(anchorDate);
|
|
@@ -71,22 +96,19 @@ function SchedulingCalendar({
|
|
|
71
96
|
return date >= span.start && date <= span.end;
|
|
72
97
|
});
|
|
73
98
|
const formatInstant = (instant) => new Intl.DateTimeFormat(locale, { timeZone, dateStyle: "medium", timeStyle: "short" }).format(new Date(instant));
|
|
74
|
-
return /* @__PURE__ */ jsxs("section", { className: cx("uix-scheduling-calendar", className), "aria-label":
|
|
99
|
+
return /* @__PURE__ */ jsxs("section", { className: cx("uix-scheduling-calendar", className), "aria-label": fillLabel(labels.region, { timeZone }), children: [
|
|
75
100
|
/* @__PURE__ */ jsxs("div", { className: "uix-scheduling-calendar__header", children: [
|
|
76
101
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
77
|
-
/* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: () => movePeriod(-1), disabled: !onAnchorDateChange, children:
|
|
78
|
-
/* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: () => movePeriod(1), disabled: !onAnchorDateChange, children:
|
|
102
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: () => movePeriod(-1), disabled: !onAnchorDateChange, children: labels.previous }),
|
|
103
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: () => movePeriod(1), disabled: !onAnchorDateChange, children: labels.next })
|
|
79
104
|
] }),
|
|
80
105
|
/* @__PURE__ */ jsx("strong", { children: view === "month" ? new Intl.DateTimeFormat(locale, { month: "long", year: "numeric", timeZone: "UTC" }).format(/* @__PURE__ */ new Date(`${anchorDate}T00:00:00Z`)) : `${dateLabel(weekStart, locale)} \u2013 ${dateLabel(weekDays[weekDays.length - 1], locale)}` }),
|
|
81
|
-
/* @__PURE__ */ jsx("div", { className: "uix-segmented", role: "group", "aria-label":
|
|
82
|
-
option[0].toUpperCase(),
|
|
83
|
-
option.slice(1)
|
|
84
|
-
] }, option)) })
|
|
106
|
+
/* @__PURE__ */ jsx("div", { className: "uix-segmented", role: "group", "aria-label": labels.viewGroup, children: ["month", "week", "agenda"].map((option) => /* @__PURE__ */ jsx("button", { type: "button", className: "uix-segmented__option", "aria-pressed": view === option, "data-selected": view === option || void 0, onClick: () => setView(option), children: viewLabel[option] }, option)) })
|
|
85
107
|
] }),
|
|
86
|
-
loading ? /* @__PURE__ */ jsx("div", { className: "uix-scheduling-calendar__state", role: "status", children:
|
|
108
|
+
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: [
|
|
87
109
|
/* @__PURE__ */ jsx("p", { children: error }),
|
|
88
|
-
onRetry && /* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--secondary", onClick: onRetry, children:
|
|
89
|
-
] }) : view === "agenda" ? /* @__PURE__ */ jsx("div", { className: "uix-scheduling-calendar__agenda", role: "region", "aria-label":
|
|
110
|
+
onRetry && /* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--secondary", onClick: onRetry, children: labels.retry })
|
|
111
|
+
] }) : 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) => {
|
|
90
112
|
const state = entry.state ?? "scheduled";
|
|
91
113
|
return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("button", { type: "button", onClick: () => onSelectEntry?.(entry), children: [
|
|
92
114
|
/* @__PURE__ */ jsxs("span", { children: [
|
|
@@ -100,7 +122,7 @@ function SchedulingCalendar({
|
|
|
100
122
|
] }),
|
|
101
123
|
/* @__PURE__ */ jsx(StatusPill, { tone: stateTone(state), children: stateLabel[state] })
|
|
102
124
|
] }) }, entry.id);
|
|
103
|
-
}) }) }) : /* @__PURE__ */ jsx("div", { ref: gridRef, className: cx("uix-scheduling-calendar__grid", view === "week" && "uix-scheduling-calendar__grid--week"), role: "group", "aria-label":
|
|
125
|
+
}) }) }) : /* @__PURE__ */ jsx("div", { ref: gridRef, className: cx("uix-scheduling-calendar__grid", view === "week" && "uix-scheduling-calendar__grid--week"), role: "group", "aria-label": fillLabel(labels.grid, { view: viewLabel[view] }), children: days.map((date, index) => {
|
|
104
126
|
const dayEntries = entriesFor(date);
|
|
105
127
|
const dayOverlays = overlaysFor(date);
|
|
106
128
|
return /* @__PURE__ */ jsxs("div", { className: "uix-scheduling-calendar__day", "data-outside": view === "month" && !monthDays[index].inMonth || void 0, children: [
|
|
@@ -109,16 +131,17 @@ function SchedulingCalendar({
|
|
|
109
131
|
/* @__PURE__ */ jsx("div", { className: "uix-scheduling-calendar__entries", children: dayEntries.map((entry) => {
|
|
110
132
|
const span = entrySpans.get(entry.id);
|
|
111
133
|
const state = entry.state ?? "scheduled";
|
|
112
|
-
return /* @__PURE__ */ jsxs("button", { type: "button", className: "uix-scheduling-calendar__entry", "data-state": state, "data-range-start": date === span.start || void 0, "data-range-end": date === span.end || void 0, onClick: () => onSelectEntry?.(entry), "aria-label":
|
|
134
|
+
return /* @__PURE__ */ jsxs("button", { type: "button", className: "uix-scheduling-calendar__entry", "data-state": state, "data-range-start": date === span.start || void 0, "data-range-end": date === span.end || void 0, onClick: () => onSelectEntry?.(entry), "aria-label": fillLabel(labels.entry, { title: entry.title, state: stateLabel[state], start: formatInstant(entry.start), end: formatInstant(entry.end) }), children: [
|
|
113
135
|
/* @__PURE__ */ jsx("span", { children: renderEntry?.(entry) ?? entry.title }),
|
|
114
136
|
/* @__PURE__ */ jsx("span", { className: "uix-visually-hidden", children: stateLabel[state] })
|
|
115
137
|
] }, entry.id);
|
|
116
138
|
}) })
|
|
117
139
|
] }, date);
|
|
118
140
|
}) }),
|
|
119
|
-
/* @__PURE__ */ jsx("div", { className: "uix-scheduling-calendar__legend", "aria-label":
|
|
141
|
+
/* @__PURE__ */ jsx("div", { className: "uix-scheduling-calendar__legend", "aria-label": labels.legend, children: ["scheduled", "conflicted", "in-progress", "blackout-violation"].map((state) => /* @__PURE__ */ jsx("span", { "data-state": state, children: stateLabel[state] }, state)) })
|
|
120
142
|
] });
|
|
121
143
|
}
|
|
122
144
|
export {
|
|
145
|
+
DEFAULT_SCHEDULING_CALENDAR_LABELS,
|
|
123
146
|
SchedulingCalendar
|
|
124
147
|
};
|
|
@@ -5,7 +5,7 @@ interface StatTrend {
|
|
|
5
5
|
direction: 'up' | 'down';
|
|
6
6
|
label: ReactNode;
|
|
7
7
|
}
|
|
8
|
-
interface StatProps extends HTMLAttributes<
|
|
8
|
+
interface StatProps extends HTMLAttributes<HTMLElement> {
|
|
9
9
|
label: ReactNode;
|
|
10
10
|
value: ReactNode;
|
|
11
11
|
/** Optional icon shown at the trailing edge of the label row. */
|
|
@@ -14,8 +14,29 @@ interface StatProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
14
14
|
meta?: ReactNode;
|
|
15
15
|
/** Trend chip rendered before the meta text. */
|
|
16
16
|
trend?: StatTrend;
|
|
17
|
+
/** Outline and value colour. `neutral` (default) is today's tile. */
|
|
18
|
+
tone?: 'neutral' | 'warning' | 'danger';
|
|
19
|
+
/** `hero` (default) is today's KPI size; `compact` is for dense fact bands. */
|
|
20
|
+
size?: 'hero' | 'compact';
|
|
21
|
+
/**
|
|
22
|
+
* Makes the whole tile a `<button>` that opens an editor (a popover or dialog). It gets
|
|
23
|
+
* `aria-haspopup="dialog"` and a trailing chevron.
|
|
24
|
+
*/
|
|
25
|
+
onActivate?: () => void;
|
|
26
|
+
/** Action word appended to the accessible name, e.g. "change" → "Priority: P1, change". */
|
|
27
|
+
activateLabel?: string;
|
|
28
|
+
/** TENSOR RX-125 (UIX-04): the spoken trend direction; English defaults. */
|
|
29
|
+
trendLabels?: {
|
|
30
|
+
up?: string;
|
|
31
|
+
down?: string;
|
|
32
|
+
};
|
|
33
|
+
/** Whether the editor this tile opens is currently open (`aria-expanded`). */
|
|
34
|
+
expanded?: boolean;
|
|
17
35
|
}
|
|
18
|
-
/**
|
|
19
|
-
|
|
36
|
+
/**
|
|
37
|
+
* KPI / stat tile over `.uix-stat`. With `onActivate` the tile is a button; the forwarded
|
|
38
|
+
* ref then points at that button, so a popover can anchor to it.
|
|
39
|
+
*/
|
|
40
|
+
declare const Stat: react.ForwardRefExoticComponent<StatProps & react.RefAttributes<HTMLElement>>;
|
|
20
41
|
|
|
21
42
|
export { Stat, type StatProps, type StatTrend };
|
package/dist/components/Stat.js
CHANGED
|
@@ -1,22 +1,63 @@
|
|
|
1
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useId } from "react";
|
|
2
3
|
import { cx } from "../cx.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
const Chevron = () => /* @__PURE__ */ jsx("span", { className: "uix-stat__chevron", "aria-hidden": "true", children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" }) }) });
|
|
5
|
+
const Stat = forwardRef(function Stat2({ label, value, icon, meta, trend, tone = "neutral", size = "hero", onActivate, activateLabel, expanded, trendLabels, className, onClick, ...props }, ref) {
|
|
6
|
+
const id = useId();
|
|
7
|
+
const interactive = onActivate != null;
|
|
8
|
+
const classes = cx(
|
|
9
|
+
"uix-stat",
|
|
10
|
+
tone !== "neutral" && `uix-stat--${tone}`,
|
|
11
|
+
size === "compact" && "uix-stat--compact",
|
|
12
|
+
interactive && "uix-stat--interactive",
|
|
13
|
+
className
|
|
14
|
+
);
|
|
15
|
+
const Row = interactive ? "span" : "div";
|
|
16
|
+
const body = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
17
|
+
/* @__PURE__ */ jsxs(Row, { className: "uix-stat__label", children: [
|
|
18
|
+
/* @__PURE__ */ jsx("span", { id: interactive ? `${id}-label` : void 0, children: label }),
|
|
19
|
+
icon,
|
|
20
|
+
interactive && /* @__PURE__ */ jsx(Chevron, {})
|
|
8
21
|
] }),
|
|
9
|
-
/* @__PURE__ */ jsx(
|
|
10
|
-
(meta != null || trend != null) && /* @__PURE__ */ jsxs(
|
|
22
|
+
/* @__PURE__ */ jsx(Row, { className: "uix-stat__value", id: interactive ? `${id}-value` : void 0, children: value }),
|
|
23
|
+
(meta != null || trend != null) && /* @__PURE__ */ jsxs(Row, { className: "uix-stat__meta", id: interactive ? `${id}-meta` : void 0, children: [
|
|
11
24
|
trend != null && /* @__PURE__ */ jsxs("span", { className: cx("uix-stat__trend", `uix-stat__trend--${trend.direction}`), children: [
|
|
12
25
|
/* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: trend.direction === "up" ? /* @__PURE__ */ jsx("path", { d: "M12 19V5m-6 6 6-6 6 6" }) : /* @__PURE__ */ jsx("path", { d: "M12 5v14m6-6-6 6-6-6" }) }),
|
|
13
|
-
/* @__PURE__ */ jsx("span", { className: "uix-visually-hidden", children: trend.direction === "up" ? "up" : "down" }),
|
|
26
|
+
/* @__PURE__ */ jsx("span", { className: "uix-visually-hidden", children: trend.direction === "up" ? trendLabels?.up ?? "up" : trendLabels?.down ?? "down" }),
|
|
14
27
|
trend.label
|
|
15
28
|
] }),
|
|
16
29
|
meta
|
|
17
30
|
] })
|
|
18
31
|
] });
|
|
19
|
-
|
|
32
|
+
if (!interactive) {
|
|
33
|
+
return /* @__PURE__ */ jsx("div", { ref, className: classes, onClick, ...props, children: body });
|
|
34
|
+
}
|
|
35
|
+
return /* @__PURE__ */ jsxs(
|
|
36
|
+
"button",
|
|
37
|
+
{
|
|
38
|
+
type: "button",
|
|
39
|
+
...props,
|
|
40
|
+
ref,
|
|
41
|
+
className: classes,
|
|
42
|
+
"aria-haspopup": "dialog",
|
|
43
|
+
"aria-expanded": expanded,
|
|
44
|
+
"aria-labelledby": `${id}-label ${id}-sep ${id}-value${activateLabel ? ` ${id}-action` : ""}`,
|
|
45
|
+
"aria-describedby": meta != null || trend != null ? `${id}-meta` : void 0,
|
|
46
|
+
onClick: (e) => {
|
|
47
|
+
onClick?.(e);
|
|
48
|
+
if (!e.defaultPrevented) onActivate();
|
|
49
|
+
},
|
|
50
|
+
children: [
|
|
51
|
+
/* @__PURE__ */ jsx("span", { id: `${id}-sep`, className: "uix-visually-hidden", children: ":" }),
|
|
52
|
+
activateLabel && /* @__PURE__ */ jsxs("span", { id: `${id}-action`, className: "uix-visually-hidden", children: [
|
|
53
|
+
", ",
|
|
54
|
+
activateLabel
|
|
55
|
+
] }),
|
|
56
|
+
body
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
);
|
|
60
|
+
});
|
|
20
61
|
export {
|
|
21
62
|
Stat
|
|
22
63
|
};
|
|
@@ -40,12 +40,16 @@ interface TrProps extends HTMLAttributes<HTMLTableRowElement> {
|
|
|
40
40
|
}
|
|
41
41
|
declare function Tr({ selected, pinned, className, children, ...props }: TrProps): react.JSX.Element;
|
|
42
42
|
interface BulkBarProps extends HTMLAttributes<HTMLDivElement> {
|
|
43
|
-
/** number of selected rows, rendered
|
|
43
|
+
/** number of selected rows, rendered through `selectedLabel` */
|
|
44
44
|
count?: number;
|
|
45
45
|
children?: ReactNode;
|
|
46
|
+
/** TENSOR RX-125 (UIX-04): translatable; English default. */
|
|
47
|
+
label?: string;
|
|
48
|
+
/** TENSOR RX-125 (UIX-04): translatable; English default. */
|
|
49
|
+
selectedLabel?: (count: number) => ReactNode;
|
|
46
50
|
}
|
|
47
51
|
/** Contextual action bar shown above the table when ≥1 row is selected. */
|
|
48
|
-
declare function BulkBar({ count, children, className, ...props }: BulkBarProps): react.JSX.Element;
|
|
52
|
+
declare function BulkBar({ count, children, label, selectedLabel, className, ...props }: BulkBarProps): react.JSX.Element;
|
|
49
53
|
interface RowActionsProps extends TdHTMLAttributes<HTMLTableCellElement> {
|
|
50
54
|
children?: ReactNode;
|
|
51
55
|
}
|
|
@@ -57,12 +61,16 @@ interface RowActionProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
|
57
61
|
/** A single hover-revealed row action button (icon inside). */
|
|
58
62
|
declare function RowAction({ children, className, type, ...props }: RowActionProps): react.JSX.Element;
|
|
59
63
|
interface ExpandToggleProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
64
|
+
/** TENSOR RX-125 (UIX-04): translatable; English default. */
|
|
65
|
+
expandLabel?: string;
|
|
66
|
+
/** TENSOR RX-125 (UIX-04): translatable; English default. */
|
|
67
|
+
collapseLabel?: string;
|
|
60
68
|
expanded?: boolean;
|
|
61
69
|
/** id of the detail row this toggle controls → `aria-controls` (UIX-A11Y-2). */
|
|
62
70
|
controls?: string;
|
|
63
71
|
}
|
|
64
72
|
/** Chevron button that expands/collapses an inline detail row. */
|
|
65
|
-
declare function ExpandToggle({ expanded, controls, className, type, ...props }: ExpandToggleProps): react.JSX.Element;
|
|
73
|
+
declare function ExpandToggle({ expanded, controls, expandLabel, collapseLabel, className, type, ...props }: ExpandToggleProps): react.JSX.Element;
|
|
66
74
|
/** Emphasised primary cell text. */
|
|
67
75
|
declare function CellStrong({ children, className, ...props }: HTMLAttributes<HTMLSpanElement>): react.JSX.Element;
|
|
68
76
|
/** Secondary line under a primary cell value (muted, meta size). */
|
package/dist/components/Table.js
CHANGED
|
@@ -66,12 +66,9 @@ function Tr({ selected, pinned, className, children, ...props }) {
|
|
|
66
66
|
}
|
|
67
67
|
);
|
|
68
68
|
}
|
|
69
|
-
function BulkBar({ count, children, className, ...props }) {
|
|
70
|
-
return /* @__PURE__ */ jsxs("div", { className: cx("uix-bulkbar", className), role: "toolbar", "aria-label":
|
|
71
|
-
count != null && /* @__PURE__ */
|
|
72
|
-
count,
|
|
73
|
-
" selected"
|
|
74
|
-
] }),
|
|
69
|
+
function BulkBar({ count, children, label = "Bulk actions", selectedLabel = (n) => `${n} selected`, className, ...props }) {
|
|
70
|
+
return /* @__PURE__ */ jsxs("div", { className: cx("uix-bulkbar", className), role: "toolbar", "aria-label": label, ...props, children: [
|
|
71
|
+
count != null && /* @__PURE__ */ jsx("span", { className: "uix-bulkbar__count", children: selectedLabel(count) }),
|
|
75
72
|
/* @__PURE__ */ jsx("span", { className: "uix-bulkbar__spacer" }),
|
|
76
73
|
children
|
|
77
74
|
] });
|
|
@@ -82,7 +79,7 @@ function RowActions({ children, className, ...props }) {
|
|
|
82
79
|
function RowAction({ children, className, type = "button", ...props }) {
|
|
83
80
|
return /* @__PURE__ */ jsx("button", { type, className: cx("uix-rowact", className), ...props, children });
|
|
84
81
|
}
|
|
85
|
-
function ExpandToggle({ expanded, controls, className, type = "button", ...props }) {
|
|
82
|
+
function ExpandToggle({ expanded, controls, expandLabel = "Expand row", collapseLabel = "Collapse row", className, type = "button", ...props }) {
|
|
86
83
|
return /* @__PURE__ */ jsx(
|
|
87
84
|
"button",
|
|
88
85
|
{
|
|
@@ -90,7 +87,7 @@ function ExpandToggle({ expanded, controls, className, type = "button", ...props
|
|
|
90
87
|
className: cx("uix-table__expand", className),
|
|
91
88
|
"aria-expanded": expanded,
|
|
92
89
|
"aria-controls": controls,
|
|
93
|
-
"aria-label": expanded ?
|
|
90
|
+
"aria-label": expanded ? collapseLabel : expandLabel,
|
|
94
91
|
...props,
|
|
95
92
|
children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M6 4l4 4-4 4" }) })
|
|
96
93
|
}
|
|
@@ -7,8 +7,21 @@ interface TabsProps {
|
|
|
7
7
|
onChange?: (value: string) => void;
|
|
8
8
|
children?: ReactNode;
|
|
9
9
|
className?: string;
|
|
10
|
+
/**
|
|
11
|
+
* What happens when the tabs don't fit. `'wrap'` (default) keeps today's layout.
|
|
12
|
+
* `'scroll'` keeps one row that scrolls sideways, with pointer-only edge buttons
|
|
13
|
+
* shown on the side that has hidden tabs. The selected tab is always scrolled into view.
|
|
14
|
+
*/
|
|
15
|
+
overflow?: 'wrap' | 'scroll';
|
|
16
|
+
/**
|
|
17
|
+
* Keyboard activation (TENSOR RX-125, UIX-11). `'automatic'` (default, unchanged):
|
|
18
|
+
* Arrow/Home/End move focus AND select, so traversing mounts every panel passed over.
|
|
19
|
+
* `'manual'`: arrows only move focus; Enter/Space (the button's own click) selects —
|
|
20
|
+
* the APG choice when a panel is costly to show. Pairs with `TabPanel keepMounted`.
|
|
21
|
+
*/
|
|
22
|
+
activation?: 'automatic' | 'manual';
|
|
10
23
|
}
|
|
11
|
-
declare function Tabs({ variant, value, onChange, children, className }: TabsProps): react.JSX.Element;
|
|
24
|
+
declare function Tabs({ variant, value, onChange, children, className, overflow, activation }: TabsProps): react.JSX.Element;
|
|
12
25
|
interface TabProps extends Omit<HTMLAttributes<HTMLButtonElement>, 'onChange'> {
|
|
13
26
|
value: string;
|
|
14
27
|
children?: ReactNode;
|
package/dist/components/Tabs.js
CHANGED
|
@@ -1,23 +1,64 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { Children, createContext, isValidElement, useContext, useEffect, useId, useMemo, useRef } from "react";
|
|
3
|
+
import { Children, createContext, isValidElement, useContext, useEffect, useId, useMemo, useRef, useState } from "react";
|
|
4
4
|
import { cx } from "../cx.js";
|
|
5
5
|
const TabsCtx = createContext({});
|
|
6
|
-
|
|
6
|
+
const Chevron = ({ d }) => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d }) });
|
|
7
|
+
const inlineOffset = (el, rtl) => rtl ? -el.scrollLeft : el.scrollLeft;
|
|
8
|
+
function Tabs({ variant = "line", value, onChange, children, className, overflow = "wrap", activation = "automatic" }) {
|
|
7
9
|
const baseId = useId();
|
|
8
10
|
const listRef = useRef(null);
|
|
9
11
|
const contextValue = useMemo(() => ({ value, onChange, baseId }), [value, onChange, baseId]);
|
|
12
|
+
const scroll = overflow === "scroll";
|
|
13
|
+
const [hidden, setHidden] = useState({ start: false, end: false });
|
|
10
14
|
const kids = Children.toArray(children);
|
|
11
15
|
const panels = kids.filter((k) => isValidElement(k) && k.type === TabPanel);
|
|
12
16
|
const tabs = kids.filter((k) => !(isValidElement(k) && k.type === TabPanel));
|
|
13
17
|
useEffect(() => {
|
|
14
|
-
const
|
|
15
|
-
if (!
|
|
16
|
-
const all = Array.from(
|
|
18
|
+
const list2 = listRef.current;
|
|
19
|
+
if (!list2) return;
|
|
20
|
+
const all = Array.from(list2.querySelectorAll('[role="tab"]'));
|
|
17
21
|
const enabled = all.filter((t) => !t.matches(":disabled"));
|
|
18
22
|
const active = enabled.find((t) => t.getAttribute("aria-selected") === "true") ?? enabled[0];
|
|
19
23
|
for (const t of all) t.tabIndex = t === active ? 0 : -1;
|
|
20
24
|
});
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
const list2 = listRef.current;
|
|
27
|
+
if (!scroll || !list2) return;
|
|
28
|
+
const measure = () => {
|
|
29
|
+
const rtl = getComputedStyle(list2).direction === "rtl";
|
|
30
|
+
const pos = inlineOffset(list2, rtl);
|
|
31
|
+
const max = list2.scrollWidth - list2.clientWidth;
|
|
32
|
+
const next = { start: pos > 1, end: pos < max - 1 };
|
|
33
|
+
setHidden((prev) => prev.start === next.start && prev.end === next.end ? prev : next);
|
|
34
|
+
};
|
|
35
|
+
measure();
|
|
36
|
+
list2.addEventListener("scroll", measure, { passive: true });
|
|
37
|
+
if (typeof ResizeObserver === "undefined") return () => list2.removeEventListener("scroll", measure);
|
|
38
|
+
const observer = new ResizeObserver(measure);
|
|
39
|
+
observer.observe(list2);
|
|
40
|
+
for (const tab of Array.from(list2.children)) observer.observe(tab);
|
|
41
|
+
return () => {
|
|
42
|
+
list2.removeEventListener("scroll", measure);
|
|
43
|
+
observer.disconnect();
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
const list2 = listRef.current;
|
|
48
|
+
const tab = list2?.querySelector('[role="tab"][aria-selected="true"]');
|
|
49
|
+
if (!scroll || !list2 || !tab) return;
|
|
50
|
+
const outer = list2.getBoundingClientRect();
|
|
51
|
+
const inner = tab.getBoundingClientRect();
|
|
52
|
+
const reserve = list2.scrollWidth > list2.clientWidth ? Number.parseFloat(getComputedStyle(list2).getPropertyValue("--uix-control-h")) || 0 : 0;
|
|
53
|
+
if (inner.left < outer.left + reserve) list2.scrollBy({ left: inner.left - outer.left - reserve });
|
|
54
|
+
else if (inner.right > outer.right - reserve) list2.scrollBy({ left: inner.right - outer.right + reserve });
|
|
55
|
+
}, [scroll, value]);
|
|
56
|
+
const page = (direction) => {
|
|
57
|
+
const list2 = listRef.current;
|
|
58
|
+
if (!list2) return;
|
|
59
|
+
const rtl = getComputedStyle(list2).direction === "rtl";
|
|
60
|
+
list2.scrollBy({ left: direction * (rtl ? -1 : 1) * list2.clientWidth * 0.8 });
|
|
61
|
+
};
|
|
21
62
|
const onKeyDown = (e) => {
|
|
22
63
|
const items = Array.from(
|
|
23
64
|
listRef.current?.querySelectorAll('[role="tab"]:not(:disabled)') ?? []
|
|
@@ -44,22 +85,50 @@ function Tabs({ variant = "line", value, onChange, children, className }) {
|
|
|
44
85
|
}
|
|
45
86
|
e.preventDefault();
|
|
46
87
|
const target = items[next];
|
|
47
|
-
if (target)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
88
|
+
if (!target) return;
|
|
89
|
+
target.focus();
|
|
90
|
+
if (activation === "automatic") target.click();
|
|
51
91
|
};
|
|
92
|
+
const list = /* @__PURE__ */ jsx(
|
|
93
|
+
"div",
|
|
94
|
+
{
|
|
95
|
+
ref: listRef,
|
|
96
|
+
role: "tablist",
|
|
97
|
+
className: cx("uix-tabs", `uix-tabs--${variant}`, scroll && "uix-tabs--scroll", className),
|
|
98
|
+
onKeyDown,
|
|
99
|
+
children: tabs
|
|
100
|
+
}
|
|
101
|
+
);
|
|
52
102
|
return /* @__PURE__ */ jsxs(TabsCtx.Provider, { value: contextValue, children: [
|
|
53
|
-
/* @__PURE__ */
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
103
|
+
scroll ? /* @__PURE__ */ jsxs("div", { className: "uix-tabs-scroller", children: [
|
|
104
|
+
/* @__PURE__ */ jsx(
|
|
105
|
+
"button",
|
|
106
|
+
{
|
|
107
|
+
type: "button",
|
|
108
|
+
className: "uix-tabs-scroller__prev",
|
|
109
|
+
"aria-hidden": "true",
|
|
110
|
+
tabIndex: -1,
|
|
111
|
+
hidden: !hidden.start,
|
|
112
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
113
|
+
onClick: () => page(-1),
|
|
114
|
+
children: /* @__PURE__ */ jsx(Chevron, { d: "m15 18-6-6 6-6" })
|
|
115
|
+
}
|
|
116
|
+
),
|
|
117
|
+
list,
|
|
118
|
+
/* @__PURE__ */ jsx(
|
|
119
|
+
"button",
|
|
120
|
+
{
|
|
121
|
+
type: "button",
|
|
122
|
+
className: "uix-tabs-scroller__next",
|
|
123
|
+
"aria-hidden": "true",
|
|
124
|
+
tabIndex: -1,
|
|
125
|
+
hidden: !hidden.end,
|
|
126
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
127
|
+
onClick: () => page(1),
|
|
128
|
+
children: /* @__PURE__ */ jsx(Chevron, { d: "m9 18 6-6-6-6" })
|
|
129
|
+
}
|
|
130
|
+
)
|
|
131
|
+
] }) : list,
|
|
63
132
|
panels
|
|
64
133
|
] });
|
|
65
134
|
}
|
|
@@ -3,6 +3,8 @@ import { HTMLAttributes, ReactNode } from 'react';
|
|
|
3
3
|
|
|
4
4
|
type ToastTone = 'success' | 'danger' | 'info';
|
|
5
5
|
interface ToastProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
|
|
6
|
+
/** TENSOR RX-125 (UIX-04): translatable; English default. */
|
|
7
|
+
dismissLabel?: string;
|
|
6
8
|
title?: ReactNode;
|
|
7
9
|
message?: ReactNode;
|
|
8
10
|
tone?: ToastTone;
|
|
@@ -10,10 +12,12 @@ interface ToastProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
|
|
|
10
12
|
onClose?: () => void;
|
|
11
13
|
leaving?: boolean;
|
|
12
14
|
}
|
|
13
|
-
declare function Toast({ title, message, tone, icon, onClose, leaving, className, ...props }: ToastProps): react.JSX.Element;
|
|
15
|
+
declare function Toast({ title, message, tone, icon, onClose, leaving, dismissLabel, className, ...props }: ToastProps): react.JSX.Element;
|
|
14
16
|
interface ToasterProps extends HTMLAttributes<HTMLDivElement> {
|
|
17
|
+
/** TENSOR RX-125 (UIX-04): translatable; English default. */
|
|
18
|
+
regionLabel?: string;
|
|
15
19
|
children?: ReactNode;
|
|
16
20
|
}
|
|
17
|
-
declare function Toaster({ children, className, ...props }: ToasterProps): react.JSX.Element;
|
|
21
|
+
declare function Toaster({ children, regionLabel, className, ...props }: ToasterProps): react.JSX.Element;
|
|
18
22
|
|
|
19
23
|
export { Toast, type ToastProps, type ToastTone, Toaster, type ToasterProps };
|
package/dist/components/Toast.js
CHANGED
|
@@ -3,7 +3,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
4
4
|
import { cx } from "../cx.js";
|
|
5
5
|
const ToasterContext = createContext(null);
|
|
6
|
-
function Toast({ title, message, tone, icon, onClose, leaving, className, ...props }) {
|
|
6
|
+
function Toast({ title, message, tone, icon, onClose, leaving, dismissLabel = "Dismiss", className, ...props }) {
|
|
7
7
|
const assertive = tone === "danger";
|
|
8
8
|
const toaster = useContext(ToasterContext);
|
|
9
9
|
const bodyRef = useRef(null);
|
|
@@ -39,12 +39,12 @@ function Toast({ title, message, tone, icon, onClose, leaving, className, ...pro
|
|
|
39
39
|
title && /* @__PURE__ */ jsx("div", { className: "uix-toast__title", children: title }),
|
|
40
40
|
message && /* @__PURE__ */ jsx("div", { className: "uix-toast__msg", children: message })
|
|
41
41
|
] }),
|
|
42
|
-
onClose && /* @__PURE__ */ jsx("button", { className: "uix-toast__close", onClick: handleClose, "aria-label":
|
|
42
|
+
onClose && /* @__PURE__ */ jsx("button", { className: "uix-toast__close", onClick: handleClose, "aria-label": dismissLabel, children: "\xD7" })
|
|
43
43
|
]
|
|
44
44
|
}
|
|
45
45
|
);
|
|
46
46
|
}
|
|
47
|
-
function Toaster({ children, className, ...props }) {
|
|
47
|
+
function Toaster({ children, regionLabel = "Notifications", className, ...props }) {
|
|
48
48
|
const [announced, setAnnounced] = useState("");
|
|
49
49
|
const clearTimer = useRef(null);
|
|
50
50
|
const announce = useCallback((text) => {
|
|
@@ -56,7 +56,7 @@ function Toaster({ children, className, ...props }) {
|
|
|
56
56
|
if (clearTimer.current != null) clearTimeout(clearTimer.current);
|
|
57
57
|
}, []);
|
|
58
58
|
const ctx = useMemo(() => ({ announce }), [announce]);
|
|
59
|
-
return /* @__PURE__ */ jsx(ToasterContext.Provider, { value: ctx, children: /* @__PURE__ */ jsxs("div", { className: cx("uix-toaster", className), role: "region", "aria-label":
|
|
59
|
+
return /* @__PURE__ */ jsx(ToasterContext.Provider, { value: ctx, children: /* @__PURE__ */ jsxs("div", { className: cx("uix-toaster", className), role: "region", "aria-label": regionLabel, ...props, children: [
|
|
60
60
|
/* @__PURE__ */ jsx("span", { className: "uix-visually-hidden", role: "status", "aria-live": "polite", children: announced }),
|
|
61
61
|
children
|
|
62
62
|
] }) });
|
|
@@ -19,6 +19,8 @@ interface TreeNodeProps {
|
|
|
19
19
|
onSelect?: (id: string) => void;
|
|
20
20
|
onFocusNode: (id: string) => void;
|
|
21
21
|
level?: number;
|
|
22
|
+
/** Whether a click on the row itself toggles a branch (see {@link TreeProps.toggleOnRowClick}). */
|
|
23
|
+
rowToggles?: boolean;
|
|
22
24
|
}
|
|
23
25
|
interface TreeProps extends Omit<HTMLAttributes<HTMLUListElement>, 'onSelect' | 'onChange'> {
|
|
24
26
|
nodes: TreeNodeData[];
|
|
@@ -38,7 +40,13 @@ interface TreeProps extends Omit<HTMLAttributes<HTMLUListElement>, 'onSelect' |
|
|
|
38
40
|
rowHeight?: number;
|
|
39
41
|
/** Scroll-viewport height (px) when virtualizing. Default 384. */
|
|
40
42
|
maxHeight?: number;
|
|
43
|
+
/**
|
|
44
|
+
* Whether clicking a branch row toggles it (TENSOR RX-125, UIX-09). Defaults to true for a
|
|
45
|
+
* pure navigation tree and to false when the tree is selectable (`selected` / `onSelect`),
|
|
46
|
+
* where a row click only selects and the chevron toggles. Enter/Space still does both.
|
|
47
|
+
*/
|
|
48
|
+
toggleOnRowClick?: boolean;
|
|
41
49
|
}
|
|
42
|
-
declare function Tree({ nodes, expanded: controlledExpanded, defaultExpanded, selected, onToggle, onSelect, virtualize, rowHeight, maxHeight, className, ...props }: TreeProps): react.JSX.Element;
|
|
50
|
+
declare function Tree({ nodes, expanded: controlledExpanded, defaultExpanded, selected, onToggle, onSelect, virtualize, rowHeight, maxHeight, toggleOnRowClick, className, ...props }: TreeProps): react.JSX.Element;
|
|
43
51
|
|
|
44
52
|
export { Tree, type TreeNodeData, type TreeNodeProps, type TreeProps };
|