@tensor_1/react 2.26.3 → 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/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/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.cjs +2 -2
- package/dist/index.cjs +2061 -1350
- package/dist/index.d.cts +596 -165
- 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/package.json +1 -1
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment as Fragment2 } from "react";
|
|
2
3
|
import { cx } from "../cx.js";
|
|
4
|
+
import { renderUixLink } from "../link.js";
|
|
3
5
|
function getPageNumbers(page, total) {
|
|
4
6
|
if (total <= 7) return Array.from({ length: total }, (_, i) => i + 1);
|
|
5
7
|
const pages = [1];
|
|
@@ -14,45 +16,65 @@ function getPageNumbers(page, total) {
|
|
|
14
16
|
const DEFAULT_PAGINATION_LABELS = {
|
|
15
17
|
region: "Pagination",
|
|
16
18
|
previous: "Previous page",
|
|
17
|
-
next: "Next page"
|
|
19
|
+
next: "Next page",
|
|
20
|
+
first: "First page"
|
|
18
21
|
};
|
|
19
|
-
function Pagination({
|
|
22
|
+
function Pagination({
|
|
23
|
+
mode = "offset",
|
|
24
|
+
page = 1,
|
|
25
|
+
pageCount = 1,
|
|
26
|
+
onChange,
|
|
27
|
+
hrefFor,
|
|
28
|
+
renderLink,
|
|
29
|
+
hasPrevious = false,
|
|
30
|
+
hasNext = false,
|
|
31
|
+
onFirst,
|
|
32
|
+
onPrevious,
|
|
33
|
+
onNext,
|
|
34
|
+
firstHref,
|
|
35
|
+
previousHref,
|
|
36
|
+
nextHref,
|
|
37
|
+
summary,
|
|
38
|
+
labels: labelOverrides,
|
|
39
|
+
className,
|
|
40
|
+
...props
|
|
41
|
+
}) {
|
|
20
42
|
const labels = { ...DEFAULT_PAGINATION_LABELS, ...labelOverrides };
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
{
|
|
43
|
+
const render = ({ key, content, label, current, disabled, href, onClick }) => {
|
|
44
|
+
if (href != null && !disabled) {
|
|
45
|
+
return /* @__PURE__ */ jsx(Fragment2, { children: renderUixLink(renderLink, {
|
|
46
|
+
href,
|
|
26
47
|
className: "uix-pagination__btn",
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
)
|
|
55
|
-
|
|
48
|
+
"aria-label": label,
|
|
49
|
+
"aria-current": current ? "page" : void 0,
|
|
50
|
+
children: content
|
|
51
|
+
}) }, key);
|
|
52
|
+
}
|
|
53
|
+
if (href != null || disabled && onClick == null) {
|
|
54
|
+
return /* @__PURE__ */ jsx("a", { className: "uix-pagination__btn", role: "link", "aria-disabled": "true", "aria-label": label, children: content }, key);
|
|
55
|
+
}
|
|
56
|
+
return /* @__PURE__ */ jsx("button", { type: "button", className: "uix-pagination__btn", "aria-label": label, "aria-current": current ? "page" : void 0, disabled, onClick, children: content }, key);
|
|
57
|
+
};
|
|
58
|
+
let controls;
|
|
59
|
+
if (mode === "cursor") {
|
|
60
|
+
const linked = firstHref != null || previousHref != null || nextHref != null;
|
|
61
|
+
controls = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
62
|
+
(onFirst || firstHref != null) && render({ key: "first", content: "\xAB", label: labels.first, disabled: !hasPrevious, href: linked ? firstHref ?? "" : void 0, onClick: onFirst }),
|
|
63
|
+
render({ key: "prev", content: "\u2039", label: labels.previous, disabled: !hasPrevious, href: linked ? previousHref ?? "" : void 0, onClick: onPrevious }),
|
|
64
|
+
summary != null && /* @__PURE__ */ jsx("span", { className: "uix-pagination__summary", children: summary }),
|
|
65
|
+
render({ key: "next", content: "\u203A", label: labels.next, disabled: !hasNext, href: linked ? nextHref ?? "" : void 0, onClick: onNext })
|
|
66
|
+
] });
|
|
67
|
+
} else {
|
|
68
|
+
const go = (target) => hrefFor ? { href: hrefFor(target) } : { onClick: () => onChange?.(target) };
|
|
69
|
+
controls = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
70
|
+
render({ key: "prev", content: "\u2039", label: labels.previous, disabled: page <= 1, ...go(page - 1) }),
|
|
71
|
+
getPageNumbers(page, pageCount).map(
|
|
72
|
+
(p, i) => p === "ellipsis" ? /* @__PURE__ */ jsx("span", { className: "uix-pagination__ellipsis", children: "\u2026" }, `e${i}`) : render({ key: `p${p}`, content: p, current: p === page, ...go(p) })
|
|
73
|
+
),
|
|
74
|
+
render({ key: "next", content: "\u203A", label: labels.next, disabled: page >= pageCount, ...go(page + 1) })
|
|
75
|
+
] });
|
|
76
|
+
}
|
|
77
|
+
return /* @__PURE__ */ jsx("nav", { "aria-label": labels.region, className: cx("uix-pagination", className), ...props, children: controls });
|
|
56
78
|
}
|
|
57
79
|
export {
|
|
58
80
|
DEFAULT_PAGINATION_LABELS,
|
|
@@ -17,6 +17,6 @@ interface PeekProps extends Omit<HTMLAttributes<HTMLDialogElement>, 'title'> {
|
|
|
17
17
|
onNavNext?: () => void;
|
|
18
18
|
hint?: string;
|
|
19
19
|
}
|
|
20
|
-
declare function Peek({ open, onClose, title, children, footer, onNavPrev, onNavNext, hint, previousLabel, nextLabel, closeLabel, className, onClick, ...rest }: PeekProps): react.JSX.Element;
|
|
20
|
+
declare function Peek({ open, onClose, title, children, footer, onNavPrev, onNavNext, hint, previousLabel: previousLabelProp, nextLabel: nextLabelProp, closeLabel: closeLabelProp, className, onClick, ...rest }: PeekProps): react.JSX.Element;
|
|
21
21
|
|
|
22
22
|
export { Peek, type PeekProps };
|
package/dist/components/Peek.js
CHANGED
|
@@ -2,12 +2,17 @@
|
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useId } from "react";
|
|
4
4
|
import { cx } from "../cx.js";
|
|
5
|
+
import { useUixLabels } from "../labels-context.js";
|
|
5
6
|
import { useDialog } from "../hooks/useDialog.js";
|
|
6
7
|
import { backdropDismiss } from "../hooks/backdropDismiss.js";
|
|
7
8
|
const CloseIcon = () => /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M4 4l8 8M12 4l-8 8" }) });
|
|
8
9
|
const ChevronUp = () => /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M2 8l4-4 4 4" }) });
|
|
9
10
|
const ChevronDown = () => /* @__PURE__ */ jsx("svg", { width: "12", height: "12", 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" }) });
|
|
10
|
-
function Peek({ open, onClose, title, children, footer, onNavPrev, onNavNext, hint, previousLabel
|
|
11
|
+
function Peek({ open, onClose, title, children, footer, onNavPrev, onNavNext, hint, previousLabel: previousLabelProp, nextLabel: nextLabelProp, closeLabel: closeLabelProp, className, onClick, ...rest }) {
|
|
12
|
+
const uixLabels = useUixLabels();
|
|
13
|
+
const previousLabel = previousLabelProp ?? uixLabels.peek?.previous ?? "Previous record";
|
|
14
|
+
const nextLabel = nextLabelProp ?? uixLabels.peek?.next ?? "Next record";
|
|
15
|
+
const closeLabel = closeLabelProp ?? uixLabels.peek?.close ?? "Close preview";
|
|
11
16
|
const ref = useDialog(open, onClose);
|
|
12
17
|
const hasNav = onNavPrev != null || onNavNext != null;
|
|
13
18
|
const titleId = useId();
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
+
import { CalendarWeekday } from '../calendar-model.js';
|
|
3
4
|
|
|
4
5
|
type SchedulingCalendarView = 'month' | 'week' | 'agenda';
|
|
5
6
|
type SchedulingEntryState = 'scheduled' | 'conflicted' | 'in-progress' | 'blackout-violation';
|
|
6
7
|
type SchedulingOverlayKind = 'maintenance' | 'blackout';
|
|
8
|
+
/**
|
|
9
|
+
* Which date text a `formatDate` call is for (TENSOR B1/C9, HAR-1347):
|
|
10
|
+
* `day` names one day (each day cell's accessible name, the week-range title);
|
|
11
|
+
* `weekday` is the short column header (the date is any day of that weekday);
|
|
12
|
+
* `month` is the month-view title (the date is the first of the month).
|
|
13
|
+
*/
|
|
14
|
+
type SchedulingDatePart = 'day' | 'weekday' | 'month';
|
|
7
15
|
interface SchedulingCalendarEntry {
|
|
8
16
|
id: string;
|
|
9
17
|
title: string;
|
|
@@ -38,6 +46,12 @@ interface SchedulingCalendarLabels {
|
|
|
38
46
|
grid: string;
|
|
39
47
|
entry: string;
|
|
40
48
|
legend: string;
|
|
49
|
+
/** The "+N more" toggle on a day with more entries than `maxEntriesPerDay`. `{count}` placeholder. */
|
|
50
|
+
moreEntries: string;
|
|
51
|
+
/** Accessible name of that toggle. `{count}` and `{date}` placeholders. */
|
|
52
|
+
moreEntriesLabel: string;
|
|
53
|
+
/** The toggle once the day is expanded in place. */
|
|
54
|
+
fewerEntries: string;
|
|
41
55
|
states: Record<SchedulingEntryState, string>;
|
|
42
56
|
}
|
|
43
57
|
declare const DEFAULT_SCHEDULING_CALENDAR_LABELS: SchedulingCalendarLabels;
|
|
@@ -53,6 +67,37 @@ interface SchedulingCalendarProps {
|
|
|
53
67
|
filter?: (entry: SchedulingCalendarEntry) => boolean;
|
|
54
68
|
renderEntry?: (entry: SchedulingCalendarEntry) => ReactNode;
|
|
55
69
|
locale?: string;
|
|
70
|
+
/** First column of the month and week grids: 0 = Sunday … 6 = Saturday. Defaults to 1 (Monday). */
|
|
71
|
+
weekStartsOn?: CalendarWeekday;
|
|
72
|
+
/**
|
|
73
|
+
* Formats every calendar date the component writes (`date` is `YYYY-MM-DD`). Use it to
|
|
74
|
+
* apply a product date format such as DD.MM.YYYY; defaults to `Intl` in `locale`.
|
|
75
|
+
*/
|
|
76
|
+
formatDate?: (date: string, part: SchedulingDatePart) => string;
|
|
77
|
+
/**
|
|
78
|
+
* Formats an entry's start or end instant (ISO 8601) for the agenda and the entry names.
|
|
79
|
+
* The component does not shift it: render it in `timeZone` yourself. Defaults to `Intl`
|
|
80
|
+
* medium date + short time in `timeZone`.
|
|
81
|
+
*/
|
|
82
|
+
formatInstant?: (instant: string) => string;
|
|
83
|
+
/**
|
|
84
|
+
* Show at most this many entries in a month or week day; the rest collapse behind a
|
|
85
|
+
* "+N more" toggle. Unset shows every entry.
|
|
86
|
+
*/
|
|
87
|
+
maxEntriesPerDay?: number;
|
|
88
|
+
/**
|
|
89
|
+
* Called by the "+N more" toggle with the day and every entry on it. When set, the
|
|
90
|
+
* consumer shows them (a popover, a drawer, the day view) and the day stays collapsed;
|
|
91
|
+
* otherwise the day expands in place.
|
|
92
|
+
*/
|
|
93
|
+
onShowMore?: (date: string, entries: SchedulingCalendarEntry[]) => void;
|
|
94
|
+
/**
|
|
95
|
+
* A small badge beside a day's date, e.g. a collision count. Keep it short (a number or a
|
|
96
|
+
* glyph): it shares a narrow column with the date and is clipped, not wrapped. It sits
|
|
97
|
+
* outside the date button, so give it its own accessible text (a visually hidden word next
|
|
98
|
+
* to the number). Return null for no badge.
|
|
99
|
+
*/
|
|
100
|
+
renderDayBadge?: (date: string, entries: SchedulingCalendarEntry[]) => ReactNode;
|
|
56
101
|
loading?: boolean;
|
|
57
102
|
error?: string;
|
|
58
103
|
onRetry?: () => void;
|
|
@@ -60,6 +105,6 @@ interface SchedulingCalendarProps {
|
|
|
60
105
|
labels?: Partial<SchedulingCalendarLabels>;
|
|
61
106
|
}
|
|
62
107
|
/** Month/week/agenda calendar for UTC ranges rendered in an explicit IANA time zone. */
|
|
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;
|
|
108
|
+
declare function SchedulingCalendar({ entries, anchorDate, timeZone, view: controlledView, onViewChange, onAnchorDateChange, onSelectEntry, overlays, filter, renderEntry, locale, weekStartsOn, formatDate, formatInstant: formatInstantProp, maxEntriesPerDay, onShowMore, renderDayBadge, loading, error, onRetry, className, labels: labelOverrides, }: SchedulingCalendarProps): react.JSX.Element;
|
|
64
109
|
|
|
65
|
-
export { DEFAULT_SCHEDULING_CALENDAR_LABELS, SchedulingCalendar, type SchedulingCalendarEntry, type SchedulingCalendarLabels, type SchedulingCalendarOverlay, type SchedulingCalendarProps, type SchedulingCalendarView, type SchedulingEntryState, type SchedulingOverlayKind };
|
|
110
|
+
export { DEFAULT_SCHEDULING_CALENDAR_LABELS, SchedulingCalendar, type SchedulingCalendarEntry, type SchedulingCalendarLabels, type SchedulingCalendarOverlay, type SchedulingCalendarProps, type SchedulingCalendarView, type SchedulingDatePart, type SchedulingEntryState, type SchedulingOverlayKind };
|
|
@@ -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 };
|