@tensor_1/react 2.26.2 → 2.27.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 +51 -0
- package/dist/calendar-model.d.ts +4 -2
- package/dist/components/AppShell.d.ts +1 -1
- package/dist/components/AppShell.js +6 -2
- package/dist/components/Button.d.ts +28 -4
- package/dist/components/Button.js +19 -9
- package/dist/components/Card.d.ts +7 -1
- package/dist/components/Card.js +3 -3
- package/dist/components/Chip.d.ts +43 -0
- package/dist/components/Chip.js +73 -0
- package/dist/components/CollapsibleSection.d.ts +19 -1
- package/dist/components/CollapsibleSection.js +5 -2
- package/dist/components/CollapsibleSectionState.d.ts +11 -0
- package/dist/components/CollapsibleSectionState.js +67 -0
- package/dist/components/CommandPalette.d.ts +1 -1
- package/dist/components/CommandPalette.js +4 -1
- package/dist/components/DateRangePicker.js +3 -1
- package/dist/components/Dialogs.d.ts +54 -3
- package/dist/components/Dialogs.js +167 -6
- package/dist/components/Drawer.d.ts +6 -1
- package/dist/components/Drawer.js +5 -2
- package/dist/components/EmojiPicker.d.ts +3 -0
- package/dist/components/EmojiPicker.js +109 -54
- package/dist/components/Kbd.d.ts +27 -0
- package/dist/components/Kbd.js +45 -0
- package/dist/components/Menu.d.ts +51 -0
- package/dist/components/Menu.js +177 -0
- package/dist/components/Modal.d.ts +1 -1
- package/dist/components/Modal.js +4 -1
- package/dist/components/Pagination.d.ts +36 -5
- package/dist/components/Pagination.js +59 -37
- package/dist/components/Peek.d.ts +1 -1
- package/dist/components/Peek.js +6 -1
- package/dist/components/Popover.d.ts +15 -5
- package/dist/components/Popover.js +66 -6
- package/dist/components/SchedulingCalendar.d.ts +47 -2
- package/dist/components/SchedulingCalendar.js +69 -22
- package/dist/components/SearchSuggest.d.ts +1 -1
- package/dist/components/SearchSuggest.js +4 -1
- package/dist/components/Sidebar.d.ts +7 -2
- package/dist/components/Sidebar.js +15 -4
- package/dist/components/Stat.d.ts +16 -0
- package/dist/components/Stat.js +12 -5
- package/dist/components/Steps.d.ts +48 -0
- package/dist/components/Steps.js +42 -0
- package/dist/components/Toast.d.ts +2 -2
- package/dist/components/Toast.js +7 -2
- package/dist/components/Tooltip.d.ts +10 -4
- package/dist/components/Tooltip.js +2 -2
- package/dist/{emoji-image-WQLh8ZQP.d.cts → emoji-image-6mPZ15Zy.d.cts} +4 -1
- package/dist/emoji.cjs +326 -76
- package/dist/emoji.d.cts +3 -3
- package/dist/hooks/useAnchoredPosition.d.ts +13 -5
- package/dist/hooks/useAnchoredPosition.js +146 -15
- package/dist/index.cjs +2278 -1372
- package/dist/index.d.cts +622 -176
- package/dist/index.d.ts +10 -4
- package/dist/index.js +24 -2
- package/dist/labels-context.d.ts +72 -0
- package/dist/labels-context.js +23 -0
- package/dist/link.d.ts +20 -0
- package/dist/link.js +9 -0
- package/dist/{overlay-position-CEGSz6Z2.d.cts → overlay-position-Cjv5E7mn.d.cts} +7 -0
- package/dist/overlay-position.d.ts +7 -0
- package/dist/overlay-position.js +7 -2
- package/dist/rich-text.cjs +324 -74
- package/dist/rich-text.d.cts +3 -3
- package/package.json +1 -1
|
@@ -3,6 +3,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
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
|
+
import { useUixLabels } from "../labels-context.js";
|
|
6
7
|
import { StatusPill } from "./StatusPill.js";
|
|
7
8
|
import { fillLabel } from "../fill-label.js";
|
|
8
9
|
const DEFAULT_SCHEDULING_CALENDAR_LABELS = {
|
|
@@ -20,10 +21,19 @@ const DEFAULT_SCHEDULING_CALENDAR_LABELS = {
|
|
|
20
21
|
grid: "{view} schedule",
|
|
21
22
|
entry: "{title}, {state}, {start} to {end}",
|
|
22
23
|
legend: "Schedule state legend",
|
|
24
|
+
moreEntries: "+{count} more",
|
|
25
|
+
moreEntriesLabel: "{count} more entries on {date}",
|
|
26
|
+
fewerEntries: "Show fewer",
|
|
23
27
|
states: { scheduled: "Scheduled", conflicted: "Conflicted", "in-progress": "In progress", "blackout-violation": "Blackout violation" }
|
|
24
28
|
};
|
|
25
29
|
const stateTone = (state) => state === "scheduled" ? "info" : state === "in-progress" ? "success" : "danger";
|
|
26
|
-
const
|
|
30
|
+
const utcDate = (date) => /* @__PURE__ */ new Date(`${date}T00:00:00Z`);
|
|
31
|
+
const INTL_PARTS = {
|
|
32
|
+
day: { dateStyle: "full", timeZone: "UTC" },
|
|
33
|
+
weekday: { weekday: "short", timeZone: "UTC" },
|
|
34
|
+
month: { month: "long", year: "numeric", timeZone: "UTC" }
|
|
35
|
+
};
|
|
36
|
+
const intlDate = (date, part, locale) => new Intl.DateTimeFormat(locale, INTL_PARTS[part]).format(utcDate(date));
|
|
27
37
|
const EMPTY_OVERLAYS = [];
|
|
28
38
|
function SchedulingCalendar({
|
|
29
39
|
entries,
|
|
@@ -37,28 +47,38 @@ function SchedulingCalendar({
|
|
|
37
47
|
filter,
|
|
38
48
|
renderEntry,
|
|
39
49
|
locale,
|
|
50
|
+
weekStartsOn = 1,
|
|
51
|
+
formatDate,
|
|
52
|
+
formatInstant: formatInstantProp,
|
|
53
|
+
maxEntriesPerDay,
|
|
54
|
+
onShowMore,
|
|
55
|
+
renderDayBadge,
|
|
40
56
|
loading,
|
|
41
57
|
error,
|
|
42
58
|
onRetry,
|
|
43
59
|
className,
|
|
44
60
|
labels: labelOverrides
|
|
45
61
|
}) {
|
|
62
|
+
const uixLabels = useUixLabels();
|
|
46
63
|
const labels = {
|
|
47
64
|
...DEFAULT_SCHEDULING_CALENDAR_LABELS,
|
|
65
|
+
...uixLabels.schedulingCalendar,
|
|
48
66
|
...labelOverrides,
|
|
49
|
-
states: { ...DEFAULT_SCHEDULING_CALENDAR_LABELS.states, ...labelOverrides?.states }
|
|
67
|
+
states: { ...DEFAULT_SCHEDULING_CALENDAR_LABELS.states, ...uixLabels.schedulingCalendar?.states, ...labelOverrides?.states }
|
|
50
68
|
};
|
|
51
69
|
const stateLabel = labels.states;
|
|
52
70
|
const viewLabel = { month: labels.viewMonth, week: labels.viewWeek, agenda: labels.viewAgenda };
|
|
53
71
|
const [internalView, setInternalView] = useState("month");
|
|
54
72
|
const view = controlledView ?? internalView;
|
|
55
73
|
const [activeDate, setActiveDate] = useState(anchorDate);
|
|
74
|
+
const [expandedDates, setExpandedDates] = useState(() => /* @__PURE__ */ new Set());
|
|
75
|
+
const dateText = (date, part) => formatDate?.(date, part) ?? intlDate(date, part, locale);
|
|
56
76
|
const gridRef = useRef(null);
|
|
57
77
|
const visibleEntries = useMemo(() => entries.filter((entry) => filter?.(entry) ?? true), [entries, filter]);
|
|
58
78
|
const entrySpans = useMemo(() => new Map(visibleEntries.map((entry) => [entry.id, zonedDateSpan(entry.start, entry.end, timeZone)])), [visibleEntries, timeZone]);
|
|
59
79
|
const overlaySpans = useMemo(() => new Map(overlays.map((overlay) => [overlay.id, { start: overlay.start.slice(0, 10), end: overlay.end.slice(0, 10) }])), [overlays]);
|
|
60
|
-
const monthDays = useMemo(() => buildMonthGrid(startOfMonth(anchorDate)), [anchorDate]);
|
|
61
|
-
const weekday = ((
|
|
80
|
+
const monthDays = useMemo(() => buildMonthGrid(startOfMonth(anchorDate), weekStartsOn), [anchorDate, weekStartsOn]);
|
|
81
|
+
const weekday = (utcDate(anchorDate).getUTCDay() - weekStartsOn + 7) % 7;
|
|
62
82
|
const weekStart = addCalendarDays(anchorDate, -weekday);
|
|
63
83
|
const weekDays = useMemo(() => Array.from({ length: 7 }, (_, index) => addCalendarDays(weekStart, index)), [weekStart]);
|
|
64
84
|
const days = useMemo(() => view === "month" ? monthDays.map((day) => day.date) : weekDays, [view, monthDays, weekDays]);
|
|
@@ -95,14 +115,26 @@ function SchedulingCalendar({
|
|
|
95
115
|
const span = overlaySpans.get(overlay.id);
|
|
96
116
|
return date >= span.start && date <= span.end;
|
|
97
117
|
});
|
|
98
|
-
const formatInstant = (instant) => new Intl.DateTimeFormat(locale, { timeZone, dateStyle: "medium", timeStyle: "short" }).format(new Date(instant));
|
|
118
|
+
const formatInstant = (instant) => formatInstantProp?.(instant) ?? new Intl.DateTimeFormat(locale, { timeZone, dateStyle: "medium", timeStyle: "short" }).format(new Date(instant));
|
|
119
|
+
const toggleMore = (date, dayEntries) => {
|
|
120
|
+
if (onShowMore) {
|
|
121
|
+
onShowMore(date, dayEntries);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
setExpandedDates((current) => {
|
|
125
|
+
const next = new Set(current);
|
|
126
|
+
if (next.has(date)) next.delete(date);
|
|
127
|
+
else next.add(date);
|
|
128
|
+
return next;
|
|
129
|
+
});
|
|
130
|
+
};
|
|
99
131
|
return /* @__PURE__ */ jsxs("section", { className: cx("uix-scheduling-calendar", className), "aria-label": fillLabel(labels.region, { timeZone }), children: [
|
|
100
132
|
/* @__PURE__ */ jsxs("div", { className: "uix-scheduling-calendar__header", children: [
|
|
101
133
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
102
134
|
/* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: () => movePeriod(-1), disabled: !onAnchorDateChange, children: labels.previous }),
|
|
103
135
|
/* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: () => movePeriod(1), disabled: !onAnchorDateChange, children: labels.next })
|
|
104
136
|
] }),
|
|
105
|
-
/* @__PURE__ */ jsx("strong", { children: view === "month" ?
|
|
137
|
+
/* @__PURE__ */ jsx("strong", { children: view === "month" ? dateText(startOfMonth(anchorDate), "month") : `${dateText(weekStart, "day")} \u2013 ${dateText(weekDays[weekDays.length - 1], "day")}` }),
|
|
106
138
|
/* @__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)) })
|
|
107
139
|
] }),
|
|
108
140
|
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: [
|
|
@@ -122,22 +154,37 @@ function SchedulingCalendar({
|
|
|
122
154
|
] }),
|
|
123
155
|
/* @__PURE__ */ jsx(StatusPill, { tone: stateTone(state), children: stateLabel[state] })
|
|
124
156
|
] }) }, entry.id);
|
|
125
|
-
}) }) }) : /* @__PURE__ */
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
157
|
+
}) }) }) : /* @__PURE__ */ jsxs("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: [
|
|
158
|
+
days.slice(0, 7).map((date) => /* @__PURE__ */ jsx("div", { className: "uix-scheduling-calendar__weekday", "aria-hidden": "true", children: dateText(date, "weekday") }, `weekday-${date}`)),
|
|
159
|
+
days.map((date, index) => {
|
|
160
|
+
const dayEntries = entriesFor(date);
|
|
161
|
+
const dayOverlays = overlaysFor(date);
|
|
162
|
+
const overflowing = maxEntriesPerDay !== void 0 && dayEntries.length > maxEntriesPerDay;
|
|
163
|
+
const collapsed = overflowing && (onShowMore !== void 0 || !expandedDates.has(date));
|
|
164
|
+
const shownEntries = collapsed ? dayEntries.slice(0, Math.max(0, maxEntriesPerDay)) : dayEntries;
|
|
165
|
+
const hiddenCount = dayEntries.length - shownEntries.length;
|
|
166
|
+
const badge = renderDayBadge?.(date, dayEntries);
|
|
167
|
+
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), "aria-label": dateText(date, "day"), children: Number(date.slice(-2)) });
|
|
168
|
+
return /* @__PURE__ */ jsxs("div", { className: "uix-scheduling-calendar__day", "data-outside": view === "month" && !monthDays[index].inMonth || void 0, children: [
|
|
169
|
+
badge == null || badge === false ? dateButton : /* @__PURE__ */ jsxs("div", { className: "uix-scheduling-calendar__dayhead", children: [
|
|
170
|
+
/* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__badge", children: badge }),
|
|
171
|
+
dateButton
|
|
172
|
+
] }),
|
|
173
|
+
dayOverlays.map((overlay) => /* @__PURE__ */ jsx("span", { className: "uix-scheduling-calendar__overlay", "data-kind": overlay.kind, children: overlay.label }, overlay.id)),
|
|
174
|
+
/* @__PURE__ */ jsxs("div", { className: "uix-scheduling-calendar__entries", children: [
|
|
175
|
+
shownEntries.map((entry) => {
|
|
176
|
+
const span = entrySpans.get(entry.id);
|
|
177
|
+
const state = entry.state ?? "scheduled";
|
|
178
|
+
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: [
|
|
179
|
+
/* @__PURE__ */ jsx("span", { children: renderEntry?.(entry) ?? entry.title }),
|
|
180
|
+
/* @__PURE__ */ jsx("span", { className: "uix-visually-hidden", children: stateLabel[state] })
|
|
181
|
+
] }, entry.id);
|
|
182
|
+
}),
|
|
183
|
+
overflowing && /* @__PURE__ */ jsx("button", { type: "button", className: "uix-scheduling-calendar__more", "aria-expanded": onShowMore ? void 0 : !collapsed, "aria-label": collapsed ? fillLabel(labels.moreEntriesLabel, { count: hiddenCount, date: dateText(date, "day") }) : void 0, onClick: () => toggleMore(date, dayEntries), children: collapsed ? fillLabel(labels.moreEntries, { count: hiddenCount }) : labels.fewerEntries })
|
|
184
|
+
] })
|
|
185
|
+
] }, date);
|
|
186
|
+
})
|
|
187
|
+
] }),
|
|
141
188
|
/* @__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)) })
|
|
142
189
|
] });
|
|
143
190
|
}
|
|
@@ -82,6 +82,6 @@ interface SearchSuggestProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onCha
|
|
|
82
82
|
* (`aria-activedescendant`), Enter opens it (the first row when none is active), Escape closes the
|
|
83
83
|
* list and a second Escape clears the text.
|
|
84
84
|
*/
|
|
85
|
-
declare function SearchSuggest({ value, onValueChange, options, onSelect, label, placeholder, highlight, heading, headingAction, footer, loading, loadingLabel, empty, error, shortcutHint, clearLabel, status, size, open: openProp, strategy, onOpenChange, inputRef, autoFocus, name, className, onBlur, ...props }: SearchSuggestProps): react.JSX.Element;
|
|
85
|
+
declare function SearchSuggest({ value, onValueChange, options, onSelect, label, placeholder, highlight, heading, headingAction, footer, loading, loadingLabel, empty, error, shortcutHint, clearLabel: clearLabelProp, status, size, open: openProp, strategy, onOpenChange, inputRef, autoFocus, name, className, onBlur, ...props }: SearchSuggestProps): react.JSX.Element;
|
|
86
86
|
|
|
87
87
|
export { SearchSuggest, type SearchSuggestAction, type SearchSuggestOption, type SearchSuggestProps };
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useEffect, useId, useImperativeHandle, useLayoutEffect, useRef, useState } from "react";
|
|
4
4
|
import { cx } from "../cx.js";
|
|
5
|
+
import { useUixLabels } from "../labels-context.js";
|
|
5
6
|
import { useAnchoredPosition } from "../hooks/useAnchoredPosition.js";
|
|
6
7
|
import { searchSegments } from "../search-suggest-model.js";
|
|
7
8
|
import { Spinner } from "./Spinner.js";
|
|
@@ -33,7 +34,7 @@ function SearchSuggest({
|
|
|
33
34
|
empty,
|
|
34
35
|
error,
|
|
35
36
|
shortcutHint,
|
|
36
|
-
clearLabel
|
|
37
|
+
clearLabel: clearLabelProp,
|
|
37
38
|
status,
|
|
38
39
|
size = "md",
|
|
39
40
|
open: openProp,
|
|
@@ -46,6 +47,8 @@ function SearchSuggest({
|
|
|
46
47
|
onBlur,
|
|
47
48
|
...props
|
|
48
49
|
}) {
|
|
50
|
+
const uixLabels = useUixLabels();
|
|
51
|
+
const clearLabel = clearLabelProp ?? uixLabels.searchSuggest?.clear ?? "Clear search";
|
|
49
52
|
const id = useId();
|
|
50
53
|
const listboxId = `${id}-listbox`;
|
|
51
54
|
const headingId = `${id}-heading`;
|
|
@@ -11,7 +11,7 @@ interface SidebarProps extends HTMLAttributes<HTMLElement> {
|
|
|
11
11
|
expandLabel?: string;
|
|
12
12
|
collapseLabel?: string;
|
|
13
13
|
}
|
|
14
|
-
declare function Sidebar({ collapsed, nav, onToggle, brand, toggleIcon, children, expandLabel, collapseLabel, className, ...props }: SidebarProps): react.JSX.Element;
|
|
14
|
+
declare function Sidebar({ collapsed, nav, onToggle, brand, toggleIcon, children, expandLabel: expandLabelProp, collapseLabel: collapseLabelProp, className, ...props }: SidebarProps): react.JSX.Element;
|
|
15
15
|
interface SidebarSectionProps {
|
|
16
16
|
label?: string;
|
|
17
17
|
children?: ReactNode;
|
|
@@ -22,9 +22,14 @@ interface NavItemProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
|
|
|
22
22
|
badge?: ReactNode;
|
|
23
23
|
active?: boolean;
|
|
24
24
|
as?: 'a' | 'button';
|
|
25
|
+
/**
|
|
26
|
+
* A second line under the label (a sub-label, a count, a long German term); it wraps,
|
|
27
|
+
* and the row grows to fit (HAR-1355; TENSOR B31). Hidden in the rail.
|
|
28
|
+
*/
|
|
29
|
+
description?: ReactNode;
|
|
25
30
|
children?: ReactNode;
|
|
26
31
|
}
|
|
27
|
-
declare function NavItem({ icon, badge, active, as: Tag, children, className, ...props }: NavItemProps): react.JSX.Element;
|
|
32
|
+
declare function NavItem({ icon, badge, active, as: Tag, description, children, className, ...props }: NavItemProps): react.JSX.Element;
|
|
28
33
|
interface NavGroupProps {
|
|
29
34
|
icon?: ReactNode;
|
|
30
35
|
label: ReactNode;
|
|
@@ -2,8 +2,12 @@
|
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useEffect, useId, useRef, useState } from "react";
|
|
4
4
|
import { cx } from "../cx.js";
|
|
5
|
+
import { useUixLabels } from "../labels-context.js";
|
|
5
6
|
const ChevronIcon = () => /* @__PURE__ */ jsx("svg", { className: "uix-navgroup__chevron", viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M2 4l4 4 4-4" }) });
|
|
6
|
-
function Sidebar({ collapsed, nav, onToggle, brand, toggleIcon, children, expandLabel
|
|
7
|
+
function Sidebar({ collapsed, nav, onToggle, brand, toggleIcon, children, expandLabel: expandLabelProp, collapseLabel: collapseLabelProp, className, ...props }) {
|
|
8
|
+
const uixLabels = useUixLabels();
|
|
9
|
+
const expandLabel = expandLabelProp ?? uixLabels.sidebar?.expand ?? "Expand sidebar";
|
|
10
|
+
const collapseLabel = collapseLabelProp ?? uixLabels.sidebar?.collapse ?? "Collapse sidebar";
|
|
7
11
|
const isCollapsed = collapsed ?? nav === "rail";
|
|
8
12
|
return /* @__PURE__ */ jsxs("nav", { className: cx("uix-sidebar", className), "data-collapsed": isCollapsed || void 0, "data-nav": nav, ...props, children: [
|
|
9
13
|
(brand != null || onToggle) && /* @__PURE__ */ jsxs("div", { className: "uix-sidebar__head", children: [
|
|
@@ -23,7 +27,12 @@ function SidebarSection({ label, children }) {
|
|
|
23
27
|
children
|
|
24
28
|
] });
|
|
25
29
|
}
|
|
26
|
-
function NavItem({ icon, badge, active, as: Tag = "a", children, className, ...props }) {
|
|
30
|
+
function NavItem({ icon, badge, active, as: Tag = "a", description, children, className, ...props }) {
|
|
31
|
+
const text = description == null ? /* @__PURE__ */ jsx("span", { className: "uix-navitem__label", children }) : /* @__PURE__ */ jsxs("span", { className: "uix-navitem__text", children: [
|
|
32
|
+
/* @__PURE__ */ jsx("span", { className: "uix-navitem__label", children }),
|
|
33
|
+
/* @__PURE__ */ jsx("span", { className: "uix-navitem__desc", children: description })
|
|
34
|
+
] });
|
|
35
|
+
const multiline = description == null ? void 0 : true;
|
|
27
36
|
if (Tag === "button") {
|
|
28
37
|
const { href: _href, ...btnProps } = props;
|
|
29
38
|
return /* @__PURE__ */ jsxs(
|
|
@@ -32,10 +41,11 @@ function NavItem({ icon, badge, active, as: Tag = "a", children, className, ...p
|
|
|
32
41
|
type: "button",
|
|
33
42
|
className: cx("uix-navitem", className),
|
|
34
43
|
"aria-current": active ? "page" : void 0,
|
|
44
|
+
"data-multiline": multiline,
|
|
35
45
|
...btnProps,
|
|
36
46
|
children: [
|
|
37
47
|
icon && /* @__PURE__ */ jsx("span", { className: "uix-navitem__icon", "aria-hidden": "true", children: icon }),
|
|
38
|
-
|
|
48
|
+
text,
|
|
39
49
|
badge != null && /* @__PURE__ */ jsx("span", { className: "uix-navitem__badge", children: badge })
|
|
40
50
|
]
|
|
41
51
|
}
|
|
@@ -46,10 +56,11 @@ function NavItem({ icon, badge, active, as: Tag = "a", children, className, ...p
|
|
|
46
56
|
{
|
|
47
57
|
className: cx("uix-navitem", className),
|
|
48
58
|
"aria-current": active ? "page" : void 0,
|
|
59
|
+
"data-multiline": multiline,
|
|
49
60
|
...props,
|
|
50
61
|
children: [
|
|
51
62
|
icon && /* @__PURE__ */ jsx("span", { className: "uix-navitem__icon", "aria-hidden": "true", children: icon }),
|
|
52
|
-
|
|
63
|
+
text,
|
|
53
64
|
badge != null && /* @__PURE__ */ jsx("span", { className: "uix-navitem__badge", children: badge })
|
|
54
65
|
]
|
|
55
66
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { HTMLAttributes, ReactNode } from 'react';
|
|
3
|
+
import { UixRenderLink } from '../link.js';
|
|
3
4
|
|
|
4
5
|
interface StatTrend {
|
|
5
6
|
direction: 'up' | 'down';
|
|
@@ -32,6 +33,21 @@ interface StatProps extends HTMLAttributes<HTMLElement> {
|
|
|
32
33
|
};
|
|
33
34
|
/** Whether the editor this tile opens is currently open (`aria-expanded`). */
|
|
34
35
|
expanded?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* What `onActivate` opens, for `aria-haspopup` (HAR-1350; TENSOR B6). Default `'dialog'`;
|
|
38
|
+
* `'menu'` for a status menu; `false` for an in-place action (no chevron either).
|
|
39
|
+
*/
|
|
40
|
+
haspopup?: 'dialog' | 'menu' | 'listbox' | false;
|
|
41
|
+
/**
|
|
42
|
+
* Makes the whole tile a link (HAR-1350; TENSOR B7, MOTUS B-A7). Server-renderable, unlike
|
|
43
|
+
* `onActivate`. Ignored when `onActivate` is set. The forwarded ref reaches the `<a>` only
|
|
44
|
+
* without `renderLink`.
|
|
45
|
+
*/
|
|
46
|
+
href?: string;
|
|
47
|
+
/** The link tile is the current page (`aria-current="page"`). */
|
|
48
|
+
current?: boolean;
|
|
49
|
+
/** Your router's link for `href`; default a plain `<a>`. */
|
|
50
|
+
renderLink?: UixRenderLink;
|
|
35
51
|
}
|
|
36
52
|
/**
|
|
37
53
|
* KPI / stat tile over `.uix-stat`. With `onActivate` the tile is a button; the forwarded
|
package/dist/components/Stat.js
CHANGED
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { forwardRef, useId } from "react";
|
|
3
3
|
import { cx } from "../cx.js";
|
|
4
|
+
import { renderUixLink } from "../link.js";
|
|
4
5
|
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 Stat = forwardRef(function Stat2({ label, value, icon, meta, trend, tone = "neutral", size = "hero", onActivate, activateLabel, expanded, trendLabels, haspopup = "dialog", href, current, renderLink, className, onClick, ...props }, ref) {
|
|
6
7
|
const id = useId();
|
|
7
8
|
const interactive = onActivate != null;
|
|
9
|
+
const linked = !interactive && href != null;
|
|
8
10
|
const classes = cx(
|
|
9
11
|
"uix-stat",
|
|
10
12
|
tone !== "neutral" && `uix-stat--${tone}`,
|
|
11
13
|
size === "compact" && "uix-stat--compact",
|
|
12
|
-
interactive && "uix-stat--interactive",
|
|
14
|
+
(interactive || linked) && "uix-stat--interactive",
|
|
15
|
+
linked && "uix-stat--link",
|
|
13
16
|
className
|
|
14
17
|
);
|
|
15
|
-
const Row = interactive ? "span" : "div";
|
|
18
|
+
const Row = interactive || linked ? "span" : "div";
|
|
16
19
|
const body = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
17
20
|
/* @__PURE__ */ jsxs(Row, { className: "uix-stat__label", children: [
|
|
18
21
|
/* @__PURE__ */ jsx("span", { id: interactive ? `${id}-label` : void 0, children: label }),
|
|
19
22
|
icon,
|
|
20
|
-
interactive && /* @__PURE__ */ jsx(Chevron, {})
|
|
23
|
+
interactive && haspopup !== false && /* @__PURE__ */ jsx(Chevron, {})
|
|
21
24
|
] }),
|
|
22
25
|
/* @__PURE__ */ jsx(Row, { className: "uix-stat__value", id: interactive ? `${id}-value` : void 0, children: value }),
|
|
23
26
|
(meta != null || trend != null) && /* @__PURE__ */ jsxs(Row, { className: "uix-stat__meta", id: interactive ? `${id}-meta` : void 0, children: [
|
|
@@ -29,6 +32,10 @@ const Stat = forwardRef(function Stat2({ label, value, icon, meta, trend, tone =
|
|
|
29
32
|
meta
|
|
30
33
|
] })
|
|
31
34
|
] });
|
|
35
|
+
if (linked) {
|
|
36
|
+
const linkProps = { ...props, href, className: classes, onClick, "aria-current": current ? "page" : void 0, children: body };
|
|
37
|
+
return /* @__PURE__ */ jsx(Fragment, { children: renderLink ? renderUixLink(renderLink, linkProps) : /* @__PURE__ */ jsx("a", { ref, ...linkProps }) });
|
|
38
|
+
}
|
|
32
39
|
if (!interactive) {
|
|
33
40
|
return /* @__PURE__ */ jsx("div", { ref, className: classes, onClick, ...props, children: body });
|
|
34
41
|
}
|
|
@@ -39,7 +46,7 @@ const Stat = forwardRef(function Stat2({ label, value, icon, meta, trend, tone =
|
|
|
39
46
|
...props,
|
|
40
47
|
ref,
|
|
41
48
|
className: classes,
|
|
42
|
-
"aria-haspopup":
|
|
49
|
+
"aria-haspopup": haspopup === false ? void 0 : haspopup,
|
|
43
50
|
"aria-expanded": expanded,
|
|
44
51
|
"aria-labelledby": `${id}-label ${id}-sep ${id}-value${activateLabel ? ` ${id}-action` : ""}`,
|
|
45
52
|
"aria-describedby": meta != null || trend != null ? `${id}-meta` : void 0,
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { LiHTMLAttributes, ReactNode, HTMLAttributes } from 'react';
|
|
3
|
+
import { UixRenderLink } from '../link.js';
|
|
4
|
+
|
|
5
|
+
type StepState = 'complete' | 'current' | 'upcoming' | 'waiting' | 'error';
|
|
6
|
+
/** The state words read after each step's title (state is never colour alone). */
|
|
7
|
+
interface StepStateLabels {
|
|
8
|
+
complete: string;
|
|
9
|
+
current: string;
|
|
10
|
+
upcoming: string;
|
|
11
|
+
waiting: string;
|
|
12
|
+
error: string;
|
|
13
|
+
}
|
|
14
|
+
declare const DEFAULT_STEP_STATE_LABELS: StepStateLabels;
|
|
15
|
+
interface StepsProps extends HTMLAttributes<HTMLOListElement> {
|
|
16
|
+
/**
|
|
17
|
+
* `horizontal` (default) lays the steps in a row and stacks them below 40rem;
|
|
18
|
+
* `vertical` always stacks, with descriptions under each title.
|
|
19
|
+
*/
|
|
20
|
+
orientation?: 'horizontal' | 'vertical';
|
|
21
|
+
/** Names the list, e.g. "Intake progress". */
|
|
22
|
+
label?: string;
|
|
23
|
+
stateLabels?: Partial<StepStateLabels>;
|
|
24
|
+
/** `Step` elements, in order. */
|
|
25
|
+
children?: ReactNode;
|
|
26
|
+
}
|
|
27
|
+
interface StepProps extends Omit<LiHTMLAttributes<HTMLLIElement>, 'title'> {
|
|
28
|
+
title: ReactNode;
|
|
29
|
+
description?: ReactNode;
|
|
30
|
+
state?: StepState;
|
|
31
|
+
/** Marker content. Default: the step number; ✓ when complete; ! on error. */
|
|
32
|
+
marker?: ReactNode;
|
|
33
|
+
/** A navigable step (a wizard you can jump back in). */
|
|
34
|
+
href?: string;
|
|
35
|
+
renderLink?: UixRenderLink;
|
|
36
|
+
onSelect?: () => void;
|
|
37
|
+
/** Set by `Steps`. */
|
|
38
|
+
index?: number;
|
|
39
|
+
/** Set by `Steps`. */
|
|
40
|
+
last?: boolean;
|
|
41
|
+
/** Set by `Steps`. */
|
|
42
|
+
stateLabels?: StepStateLabels;
|
|
43
|
+
}
|
|
44
|
+
/** Numbered progress through a multi-step flow (HAR-1362; TENSOR C13, MOTUS C-9). */
|
|
45
|
+
declare function Steps({ orientation, label, stateLabels, className, children, ...props }: StepsProps): react.JSX.Element;
|
|
46
|
+
declare function Step({ title, description, state, marker, href, renderLink, onSelect, index, last, stateLabels, className, ...props }: StepProps): react.JSX.Element;
|
|
47
|
+
|
|
48
|
+
export { DEFAULT_STEP_STATE_LABELS, Step, type StepProps, type StepState, type StepStateLabels, Steps, type StepsProps };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Children, cloneElement, isValidElement } from "react";
|
|
3
|
+
import { cx } from "../cx.js";
|
|
4
|
+
import { renderUixLink } from "../link.js";
|
|
5
|
+
const DEFAULT_STEP_STATE_LABELS = {
|
|
6
|
+
complete: "Completed",
|
|
7
|
+
current: "Current step",
|
|
8
|
+
upcoming: "Not started",
|
|
9
|
+
waiting: "Waiting",
|
|
10
|
+
error: "Needs attention"
|
|
11
|
+
};
|
|
12
|
+
const CSS_STATE = { complete: "done", current: "active", upcoming: void 0, waiting: "waiting", error: "error" };
|
|
13
|
+
const Check = () => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 16 16", width: "12", height: "12", fill: "none", stroke: "currentColor", strokeWidth: 2.25, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M3.5 8.5l3 3 6-7" }) });
|
|
14
|
+
function Steps({ orientation = "horizontal", label, stateLabels, className, children, ...props }) {
|
|
15
|
+
const labels = { ...DEFAULT_STEP_STATE_LABELS, ...stateLabels };
|
|
16
|
+
const steps = Children.toArray(children).filter(isValidElement);
|
|
17
|
+
return /* @__PURE__ */ jsx("ol", { "aria-label": label, className: cx("uix-steps", "uix-steps--list", `uix-steps--${orientation}`, className), ...props, children: steps.map((step, index) => cloneElement(step, { index, last: index === steps.length - 1, stateLabels: labels })) });
|
|
18
|
+
}
|
|
19
|
+
function Step({ title, description, state = "upcoming", marker, href, renderLink, onSelect, index = 0, last = false, stateLabels = DEFAULT_STEP_STATE_LABELS, className, ...props }) {
|
|
20
|
+
const glyph = marker ?? (state === "complete" ? /* @__PURE__ */ jsx(Check, {}) : state === "error" ? "!" : index + 1);
|
|
21
|
+
const name = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
22
|
+
/* @__PURE__ */ jsx("span", { className: "uix-step__title", children: title }),
|
|
23
|
+
/* @__PURE__ */ jsxs("span", { className: "uix-visually-hidden", children: [
|
|
24
|
+
", ",
|
|
25
|
+
stateLabels[state]
|
|
26
|
+
] })
|
|
27
|
+
] });
|
|
28
|
+
const titled = href != null ? renderUixLink(renderLink, { href, className: "uix-step__action", children: name }) : onSelect ? /* @__PURE__ */ jsx("button", { type: "button", className: "uix-step__action", onClick: onSelect, children: name }) : /* @__PURE__ */ jsx("span", { className: "uix-step__action", children: name });
|
|
29
|
+
return /* @__PURE__ */ jsxs("li", { className: cx("uix-step", className), "data-state": CSS_STATE[state], "aria-current": state === "current" ? "step" : void 0, ...props, children: [
|
|
30
|
+
/* @__PURE__ */ jsx("span", { className: "uix-step__marker", "aria-hidden": "true", children: glyph }),
|
|
31
|
+
/* @__PURE__ */ jsxs("span", { className: "uix-step__text", children: [
|
|
32
|
+
titled,
|
|
33
|
+
description != null && /* @__PURE__ */ jsx("span", { className: "uix-step__desc", children: description })
|
|
34
|
+
] }),
|
|
35
|
+
!last && /* @__PURE__ */ jsx("span", { className: "uix-step__connector", "aria-hidden": "true", "data-done": state === "complete" || void 0 })
|
|
36
|
+
] });
|
|
37
|
+
}
|
|
38
|
+
export {
|
|
39
|
+
DEFAULT_STEP_STATE_LABELS,
|
|
40
|
+
Step,
|
|
41
|
+
Steps
|
|
42
|
+
};
|
|
@@ -12,12 +12,12 @@ interface ToastProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
|
|
|
12
12
|
onClose?: () => void;
|
|
13
13
|
leaving?: boolean;
|
|
14
14
|
}
|
|
15
|
-
declare function Toast({ title, message, tone, icon, onClose, leaving, dismissLabel, className, ...props }: ToastProps): react.JSX.Element;
|
|
15
|
+
declare function Toast({ title, message, tone, icon, onClose, leaving, dismissLabel: dismissLabelProp, className, ...props }: ToastProps): react.JSX.Element;
|
|
16
16
|
interface ToasterProps extends HTMLAttributes<HTMLDivElement> {
|
|
17
17
|
/** TENSOR RX-125 (UIX-04): translatable; English default. */
|
|
18
18
|
regionLabel?: string;
|
|
19
19
|
children?: ReactNode;
|
|
20
20
|
}
|
|
21
|
-
declare function Toaster({ children, regionLabel, className, ...props }: ToasterProps): react.JSX.Element;
|
|
21
|
+
declare function Toaster({ children, regionLabel: regionLabelProp, className, ...props }: ToasterProps): react.JSX.Element;
|
|
22
22
|
|
|
23
23
|
export { Toast, type ToastProps, type ToastTone, Toaster, type ToasterProps };
|
package/dist/components/Toast.js
CHANGED
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
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
|
+
import { useUixLabels } from "../labels-context.js";
|
|
5
6
|
const ToasterContext = createContext(null);
|
|
6
|
-
function Toast({ title, message, tone, icon, onClose, leaving, dismissLabel
|
|
7
|
+
function Toast({ title, message, tone, icon, onClose, leaving, dismissLabel: dismissLabelProp, className, ...props }) {
|
|
8
|
+
const uixLabels = useUixLabels();
|
|
9
|
+
const dismissLabel = dismissLabelProp ?? uixLabels.toast?.dismiss ?? "Dismiss";
|
|
7
10
|
const assertive = tone === "danger";
|
|
8
11
|
const toaster = useContext(ToasterContext);
|
|
9
12
|
const bodyRef = useRef(null);
|
|
@@ -44,7 +47,9 @@ function Toast({ title, message, tone, icon, onClose, leaving, dismissLabel = "D
|
|
|
44
47
|
}
|
|
45
48
|
);
|
|
46
49
|
}
|
|
47
|
-
function Toaster({ children, regionLabel
|
|
50
|
+
function Toaster({ children, regionLabel: regionLabelProp, className, ...props }) {
|
|
51
|
+
const uixLabels = useUixLabels();
|
|
52
|
+
const regionLabel = regionLabelProp ?? uixLabels.toaster?.region ?? "Notifications";
|
|
48
53
|
const [announced, setAnnounced] = useState("");
|
|
49
54
|
const clearTimer = useRef(null);
|
|
50
55
|
const announce = useCallback((text) => {
|
|
@@ -2,9 +2,15 @@ import * as react from 'react';
|
|
|
2
2
|
import { HTMLAttributes, ReactNode } from 'react';
|
|
3
3
|
import { Placement } from '../overlay-position.js';
|
|
4
4
|
|
|
5
|
-
interface TooltipProps extends HTMLAttributes<HTMLSpanElement> {
|
|
6
|
-
/** Tooltip text. */
|
|
7
|
-
label
|
|
5
|
+
interface TooltipProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'content'> {
|
|
6
|
+
/** Tooltip text. Pass this or `content`. */
|
|
7
|
+
label?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Rich, non-interactive tooltip content, e.g. a short list (HAR-1349; TENSOR A3). It is
|
|
10
|
+
* still a `role="tooltip"` description: no links or buttons inside — use `InfoTip` or
|
|
11
|
+
* `Popover` for content the user must reach. Wins over `label` when both are set.
|
|
12
|
+
*/
|
|
13
|
+
content?: ReactNode;
|
|
8
14
|
/** Preferred placement. Default `'top'`. */
|
|
9
15
|
placement?: Placement;
|
|
10
16
|
children?: ReactNode;
|
|
@@ -17,6 +23,6 @@ interface TooltipProps extends HTMLAttributes<HTMLSpanElement> {
|
|
|
17
23
|
* (document-level, per WCAG 1.4.13), and hoverable: the bubble stays open while
|
|
18
24
|
* the pointer is over it (UIX-A11Y-1).
|
|
19
25
|
*/
|
|
20
|
-
declare function Tooltip({ label, placement, className, children, ...props }: TooltipProps): react.JSX.Element;
|
|
26
|
+
declare function Tooltip({ label, content, placement, className, children, ...props }: TooltipProps): react.JSX.Element;
|
|
21
27
|
|
|
22
28
|
export { Tooltip, type TooltipProps };
|
|
@@ -3,7 +3,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { cloneElement, isValidElement, useEffect, useId, useRef, useState } from "react";
|
|
4
4
|
import { cx } from "../cx.js";
|
|
5
5
|
import { useAnchoredPosition } from "../hooks/useAnchoredPosition.js";
|
|
6
|
-
function Tooltip({ label, placement = "top", className, children, ...props }) {
|
|
6
|
+
function Tooltip({ label, content, placement = "top", className, children, ...props }) {
|
|
7
7
|
const triggerRef = useRef(null);
|
|
8
8
|
const bubbleRef = useRef(null);
|
|
9
9
|
const closeTimer = useRef(null);
|
|
@@ -92,7 +92,7 @@ function Tooltip({ label, placement = "top", className, children, ...props }) {
|
|
|
92
92
|
onMouseEnter: cancelClose,
|
|
93
93
|
onMouseLeave: scheduleHide,
|
|
94
94
|
...bubbleAttr,
|
|
95
|
-
children: label
|
|
95
|
+
children: content ?? label
|
|
96
96
|
}
|
|
97
97
|
)
|
|
98
98
|
]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactElement } from 'react';
|
|
3
|
-
import { P as Placement } from './overlay-position-
|
|
3
|
+
import { P as Placement } from './overlay-position-Cjv5E7mn.cjs';
|
|
4
4
|
|
|
5
5
|
type EmojiLocale = 'en' | 'de';
|
|
6
6
|
interface EmojiEntry {
|
|
@@ -106,6 +106,9 @@ declare const EMOJI_GRID_COLUMNS = 8;
|
|
|
106
106
|
* Emoji picker in a native popover: search, quick picks, recently used and the
|
|
107
107
|
* Unicode categories, with names in English or German. The emoji dataset loads on
|
|
108
108
|
* first open. The grid is one tab stop; arrow keys, Home and End move within it.
|
|
109
|
+
* The picker is one size in every state (loading, searching, failed), so it keeps the
|
|
110
|
+
* side it opened on; it stays attached to its trigger while the page scrolls and
|
|
111
|
+
* closes once the trigger is scrolled out of view.
|
|
109
112
|
*/
|
|
110
113
|
declare function EmojiPicker({ onSelect, trigger, labels: labelOverrides, locale, quickPicks, loadData, recentStorageKey, placement, onOpenChange, className, emojiImageBaseUrl, }: EmojiPickerProps): react.JSX.Element;
|
|
111
114
|
|