@tensor_1/react 2.20.0 → 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/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/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 +5 -0
- package/dist/components/Stat.js +2 -2
- package/dist/components/Table.d.ts +11 -3
- package/dist/components/Table.js +5 -8
- package/dist/components/Tabs.d.ts +8 -1
- package/dist/components/Tabs.js +4 -5
- 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/fill-label.d.ts +7 -0
- package/dist/fill-label.js +9 -0
- package/dist/index.cjs +765 -463
- package/dist/index.d.cts +340 -27
- package/dist/index.d.ts +10 -10
- package/dist/index.js +18 -9
- package/package.json +1 -1
|
@@ -3,6 +3,29 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { useEffect, useRef, useState } from "react";
|
|
4
4
|
import { cx } from "../cx.js";
|
|
5
5
|
import { reorder } from "../table-engine.js";
|
|
6
|
+
import { fillLabel } from "../fill-label.js";
|
|
7
|
+
const DEFAULT_BUILDER_CANVAS_LABELS = {
|
|
8
|
+
region: "Builder canvas",
|
|
9
|
+
palette: "Item palette",
|
|
10
|
+
paletteHeading: "Palette",
|
|
11
|
+
paletteEmpty: "No item types available.",
|
|
12
|
+
add: "Add",
|
|
13
|
+
addFirst: "Add {label}",
|
|
14
|
+
canvasHeading: "Canvas",
|
|
15
|
+
loading: "Loading canvas\u2026",
|
|
16
|
+
retry: "Try again",
|
|
17
|
+
moveUp: "Up",
|
|
18
|
+
moveUpNamed: "Move {label} up",
|
|
19
|
+
moveDown: "Down",
|
|
20
|
+
moveDownNamed: "Move {label} down",
|
|
21
|
+
remove: "Remove",
|
|
22
|
+
removeNamed: "Remove {label}",
|
|
23
|
+
properties: "Item properties",
|
|
24
|
+
propertiesHeading: "Properties",
|
|
25
|
+
propertiesEmpty: "Select a canvas item to edit its properties.",
|
|
26
|
+
type: "Type",
|
|
27
|
+
label: "Label"
|
|
28
|
+
};
|
|
6
29
|
function BuilderCanvas({
|
|
7
30
|
items,
|
|
8
31
|
onItemsChange,
|
|
@@ -15,8 +38,10 @@ function BuilderCanvas({
|
|
|
15
38
|
error,
|
|
16
39
|
onRetry,
|
|
17
40
|
emptyMessage = "Add an item from the palette to begin.",
|
|
18
|
-
className
|
|
41
|
+
className,
|
|
42
|
+
labels: labelOverrides
|
|
19
43
|
}) {
|
|
44
|
+
const labels = { ...DEFAULT_BUILDER_CANVAS_LABELS, ...labelOverrides };
|
|
20
45
|
const [internalSelectedId, setInternalSelectedId] = useState();
|
|
21
46
|
const [draggedId, setDraggedId] = useState();
|
|
22
47
|
const propertyHeadingRef = useRef(null);
|
|
@@ -57,26 +82,23 @@ function BuilderCanvas({
|
|
|
57
82
|
if (from >= 0 && to >= 0) onItemsChange(reorder(items, from, to));
|
|
58
83
|
setDraggedId(void 0);
|
|
59
84
|
};
|
|
60
|
-
return /* @__PURE__ */ jsxs("section", { className: cx("uix-builder-canvas", className), "aria-label":
|
|
61
|
-
/* @__PURE__ */ jsxs("aside", { className: "uix-builder-canvas__palette", "aria-label":
|
|
62
|
-
/* @__PURE__ */ jsx("h3", { children:
|
|
63
|
-
palette.length === 0 ? /* @__PURE__ */ jsx("p", { children:
|
|
85
|
+
return /* @__PURE__ */ jsxs("section", { className: cx("uix-builder-canvas", className), "aria-label": labels.region, children: [
|
|
86
|
+
/* @__PURE__ */ jsxs("aside", { className: "uix-builder-canvas__palette", "aria-label": labels.palette, children: [
|
|
87
|
+
/* @__PURE__ */ jsx("h3", { children: labels.paletteHeading }),
|
|
88
|
+
palette.length === 0 ? /* @__PURE__ */ jsx("p", { children: labels.paletteEmpty }) : /* @__PURE__ */ jsx("ul", { children: palette.map((item) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("button", { type: "button", onClick: () => add(item), children: [
|
|
64
89
|
/* @__PURE__ */ jsx("strong", { children: item.label }),
|
|
65
90
|
item.description && /* @__PURE__ */ jsx("span", { children: item.description }),
|
|
66
|
-
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children:
|
|
91
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: labels.add })
|
|
67
92
|
] }) }, item.id)) })
|
|
68
93
|
] }),
|
|
69
94
|
/* @__PURE__ */ jsxs("div", { className: "uix-builder-canvas__workspace", children: [
|
|
70
|
-
/* @__PURE__ */ jsx("h3", { children:
|
|
71
|
-
loading ? /* @__PURE__ */ jsx("div", { className: "uix-builder-canvas__state", role: "status", children:
|
|
95
|
+
/* @__PURE__ */ jsx("h3", { children: labels.canvasHeading }),
|
|
96
|
+
loading ? /* @__PURE__ */ jsx("div", { className: "uix-builder-canvas__state", role: "status", children: labels.loading }) : error ? /* @__PURE__ */ jsxs("div", { className: "uix-builder-canvas__state", role: "alert", children: [
|
|
72
97
|
/* @__PURE__ */ jsx("p", { children: error }),
|
|
73
|
-
onRetry && /* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--secondary", onClick: onRetry, children:
|
|
98
|
+
onRetry && /* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--secondary", onClick: onRetry, children: labels.retry })
|
|
74
99
|
] }) : items.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "uix-builder-canvas__state", children: [
|
|
75
100
|
/* @__PURE__ */ jsx("p", { children: emptyMessage }),
|
|
76
|
-
palette[0] && /* @__PURE__ */
|
|
77
|
-
"Add ",
|
|
78
|
-
palette[0].label
|
|
79
|
-
] })
|
|
101
|
+
palette[0] && /* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--primary", onClick: () => add(palette[0]), children: fillLabel(labels.addFirst, { label: palette[0].label }) })
|
|
80
102
|
] }) : /* @__PURE__ */ jsx("ol", { className: "uix-builder-canvas__items", children: items.map((item, index) => /* @__PURE__ */ jsxs(
|
|
81
103
|
"li",
|
|
82
104
|
{
|
|
@@ -92,26 +114,27 @@ function BuilderCanvas({
|
|
|
92
114
|
item.description && /* @__PURE__ */ jsx("span", { children: item.description })
|
|
93
115
|
] }) }),
|
|
94
116
|
/* @__PURE__ */ jsxs("span", { className: "uix-builder-canvas__item-actions", children: [
|
|
95
|
-
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => move(item.id, -1), disabled: index === 0, "aria-label":
|
|
96
|
-
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => move(item.id, 1), disabled: index === items.length - 1, "aria-label":
|
|
97
|
-
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => remove(item.id), "aria-label":
|
|
117
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => move(item.id, -1), disabled: index === 0, "aria-label": fillLabel(labels.moveUpNamed, { label: item.label }), children: labels.moveUp }),
|
|
118
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => move(item.id, 1), disabled: index === items.length - 1, "aria-label": fillLabel(labels.moveDownNamed, { label: item.label }), children: labels.moveDown }),
|
|
119
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => remove(item.id), "aria-label": fillLabel(labels.removeNamed, { label: item.label }), children: labels.remove })
|
|
98
120
|
] })
|
|
99
121
|
]
|
|
100
122
|
},
|
|
101
123
|
item.id
|
|
102
124
|
)) })
|
|
103
125
|
] }),
|
|
104
|
-
/* @__PURE__ */ jsxs("aside", { className: "uix-builder-canvas__properties", "aria-label":
|
|
105
|
-
/* @__PURE__ */ jsx("h3", { ref: propertyHeadingRef, tabIndex: -1, children:
|
|
106
|
-
!selected ? /* @__PURE__ */ jsx("p", { children:
|
|
107
|
-
/* @__PURE__ */ jsx("dt", { children:
|
|
126
|
+
/* @__PURE__ */ jsxs("aside", { className: "uix-builder-canvas__properties", "aria-label": labels.properties, children: [
|
|
127
|
+
/* @__PURE__ */ jsx("h3", { ref: propertyHeadingRef, tabIndex: -1, children: labels.propertiesHeading }),
|
|
128
|
+
!selected ? /* @__PURE__ */ jsx("p", { children: labels.propertiesEmpty }) : renderProperties?.(selected, (updated) => onItemsChange(items.map((item) => item.id === updated.id ? updated : item))) ?? /* @__PURE__ */ jsxs("dl", { children: [
|
|
129
|
+
/* @__PURE__ */ jsx("dt", { children: labels.type }),
|
|
108
130
|
/* @__PURE__ */ jsx("dd", { children: selected.type }),
|
|
109
|
-
/* @__PURE__ */ jsx("dt", { children:
|
|
131
|
+
/* @__PURE__ */ jsx("dt", { children: labels.label }),
|
|
110
132
|
/* @__PURE__ */ jsx("dd", { children: selected.label })
|
|
111
133
|
] })
|
|
112
134
|
] })
|
|
113
135
|
] });
|
|
114
136
|
}
|
|
115
137
|
export {
|
|
116
|
-
BuilderCanvas
|
|
138
|
+
BuilderCanvas,
|
|
139
|
+
DEFAULT_BUILDER_CANVAS_LABELS
|
|
117
140
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ChartProps } from './ChartCore.js';
|
|
3
|
-
export { ChartLegend, ChartLegendItem, ChartLegendItemProps, ChartLegendProps, ChartMetric, ChartMetricProps, ChartTableRow, uixChartPalette } from './ChartCore.js';
|
|
3
|
+
export { ChartLabels, ChartLegend, ChartLegendItem, ChartLegendItemProps, ChartLegendProps, ChartMetric, ChartMetricProps, ChartTableRow, DEFAULT_CHART_LABELS, uixChartPalette } from './ChartCore.js';
|
|
4
4
|
import 'echarts';
|
|
5
5
|
|
|
6
6
|
/** Full-compatibility adapter. Use `@tensor_1/react/chart/preset` for the lean preset. */
|
package/dist/components/Chart.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
3
|
import * as echarts from "echarts";
|
|
4
4
|
import { ChartCore } from "./ChartCore.js";
|
|
5
|
-
import { ChartLegend, ChartLegendItem, ChartMetric, uixChartPalette } from "./ChartCore.js";
|
|
5
|
+
import { ChartLegend, ChartLegendItem, ChartMetric, DEFAULT_CHART_LABELS, uixChartPalette } from "./ChartCore.js";
|
|
6
6
|
function Chart(props) {
|
|
7
7
|
return /* @__PURE__ */ jsx(ChartCore, { ...props, engine: echarts });
|
|
8
8
|
}
|
|
@@ -11,5 +11,6 @@ export {
|
|
|
11
11
|
ChartLegend,
|
|
12
12
|
ChartLegendItem,
|
|
13
13
|
ChartMetric,
|
|
14
|
+
DEFAULT_CHART_LABELS,
|
|
14
15
|
uixChartPalette
|
|
15
16
|
};
|
|
@@ -11,6 +11,19 @@ declare function uixChartPalette(): string[];
|
|
|
11
11
|
interface ChartTableRow {
|
|
12
12
|
[key: string]: string | number;
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Every word the chart renders itself (TENSOR RX-125, UIX-04 / UIX-05). The
|
|
16
|
+
* loading text used to live in CSS `content:`, where nothing could translate it.
|
|
17
|
+
*/
|
|
18
|
+
interface ChartLabels {
|
|
19
|
+
/** Shown (and announced inside the busy region) while `loading`. */
|
|
20
|
+
loading: ReactNode;
|
|
21
|
+
/** Accessible name when neither `ariaLabel` nor `title` is given. */
|
|
22
|
+
chart: string;
|
|
23
|
+
/** Appended to the chart's name for its data-table alternative. */
|
|
24
|
+
dataTableSuffix: string;
|
|
25
|
+
}
|
|
26
|
+
declare const DEFAULT_CHART_LABELS: ChartLabels;
|
|
14
27
|
interface ChartProps {
|
|
15
28
|
option: EChartsOption;
|
|
16
29
|
title?: string;
|
|
@@ -27,12 +40,14 @@ interface ChartProps {
|
|
|
27
40
|
footer?: ReactNode;
|
|
28
41
|
loading?: boolean;
|
|
29
42
|
empty?: ReactNode;
|
|
43
|
+
/** Translatable words; English defaults in {@link DEFAULT_CHART_LABELS}. */
|
|
44
|
+
labels?: Partial<ChartLabels>;
|
|
30
45
|
}
|
|
31
46
|
interface ChartCoreProps extends ChartProps {
|
|
32
47
|
engine: ChartEngine;
|
|
33
48
|
}
|
|
34
49
|
/** Shared chart lifecycle. Runtime adapters only choose the ECharts build. */
|
|
35
|
-
declare function ChartCore({ engine, option, title, subtitle, height, className, style, ariaLabel, tableData, tableHeaders, onReady, header, headerAction, footer, loading, empty, }: ChartCoreProps): react.JSX.Element;
|
|
50
|
+
declare function ChartCore({ engine, option, title, subtitle, height, className, style, ariaLabel, tableData, tableHeaders, onReady, header, headerAction, footer, loading, empty, labels: labelOverrides, }: ChartCoreProps): react.JSX.Element;
|
|
36
51
|
interface ChartMetricProps extends HTMLAttributes<HTMLDivElement> {
|
|
37
52
|
value: ReactNode;
|
|
38
53
|
label?: ReactNode;
|
|
@@ -51,4 +66,4 @@ interface ChartLegendItemProps extends HTMLAttributes<HTMLLIElement> {
|
|
|
51
66
|
}
|
|
52
67
|
declare function ChartLegendItem({ label, color, swatch, className, style, ...props }: ChartLegendItemProps): react.JSX.Element;
|
|
53
68
|
|
|
54
|
-
export { ChartCore, type ChartEngine, ChartLegend, ChartLegendItem, type ChartLegendItemProps, type ChartLegendProps, ChartMetric, type ChartMetricProps, type ChartProps, type ChartTableRow, uixChartPalette };
|
|
69
|
+
export { ChartCore, type ChartEngine, type ChartLabels, ChartLegend, ChartLegendItem, type ChartLegendItemProps, type ChartLegendProps, ChartMetric, type ChartMetricProps, type ChartProps, type ChartTableRow, DEFAULT_CHART_LABELS, uixChartPalette };
|
|
@@ -15,6 +15,11 @@ function motionSafe(option) {
|
|
|
15
15
|
}
|
|
16
16
|
return option;
|
|
17
17
|
}
|
|
18
|
+
const DEFAULT_CHART_LABELS = {
|
|
19
|
+
loading: "Loading chart\u2026",
|
|
20
|
+
chart: "Chart",
|
|
21
|
+
dataTableSuffix: "data table"
|
|
22
|
+
};
|
|
18
23
|
function ChartCore({
|
|
19
24
|
engine,
|
|
20
25
|
option,
|
|
@@ -31,8 +36,10 @@ function ChartCore({
|
|
|
31
36
|
headerAction,
|
|
32
37
|
footer,
|
|
33
38
|
loading = false,
|
|
34
|
-
empty
|
|
39
|
+
empty,
|
|
40
|
+
labels: labelOverrides
|
|
35
41
|
}) {
|
|
42
|
+
const labels = { ...DEFAULT_CHART_LABELS, ...labelOverrides };
|
|
36
43
|
const containerRef = useRef(null);
|
|
37
44
|
const chartRef = useRef(null);
|
|
38
45
|
const appliedOptionRef = useRef(null);
|
|
@@ -87,7 +94,7 @@ function ChartCore({
|
|
|
87
94
|
appliedOptionRef.current = option;
|
|
88
95
|
}, [option]);
|
|
89
96
|
const heightValue = typeof height === "number" ? `${height}px` : height;
|
|
90
|
-
const effectiveAriaLabel = ariaLabel ?? title ??
|
|
97
|
+
const effectiveAriaLabel = ariaLabel ?? title ?? labels.chart;
|
|
91
98
|
const hasTable = Boolean(tableData?.length && tableHeaders?.length);
|
|
92
99
|
return /* @__PURE__ */ jsxs("div", { className: cx("uix-chart", className), style, children: [
|
|
93
100
|
title != null || subtitle != null || header != null || headerAction != null ? /* @__PURE__ */ jsxs("div", { className: "uix-chart__header", children: [
|
|
@@ -98,17 +105,20 @@ function ChartCore({
|
|
|
98
105
|
] }),
|
|
99
106
|
headerAction
|
|
100
107
|
] }) : null,
|
|
101
|
-
hasEmpty ? /* @__PURE__ */ jsx("div", { className: "uix-chart__plot", "data-empty": true, children: empty }) : /* @__PURE__ */
|
|
102
|
-
"
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
108
|
+
hasEmpty ? /* @__PURE__ */ jsx("div", { className: "uix-chart__plot", "data-empty": true, children: empty }) : /* @__PURE__ */ jsxs("div", { className: "uix-chart__plot", "data-loading": loading || void 0, "aria-busy": loading || void 0, children: [
|
|
109
|
+
loading ? /* @__PURE__ */ jsx("span", { className: "uix-chart__loading", children: labels.loading }) : null,
|
|
110
|
+
/* @__PURE__ */ jsx(
|
|
111
|
+
"div",
|
|
112
|
+
{
|
|
113
|
+
ref: containerRef,
|
|
114
|
+
style: { height: heightValue },
|
|
115
|
+
role: "img",
|
|
116
|
+
"aria-label": effectiveAriaLabel,
|
|
117
|
+
"aria-describedby": hasTable ? tableId : void 0
|
|
118
|
+
}
|
|
119
|
+
)
|
|
120
|
+
] }),
|
|
121
|
+
hasTable ? /* @__PURE__ */ jsxs("table", { id: tableId, className: "uix-visually-hidden", "aria-label": `${effectiveAriaLabel} \u2014 ${labels.dataTableSuffix}`, children: [
|
|
112
122
|
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: tableHeaders.map((label) => /* @__PURE__ */ jsx("th", { scope: "col", children: label }, label)) }) }),
|
|
113
123
|
/* @__PURE__ */ jsx("tbody", { children: tableData.map((row, index) => /* @__PURE__ */ jsx("tr", { children: tableHeaders.map((label) => /* @__PURE__ */ jsx("td", { children: row[label] }, label)) }, index)) })
|
|
114
124
|
] }) : null,
|
|
@@ -137,5 +147,6 @@ export {
|
|
|
137
147
|
ChartLegend,
|
|
138
148
|
ChartLegendItem,
|
|
139
149
|
ChartMetric,
|
|
150
|
+
DEFAULT_CHART_LABELS,
|
|
140
151
|
uixChartPalette
|
|
141
152
|
};
|
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Every word the colour picker renders (TENSOR RX-125, UIX-12). `{label}`, `{color}`,
|
|
5
|
+
* `{ratio}` and `{minimum}` are placeholders; `sample` is the contrast preview text.
|
|
6
|
+
*/
|
|
7
|
+
interface ColorPickerLabels {
|
|
8
|
+
trigger: string;
|
|
9
|
+
sample: string;
|
|
10
|
+
hue: string;
|
|
11
|
+
saturation: string;
|
|
12
|
+
brightness: string;
|
|
13
|
+
hex: string;
|
|
14
|
+
set: string;
|
|
15
|
+
presets: string;
|
|
16
|
+
recent: string;
|
|
17
|
+
usePreset: string;
|
|
18
|
+
useRecent: string;
|
|
19
|
+
contrastPasses: string;
|
|
20
|
+
contrastFails: string;
|
|
21
|
+
done: string;
|
|
22
|
+
}
|
|
23
|
+
declare const DEFAULT_COLOR_PICKER_LABELS: ColorPickerLabels;
|
|
3
24
|
interface ColorPickerProps {
|
|
4
25
|
value: string;
|
|
5
26
|
onChange: (value: string) => void;
|
|
@@ -10,8 +31,9 @@ interface ColorPickerProps {
|
|
|
10
31
|
label?: string;
|
|
11
32
|
disabled?: boolean;
|
|
12
33
|
className?: string;
|
|
34
|
+
labels?: Partial<ColorPickerLabels>;
|
|
13
35
|
}
|
|
14
36
|
/** Token-friendly HSV/hex picker with a fully keyboard-operable path and contrast feedback. */
|
|
15
|
-
declare function ColorPicker({ value, onChange, foreground, presets, recent, minimumContrast, label, disabled, className, }: ColorPickerProps): react.JSX.Element;
|
|
37
|
+
declare function ColorPicker({ value, onChange, foreground, presets, recent, minimumContrast, label, disabled, className, labels: labelOverrides, }: ColorPickerProps): react.JSX.Element;
|
|
16
38
|
|
|
17
|
-
export { ColorPicker, type ColorPickerProps };
|
|
39
|
+
export { ColorPicker, type ColorPickerLabels, type ColorPickerProps, DEFAULT_COLOR_PICKER_LABELS };
|
|
@@ -3,6 +3,23 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { useEffect, useId, useMemo, useRef, useState } from "react";
|
|
4
4
|
import { cx } from "../cx.js";
|
|
5
5
|
import { contrastRatio, hexToRgb, hsvToHex, normalizeHex, rgbToHsv } from "../color-model.js";
|
|
6
|
+
import { fillLabel } from "../fill-label.js";
|
|
7
|
+
const DEFAULT_COLOR_PICKER_LABELS = {
|
|
8
|
+
trigger: "{label}: {color}",
|
|
9
|
+
sample: "Aa",
|
|
10
|
+
hue: "Hue",
|
|
11
|
+
saturation: "Saturation",
|
|
12
|
+
brightness: "Brightness",
|
|
13
|
+
hex: "Hex",
|
|
14
|
+
set: "Set",
|
|
15
|
+
presets: "Presets",
|
|
16
|
+
recent: "Recent",
|
|
17
|
+
usePreset: "Use preset {color}",
|
|
18
|
+
useRecent: "Use recent color {color}",
|
|
19
|
+
contrastPasses: "Contrast {ratio}:1 \u2014 passes WCAG AA ({minimum}:1)",
|
|
20
|
+
contrastFails: "Contrast {ratio}:1 \u2014 does not meet WCAG AA ({minimum}:1)",
|
|
21
|
+
done: "Done"
|
|
22
|
+
};
|
|
6
23
|
const EMPTY_COLORS = [];
|
|
7
24
|
function ColorPicker({
|
|
8
25
|
value,
|
|
@@ -13,8 +30,10 @@ function ColorPicker({
|
|
|
13
30
|
minimumContrast = 4.5,
|
|
14
31
|
label = "Choose color",
|
|
15
32
|
disabled,
|
|
16
|
-
className
|
|
33
|
+
className,
|
|
34
|
+
labels: labelOverrides
|
|
17
35
|
}) {
|
|
36
|
+
const labels = { ...DEFAULT_COLOR_PICKER_LABELS, ...labelOverrides };
|
|
18
37
|
const id = useId();
|
|
19
38
|
const triggerRef = useRef(null);
|
|
20
39
|
const dialogRef = useRef(null);
|
|
@@ -75,7 +94,7 @@ function ColorPicker({
|
|
|
75
94
|
ref: triggerRef,
|
|
76
95
|
type: "button",
|
|
77
96
|
className: "uix-color-picker__trigger",
|
|
78
|
-
"aria-label":
|
|
97
|
+
"aria-label": fillLabel(labels.trigger, { label, color: normalized }),
|
|
79
98
|
"aria-expanded": open,
|
|
80
99
|
"aria-controls": `${id}-dialog`,
|
|
81
100
|
onClick: () => setOpen((current) => !current),
|
|
@@ -97,9 +116,10 @@ function ColorPicker({
|
|
|
97
116
|
className: "uix-color-picker__popover",
|
|
98
117
|
onKeyDown: onDialogKeyDown,
|
|
99
118
|
children: [
|
|
100
|
-
/* @__PURE__ */ jsx("div", { className: "uix-color-picker__preview", style: sampleStyle, children: /* @__PURE__ */ jsx("span", { style: { color: foreground }, children:
|
|
119
|
+
/* @__PURE__ */ jsx("div", { className: "uix-color-picker__preview", style: sampleStyle, children: /* @__PURE__ */ jsx("span", { style: { color: foreground }, children: labels.sample }) }),
|
|
101
120
|
/* @__PURE__ */ jsxs("label", { className: "uix-color-picker__range", children: [
|
|
102
|
-
|
|
121
|
+
labels.hue,
|
|
122
|
+
" ",
|
|
103
123
|
/* @__PURE__ */ jsxs("span", { children: [
|
|
104
124
|
Math.round(hsv.h),
|
|
105
125
|
"\xB0"
|
|
@@ -107,7 +127,8 @@ function ColorPicker({
|
|
|
107
127
|
/* @__PURE__ */ jsx("input", { type: "range", min: "0", max: "359", value: Math.round(hsv.h), onChange: (event) => updateHsv({ h: Number(event.currentTarget.value) }) })
|
|
108
128
|
] }),
|
|
109
129
|
/* @__PURE__ */ jsxs("label", { className: "uix-color-picker__range", children: [
|
|
110
|
-
|
|
130
|
+
labels.saturation,
|
|
131
|
+
" ",
|
|
111
132
|
/* @__PURE__ */ jsxs("span", { children: [
|
|
112
133
|
Math.round(hsv.s * 100),
|
|
113
134
|
"%"
|
|
@@ -115,7 +136,8 @@ function ColorPicker({
|
|
|
115
136
|
/* @__PURE__ */ jsx("input", { type: "range", min: "0", max: "100", value: Math.round(hsv.s * 100), onChange: (event) => updateHsv({ s: Number(event.currentTarget.value) / 100 }) })
|
|
116
137
|
] }),
|
|
117
138
|
/* @__PURE__ */ jsxs("label", { className: "uix-color-picker__range", children: [
|
|
118
|
-
|
|
139
|
+
labels.brightness,
|
|
140
|
+
" ",
|
|
119
141
|
/* @__PURE__ */ jsxs("span", { children: [
|
|
120
142
|
Math.round(hsv.v * 100),
|
|
121
143
|
"%"
|
|
@@ -123,36 +145,32 @@ function ColorPicker({
|
|
|
123
145
|
/* @__PURE__ */ jsx("input", { type: "range", min: "0", max: "100", value: Math.round(hsv.v * 100), onChange: (event) => updateHsv({ v: Number(event.currentTarget.value) / 100 }) })
|
|
124
146
|
] }),
|
|
125
147
|
/* @__PURE__ */ jsxs("label", { className: "uix-color-picker__hex", children: [
|
|
126
|
-
|
|
148
|
+
labels.hex,
|
|
127
149
|
/* @__PURE__ */ jsxs("span", { className: "uix-color-picker__hex-row", children: [
|
|
128
150
|
/* @__PURE__ */ jsx("input", { className: "uix-input", value: hexText, onChange: (event) => setHexText(event.currentTarget.value), onBlur: commitHex, onKeyDown: (event) => {
|
|
129
151
|
if (event.key === "Enter") commitHex();
|
|
130
152
|
}, "aria-invalid": !normalizeHex(hexText) }),
|
|
131
|
-
/* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--secondary uix-btn--sm", onClick: commitHex, disabled: !normalizeHex(hexText), children:
|
|
153
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--secondary uix-btn--sm", onClick: commitHex, disabled: !normalizeHex(hexText), children: labels.set })
|
|
132
154
|
] })
|
|
133
155
|
] }),
|
|
134
156
|
(presets.length > 0 || recent.length > 0) && /* @__PURE__ */ jsxs("div", { className: "uix-color-picker__palettes", children: [
|
|
135
157
|
presets.length > 0 && /* @__PURE__ */ jsxs("fieldset", { children: [
|
|
136
|
-
/* @__PURE__ */ jsx("legend", { children:
|
|
137
|
-
/* @__PURE__ */ jsx("div", { children: presets.map((color) => /* @__PURE__ */ jsx("button", { type: "button", className: "uix-color-picker__preset", style: { backgroundColor: normalizeHex(color) ?? color }, onClick: () => choose(color), "aria-label":
|
|
158
|
+
/* @__PURE__ */ jsx("legend", { children: labels.presets }),
|
|
159
|
+
/* @__PURE__ */ jsx("div", { children: presets.map((color) => /* @__PURE__ */ jsx("button", { type: "button", className: "uix-color-picker__preset", style: { backgroundColor: normalizeHex(color) ?? color }, onClick: () => choose(color), "aria-label": fillLabel(labels.usePreset, { color }) }, color)) })
|
|
138
160
|
] }),
|
|
139
161
|
recent.length > 0 && /* @__PURE__ */ jsxs("fieldset", { children: [
|
|
140
|
-
/* @__PURE__ */ jsx("legend", { children:
|
|
141
|
-
/* @__PURE__ */ jsx("div", { children: recent.map((color) => /* @__PURE__ */ jsx("button", { type: "button", className: "uix-color-picker__preset", style: { backgroundColor: normalizeHex(color) ?? color }, onClick: () => choose(color), "aria-label":
|
|
162
|
+
/* @__PURE__ */ jsx("legend", { children: labels.recent }),
|
|
163
|
+
/* @__PURE__ */ jsx("div", { children: recent.map((color) => /* @__PURE__ */ jsx("button", { type: "button", className: "uix-color-picker__preset", style: { backgroundColor: normalizeHex(color) ?? color }, onClick: () => choose(color), "aria-label": fillLabel(labels.useRecent, { color }) }, color)) })
|
|
142
164
|
] })
|
|
143
165
|
] }),
|
|
144
|
-
/* @__PURE__ */
|
|
145
|
-
|
|
146
|
-
ratio.toFixed(2),
|
|
147
|
-
":1 \u2014 ",
|
|
148
|
-
passes ? `passes WCAG AA (${minimumContrast}:1)` : `does not meet WCAG AA (${minimumContrast}:1)`
|
|
149
|
-
] }),
|
|
150
|
-
/* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--primary uix-btn--sm", onClick: close, children: "Done" })
|
|
166
|
+
/* @__PURE__ */ jsx("p", { className: cx("uix-color-picker__contrast", passes ? "uix-color-picker__contrast--pass" : "uix-color-picker__contrast--fail"), role: "status", "aria-live": "polite", children: fillLabel(passes ? labels.contrastPasses : labels.contrastFails, { ratio: ratio.toFixed(2), minimum: minimumContrast }) }),
|
|
167
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--primary uix-btn--sm", onClick: close, children: labels.done })
|
|
151
168
|
]
|
|
152
169
|
}
|
|
153
170
|
)
|
|
154
171
|
] });
|
|
155
172
|
}
|
|
156
173
|
export {
|
|
157
|
-
ColorPicker
|
|
174
|
+
ColorPicker,
|
|
175
|
+
DEFAULT_COLOR_PICKER_LABELS
|
|
158
176
|
};
|
|
@@ -2,6 +2,8 @@ import * as react from 'react';
|
|
|
2
2
|
import { ReactNode, HTMLAttributes, InputHTMLAttributes } from 'react';
|
|
3
3
|
|
|
4
4
|
interface CommandPaletteProps extends HTMLAttributes<HTMLDivElement> {
|
|
5
|
+
/** TENSOR RX-125 (UIX-04): translatable; English default. */
|
|
6
|
+
inputLabel?: string;
|
|
5
7
|
/** Props for the search input (value/onChange/placeholder wired by the consumer). */
|
|
6
8
|
inputProps?: InputHTMLAttributes<HTMLInputElement>;
|
|
7
9
|
children?: ReactNode;
|
|
@@ -12,7 +14,7 @@ interface CommandPaletteProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
12
14
|
* a combobox over the listbox of `CommandItem`s; spreads override every default, so
|
|
13
15
|
* consumers already passing their own roles/labels keep working.
|
|
14
16
|
*/
|
|
15
|
-
declare function CommandPalette({ inputProps, children, className, ...props }: CommandPaletteProps): react.JSX.Element;
|
|
17
|
+
declare function CommandPalette({ inputProps, children, inputLabel, className, ...props }: CommandPaletteProps): react.JSX.Element;
|
|
16
18
|
interface CommandGroupProps {
|
|
17
19
|
label?: ReactNode;
|
|
18
20
|
children?: ReactNode;
|
|
@@ -3,7 +3,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { createContext, useCallback, useContext, useEffect, useId, useMemo, useState } from "react";
|
|
4
4
|
import { cx } from "../cx.js";
|
|
5
5
|
const CmdkContext = createContext(null);
|
|
6
|
-
function CommandPalette({ inputProps, children, className, ...props }) {
|
|
6
|
+
function CommandPalette({ inputProps, children, inputLabel = "Command palette", className, ...props }) {
|
|
7
7
|
const listId = useId();
|
|
8
8
|
const [activeId, setActiveId] = useState(null);
|
|
9
9
|
const [count, setCount] = useState(0);
|
|
@@ -30,7 +30,7 @@ function CommandPalette({ inputProps, children, className, ...props }) {
|
|
|
30
30
|
"aria-autocomplete": "list",
|
|
31
31
|
"aria-controls": listId,
|
|
32
32
|
"aria-activedescendant": activeId ?? void 0,
|
|
33
|
-
"aria-label":
|
|
33
|
+
"aria-label": inputLabel,
|
|
34
34
|
...inputProps
|
|
35
35
|
}
|
|
36
36
|
),
|
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { DateRangeValue } from '../calendar-model.js';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Every word the picker renders or announces (TENSOR RX-125, UIX-13). `{start}` and
|
|
6
|
+
* `{end}` are replaced with the locale-formatted dates.
|
|
7
|
+
*/
|
|
8
|
+
interface DateRangePickerLabels {
|
|
9
|
+
previous: string;
|
|
10
|
+
previousMonth: string;
|
|
11
|
+
next: string;
|
|
12
|
+
nextMonth: string;
|
|
13
|
+
noneSelected: string;
|
|
14
|
+
startSelected: string;
|
|
15
|
+
rangeSelected: string;
|
|
16
|
+
rangeStart: string;
|
|
17
|
+
rangeEnd: string;
|
|
18
|
+
inRange: string;
|
|
19
|
+
}
|
|
20
|
+
declare const DEFAULT_DATE_RANGE_PICKER_LABELS: DateRangePickerLabels;
|
|
4
21
|
interface DateRangePickerProps {
|
|
5
22
|
value: DateRangeValue;
|
|
6
23
|
onChange: (value: DateRangeValue) => void;
|
|
@@ -16,8 +33,10 @@ interface DateRangePickerProps {
|
|
|
16
33
|
invalidMessage?: string;
|
|
17
34
|
disabled?: boolean;
|
|
18
35
|
className?: string;
|
|
36
|
+
/** Translatable words; English defaults in {@link DEFAULT_DATE_RANGE_PICKER_LABELS}. */
|
|
37
|
+
labels?: Partial<DateRangePickerLabels>;
|
|
19
38
|
}
|
|
20
39
|
/** Controlled, two-month range picker using the UIx calendar language. */
|
|
21
|
-
declare function DateRangePicker({ value, onChange, visibleMonth: controlledMonth, onVisibleMonthChange, months, min, max, isDateDisabled, locale, label, invalid, invalidMessage, disabled, className, }: DateRangePickerProps): react.JSX.Element;
|
|
40
|
+
declare function DateRangePicker({ value, onChange, visibleMonth: controlledMonth, onVisibleMonthChange, months, min, max, isDateDisabled, locale, label, invalid, invalidMessage, disabled, className, labels: labelOverrides, }: DateRangePickerProps): react.JSX.Element;
|
|
22
41
|
|
|
23
|
-
export { DateRangePicker, type DateRangePickerProps };
|
|
42
|
+
export { DEFAULT_DATE_RANGE_PICKER_LABELS, DateRangePicker, type DateRangePickerLabels, type DateRangePickerProps };
|
|
@@ -3,8 +3,24 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { useEffect, useId, useMemo, useRef, useState } from "react";
|
|
4
4
|
import { addCalendarDays, addCalendarMonths, buildMonthGrid, isDateInRange, isDateUnavailable, selectRangeDate, startOfMonth } from "../calendar-model.js";
|
|
5
5
|
import { cx } from "../cx.js";
|
|
6
|
+
const DEFAULT_DATE_RANGE_PICKER_LABELS = {
|
|
7
|
+
previous: "Previous",
|
|
8
|
+
previousMonth: "Show previous month",
|
|
9
|
+
next: "Next",
|
|
10
|
+
nextMonth: "Show next month",
|
|
11
|
+
noneSelected: "No date range selected.",
|
|
12
|
+
startSelected: "Start date {start} selected. Choose an end date.",
|
|
13
|
+
rangeSelected: "{start} to {end} selected.",
|
|
14
|
+
rangeStart: "start of selected range",
|
|
15
|
+
rangeEnd: "end of selected range",
|
|
16
|
+
inRange: "in selected range"
|
|
17
|
+
};
|
|
6
18
|
const monthLabel = (date, locale) => new Intl.DateTimeFormat(locale, { month: "long", year: "numeric", timeZone: "UTC" }).format(/* @__PURE__ */ new Date(`${date}T00:00:00Z`));
|
|
7
19
|
const dateLabel = (date, locale) => new Intl.DateTimeFormat(locale, { dateStyle: "full", timeZone: "UTC" }).format(/* @__PURE__ */ new Date(`${date}T00:00:00Z`));
|
|
20
|
+
const weekdayNames = (locale) => {
|
|
21
|
+
const fmt = new Intl.DateTimeFormat(locale, { weekday: "short", timeZone: "UTC" });
|
|
22
|
+
return Array.from({ length: 7 }, (_, i) => fmt.format(new Date(Date.UTC(2024, 0, 1 + i))));
|
|
23
|
+
};
|
|
8
24
|
function DateRangePicker({
|
|
9
25
|
value,
|
|
10
26
|
onChange,
|
|
@@ -19,9 +35,12 @@ function DateRangePicker({
|
|
|
19
35
|
invalid,
|
|
20
36
|
invalidMessage = "The selected date range is invalid.",
|
|
21
37
|
disabled,
|
|
22
|
-
className
|
|
38
|
+
className,
|
|
39
|
+
labels: labelOverrides
|
|
23
40
|
}) {
|
|
24
41
|
const id = useId();
|
|
42
|
+
const labels = { ...DEFAULT_DATE_RANGE_PICKER_LABELS, ...labelOverrides };
|
|
43
|
+
const weekdays = useMemo(() => weekdayNames(locale), [locale]);
|
|
25
44
|
const [internalMonth, setInternalMonth] = useState(() => startOfMonth(controlledMonth ?? value.start ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)));
|
|
26
45
|
const visibleMonth = startOfMonth(controlledMonth ?? internalMonth);
|
|
27
46
|
const [activeDate, setActiveDate] = useState(value.end ?? value.start ?? visibleMonth);
|
|
@@ -76,18 +95,18 @@ function DateRangePicker({
|
|
|
76
95
|
focusDate(addCalendarMonths(date, event.key === "PageUp" ? -1 : 1));
|
|
77
96
|
}
|
|
78
97
|
};
|
|
79
|
-
const announcement = !value.start ?
|
|
98
|
+
const announcement = !value.start ? labels.noneSelected : !value.end ? labels.startSelected.replace("{start}", dateLabel(value.start, locale)) : labels.rangeSelected.replace("{start}", dateLabel(value.start, locale)).replace("{end}", dateLabel(value.end, locale));
|
|
80
99
|
return /* @__PURE__ */ jsxs("section", { className: cx("uix-date-range-picker", rangeInvalid && "uix-date-range-picker--invalid", className), "aria-labelledby": `${id}-label`, children: [
|
|
81
100
|
/* @__PURE__ */ jsxs("div", { className: "uix-date-range-picker__header", children: [
|
|
82
101
|
/* @__PURE__ */ jsx("h3", { id: `${id}-label`, children: label }),
|
|
83
102
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
84
|
-
/* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: () => setMonth(addCalendarMonths(visibleMonth, -1)), "aria-label":
|
|
85
|
-
/* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: () => setMonth(addCalendarMonths(visibleMonth, 1)), "aria-label":
|
|
103
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: () => setMonth(addCalendarMonths(visibleMonth, -1)), "aria-label": labels.previousMonth, children: labels.previous }),
|
|
104
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: () => setMonth(addCalendarMonths(visibleMonth, 1)), "aria-label": labels.nextMonth, children: labels.next })
|
|
86
105
|
] })
|
|
87
106
|
] }),
|
|
88
107
|
/* @__PURE__ */ jsx("div", { ref: rootRef, className: "uix-date-range-picker__months", children: calendars.map(({ month, days }) => /* @__PURE__ */ jsxs("div", { className: "uix-date-range-picker__month", children: [
|
|
89
108
|
/* @__PURE__ */ jsx("h4", { children: monthLabel(month, locale) }),
|
|
90
|
-
/* @__PURE__ */ jsx("div", { className: "uix-date-range-picker__weekdays", "aria-hidden": "true", children:
|
|
109
|
+
/* @__PURE__ */ jsx("div", { className: "uix-date-range-picker__weekdays", "aria-hidden": "true", children: weekdays.map((day, index) => /* @__PURE__ */ jsx("span", { children: day }, index)) }),
|
|
91
110
|
/* @__PURE__ */ jsxs("div", { className: "uix-date-range-picker__grid", role: "group", "aria-label": monthLabel(month, locale), children: [
|
|
92
111
|
Array.from({ length: ((/* @__PURE__ */ new Date(`${month}T00:00:00Z`)).getUTCDay() + 6) % 7 }, (_, index) => /* @__PURE__ */ jsx("span", { "aria-hidden": "true" }, `blank-${index}`)),
|
|
93
112
|
days.map((day) => {
|
|
@@ -101,7 +120,7 @@ function DateRangePicker({
|
|
|
101
120
|
"data-range-edge": edge,
|
|
102
121
|
"data-in-range": isDateInRange(day.date, value) || void 0,
|
|
103
122
|
"aria-pressed": edge != null || isDateInRange(day.date, value),
|
|
104
|
-
"aria-label": `${dateLabel(day.date, locale)}${edge ? `, ${edge
|
|
123
|
+
"aria-label": `${dateLabel(day.date, locale)}${edge ? `, ${edge === "start" ? labels.rangeStart : labels.rangeEnd}` : isDateInRange(day.date, value) ? `, ${labels.inRange}` : ""}`,
|
|
105
124
|
tabIndex: activeDate === day.date ? 0 : -1,
|
|
106
125
|
disabled: unavailable,
|
|
107
126
|
onFocus: () => setActiveDate(day.date),
|
|
@@ -119,5 +138,6 @@ function DateRangePicker({
|
|
|
119
138
|
] });
|
|
120
139
|
}
|
|
121
140
|
export {
|
|
141
|
+
DEFAULT_DATE_RANGE_PICKER_LABELS,
|
|
122
142
|
DateRangePicker
|
|
123
143
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { HTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
import { HTMLAttributes, ReactNode, MouseEvent } from 'react';
|
|
3
3
|
|
|
4
4
|
interface DetailPageTab {
|
|
5
5
|
id: string;
|
|
@@ -12,6 +12,14 @@ interface DetailPageMetric {
|
|
|
12
12
|
label: ReactNode;
|
|
13
13
|
value: ReactNode;
|
|
14
14
|
}
|
|
15
|
+
/** What `renderLink` receives: render it as your router's link (TENSOR RX-125, UIX-10). */
|
|
16
|
+
interface DetailPageLinkProps {
|
|
17
|
+
href: string;
|
|
18
|
+
className: string;
|
|
19
|
+
children: ReactNode;
|
|
20
|
+
'aria-current'?: 'page';
|
|
21
|
+
onClick?: (event: MouseEvent<HTMLAnchorElement>) => void;
|
|
22
|
+
}
|
|
15
23
|
interface DetailPageProps extends Omit<HTMLAttributes<HTMLElement>, 'title'> {
|
|
16
24
|
back?: {
|
|
17
25
|
href: string;
|
|
@@ -26,8 +34,19 @@ interface DetailPageProps extends Omit<HTMLAttributes<HTMLElement>, 'title'> {
|
|
|
26
34
|
side?: ReactNode;
|
|
27
35
|
children?: ReactNode;
|
|
28
36
|
tabsLabel?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Renders the back link and every tab link. Default: a plain `<a>`. Pass your
|
|
39
|
+
* router's link (e.g. next/link) so switching tabs never reloads the page.
|
|
40
|
+
*/
|
|
41
|
+
renderLink?: (props: DetailPageLinkProps) => ReactNode;
|
|
42
|
+
/**
|
|
43
|
+
* Controlled tabs: called with the tab id on a plain left click, which then does
|
|
44
|
+
* not navigate. `href` stays the progressive-enhancement fallback (new tab,
|
|
45
|
+
* modified click, no JS).
|
|
46
|
+
*/
|
|
47
|
+
onTabSelect?: (id: string) => void;
|
|
29
48
|
}
|
|
30
49
|
/** Framework-neutral record-detail scaffold; routing and data policy stay with the application. */
|
|
31
|
-
declare function DetailPage({ back, eyebrow, title, subtitle, actions, tabs, metrics, side, children, tabsLabel, className, ...props }: DetailPageProps): react.JSX.Element;
|
|
50
|
+
declare function DetailPage({ back, eyebrow, title, subtitle, actions, tabs, metrics, side, children, tabsLabel, renderLink, onTabSelect, className, ...props }: DetailPageProps): react.JSX.Element;
|
|
32
51
|
|
|
33
|
-
export { DetailPage, type DetailPageMetric, type DetailPageProps, type DetailPageTab };
|
|
52
|
+
export { DetailPage, type DetailPageLinkProps, type DetailPageMetric, type DetailPageProps, type DetailPageTab };
|