@tensor_1/react 2.38.0 → 2.40.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 (60) hide show
  1. package/README.md +65 -0
  2. package/dist/{chart-theme-Cl0IrkKQ.d.cts → ChartCore-BKK2HEfi.d.cts} +77 -50
  3. package/dist/chart-analytic-guide.d.ts +6 -0
  4. package/dist/chart-analytic-guide.js +10 -0
  5. package/dist/chart-analytic-model.d.ts +145 -0
  6. package/dist/chart-analytic-model.js +259 -0
  7. package/dist/chart-analytic.cjs +1379 -0
  8. package/dist/chart-analytic.d.cts +339 -0
  9. package/dist/chart-analytic.d.ts +19 -0
  10. package/dist/chart-analytic.js +95 -0
  11. package/dist/chart-panels-model.d.ts +92 -0
  12. package/dist/chart-panels-model.js +284 -0
  13. package/dist/chart-preset.cjs +41 -4
  14. package/dist/chart-preset.d.cts +2 -2
  15. package/dist/chart-preset.d.ts +1 -1
  16. package/dist/chart-stats-o4zDhpVm.d.cts +46 -0
  17. package/dist/chart-stats.d.ts +46 -0
  18. package/dist/chart-stats.js +89 -0
  19. package/dist/chart-theme.d.ts +3 -0
  20. package/dist/chart-theme.js +2 -0
  21. package/dist/chart.cjs +41 -4
  22. package/dist/chart.d.cts +2 -2
  23. package/dist/chart.d.ts +1 -1
  24. package/dist/components/Chart.d.ts +1 -1
  25. package/dist/components/ChartCore.d.ts +23 -3
  26. package/dist/components/ChartCore.js +40 -5
  27. package/dist/components/ChartPanels.d.ts +52 -0
  28. package/dist/components/ChartPanels.js +170 -0
  29. package/dist/components/Comments.d.ts +4 -1
  30. package/dist/components/Comments.js +3 -2
  31. package/dist/components/InsightCard.d.ts +86 -0
  32. package/dist/components/InsightCard.js +118 -0
  33. package/dist/components/Markdown.d.ts +9 -2
  34. package/dist/components/Markdown.js +16 -2
  35. package/dist/components/RichTextEditor.d.ts +17 -0
  36. package/dist/components/RichTextEditor.js +179 -11
  37. package/dist/components/ToggleRow.d.ts +8 -1
  38. package/dist/components/ToggleRow.js +17 -3
  39. package/dist/correlation-scatter-model.d.ts +68 -0
  40. package/dist/correlation-scatter-model.js +179 -0
  41. package/dist/icons/Icon.d.ts +17 -17
  42. package/dist/index.cjs +1663 -1427
  43. package/dist/index.d.cts +96 -3
  44. package/dist/index.d.ts +2 -0
  45. package/dist/index.js +10 -0
  46. package/dist/markdown-model-Cuwi6pIk.d.cts +110 -0
  47. package/dist/markdown-model.d.ts +38 -1
  48. package/dist/markdown-model.js +67 -0
  49. package/dist/markdown.cjs +90 -4
  50. package/dist/markdown.d.cts +11 -75
  51. package/dist/markdown.d.ts +1 -1
  52. package/dist/markdown.js +16 -2
  53. package/dist/rich-text/mention.d.ts +14 -0
  54. package/dist/rich-text/mention.js +57 -0
  55. package/dist/rich-text/pipeline.js +4 -1
  56. package/dist/rich-text.cjs +1795 -1483
  57. package/dist/rich-text.d.cts +27 -1
  58. package/dist/rich-text.d.ts +3 -0
  59. package/dist/rich-text.js +6 -0
  60. package/package.json +6 -1
package/dist/chart.cjs CHANGED
@@ -77,6 +77,8 @@ function uixChartTokens(source) {
77
77
  event: t("chart-event"),
78
78
  partial: t("chart-partial"),
79
79
  comparison: t("chart-comparison"),
80
+ warning: t("warning"),
81
+ danger: t("danger"),
80
82
  surface: t("surface"),
81
83
  text: t("text"),
82
84
  textMuted: t("text-muted"),
@@ -159,7 +161,8 @@ function uixChartPalette() {
159
161
  return [1, 2, 3, 4, 5, 6, 7, 8].map((i) => readToken(`--uix-chart-${i}`));
160
162
  }
161
163
  function themed(option, theme, element) {
162
- return theme === "none" ? option : mergeChartTheme(uixChartTheme(element), option);
164
+ const resolved = typeof option === "function" ? option(uixChartTokens(element)) : option;
165
+ return theme === "none" ? resolved : mergeChartTheme(uixChartTheme(element), resolved);
163
166
  }
164
167
  function motionSafe(option) {
165
168
  if (typeof matchMedia !== "undefined" && matchMedia("(prefers-reduced-motion: reduce)").matches) {
@@ -190,17 +193,22 @@ function ChartCore({
190
193
  loading = false,
191
194
  empty,
192
195
  labels: labelOverrides,
193
- theme = "uix"
196
+ theme = "uix",
197
+ group,
198
+ onEvents,
199
+ tables
194
200
  }) {
195
201
  const labels = { ...DEFAULT_CHART_LABELS, ...labelOverrides };
196
202
  const containerRef = (0, import_react.useRef)(null);
197
203
  const chartRef = (0, import_react.useRef)(null);
198
204
  const appliedOptionRef = (0, import_react.useRef)(null);
205
+ const onEventsRef = (0, import_react.useRef)(onEvents);
206
+ onEventsRef.current = onEvents;
199
207
  const latestOptionRef = (0, import_react.useRef)(option);
200
208
  const onReadyRef = (0, import_react.useRef)(onReady);
201
209
  const tableId = (0, import_react.useId)();
202
210
  const hasEmpty = empty != null;
203
- const hasTextAlternative = Boolean(ariaLabel || title || tableData?.length);
211
+ const hasTextAlternative = Boolean(ariaLabel || title || tableData?.length || tables?.length);
204
212
  const hasTextAlternativeRef = (0, import_react.useRef)(hasTextAlternative);
205
213
  const themeRef = (0, import_react.useRef)(theme);
206
214
  latestOptionRef.current = option;
@@ -252,6 +260,29 @@ function ChartCore({
252
260
  appliedOptionRef.current = null;
253
261
  };
254
262
  }, [engine, hasEmpty]);
263
+ const eventNames = Object.keys(onEvents ?? {}).sort().join("|");
264
+ (0, import_react.useEffect)(() => {
265
+ const chart = chartRef.current;
266
+ if (!chart || !eventNames) return;
267
+ const bound = eventNames.split("|").map((name) => {
268
+ const handler = (params) => onEventsRef.current?.[name]?.(params);
269
+ chart.on(name, handler);
270
+ return [name, handler];
271
+ });
272
+ return () => {
273
+ if (chart.isDisposed()) return;
274
+ for (const [name, handler] of bound) chart.off(name, handler);
275
+ };
276
+ }, [engine, hasEmpty, eventNames]);
277
+ (0, import_react.useEffect)(() => {
278
+ const chart = chartRef.current;
279
+ if (!chart || !group) return;
280
+ chart.group = group;
281
+ engine.connect?.(group);
282
+ return () => {
283
+ if (!chart.isDisposed()) chart.group = "";
284
+ };
285
+ }, [engine, hasEmpty, group]);
255
286
  (0, import_react.useEffect)(() => {
256
287
  const chart = chartRef.current;
257
288
  const element = containerRef.current;
@@ -262,6 +293,8 @@ function ChartCore({
262
293
  const heightValue = typeof height === "number" ? `${height}px` : height;
263
294
  const effectiveAriaLabel = ariaLabel ?? title ?? labels.chart;
264
295
  const hasTable = Boolean(tableData?.length && tableHeaders?.length);
296
+ const extraTables = (tables ?? []).filter((table) => table.headers.length && table.rows.length);
297
+ const describedBy = [hasTable ? tableId : null, ...extraTables.map((_, index) => `${tableId}-${index}`)].filter(Boolean).join(" ") || void 0;
265
298
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: cx("uix-chart", className), style, children: [
266
299
  title != null || subtitle != null || header != null || headerAction != null ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "uix-chart__header", children: [
267
300
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "uix-chart__heading", children: [
@@ -280,7 +313,7 @@ function ChartCore({
280
313
  style: { height: heightValue },
281
314
  role: "img",
282
315
  "aria-label": effectiveAriaLabel,
283
- "aria-describedby": hasTable ? tableId : void 0
316
+ "aria-describedby": describedBy
284
317
  }
285
318
  )
286
319
  ] }),
@@ -288,6 +321,10 @@ function ChartCore({
288
321
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tr", { children: tableHeaders.map((label) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { scope: "col", children: label }, label)) }) }),
289
322
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: tableData.map((row, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tr", { children: tableHeaders.map((label) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: row[label] }, label)) }, index)) })
290
323
  ] }) : null,
324
+ extraTables.map((table, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", { id: `${tableId}-${index}`, className: "uix-visually-hidden", "aria-label": table.caption, children: [
325
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tr", { children: table.headers.map((label) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { scope: "col", children: label }, label)) }) }),
326
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: table.rows.map((row, rowIndex) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tr", { children: table.headers.map((label) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: row[label] }, label)) }, rowIndex)) })
327
+ ] }, index)),
291
328
  footer != null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "uix-chart__footer", children: footer })
292
329
  ] });
293
330
  }
package/dist/chart.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
- import { C as ChartProps } from './chart-theme-Cl0IrkKQ.cjs';
3
- export { a as ChartLabels, b as ChartLegend, c as ChartLegendItem, d as ChartLegendItemProps, e as ChartLegendProps, f as ChartMetric, g as ChartMetricProps, h as ChartTableRow, i as ChartTokenSource, D as DEFAULT_CHART_LABELS, U as UixChartTokens, m as mergeChartTheme, u as uixChartAreaGradient, j as uixChartPalette, k as uixChartTheme, l as uixChartTokens } from './chart-theme-Cl0IrkKQ.cjs';
2
+ import { a as ChartProps } from './ChartCore-BKK2HEfi.cjs';
3
+ export { c as ChartEventHandler, C as ChartLabels, d as ChartLegend, e as ChartLegendItem, f as ChartLegendItemProps, g as ChartLegendProps, h as ChartMetric, i as ChartMetricProps, j as ChartOption, k as ChartTable, l as ChartTableRow, m as ChartTokenSource, D as DEFAULT_CHART_LABELS, U as UixChartTokens, n as mergeChartTheme, u as uixChartAreaGradient, o as uixChartPalette, p as uixChartTheme, q as uixChartTokens } from './ChartCore-BKK2HEfi.cjs';
4
4
  import 'echarts';
5
5
 
6
6
  /** Full-compatibility adapter. Use `@tensor_1/react/chart/preset` for the lean preset. */
package/dist/chart.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { Chart } from './components/Chart.js';
2
- export { ChartLabels, ChartLegend, ChartLegendItem, ChartLegendItemProps, ChartLegendProps, ChartMetric, ChartMetricProps, ChartProps, ChartTableRow, DEFAULT_CHART_LABELS, uixChartPalette } from './components/ChartCore.js';
2
+ export { ChartEventHandler, ChartLabels, ChartLegend, ChartLegendItem, ChartLegendItemProps, ChartLegendProps, ChartMetric, ChartMetricProps, ChartOption, ChartProps, ChartTable, ChartTableRow, DEFAULT_CHART_LABELS, uixChartPalette } from './components/ChartCore.js';
3
3
  export { ChartTokenSource, UixChartTokens, mergeChartTheme, uixChartAreaGradient, uixChartTheme, uixChartTokens } from './chart-theme.js';
4
4
  import 'react';
5
5
  import 'echarts';
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
2
  import { ChartProps } from './ChartCore.js';
3
- export { ChartLabels, ChartLegend, ChartLegendItem, ChartLegendItemProps, ChartLegendProps, ChartMetric, ChartMetricProps, ChartTableRow, DEFAULT_CHART_LABELS, uixChartPalette } from './ChartCore.js';
3
+ export { ChartEventHandler, ChartLabels, ChartLegend, ChartLegendItem, ChartLegendItemProps, ChartLegendProps, ChartMetric, ChartMetricProps, ChartOption, ChartTable, ChartTableRow, DEFAULT_CHART_LABELS, uixChartPalette } from './ChartCore.js';
4
4
  export { ChartTokenSource, UixChartTokens, mergeChartTheme, uixChartAreaGradient, uixChartTheme, uixChartTokens } from '../chart-theme.js';
5
5
  import 'echarts';
6
6
 
@@ -1,12 +1,25 @@
1
1
  import * as react from 'react';
2
2
  import { CSSProperties, ReactNode, HTMLAttributes } from 'react';
3
3
  import { EChartsOption, ECharts } from 'echarts';
4
+ import { UixChartTokens } from '../chart-theme.js';
4
5
 
5
6
  interface ChartEngine {
6
7
  init(element: HTMLElement, theme?: string | null, options?: {
7
8
  renderer?: 'svg' | 'canvas';
8
9
  }): ECharts;
10
+ /** Optional: links charts that share a `group` (one crosshair and tooltip across cards). */
11
+ connect?(group: string): void;
9
12
  }
13
+ /** An option, or a function that builds it from the chart tokens of the current theme (re-run on a theme flip). */
14
+ type ChartOption = EChartsOption | ((tokens: UixChartTokens) => EChartsOption);
15
+ /** A further text alternative: one visually hidden table (ChartPanels gives one per panel). */
16
+ interface ChartTable {
17
+ caption: string;
18
+ headers: string[];
19
+ rows: ChartTableRow[];
20
+ }
21
+ /** An ECharts event handler; `params` is ECharts' event object. */
22
+ type ChartEventHandler = (params: unknown) => void;
10
23
  declare function uixChartPalette(): string[];
11
24
  interface ChartTableRow {
12
25
  [key: string]: string | number;
@@ -25,7 +38,8 @@ interface ChartLabels {
25
38
  }
26
39
  declare const DEFAULT_CHART_LABELS: ChartLabels;
27
40
  interface ChartProps {
28
- option: EChartsOption;
41
+ /** The ECharts option, or a function of the theme's chart tokens (re-run when the theme flips). */
42
+ option: ChartOption;
29
43
  title?: string;
30
44
  subtitle?: string;
31
45
  height?: string | number;
@@ -47,12 +61,18 @@ interface ChartProps {
47
61
  * grid and the UIx tooltip — and re-applies it when the page theme changes. `'none'` renders `option` as is.
48
62
  */
49
63
  theme?: 'uix' | 'none';
64
+ /** Charts with the same group share one crosshair and tooltip (`echarts.connect`). */
65
+ group?: string;
66
+ /** ECharts event handlers by event name (`click` for drill-down). */
67
+ onEvents?: Record<string, ChartEventHandler>;
68
+ /** Further hidden data tables (after `tableData`), each named in the chart's description. */
69
+ tables?: ChartTable[];
50
70
  }
51
71
  interface ChartCoreProps extends ChartProps {
52
72
  engine: ChartEngine;
53
73
  }
54
74
  /** Shared chart lifecycle. Runtime adapters only choose the ECharts build. */
55
- declare function ChartCore({ engine, option, title, subtitle, height, className, style, ariaLabel, tableData, tableHeaders, onReady, header, headerAction, footer, loading, empty, labels: labelOverrides, theme, }: ChartCoreProps): react.JSX.Element;
75
+ declare function ChartCore({ engine, option, title, subtitle, height, className, style, ariaLabel, tableData, tableHeaders, onReady, header, headerAction, footer, loading, empty, labels: labelOverrides, theme, group, onEvents, tables, }: ChartCoreProps): react.JSX.Element;
56
76
  interface ChartMetricProps extends HTMLAttributes<HTMLDivElement> {
57
77
  value: ReactNode;
58
78
  label?: ReactNode;
@@ -71,4 +91,4 @@ interface ChartLegendItemProps extends HTMLAttributes<HTMLLIElement> {
71
91
  }
72
92
  declare function ChartLegendItem({ label, color, swatch, className, style, ...props }: ChartLegendItemProps): react.JSX.Element;
73
93
 
74
- export { ChartCore, type ChartEngine, type ChartLabels, ChartLegend, ChartLegendItem, type ChartLegendItemProps, type ChartLegendProps, ChartMetric, type ChartMetricProps, type ChartProps, type ChartTableRow, DEFAULT_CHART_LABELS, uixChartPalette };
94
+ export { ChartCore, type ChartEngine, type ChartEventHandler, type ChartLabels, ChartLegend, ChartLegendItem, type ChartLegendItemProps, type ChartLegendProps, ChartMetric, type ChartMetricProps, type ChartOption, type ChartProps, type ChartTable, type ChartTableRow, DEFAULT_CHART_LABELS, uixChartPalette };
@@ -2,7 +2,7 @@
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useEffect, useId, useRef } from "react";
4
4
  import { cx } from "../cx.js";
5
- import { mergeChartTheme, uixChartTheme } from "../chart-theme.js";
5
+ import { mergeChartTheme, uixChartTheme, uixChartTokens } from "../chart-theme.js";
6
6
  function readToken(name) {
7
7
  if (typeof getComputedStyle === "undefined") return "";
8
8
  return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
@@ -11,7 +11,8 @@ function uixChartPalette() {
11
11
  return [1, 2, 3, 4, 5, 6, 7, 8].map((i) => readToken(`--uix-chart-${i}`));
12
12
  }
13
13
  function themed(option, theme, element) {
14
- return theme === "none" ? option : mergeChartTheme(uixChartTheme(element), option);
14
+ const resolved = typeof option === "function" ? option(uixChartTokens(element)) : option;
15
+ return theme === "none" ? resolved : mergeChartTheme(uixChartTheme(element), resolved);
15
16
  }
16
17
  function motionSafe(option) {
17
18
  if (typeof matchMedia !== "undefined" && matchMedia("(prefers-reduced-motion: reduce)").matches) {
@@ -42,17 +43,22 @@ function ChartCore({
42
43
  loading = false,
43
44
  empty,
44
45
  labels: labelOverrides,
45
- theme = "uix"
46
+ theme = "uix",
47
+ group,
48
+ onEvents,
49
+ tables
46
50
  }) {
47
51
  const labels = { ...DEFAULT_CHART_LABELS, ...labelOverrides };
48
52
  const containerRef = useRef(null);
49
53
  const chartRef = useRef(null);
50
54
  const appliedOptionRef = useRef(null);
55
+ const onEventsRef = useRef(onEvents);
56
+ onEventsRef.current = onEvents;
51
57
  const latestOptionRef = useRef(option);
52
58
  const onReadyRef = useRef(onReady);
53
59
  const tableId = useId();
54
60
  const hasEmpty = empty != null;
55
- const hasTextAlternative = Boolean(ariaLabel || title || tableData?.length);
61
+ const hasTextAlternative = Boolean(ariaLabel || title || tableData?.length || tables?.length);
56
62
  const hasTextAlternativeRef = useRef(hasTextAlternative);
57
63
  const themeRef = useRef(theme);
58
64
  latestOptionRef.current = option;
@@ -104,6 +110,29 @@ function ChartCore({
104
110
  appliedOptionRef.current = null;
105
111
  };
106
112
  }, [engine, hasEmpty]);
113
+ const eventNames = Object.keys(onEvents ?? {}).sort().join("|");
114
+ useEffect(() => {
115
+ const chart = chartRef.current;
116
+ if (!chart || !eventNames) return;
117
+ const bound = eventNames.split("|").map((name) => {
118
+ const handler = (params) => onEventsRef.current?.[name]?.(params);
119
+ chart.on(name, handler);
120
+ return [name, handler];
121
+ });
122
+ return () => {
123
+ if (chart.isDisposed()) return;
124
+ for (const [name, handler] of bound) chart.off(name, handler);
125
+ };
126
+ }, [engine, hasEmpty, eventNames]);
127
+ useEffect(() => {
128
+ const chart = chartRef.current;
129
+ if (!chart || !group) return;
130
+ chart.group = group;
131
+ engine.connect?.(group);
132
+ return () => {
133
+ if (!chart.isDisposed()) chart.group = "";
134
+ };
135
+ }, [engine, hasEmpty, group]);
107
136
  useEffect(() => {
108
137
  const chart = chartRef.current;
109
138
  const element = containerRef.current;
@@ -114,6 +143,8 @@ function ChartCore({
114
143
  const heightValue = typeof height === "number" ? `${height}px` : height;
115
144
  const effectiveAriaLabel = ariaLabel ?? title ?? labels.chart;
116
145
  const hasTable = Boolean(tableData?.length && tableHeaders?.length);
146
+ const extraTables = (tables ?? []).filter((table) => table.headers.length && table.rows.length);
147
+ const describedBy = [hasTable ? tableId : null, ...extraTables.map((_, index) => `${tableId}-${index}`)].filter(Boolean).join(" ") || void 0;
117
148
  return /* @__PURE__ */ jsxs("div", { className: cx("uix-chart", className), style, children: [
118
149
  title != null || subtitle != null || header != null || headerAction != null ? /* @__PURE__ */ jsxs("div", { className: "uix-chart__header", children: [
119
150
  /* @__PURE__ */ jsxs("div", { className: "uix-chart__heading", children: [
@@ -132,7 +163,7 @@ function ChartCore({
132
163
  style: { height: heightValue },
133
164
  role: "img",
134
165
  "aria-label": effectiveAriaLabel,
135
- "aria-describedby": hasTable ? tableId : void 0
166
+ "aria-describedby": describedBy
136
167
  }
137
168
  )
138
169
  ] }),
@@ -140,6 +171,10 @@ function ChartCore({
140
171
  /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: tableHeaders.map((label) => /* @__PURE__ */ jsx("th", { scope: "col", children: label }, label)) }) }),
141
172
  /* @__PURE__ */ jsx("tbody", { children: tableData.map((row, index) => /* @__PURE__ */ jsx("tr", { children: tableHeaders.map((label) => /* @__PURE__ */ jsx("td", { children: row[label] }, label)) }, index)) })
142
173
  ] }) : null,
174
+ extraTables.map((table, index) => /* @__PURE__ */ jsxs("table", { id: `${tableId}-${index}`, className: "uix-visually-hidden", "aria-label": table.caption, children: [
175
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: table.headers.map((label) => /* @__PURE__ */ jsx("th", { scope: "col", children: label }, label)) }) }),
176
+ /* @__PURE__ */ jsx("tbody", { children: table.rows.map((row, rowIndex) => /* @__PURE__ */ jsx("tr", { children: table.headers.map((label) => /* @__PURE__ */ jsx("td", { children: row[label] }, label)) }, rowIndex)) })
177
+ ] }, index)),
143
178
  footer != null && /* @__PURE__ */ jsx("div", { className: "uix-chart__footer", children: footer })
144
179
  ] });
145
180
  }
@@ -0,0 +1,52 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+ import { ChartLabels, ChartEngine } from './ChartCore.js';
4
+ import { ChartPanelsSpec, ChartPanelsLabels } from '../chart-panels-model.js';
5
+ import { ChartX, ChartEvent } from '../chart-analytic-model.js';
6
+ import { CorrelationScatterSpec, CorrelationScatterLabels, CorrelationPoint } from '../correlation-scatter-model.js';
7
+ import 'echarts';
8
+ import '../chart-theme.js';
9
+ import '../chart-stats.js';
10
+
11
+ interface ChartFrameProps {
12
+ /** Accessible name of the whole chart. Required: a chart without one tells a screen reader nothing. */
13
+ ariaLabel: string;
14
+ title?: string;
15
+ subtitle?: string;
16
+ header?: ReactNode;
17
+ headerAction?: ReactNode;
18
+ footer?: ReactNode;
19
+ loading?: boolean;
20
+ empty?: ReactNode;
21
+ className?: string;
22
+ /** Charts with the same group share one crosshair across cards. */
23
+ group?: string;
24
+ }
25
+ interface ChartPanelsProps extends ChartPanelsSpec, ChartFrameProps {
26
+ labels?: Partial<ChartPanelsLabels> & Partial<ChartLabels>;
27
+ /** A click on a panel's point. */
28
+ onPointClick?: (panelId: string, x: ChartX) => void;
29
+ /** A click on an event pin. Without a handler, an event with `href` navigates there. */
30
+ onEventClick?: (event: ChartEvent) => void;
31
+ /** Show the legend (series of multi-series panels, comparison, projection). Default: when there is one. */
32
+ legend?: boolean;
33
+ }
34
+ /** Aligned panels on one time axis with one crosshair (HAR-1554). Different units never share an axis. */
35
+ declare function ChartPanelsCore({ engine, x, panels, events, partialLast, zoom, ariaLabel, title, subtitle, header, headerAction, footer, loading, empty, className, group, labels, onPointClick, onEventClick, legend, }: ChartPanelsProps & {
36
+ engine: ChartEngine;
37
+ }): react.JSX.Element;
38
+ interface CorrelationScatterProps extends CorrelationScatterSpec, ChartFrameProps {
39
+ labels?: Partial<CorrelationScatterLabels> & Partial<ChartLabels>;
40
+ /** Show r, n and "association, not cause" under the plot. Default true. */
41
+ showStats?: boolean;
42
+ /** Plot height in px. Default 280. */
43
+ height?: number;
44
+ /** A click on a point. Without a handler, a point with `href` navigates there. */
45
+ onPointClick?: (point: CorrelationPoint) => void;
46
+ }
47
+ /** How two variables move together: dots, a trend line, r and n, and an optional breaking point (HAR-1554). */
48
+ declare function CorrelationScatterCore({ engine, points, xLabel, yLabel, trend, percentiles, thresholds, breakpoint, minPoints, ariaLabel, title, subtitle, header, headerAction, footer, loading, empty, className, group, labels, showStats, height, onPointClick, }: CorrelationScatterProps & {
49
+ engine: ChartEngine;
50
+ }): react.JSX.Element;
51
+
52
+ export { ChartPanelsCore, type ChartPanelsProps, CorrelationScatterCore, type CorrelationScatterProps };
@@ -0,0 +1,170 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import { useMemo } from "react";
4
+ import { ChartCore, ChartLegend, ChartLegendItem } from "./ChartCore.js";
5
+ import { chartPanelsModel } from "../chart-panels-model.js";
6
+ import { DEFAULT_CORRELATION_SCATTER_LABELS, correlationScatterModel } from "../correlation-scatter-model.js";
7
+ const CSS_VAR_TOKENS = {
8
+ palette: [1, 2, 3, 4, 5, 6, 7, 8].map((i) => `var(--uix-chart-${i})`),
9
+ neutral: "var(--uix-chart-neutral)",
10
+ grid: "var(--uix-chart-grid)",
11
+ axis: "var(--uix-chart-axis)",
12
+ reference: "var(--uix-chart-reference)",
13
+ zoneWarning: "var(--uix-chart-zone-warning)",
14
+ zoneDanger: "var(--uix-chart-zone-danger)",
15
+ forecastBand: "var(--uix-chart-forecast-band)",
16
+ event: "var(--uix-chart-event)",
17
+ partial: "var(--uix-chart-partial)",
18
+ comparison: "var(--uix-chart-comparison)",
19
+ warning: "var(--uix-warning)",
20
+ danger: "var(--uix-danger)",
21
+ surface: "var(--uix-surface)",
22
+ text: "var(--uix-text)",
23
+ textMuted: "var(--uix-text-muted)",
24
+ border: "var(--uix-border-strong)",
25
+ fontSans: "inherit",
26
+ fontMono: "inherit"
27
+ };
28
+ function ChartPanelsCore({
29
+ engine,
30
+ x,
31
+ panels,
32
+ events,
33
+ partialLast,
34
+ zoom,
35
+ ariaLabel,
36
+ title,
37
+ subtitle,
38
+ header,
39
+ headerAction,
40
+ footer,
41
+ loading,
42
+ empty,
43
+ className,
44
+ group,
45
+ labels,
46
+ onPointClick,
47
+ onEventClick,
48
+ legend
49
+ }) {
50
+ const spec = useMemo(() => ({ x, panels, events, partialLast, zoom }), [x, panels, events, partialLast, zoom]);
51
+ const layout = useMemo(() => chartPanelsModel(spec, CSS_VAR_TOKENS, labels), [spec, labels]);
52
+ const option = useMemo(() => (tokens) => chartPanelsModel(spec, tokens, labels).option, [spec, labels]);
53
+ const onEvents = useMemo(() => ({
54
+ click: (raw) => {
55
+ const params = raw;
56
+ const value = Array.isArray(params.value) ? params.value : [];
57
+ if (params.seriesId && params.seriesId === layout.laneSeriesId) {
58
+ const event = spec.events?.[Number(value[2])];
59
+ if (!event) return;
60
+ if (onEventClick) onEventClick(event);
61
+ else if (event.href && typeof window !== "undefined") window.location.assign(event.href);
62
+ return;
63
+ }
64
+ const panelId = params.seriesId ? layout.seriesPanel[params.seriesId] : void 0;
65
+ if (panelId && onPointClick) onPointClick(panelId, value[0] ?? params.name);
66
+ }
67
+ }), [layout, spec.events, onEventClick, onPointClick]);
68
+ const showLegend = legend ?? layout.legend.length > 0;
69
+ const legendNode = showLegend && layout.legend.length ? /* @__PURE__ */ jsx(ChartLegend, { children: layout.legend.map((item) => /* @__PURE__ */ jsx(ChartLegendItem, { label: item.label, color: item.color, swatch: item.swatch }, `${item.swatch}:${item.label}`)) }) : null;
70
+ const footerNode = legendNode || footer != null ? /* @__PURE__ */ jsxs(Fragment, { children: [
71
+ legendNode,
72
+ footer
73
+ ] }) : void 0;
74
+ return /* @__PURE__ */ jsx(
75
+ ChartCore,
76
+ {
77
+ engine,
78
+ option,
79
+ height: layout.height,
80
+ ariaLabel,
81
+ title,
82
+ subtitle,
83
+ header,
84
+ headerAction,
85
+ footer: footerNode,
86
+ loading,
87
+ empty,
88
+ className,
89
+ group,
90
+ labels,
91
+ onEvents,
92
+ tables: layout.tables
93
+ }
94
+ );
95
+ }
96
+ function CorrelationScatterCore({
97
+ engine,
98
+ points,
99
+ xLabel,
100
+ yLabel,
101
+ trend,
102
+ percentiles,
103
+ thresholds,
104
+ breakpoint,
105
+ minPoints,
106
+ ariaLabel,
107
+ title,
108
+ subtitle,
109
+ header,
110
+ headerAction,
111
+ footer,
112
+ loading,
113
+ empty,
114
+ className,
115
+ group,
116
+ labels,
117
+ showStats = true,
118
+ height = 280,
119
+ onPointClick
120
+ }) {
121
+ const spec = useMemo(
122
+ () => ({ points, xLabel, yLabel, trend, percentiles, thresholds, breakpoint, minPoints }),
123
+ [points, xLabel, yLabel, trend, percentiles, thresholds, breakpoint, minPoints]
124
+ );
125
+ const layout = useMemo(() => correlationScatterModel(spec, CSS_VAR_TOKENS, labels), [spec, labels]);
126
+ const option = useMemo(() => (tokens) => correlationScatterModel(spec, tokens, labels).option, [spec, labels]);
127
+ const usable = useMemo(() => spec.points.filter((p) => Number.isFinite(p.x) && Number.isFinite(p.y)), [spec.points]);
128
+ const onEvents = useMemo(() => ({
129
+ click: (raw) => {
130
+ const params = raw;
131
+ if (params.seriesId !== layout.hitSeriesId || !Array.isArray(params.value)) return;
132
+ const point = usable[Number(params.value[2])];
133
+ if (!point) return;
134
+ if (onPointClick) onPointClick(point);
135
+ else if (point.href && typeof window !== "undefined") window.location.assign(point.href);
136
+ }
137
+ }), [layout.hitSeriesId, usable, onPointClick]);
138
+ const notEnough = !layout.enough ? /* @__PURE__ */ jsx("span", { className: "uix-chart__insufficient", children: (labels?.notEnough ?? DEFAULT_CORRELATION_SCATTER_LABELS.notEnough)(layout.n, layout.minPoints) }) : null;
139
+ const stats = showStats && layout.enough ? /* @__PURE__ */ jsx("p", { className: "uix-chart__stats", children: layout.statsText }) : null;
140
+ const footerNode = stats || footer != null ? /* @__PURE__ */ jsxs(Fragment, { children: [
141
+ stats,
142
+ footer
143
+ ] }) : void 0;
144
+ return /* @__PURE__ */ jsx(
145
+ ChartCore,
146
+ {
147
+ engine,
148
+ option,
149
+ height,
150
+ ariaLabel,
151
+ title,
152
+ subtitle,
153
+ header,
154
+ headerAction,
155
+ footer: footerNode,
156
+ loading,
157
+ empty: empty ?? notEnough ?? void 0,
158
+ className,
159
+ group,
160
+ labels,
161
+ onEvents,
162
+ tableHeaders: layout.table.headers,
163
+ tableData: layout.table.rows
164
+ }
165
+ );
166
+ }
167
+ export {
168
+ ChartPanelsCore,
169
+ CorrelationScatterCore
170
+ };
@@ -11,7 +11,10 @@ interface CommentsProps extends HTMLAttributes<HTMLDivElement> {
11
11
  */
12
12
  declare function Comments({ children, className, ...props }: CommentsProps): react.JSX.Element;
13
13
  interface CommentProps extends HTMLAttributes<HTMLDivElement> {
14
- /** Usually an `<Avatar />` (the grid reserves the leading column). */
14
+ /**
15
+ * Usually an `<Avatar />` (the grid reserves the leading column). `false` drops the
16
+ * column, for a comment that sits on a rail which already marks each row (a timeline).
17
+ */
15
18
  avatar?: ReactNode;
16
19
  author?: ReactNode;
17
20
  meta?: ReactNode;
@@ -17,14 +17,15 @@ function Comment({
17
17
  }) {
18
18
  const system = variant === "system";
19
19
  const byline = author ?? (system ? systemLabel : void 0);
20
+ const bare = avatar === false;
20
21
  return /* @__PURE__ */ jsxs(
21
22
  "div",
22
23
  {
23
- className: cx("uix-comment", system && "uix-comment--system", className),
24
+ className: cx("uix-comment", system && "uix-comment--system", bare && "uix-comment--bare", className),
24
25
  "data-variant": system ? "system" : void 0,
25
26
  ...props,
26
27
  children: [
27
- avatar ?? /* @__PURE__ */ jsx("span", { "aria-hidden": "true" }),
28
+ bare ? null : avatar ?? /* @__PURE__ */ jsx("span", { "aria-hidden": "true" }),
28
29
  /* @__PURE__ */ jsxs("div", { className: "uix-comment__body", children: [
29
30
  (byline != null || meta != null) && /* @__PURE__ */ jsxs("div", { className: "uix-comment__meta", children: [
30
31
  byline != null && /* @__PURE__ */ jsx("span", { className: "uix-comment__author", children: byline }),
@@ -0,0 +1,86 @@
1
+ import * as react from 'react';
2
+ import { HTMLAttributes, ReactNode } from 'react';
3
+
4
+ type InsightStatusTone = 'ok' | 'warning' | 'critical' | 'unknown';
5
+ interface InsightStatus {
6
+ tone: InsightStatusTone;
7
+ /** The words: the status is always readable without its colour or icon. */
8
+ label: string;
9
+ }
10
+ interface InsightComparison {
11
+ text: ReactNode;
12
+ /** Direction × whether that direction is good; `neutral` when it is neither. */
13
+ tone?: 'neutral' | 'positive' | 'negative' | 'warning';
14
+ }
15
+ interface InsightTarget {
16
+ actual: number;
17
+ target: number;
18
+ /** Qualitative bands from the start of the scale, e.g. [[0, 10], [10, 15], [15, 24]]. */
19
+ ranges?: [number, number][];
20
+ /** The scale's end. Default: the larger of the last range, the actual and the target, plus 10 %. */
21
+ max?: number;
22
+ }
23
+ interface InsightKpi {
24
+ value: ReactNode;
25
+ label: ReactNode;
26
+ comparison?: InsightComparison;
27
+ target?: InsightTarget;
28
+ }
29
+ interface InsightCoverage {
30
+ covered: number;
31
+ total: number;
32
+ /** What was counted, e.g. "changes classified". */
33
+ note: ReactNode;
34
+ /** Shown when the last period is still filling up. */
35
+ partialPeriod?: ReactNode;
36
+ }
37
+ /** Every word the card writes itself. English defaults in {@link DEFAULT_INSIGHT_CARD_LABELS}. */
38
+ interface InsightCardLabels {
39
+ /** "412 of 431 changes classified (96%)". */
40
+ coverage: (covered: number, total: number, percent: number, note: ReactNode) => ReactNode;
41
+ /** The bullet's accessible description. */
42
+ bullet: (actual: string, target: string) => string;
43
+ source: string;
44
+ updated: string;
45
+ kpis: string;
46
+ formatNumber: (value: number) => string;
47
+ }
48
+ declare const DEFAULT_INSIGHT_CARD_LABELS: InsightCardLabels;
49
+ interface InsightCardProps extends Omit<HTMLAttributes<HTMLElement>, 'title'> {
50
+ eyebrow?: ReactNode;
51
+ /** The answer, as a sentence (≤ 10 words reads best). */
52
+ headline: ReactNode;
53
+ /** The implication and the suggested decision. */
54
+ sowhat?: ReactNode;
55
+ status?: InsightStatus;
56
+ kpis?: InsightKpi[];
57
+ coverage?: InsightCoverage;
58
+ /** One drill-down action (a link or button). */
59
+ actions?: ReactNode;
60
+ source?: ReactNode;
61
+ updated?: ReactNode;
62
+ /** `split` puts the aside on the right when the card is ≥ 60rem (960 px) wide; below, it stacks. */
63
+ layout?: 'stack' | 'split';
64
+ /**
65
+ * `card` (default) draws its own border and surface. `plain` draws none, for a card that sits
66
+ * inside another frame (a dashboard widget), so there is no card inside a card.
67
+ */
68
+ variant?: 'card' | 'plain';
69
+ aside?: ReactNode;
70
+ /** The headline's heading level. Default 3. */
71
+ headingLevel?: 2 | 3 | 4;
72
+ labels?: Partial<InsightCardLabels>;
73
+ children?: ReactNode;
74
+ }
75
+ declare function InsightCard({ eyebrow, headline, sowhat, status, kpis, coverage, actions, source, updated, layout, variant, aside, headingLevel, labels: labelOverrides, className, children, ...props }: InsightCardProps): react.JSX.Element;
76
+ interface BulletProps extends Omit<HTMLAttributes<HTMLDivElement>, 'role'>, InsightTarget {
77
+ /** The accessible description, e.g. "18.7 h against a target of 15 h". */
78
+ label: string;
79
+ }
80
+ /**
81
+ * A bullet graph (Few): grey qualitative bands, a dark bar for the actual, a tick for the target.
82
+ * It replaces a gauge: the comparison is a length on one scale, not an angle.
83
+ */
84
+ declare function Bullet({ actual, target, ranges, max, label, className, style, ...props }: BulletProps): react.JSX.Element;
85
+
86
+ export { Bullet, type BulletProps, DEFAULT_INSIGHT_CARD_LABELS, InsightCard, type InsightCardLabels, type InsightCardProps, type InsightComparison, type InsightCoverage, type InsightKpi, type InsightStatus, type InsightStatusTone, type InsightTarget };