@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.
Files changed (65) hide show
  1. package/dist/{ChartCore-Do6gRBN3.d.cts → ChartCore-DzWe10bN.d.cts} +16 -1
  2. package/dist/chart-preset.cjs +25 -13
  3. package/dist/chart-preset.d.cts +2 -2
  4. package/dist/chart-preset.d.ts +1 -1
  5. package/dist/chart-preset.js +2 -1
  6. package/dist/chart.cjs +25 -13
  7. package/dist/chart.d.cts +2 -2
  8. package/dist/chart.d.ts +1 -1
  9. package/dist/chart.js +2 -1
  10. package/dist/components/AppShell.d.ts +5 -1
  11. package/dist/components/AppShell.js +4 -2
  12. package/dist/components/Avatar.d.ts +3 -1
  13. package/dist/components/Avatar.js +2 -2
  14. package/dist/components/BrandProfiles.d.ts +29 -2
  15. package/dist/components/BrandProfiles.js +45 -20
  16. package/dist/components/BuilderCanvas.d.ts +27 -2
  17. package/dist/components/BuilderCanvas.js +45 -22
  18. package/dist/components/Chart.d.ts +1 -1
  19. package/dist/components/Chart.js +2 -1
  20. package/dist/components/ChartCore.d.ts +17 -2
  21. package/dist/components/ChartCore.js +24 -13
  22. package/dist/components/ColorPicker.d.ts +24 -2
  23. package/dist/components/ColorPicker.js +38 -20
  24. package/dist/components/CommandPalette.d.ts +3 -1
  25. package/dist/components/CommandPalette.js +2 -2
  26. package/dist/components/DateRangePicker.d.ts +21 -2
  27. package/dist/components/DateRangePicker.js +26 -6
  28. package/dist/components/DetailPage.d.ts +22 -3
  29. package/dist/components/DetailPage.js +33 -3
  30. package/dist/components/DiffViewer.d.ts +34 -3
  31. package/dist/components/DiffViewer.js +60 -40
  32. package/dist/components/Drawer.d.ts +3 -1
  33. package/dist/components/Drawer.js +2 -2
  34. package/dist/components/Inbox.d.ts +3 -1
  35. package/dist/components/Inbox.js +2 -2
  36. package/dist/components/MatchReview.d.ts +49 -2
  37. package/dist/components/MatchReview.js +72 -30
  38. package/dist/components/Meter.d.ts +3 -1
  39. package/dist/components/Meter.js +3 -2
  40. package/dist/components/Pagination.d.ts +10 -2
  41. package/dist/components/Pagination.js +11 -4
  42. package/dist/components/Peek.d.ts +7 -1
  43. package/dist/components/Peek.js +4 -4
  44. package/dist/components/RelationshipGraph.js +6 -2
  45. package/dist/components/RuleBuilder.d.ts +42 -2
  46. package/dist/components/RuleBuilder.js +71 -37
  47. package/dist/components/SchedulingCalendar.d.ts +25 -2
  48. package/dist/components/SchedulingCalendar.js +38 -15
  49. package/dist/components/Stat.d.ts +5 -0
  50. package/dist/components/Stat.js +2 -2
  51. package/dist/components/Table.d.ts +11 -3
  52. package/dist/components/Table.js +5 -8
  53. package/dist/components/Tabs.d.ts +8 -1
  54. package/dist/components/Tabs.js +4 -5
  55. package/dist/components/Toast.d.ts +6 -2
  56. package/dist/components/Toast.js +4 -4
  57. package/dist/components/Tree.d.ts +9 -1
  58. package/dist/components/Tree.js +55 -8
  59. package/dist/fill-label.d.ts +7 -0
  60. package/dist/fill-label.js +9 -0
  61. package/dist/index.cjs +765 -463
  62. package/dist/index.d.cts +340 -27
  63. package/dist/index.d.ts +10 -10
  64. package/dist/index.js +18 -9
  65. 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": "Builder canvas", children: [
61
- /* @__PURE__ */ jsxs("aside", { className: "uix-builder-canvas__palette", "aria-label": "Item palette", children: [
62
- /* @__PURE__ */ jsx("h3", { children: "Palette" }),
63
- palette.length === 0 ? /* @__PURE__ */ jsx("p", { children: "No item types available." }) : /* @__PURE__ */ jsx("ul", { children: palette.map((item) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("button", { type: "button", onClick: () => add(item), 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: "Add" })
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: "Canvas" }),
71
- loading ? /* @__PURE__ */ jsx("div", { className: "uix-builder-canvas__state", role: "status", children: "Loading canvas\u2026" }) : error ? /* @__PURE__ */ jsxs("div", { className: "uix-builder-canvas__state", role: "alert", 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: "Try again" })
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__ */ jsxs("button", { type: "button", className: "uix-btn uix-btn--primary", onClick: () => add(palette[0]), children: [
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": `Move ${item.label} up`, children: "Up" }),
96
- /* @__PURE__ */ jsx("button", { type: "button", onClick: () => move(item.id, 1), disabled: index === items.length - 1, "aria-label": `Move ${item.label} down`, children: "Down" }),
97
- /* @__PURE__ */ jsx("button", { type: "button", onClick: () => remove(item.id), "aria-label": `Remove ${item.label}`, children: "Remove" })
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": "Item properties", children: [
105
- /* @__PURE__ */ jsx("h3", { ref: propertyHeadingRef, tabIndex: -1, children: "Properties" }),
106
- !selected ? /* @__PURE__ */ jsx("p", { children: "Select a canvas item to edit its properties." }) : renderProperties?.(selected, (updated) => onItemsChange(items.map((item) => item.id === updated.id ? updated : item))) ?? /* @__PURE__ */ jsxs("dl", { children: [
107
- /* @__PURE__ */ jsx("dt", { children: "Type" }),
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: "Label" }),
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. */
@@ -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 ?? "Chart";
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__ */ jsx("div", { className: "uix-chart__plot", "data-loading": loading || void 0, "aria-busy": loading || void 0, children: /* @__PURE__ */ jsx(
102
- "div",
103
- {
104
- ref: containerRef,
105
- style: { height: heightValue },
106
- role: "img",
107
- "aria-label": effectiveAriaLabel,
108
- "aria-describedby": hasTable ? tableId : void 0
109
- }
110
- ) }),
111
- hasTable ? /* @__PURE__ */ jsxs("table", { id: tableId, className: "uix-visually-hidden", "aria-label": `${effectiveAriaLabel} \u2014 data table`, children: [
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": `${label}: ${normalized}`,
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: "Aa" }) }),
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
- "Hue ",
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
- "Saturation ",
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
- "Brightness ",
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
- "Hex",
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: "Set" })
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: "Presets" }),
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": `Use preset ${color}` }, color)) })
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: "Recent" }),
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": `Use recent color ${color}` }, color)) })
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__ */ jsxs("p", { className: cx("uix-color-picker__contrast", passes ? "uix-color-picker__contrast--pass" : "uix-color-picker__contrast--fail"), role: "status", "aria-live": "polite", children: [
145
- "Contrast ",
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": "Command palette",
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 ? "No date range selected." : !value.end ? `Start date ${dateLabel(value.start, locale)} selected. Choose an end date.` : `${dateLabel(value.start, locale)} to ${dateLabel(value.end, locale)} selected.`;
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": "Show previous month", children: "Previous" }),
85
- /* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--ghost uix-btn--sm", onClick: () => setMonth(addCalendarMonths(visibleMonth, 1)), "aria-label": "Show next month", children: "Next" })
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: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"].map((day) => /* @__PURE__ */ jsx("span", { children: day }, day)) }),
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} of selected range` : isDateInRange(day.date, value) ? ", in selected range" : ""}`,
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 };