@icen.ai/ui 0.11.0 → 0.11.1

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 (93) hide show
  1. package/README.md +2 -2
  2. package/dist/base.css +11 -6
  3. package/dist/behaviors/ai-branch.mjs +13 -0
  4. package/dist/behaviors/ai-chat.mjs +13 -0
  5. package/dist/behaviors/ai-composer.mjs +13 -0
  6. package/dist/behaviors/ai-core.mjs +13 -0
  7. package/dist/behaviors/ai-diff.mjs +13 -0
  8. package/dist/behaviors/ai-feedback.mjs +13 -0
  9. package/dist/behaviors/ai-panel.mjs +13 -0
  10. package/dist/behaviors/ai-provider.mjs +13 -0
  11. package/dist/behaviors/ai-threads.mjs +13 -0
  12. package/dist/behaviors/ai-tool.mjs +242 -0
  13. package/dist/behaviors/ai-tools.mjs +242 -0
  14. package/dist/behaviors/autocomplete.mjs +13 -0
  15. package/dist/behaviors/back-top.mjs +13 -0
  16. package/dist/behaviors/carousel.mjs +13 -0
  17. package/dist/behaviors/cascader.mjs +13 -0
  18. package/dist/behaviors/charts.d.mts +12 -1
  19. package/dist/behaviors/charts.mjs +242 -0
  20. package/dist/behaviors/code-block.mjs +13 -0
  21. package/dist/behaviors/context-menu.mjs +13 -0
  22. package/dist/behaviors/controls.mjs +13 -0
  23. package/dist/behaviors/copy.mjs +13 -0
  24. package/dist/behaviors/datatable.mjs +13 -0
  25. package/dist/behaviors/date-picker.mjs +13 -0
  26. package/dist/behaviors/form-validate.mjs +13 -0
  27. package/dist/behaviors/i18n.d.mts +13 -0
  28. package/dist/behaviors/i18n.mjs +26 -0
  29. package/dist/behaviors/input.mjs +13 -0
  30. package/dist/behaviors/kb-access.mjs +13 -0
  31. package/dist/behaviors/kb-acl.mjs +13 -0
  32. package/dist/behaviors/kb-answer.mjs +242 -0
  33. package/dist/behaviors/kb-audit.mjs +13 -0
  34. package/dist/behaviors/kb-canvas.mjs +13 -0
  35. package/dist/behaviors/kb-chain.mjs +13 -0
  36. package/dist/behaviors/kb-checkpoint.mjs +13 -0
  37. package/dist/behaviors/kb-chunks.mjs +13 -0
  38. package/dist/behaviors/kb-citation.mjs +13 -0
  39. package/dist/behaviors/kb-clarify.mjs +13 -0
  40. package/dist/behaviors/kb-conflict.mjs +13 -0
  41. package/dist/behaviors/kb-connector.mjs +13 -0
  42. package/dist/behaviors/kb-core.mjs +13 -0
  43. package/dist/behaviors/kb-eval.mjs +13 -0
  44. package/dist/behaviors/kb-explain.mjs +13 -0
  45. package/dist/behaviors/kb-filter.mjs +13 -0
  46. package/dist/behaviors/kb-gap.mjs +13 -0
  47. package/dist/behaviors/kb-hittest.mjs +13 -0
  48. package/dist/behaviors/kb-metadata.mjs +13 -0
  49. package/dist/behaviors/kb-passage.mjs +13 -0
  50. package/dist/behaviors/kb-pipeline.mjs +13 -0
  51. package/dist/behaviors/kb-qa.mjs +13 -0
  52. package/dist/behaviors/kb-rerank.mjs +13 -0
  53. package/dist/behaviors/kb-retrieval.mjs +13 -0
  54. package/dist/behaviors/kb-review.mjs +13 -0
  55. package/dist/behaviors/kb-sandbox.mjs +13 -0
  56. package/dist/behaviors/kb-segment.mjs +13 -0
  57. package/dist/behaviors/kb-select.mjs +13 -0
  58. package/dist/behaviors/kb-sources.mjs +13 -0
  59. package/dist/behaviors/kb-sql.mjs +13 -0
  60. package/dist/behaviors/kb-trace.mjs +13 -0
  61. package/dist/behaviors/kb-visibility.mjs +13 -0
  62. package/dist/behaviors/kb-who-can.mjs +13 -0
  63. package/dist/behaviors/mentions.mjs +13 -0
  64. package/dist/behaviors/modal.mjs +13 -0
  65. package/dist/behaviors/motion.d.mts +16 -0
  66. package/dist/behaviors/motion.mjs +39 -0
  67. package/dist/behaviors/notification.mjs +13 -0
  68. package/dist/behaviors/panel.mjs +13 -0
  69. package/dist/behaviors/select.mjs +13 -0
  70. package/dist/behaviors/suggestions.mjs +13 -0
  71. package/dist/behaviors/tag-input.mjs +13 -0
  72. package/dist/behaviors/theme-switcher.mjs +13 -0
  73. package/dist/behaviors/toast.mjs +13 -0
  74. package/dist/behaviors/tree-select.mjs +13 -0
  75. package/dist/behaviors/upload.mjs +13 -0
  76. package/dist/components/charts.css +29 -0
  77. package/dist/index.d.mts +1 -1
  78. package/dist/index.mjs +257 -1
  79. package/dist/react.mjs +242 -0
  80. package/dist/registry.json +3 -1
  81. package/dist/tokens/design.css +15 -9
  82. package/dist/tokens/dtcg.json +3594 -738
  83. package/dist/tokens/figma.variables.json +1705 -157
  84. package/dist/tokens/motion.css +51 -0
  85. package/dist/tokens/pixel.css +7 -0
  86. package/dist/tokens/tokens-studio.json +3007 -679
  87. package/dist/tokens/typography.css +33 -0
  88. package/dist/tokens.css +106 -9
  89. package/dist/ui.css +146 -15
  90. package/dist/vue.mjs +242 -0
  91. package/docs/spec/charts-scale-a11y.md +115 -0
  92. package/docs/spec/motion-typography.md +105 -0
  93. package/package.json +2 -1
package/dist/index.d.mts CHANGED
@@ -23,7 +23,7 @@ export { OpenPopoverOptions, PanelSizing, PopoverAlign, PopoverLayout, PopoverLa
23
23
  export { initAccordion } from './behaviors/accordion.mjs';
24
24
  export { initTree } from './behaviors/tree.mjs';
25
25
  export { initCarousel } from './behaviors/carousel.mjs';
26
- export { ChartCalendarOptions, ChartChromeOptions, ChartDatum, ChartDatumDetail, ChartDims, ChartEventDetail, ChartEventName, ChartGaugeOptions, ChartHandle, ChartHeatmapOptions, ChartI18nLabels, ChartLineOptions, ChartMark, ChartMarkData, ChartMarkTone, ChartPointSpec, ChartRadarOptions, ChartScatterOptions, ChartSegment, ChartSegmentsOptions, ChartSeriesOptions, ChartSeriesSpec, ChartSpec, ChartTone, ChartType, ChartVBarOptions, ChartValueFormat, DatumChartHandle, GraphEdgeSpec, GraphLayoutOptions, GraphNodeSpec, GraphSpec, HeatmapDatum, MapPointSpec, MapSpec, RadarSeries, bindChartEvents, chartFormatValue, inferChartType, layoutGraph, lttb, normalizeChartSpec, registerChartTone, renderArea, renderCalendar, renderChart, renderDonut, renderGauge, renderGraph, renderHBar, renderHeatmap, renderLine, renderMap, renderRadar, renderScatter, renderSparkline, renderStack, renderVBar } from './behaviors/charts.mjs';
26
+ export { ChartA11ySpec, ChartCalendarOptions, ChartChromeOptions, ChartDatum, ChartDatumDetail, ChartDims, ChartEventDetail, ChartEventName, ChartGaugeOptions, ChartHandle, ChartHeatmapOptions, ChartI18nLabels, ChartLineOptions, ChartMark, ChartMarkData, ChartMarkTone, ChartPointSpec, ChartRadarOptions, ChartScatterOptions, ChartSegment, ChartSegmentsOptions, ChartSeriesOptions, ChartSeriesSpec, ChartSpec, ChartTone, ChartType, ChartVBarOptions, ChartValueFormat, DatumChartHandle, GraphEdgeSpec, GraphLayoutOptions, GraphNodeSpec, GraphSpec, HeatmapDatum, MapPointSpec, MapSpec, RadarSeries, bindChartEvents, chartFormatValue, inferChartType, layoutGraph, lttb, normalizeChartSpec, registerChartTone, renderArea, renderCalendar, renderChart, renderDonut, renderGauge, renderGraph, renderHBar, renderHeatmap, renderLine, renderMap, renderRadar, renderScatter, renderSparkline, renderStack, renderVBar } from './behaviors/charts.mjs';
27
27
  export { initNav } from './behaviors/nav.mjs';
28
28
  export { initSidebar } from './behaviors/sidebar.mjs';
29
29
  export { NotificationAction, NotificationCenterHandle, NotificationConfig, NotificationConfirmOptions, NotificationHandle, NotificationKind, NotificationOptions, NotificationPushInput, NotificationRecord, NotificationUpdatePatch, createNotificationCenter, initNotification, notify } from './behaviors/notification.mjs';
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  // package.json
2
2
  var package_default = {
3
3
  name: "@icen.ai/ui",
4
- version: "0.11.0",
4
+ version: "0.11.1",
5
5
  description: "Icen Design System \u2014 \u8BBE\u8BA1 tokens\uFF086 \u4E3B\u9898 \xD7 4 \u539F\u8BED \xD7 \u660E\u6697\uFF09+ \u65E0\u6846\u67B6\u7EC4\u4EF6 CSS/\u884C\u4E3A JS + AI \u539F\u751F\u7EC4\u4EF6\u65CF\uFF087 \u6001\u72B6\u6001\u673A/\u591A\u6A21\u6001/\u5B9A\u4EF7\u5BA1\u8BA1\uFF09+ \u56FE\u8868\u901A\u7528\u5C42\uFF08ChartSpec \u7EAF JSON\uFF09+ AI \u5DE5\u5177\u4F53\u7CFB",
6
6
  repository: {
7
7
  type: "git",
@@ -63,6 +63,7 @@ var package_default = {
63
63
  dtcg: "bun scripts/dtcg.mjs",
64
64
  "design-tokens": "bun scripts/design-tokens.mjs",
65
65
  guard: "bun scripts/guard/index.ts",
66
+ meta: "bun scripts/meta.mjs",
66
67
  contracts: "bun scripts/contracts.ts --check",
67
68
  "contracts:update": "bun scripts/contracts.ts --write",
68
69
  "contracts:diff": "bun scripts/contracts.ts --diff",
@@ -1148,6 +1149,19 @@ var ICEN_LOCALE_ZH_CN = {
1148
1149
  "charts.noData": "\u6682\u65E0\u6570\u636E",
1149
1150
  "charts.resetZoom": "\u91CD\u7F6E\u7F29\u653E",
1150
1151
  "charts.sampled": "\u5DF2\u62BD\u6837 {p0}\u2192{p1} \u70B9",
1152
+ "charts.a11y.keyboardHint": "\u65B9\u5411\u952E\u9010\u70B9\u6D4F\u89C8\uFF0CHome/End \u8DF3\u9996\u5C3E\uFF0CEsc \u91CD\u7F6E\u7F29\u653E",
1153
+ "charts.a11y.max": "\u6700\u5927 {p0}\uFF08{p1}\uFF09",
1154
+ "charts.a11y.min": "\u6700\u5C0F {p0}\uFF08{p1}\uFF09",
1155
+ "charts.a11y.point": "{p0}\uFF1A{p1}\uFF0C\u7B2C {p2}/{p3} \u70B9",
1156
+ "charts.a11y.pointSeries": "{p0} \xB7 {p1}\uFF1A{p2}\uFF0C\u7B2C {p3}/{p4} \u70B9",
1157
+ "charts.a11y.range": "\u7C7B\u76EE {p0} \u81F3 {p1}",
1158
+ "charts.a11y.scatterRange": "x \u8303\u56F4 {p0}\u2013{p1}\uFF0Cy \u8303\u56F4 {p2}\u2013{p3}",
1159
+ "charts.a11y.seriesCount": "{p0} \u4E2A\u7CFB\u5217",
1160
+ "charts.a11y.tableCaption": "{p0} \u6570\u636E\u8868",
1161
+ "charts.a11y.trend": "\u9996\u7CFB\u5217\u8D8B\u52BF{p0}",
1162
+ "charts.a11y.trendDown": "\u4E0B\u964D",
1163
+ "charts.a11y.trendFlat": "\u6301\u5E73",
1164
+ "charts.a11y.trendUp": "\u4E0A\u5347",
1151
1165
  "charts.type.area": "\u9762\u79EF\u56FE",
1152
1166
  "charts.type.donut": "\u73AF\u5F62\u56FE",
1153
1167
  "charts.type.gauge": "\u4EEA\u8868\u76D8",
@@ -1984,6 +1998,19 @@ var ICEN_LOCALE_EN_US = {
1984
1998
  "charts.noData": "No data",
1985
1999
  "charts.resetZoom": "Reset zoom",
1986
2000
  "charts.sampled": "Sampled \u2192 {p0} points",
2001
+ "charts.a11y.keyboardHint": "Arrow keys browse points, Home/End jump to ends, Esc resets zoom",
2002
+ "charts.a11y.max": "max {p0} ({p1})",
2003
+ "charts.a11y.min": "min {p0} ({p1})",
2004
+ "charts.a11y.point": "{p0}: {p1}, point {p2} of {p3}",
2005
+ "charts.a11y.pointSeries": "{p0} \xB7 {p1}: {p2}, point {p3} of {p4}",
2006
+ "charts.a11y.range": "categories {p0} to {p1}",
2007
+ "charts.a11y.scatterRange": "x {p0}\u2013{p1}, y {p2}\u2013{p3}",
2008
+ "charts.a11y.seriesCount": "{p0} series",
2009
+ "charts.a11y.tableCaption": "{p0} data table",
2010
+ "charts.a11y.trend": "first-series trend: {p0}",
2011
+ "charts.a11y.trendDown": "down",
2012
+ "charts.a11y.trendFlat": "flat",
2013
+ "charts.a11y.trendUp": "up",
1987
2014
  "charts.type.area": "Area chart",
1988
2015
  "charts.type.donut": "Donut chart",
1989
2016
  "charts.type.gauge": "Gauge",
@@ -13070,6 +13097,14 @@ function normalizeChartSpec(raw) {
13070
13097
  }
13071
13098
  if (s.zoom === "x" || s.zoom === "brush") spec.zoom = s.zoom;
13072
13099
  if (typeof s.loading === "boolean") spec.loading = s.loading;
13100
+ if (s.a11y && typeof s.a11y === "object") {
13101
+ const a = s.a11y;
13102
+ const a11y = {};
13103
+ if (typeof a.summary === "boolean") a11y.summary = a.summary;
13104
+ if (typeof a.table === "boolean") a11y.table = a.table;
13105
+ if (typeof a.keyboard === "boolean") a11y.keyboard = a.keyboard;
13106
+ if (Object.keys(a11y).length > 0) spec.a11y = a11y;
13107
+ }
13073
13108
  if (Array.isArray(s.marks)) {
13074
13109
  const marks = [];
13075
13110
  for (const raw2 of s.marks) {
@@ -13425,6 +13460,81 @@ function paintVBarChartMarks(root, view, spec, marks, formatValue2, warned) {
13425
13460
  function chartAriaLabel(title, typeName) {
13426
13461
  return title ? `${typeName}\uFF1A${title}` : typeName;
13427
13462
  }
13463
+ function summarizeChart(view, type, fmt2) {
13464
+ const series = view.series && view.series.length > 0 ? view.series : [{ name: "", values: view.values ?? [] }];
13465
+ const parts = [];
13466
+ if (series.length > 1) parts.push(t("charts.a11y.seriesCount", { p0: series.length }));
13467
+ if (type === "scatter") {
13468
+ const pts = view.points ?? [];
13469
+ if (pts.length > 0) {
13470
+ const xs = pts.map((p) => p.x);
13471
+ const ys = pts.map((p) => p.y);
13472
+ parts.push(t("charts.a11y.scatterRange", { p0: fmt2(Math.min(...xs)), p1: fmt2(Math.max(...xs)), p2: fmt2(Math.min(...ys)), p3: fmt2(Math.max(...ys)) }));
13473
+ }
13474
+ return parts.join("\uFF1B");
13475
+ }
13476
+ const labels = view.labels ?? [];
13477
+ if (labels.length > 1) parts.push(t("charts.a11y.range", { p0: labels[0], p1: labels[labels.length - 1] }));
13478
+ let min = Infinity;
13479
+ let max = -Infinity;
13480
+ let minAt = "";
13481
+ let maxAt = "";
13482
+ for (const s of series) {
13483
+ s.values.forEach((v, i) => {
13484
+ if (v < min) {
13485
+ min = v;
13486
+ minAt = labels[i] ?? String(i + 1);
13487
+ }
13488
+ if (v > max) {
13489
+ max = v;
13490
+ maxAt = labels[i] ?? String(i + 1);
13491
+ }
13492
+ });
13493
+ }
13494
+ if (Number.isFinite(min) && Number.isFinite(max)) {
13495
+ parts.push(t("charts.a11y.min", { p0: fmt2(min), p1: minAt }));
13496
+ parts.push(t("charts.a11y.max", { p0: fmt2(max), p1: maxAt }));
13497
+ }
13498
+ const first = series[0]?.values ?? [];
13499
+ if (first.length > 1) {
13500
+ const d = first[first.length - 1] - first[0];
13501
+ const key = Math.abs(d) < 1e-9 ? "charts.a11y.trendFlat" : d > 0 ? "charts.a11y.trendUp" : "charts.a11y.trendDown";
13502
+ parts.push(t("charts.a11y.trend", { p0: t(key) }));
13503
+ }
13504
+ return parts.join("\uFF1B");
13505
+ }
13506
+ function buildChartTable(view, spec, type, fmt2) {
13507
+ const table = document.createElement("table");
13508
+ table.className = "icen-chart-table";
13509
+ const caption = document.createElement("caption");
13510
+ caption.textContent = t("charts.a11y.tableCaption", { p0: spec.title ?? String(type) });
13511
+ table.appendChild(caption);
13512
+ const addRow = (target, cells, head) => {
13513
+ const tr = document.createElement("tr");
13514
+ for (const c of cells) {
13515
+ const cell = document.createElement(head ? "th" : "td");
13516
+ if (head) cell.scope = target.tagName === "THEAD" ? "col" : "row";
13517
+ cell.textContent = c;
13518
+ tr.appendChild(cell);
13519
+ }
13520
+ target.appendChild(tr);
13521
+ };
13522
+ const thead = table.createTHead();
13523
+ const tbody = table.createTBody();
13524
+ if (type === "scatter") {
13525
+ addRow(thead, ["x", "y"], true);
13526
+ for (const p of view.points ?? []) addRow(tbody, [fmt2(p.x), fmt2(p.y)], false);
13527
+ return table;
13528
+ }
13529
+ const series = view.series && view.series.length > 0 ? view.series : [{ name: "", values: view.values ?? [] }];
13530
+ const labels = view.labels ?? [];
13531
+ addRow(thead, ["", ...series.map((s, i) => s.name || String(i + 1))], true);
13532
+ const n = Math.max(labels.length, ...series.map((s) => s.values.length));
13533
+ for (let i = 0; i < n; i++) {
13534
+ addRow(tbody, [labels[i] ?? String(i + 1), ...series.map((s) => fmt2(s.values[i] ?? 0))], false);
13535
+ }
13536
+ return table;
13537
+ }
13428
13538
  function svgDimsOf(svg6) {
13429
13539
  const vb = (svg6.getAttribute("viewBox") ?? "").split(/\s+/).map(Number);
13430
13540
  const w = Number.isFinite(vb[2]) && (vb[2] ?? 0) > 0 ? vb[2] : CHART_LINE_W;
@@ -13765,6 +13875,7 @@ function renderChart(el2, raw) {
13765
13875
  if (!isZoomableType(t2) || fullLength() < 2) return;
13766
13876
  const plot = t2 === "vbar" ? root.querySelector(".icen-chart-vbar") : root.querySelector(".icen-chart-line svg");
13767
13877
  if (!plot) return;
13878
+ plot.style.touchAction = current2.zoom === "x" ? "pan-y" : "none";
13768
13879
  const on = (type, fn) => {
13769
13880
  plot.addEventListener(type, fn);
13770
13881
  };
@@ -13820,6 +13931,149 @@ function renderChart(el2, raw) {
13820
13931
  render();
13821
13932
  });
13822
13933
  };
13934
+ const KB_TYPES = /* @__PURE__ */ new Set(["line", "area", "vbar", "hbar", "scatter"]);
13935
+ const kbCursor = { si: 0, i: 0, active: false };
13936
+ let liveRegion = null;
13937
+ let a11ySeq = 0;
13938
+ const a11yOf = () => ({
13939
+ summary: current2.a11y?.summary !== false,
13940
+ table: current2.a11y?.table === true,
13941
+ keyboard: current2.a11y?.keyboard !== false
13942
+ });
13943
+ const seriesViewOf = (view) => view.series && view.series.length > 0 ? view.series : [{ name: "", values: view.values ?? [] }];
13944
+ const kbPointCount = (view) => typeOf() === "scatter" ? view.points?.length ?? 0 : Math.max(0, ...seriesViewOf(view).map((s) => s.values.length), view.labels?.length ?? 0);
13945
+ const kbCursorEl = () => {
13946
+ const t2 = typeOf();
13947
+ const view = viewOf();
13948
+ const series = seriesViewOf(view);
13949
+ const n = kbPointCount(view);
13950
+ if (n < 1) return null;
13951
+ const si = clampNum(kbCursor.si, 0, Math.max(0, series.length - 1));
13952
+ const i = clampNum(kbCursor.i, 0, n - 1);
13953
+ if (t2 === "line" || t2 === "area") {
13954
+ const svg6 = root.querySelector(".icen-chart-line svg");
13955
+ if (!svg6) return null;
13956
+ const dots = svg6.querySelectorAll("circle");
13957
+ let base = 0;
13958
+ for (let k = 0; k < si; k++) base += Math.min(series[k].values.length, n);
13959
+ return dots.item(base + i) ?? null;
13960
+ }
13961
+ if (t2 === "scatter") return root.querySelector(".icen-chart-scatter svg")?.querySelectorAll("circle").item(i) ?? null;
13962
+ if (t2 === "vbar") return root.querySelectorAll(".icen-chart-vbar-bar").item(series.length > 1 ? i * series.length + si : i) ?? null;
13963
+ if (t2 === "hbar") return root.querySelectorAll(".icen-chart-hbar-bar").item(i) ?? null;
13964
+ return null;
13965
+ };
13966
+ const announceKb = (text) => {
13967
+ if (!liveRegion || !liveRegion.isConnected) {
13968
+ liveRegion = document.createElement("div");
13969
+ liveRegion.className = "icen-chart-live";
13970
+ liveRegion.setAttribute("aria-live", "polite");
13971
+ root.appendChild(liveRegion);
13972
+ }
13973
+ liveRegion.textContent = "";
13974
+ liveRegion.textContent = text;
13975
+ };
13976
+ const applyKbCursor = () => {
13977
+ for (const el3 of root.querySelectorAll(".icen-chart-kb-focus")) el3.classList.remove("icen-chart-kb-focus");
13978
+ if (!kbCursor.active) return;
13979
+ kbCursorEl()?.classList.add("icen-chart-kb-focus");
13980
+ };
13981
+ const announceCursor = () => {
13982
+ const view = viewOf();
13983
+ const series = seriesViewOf(view);
13984
+ const n = kbPointCount(view);
13985
+ if (n < 1) return;
13986
+ const si = clampNum(kbCursor.si, 0, Math.max(0, series.length - 1));
13987
+ const i = clampNum(kbCursor.i, 0, Math.max(0, n - 1));
13988
+ const fmt2 = chartFormatValue(current2.format);
13989
+ if (typeOf() === "scatter") {
13990
+ const p = view.points?.[i];
13991
+ if (!p) return;
13992
+ announceKb(t("charts.a11y.point", { p0: `x ${fmt2(p.x)}`, p1: `y ${fmt2(p.y)}`, p2: i + 1, p3: n }));
13993
+ return;
13994
+ }
13995
+ const label = view.labels?.[i] ?? String(i + 1);
13996
+ const value = series[si]?.values[i] ?? 0;
13997
+ if (series.length > 1) announceKb(t("charts.a11y.pointSeries", { p0: series[si].name || String(si + 1), p1: label, p2: fmt2(value), p3: i + 1, p4: n }));
13998
+ else announceKb(t("charts.a11y.point", { p0: label, p1: fmt2(value), p2: i + 1, p3: n }));
13999
+ };
14000
+ const onKbKeydown = (e) => {
14001
+ const conf = a11yOf();
14002
+ if (!conf.keyboard || !KB_TYPES.has(typeOf())) return;
14003
+ const g = root.querySelector("[data-icen-chart-kb]");
14004
+ if (!g || e.target !== g) return;
14005
+ if (e.key === "Escape") {
14006
+ if (zoomWindow) {
14007
+ zoomWindow = null;
14008
+ kbCursor.active = false;
14009
+ render();
14010
+ }
14011
+ return;
14012
+ }
14013
+ const view = viewOf();
14014
+ const series = seriesViewOf(view);
14015
+ const n = kbPointCount(view);
14016
+ if (n < 1) return;
14017
+ const siMax = Math.max(0, series.length - 1);
14018
+ let moved = true;
14019
+ switch (e.key) {
14020
+ case "ArrowRight":
14021
+ kbCursor.i = clampNum(kbCursor.i + 1, 0, n - 1);
14022
+ break;
14023
+ case "ArrowLeft":
14024
+ kbCursor.i = clampNum(kbCursor.i - 1, 0, n - 1);
14025
+ break;
14026
+ case "ArrowDown":
14027
+ kbCursor.si = clampNum(kbCursor.si + 1, 0, siMax);
14028
+ break;
14029
+ case "ArrowUp":
14030
+ kbCursor.si = clampNum(kbCursor.si - 1, 0, siMax);
14031
+ break;
14032
+ case "Home":
14033
+ kbCursor.i = 0;
14034
+ break;
14035
+ case "End":
14036
+ kbCursor.i = n - 1;
14037
+ break;
14038
+ default:
14039
+ moved = false;
14040
+ }
14041
+ if (!moved) return;
14042
+ e.preventDefault();
14043
+ kbCursor.active = true;
14044
+ applyKbCursor();
14045
+ announceCursor();
14046
+ };
14047
+ root.addEventListener("keydown", onKbKeydown);
14048
+ const applyChartA11yPlus = (view, spec, type, formatValue2) => {
14049
+ const conf = a11yOf();
14050
+ const graphic = root.querySelector('[role="img"]');
14051
+ if (!graphic) return;
14052
+ const body = root.querySelector(".icen-chart-body") ?? graphic.parentElement ?? root;
14053
+ const summaryParts = [];
14054
+ if (conf.summary) summaryParts.push(summarizeChart(view, type, formatValue2));
14055
+ if (conf.keyboard && KB_TYPES.has(type)) summaryParts.push(t("charts.a11y.keyboardHint"));
14056
+ const summaryText = summaryParts.filter(Boolean).join("\uFF1B");
14057
+ if (summaryText) {
14058
+ const p = document.createElement("p");
14059
+ p.className = "icen-chart-summary";
14060
+ p.id = `icen-chart-a11y-${++a11ySeq}`;
14061
+ p.textContent = summaryText;
14062
+ body.appendChild(p);
14063
+ graphic.setAttribute("aria-describedby", p.id);
14064
+ } else {
14065
+ graphic.removeAttribute("aria-describedby");
14066
+ }
14067
+ if (conf.keyboard && KB_TYPES.has(type)) {
14068
+ graphic.setAttribute("tabindex", "0");
14069
+ graphic.setAttribute("data-icen-chart-kb", "");
14070
+ } else {
14071
+ graphic.removeAttribute("tabindex");
14072
+ graphic.removeAttribute("data-icen-chart-kb");
14073
+ }
14074
+ if (conf.table) body.appendChild(buildChartTable(view, spec, type, formatValue2));
14075
+ applyKbCursor();
14076
+ };
13823
14077
  const render = () => {
13824
14078
  if (!current2.type) current2 = { ...current2, type: inferChartType(current2) };
13825
14079
  const spec = current2;
@@ -13900,6 +14154,7 @@ function renderChart(el2, raw) {
13900
14154
  }
13901
14155
  }
13902
14156
  if (loading) return;
14157
+ applyChartA11yPlus(view, spec, type, formatValue2);
13903
14158
  paintChartMarks(root, view, spec, formatValue2, warnedMarks);
13904
14159
  bindZoomGesture();
13905
14160
  };
@@ -13981,6 +14236,7 @@ function renderChart(el2, raw) {
13981
14236
  releaseFluidWidth(root);
13982
14237
  hiddenLegendKeys.delete(root);
13983
14238
  zoomDragCleanup?.();
14239
+ root.removeEventListener("keydown", onKbKeydown);
13984
14240
  root.textContent = "";
13985
14241
  root.classList.remove("is-loading");
13986
14242
  unbindEvents();
package/dist/react.mjs CHANGED
@@ -349,6 +349,19 @@ var ICEN_LOCALE_ZH_CN = {
349
349
  "charts.noData": "\u6682\u65E0\u6570\u636E",
350
350
  "charts.resetZoom": "\u91CD\u7F6E\u7F29\u653E",
351
351
  "charts.sampled": "\u5DF2\u62BD\u6837 {p0}\u2192{p1} \u70B9",
352
+ "charts.a11y.keyboardHint": "\u65B9\u5411\u952E\u9010\u70B9\u6D4F\u89C8\uFF0CHome/End \u8DF3\u9996\u5C3E\uFF0CEsc \u91CD\u7F6E\u7F29\u653E",
353
+ "charts.a11y.max": "\u6700\u5927 {p0}\uFF08{p1}\uFF09",
354
+ "charts.a11y.min": "\u6700\u5C0F {p0}\uFF08{p1}\uFF09",
355
+ "charts.a11y.point": "{p0}\uFF1A{p1}\uFF0C\u7B2C {p2}/{p3} \u70B9",
356
+ "charts.a11y.pointSeries": "{p0} \xB7 {p1}\uFF1A{p2}\uFF0C\u7B2C {p3}/{p4} \u70B9",
357
+ "charts.a11y.range": "\u7C7B\u76EE {p0} \u81F3 {p1}",
358
+ "charts.a11y.scatterRange": "x \u8303\u56F4 {p0}\u2013{p1}\uFF0Cy \u8303\u56F4 {p2}\u2013{p3}",
359
+ "charts.a11y.seriesCount": "{p0} \u4E2A\u7CFB\u5217",
360
+ "charts.a11y.tableCaption": "{p0} \u6570\u636E\u8868",
361
+ "charts.a11y.trend": "\u9996\u7CFB\u5217\u8D8B\u52BF{p0}",
362
+ "charts.a11y.trendDown": "\u4E0B\u964D",
363
+ "charts.a11y.trendFlat": "\u6301\u5E73",
364
+ "charts.a11y.trendUp": "\u4E0A\u5347",
352
365
  "charts.type.area": "\u9762\u79EF\u56FE",
353
366
  "charts.type.donut": "\u73AF\u5F62\u56FE",
354
367
  "charts.type.gauge": "\u4EEA\u8868\u76D8",
@@ -2241,6 +2254,14 @@ function normalizeChartSpec(raw) {
2241
2254
  }
2242
2255
  if (s.zoom === "x" || s.zoom === "brush") spec.zoom = s.zoom;
2243
2256
  if (typeof s.loading === "boolean") spec.loading = s.loading;
2257
+ if (s.a11y && typeof s.a11y === "object") {
2258
+ const a = s.a11y;
2259
+ const a11y = {};
2260
+ if (typeof a.summary === "boolean") a11y.summary = a.summary;
2261
+ if (typeof a.table === "boolean") a11y.table = a.table;
2262
+ if (typeof a.keyboard === "boolean") a11y.keyboard = a.keyboard;
2263
+ if (Object.keys(a11y).length > 0) spec.a11y = a11y;
2264
+ }
2244
2265
  if (Array.isArray(s.marks)) {
2245
2266
  const marks = [];
2246
2267
  for (const raw2 of s.marks) {
@@ -2596,6 +2617,81 @@ function paintVBarChartMarks(root, view, spec, marks, formatValue, warned) {
2596
2617
  function chartAriaLabel(title, typeName) {
2597
2618
  return title ? `${typeName}\uFF1A${title}` : typeName;
2598
2619
  }
2620
+ function summarizeChart(view, type, fmt2) {
2621
+ const series = view.series && view.series.length > 0 ? view.series : [{ name: "", values: view.values ?? [] }];
2622
+ const parts = [];
2623
+ if (series.length > 1) parts.push(t("charts.a11y.seriesCount", { p0: series.length }));
2624
+ if (type === "scatter") {
2625
+ const pts = view.points ?? [];
2626
+ if (pts.length > 0) {
2627
+ const xs = pts.map((p) => p.x);
2628
+ const ys = pts.map((p) => p.y);
2629
+ parts.push(t("charts.a11y.scatterRange", { p0: fmt2(Math.min(...xs)), p1: fmt2(Math.max(...xs)), p2: fmt2(Math.min(...ys)), p3: fmt2(Math.max(...ys)) }));
2630
+ }
2631
+ return parts.join("\uFF1B");
2632
+ }
2633
+ const labels = view.labels ?? [];
2634
+ if (labels.length > 1) parts.push(t("charts.a11y.range", { p0: labels[0], p1: labels[labels.length - 1] }));
2635
+ let min = Infinity;
2636
+ let max = -Infinity;
2637
+ let minAt = "";
2638
+ let maxAt = "";
2639
+ for (const s of series) {
2640
+ s.values.forEach((v, i) => {
2641
+ if (v < min) {
2642
+ min = v;
2643
+ minAt = labels[i] ?? String(i + 1);
2644
+ }
2645
+ if (v > max) {
2646
+ max = v;
2647
+ maxAt = labels[i] ?? String(i + 1);
2648
+ }
2649
+ });
2650
+ }
2651
+ if (Number.isFinite(min) && Number.isFinite(max)) {
2652
+ parts.push(t("charts.a11y.min", { p0: fmt2(min), p1: minAt }));
2653
+ parts.push(t("charts.a11y.max", { p0: fmt2(max), p1: maxAt }));
2654
+ }
2655
+ const first = series[0]?.values ?? [];
2656
+ if (first.length > 1) {
2657
+ const d = first[first.length - 1] - first[0];
2658
+ const key = Math.abs(d) < 1e-9 ? "charts.a11y.trendFlat" : d > 0 ? "charts.a11y.trendUp" : "charts.a11y.trendDown";
2659
+ parts.push(t("charts.a11y.trend", { p0: t(key) }));
2660
+ }
2661
+ return parts.join("\uFF1B");
2662
+ }
2663
+ function buildChartTable(view, spec, type, fmt2) {
2664
+ const table = document.createElement("table");
2665
+ table.className = "icen-chart-table";
2666
+ const caption = document.createElement("caption");
2667
+ caption.textContent = t("charts.a11y.tableCaption", { p0: spec.title ?? String(type) });
2668
+ table.appendChild(caption);
2669
+ const addRow = (target, cells, head) => {
2670
+ const tr = document.createElement("tr");
2671
+ for (const c of cells) {
2672
+ const cell = document.createElement(head ? "th" : "td");
2673
+ if (head) cell.scope = target.tagName === "THEAD" ? "col" : "row";
2674
+ cell.textContent = c;
2675
+ tr.appendChild(cell);
2676
+ }
2677
+ target.appendChild(tr);
2678
+ };
2679
+ const thead = table.createTHead();
2680
+ const tbody = table.createTBody();
2681
+ if (type === "scatter") {
2682
+ addRow(thead, ["x", "y"], true);
2683
+ for (const p of view.points ?? []) addRow(tbody, [fmt2(p.x), fmt2(p.y)], false);
2684
+ return table;
2685
+ }
2686
+ const series = view.series && view.series.length > 0 ? view.series : [{ name: "", values: view.values ?? [] }];
2687
+ const labels = view.labels ?? [];
2688
+ addRow(thead, ["", ...series.map((s, i) => s.name || String(i + 1))], true);
2689
+ const n = Math.max(labels.length, ...series.map((s) => s.values.length));
2690
+ for (let i = 0; i < n; i++) {
2691
+ addRow(tbody, [labels[i] ?? String(i + 1), ...series.map((s) => fmt2(s.values[i] ?? 0))], false);
2692
+ }
2693
+ return table;
2694
+ }
2599
2695
  function svgDimsOf(svg) {
2600
2696
  const vb = (svg.getAttribute("viewBox") ?? "").split(/\s+/).map(Number);
2601
2697
  const w = Number.isFinite(vb[2]) && (vb[2] ?? 0) > 0 ? vb[2] : CHART_LINE_W;
@@ -2936,6 +3032,7 @@ function renderChart(el, raw) {
2936
3032
  if (!isZoomableType(t2) || fullLength() < 2) return;
2937
3033
  const plot = t2 === "vbar" ? root.querySelector(".icen-chart-vbar") : root.querySelector(".icen-chart-line svg");
2938
3034
  if (!plot) return;
3035
+ plot.style.touchAction = current2.zoom === "x" ? "pan-y" : "none";
2939
3036
  const on = (type, fn) => {
2940
3037
  plot.addEventListener(type, fn);
2941
3038
  };
@@ -2991,6 +3088,149 @@ function renderChart(el, raw) {
2991
3088
  render();
2992
3089
  });
2993
3090
  };
3091
+ const KB_TYPES = /* @__PURE__ */ new Set(["line", "area", "vbar", "hbar", "scatter"]);
3092
+ const kbCursor = { si: 0, i: 0, active: false };
3093
+ let liveRegion = null;
3094
+ let a11ySeq = 0;
3095
+ const a11yOf = () => ({
3096
+ summary: current2.a11y?.summary !== false,
3097
+ table: current2.a11y?.table === true,
3098
+ keyboard: current2.a11y?.keyboard !== false
3099
+ });
3100
+ const seriesViewOf = (view) => view.series && view.series.length > 0 ? view.series : [{ name: "", values: view.values ?? [] }];
3101
+ const kbPointCount = (view) => typeOf() === "scatter" ? view.points?.length ?? 0 : Math.max(0, ...seriesViewOf(view).map((s) => s.values.length), view.labels?.length ?? 0);
3102
+ const kbCursorEl = () => {
3103
+ const t2 = typeOf();
3104
+ const view = viewOf();
3105
+ const series = seriesViewOf(view);
3106
+ const n = kbPointCount(view);
3107
+ if (n < 1) return null;
3108
+ const si = clampNum(kbCursor.si, 0, Math.max(0, series.length - 1));
3109
+ const i = clampNum(kbCursor.i, 0, n - 1);
3110
+ if (t2 === "line" || t2 === "area") {
3111
+ const svg = root.querySelector(".icen-chart-line svg");
3112
+ if (!svg) return null;
3113
+ const dots = svg.querySelectorAll("circle");
3114
+ let base = 0;
3115
+ for (let k = 0; k < si; k++) base += Math.min(series[k].values.length, n);
3116
+ return dots.item(base + i) ?? null;
3117
+ }
3118
+ if (t2 === "scatter") return root.querySelector(".icen-chart-scatter svg")?.querySelectorAll("circle").item(i) ?? null;
3119
+ if (t2 === "vbar") return root.querySelectorAll(".icen-chart-vbar-bar").item(series.length > 1 ? i * series.length + si : i) ?? null;
3120
+ if (t2 === "hbar") return root.querySelectorAll(".icen-chart-hbar-bar").item(i) ?? null;
3121
+ return null;
3122
+ };
3123
+ const announceKb = (text) => {
3124
+ if (!liveRegion || !liveRegion.isConnected) {
3125
+ liveRegion = document.createElement("div");
3126
+ liveRegion.className = "icen-chart-live";
3127
+ liveRegion.setAttribute("aria-live", "polite");
3128
+ root.appendChild(liveRegion);
3129
+ }
3130
+ liveRegion.textContent = "";
3131
+ liveRegion.textContent = text;
3132
+ };
3133
+ const applyKbCursor = () => {
3134
+ for (const el2 of root.querySelectorAll(".icen-chart-kb-focus")) el2.classList.remove("icen-chart-kb-focus");
3135
+ if (!kbCursor.active) return;
3136
+ kbCursorEl()?.classList.add("icen-chart-kb-focus");
3137
+ };
3138
+ const announceCursor = () => {
3139
+ const view = viewOf();
3140
+ const series = seriesViewOf(view);
3141
+ const n = kbPointCount(view);
3142
+ if (n < 1) return;
3143
+ const si = clampNum(kbCursor.si, 0, Math.max(0, series.length - 1));
3144
+ const i = clampNum(kbCursor.i, 0, Math.max(0, n - 1));
3145
+ const fmt2 = chartFormatValue(current2.format);
3146
+ if (typeOf() === "scatter") {
3147
+ const p = view.points?.[i];
3148
+ if (!p) return;
3149
+ announceKb(t("charts.a11y.point", { p0: `x ${fmt2(p.x)}`, p1: `y ${fmt2(p.y)}`, p2: i + 1, p3: n }));
3150
+ return;
3151
+ }
3152
+ const label = view.labels?.[i] ?? String(i + 1);
3153
+ const value = series[si]?.values[i] ?? 0;
3154
+ if (series.length > 1) announceKb(t("charts.a11y.pointSeries", { p0: series[si].name || String(si + 1), p1: label, p2: fmt2(value), p3: i + 1, p4: n }));
3155
+ else announceKb(t("charts.a11y.point", { p0: label, p1: fmt2(value), p2: i + 1, p3: n }));
3156
+ };
3157
+ const onKbKeydown = (e) => {
3158
+ const conf = a11yOf();
3159
+ if (!conf.keyboard || !KB_TYPES.has(typeOf())) return;
3160
+ const g = root.querySelector("[data-icen-chart-kb]");
3161
+ if (!g || e.target !== g) return;
3162
+ if (e.key === "Escape") {
3163
+ if (zoomWindow) {
3164
+ zoomWindow = null;
3165
+ kbCursor.active = false;
3166
+ render();
3167
+ }
3168
+ return;
3169
+ }
3170
+ const view = viewOf();
3171
+ const series = seriesViewOf(view);
3172
+ const n = kbPointCount(view);
3173
+ if (n < 1) return;
3174
+ const siMax = Math.max(0, series.length - 1);
3175
+ let moved = true;
3176
+ switch (e.key) {
3177
+ case "ArrowRight":
3178
+ kbCursor.i = clampNum(kbCursor.i + 1, 0, n - 1);
3179
+ break;
3180
+ case "ArrowLeft":
3181
+ kbCursor.i = clampNum(kbCursor.i - 1, 0, n - 1);
3182
+ break;
3183
+ case "ArrowDown":
3184
+ kbCursor.si = clampNum(kbCursor.si + 1, 0, siMax);
3185
+ break;
3186
+ case "ArrowUp":
3187
+ kbCursor.si = clampNum(kbCursor.si - 1, 0, siMax);
3188
+ break;
3189
+ case "Home":
3190
+ kbCursor.i = 0;
3191
+ break;
3192
+ case "End":
3193
+ kbCursor.i = n - 1;
3194
+ break;
3195
+ default:
3196
+ moved = false;
3197
+ }
3198
+ if (!moved) return;
3199
+ e.preventDefault();
3200
+ kbCursor.active = true;
3201
+ applyKbCursor();
3202
+ announceCursor();
3203
+ };
3204
+ root.addEventListener("keydown", onKbKeydown);
3205
+ const applyChartA11yPlus = (view, spec, type, formatValue) => {
3206
+ const conf = a11yOf();
3207
+ const graphic = root.querySelector('[role="img"]');
3208
+ if (!graphic) return;
3209
+ const body = root.querySelector(".icen-chart-body") ?? graphic.parentElement ?? root;
3210
+ const summaryParts = [];
3211
+ if (conf.summary) summaryParts.push(summarizeChart(view, type, formatValue));
3212
+ if (conf.keyboard && KB_TYPES.has(type)) summaryParts.push(t("charts.a11y.keyboardHint"));
3213
+ const summaryText = summaryParts.filter(Boolean).join("\uFF1B");
3214
+ if (summaryText) {
3215
+ const p = document.createElement("p");
3216
+ p.className = "icen-chart-summary";
3217
+ p.id = `icen-chart-a11y-${++a11ySeq}`;
3218
+ p.textContent = summaryText;
3219
+ body.appendChild(p);
3220
+ graphic.setAttribute("aria-describedby", p.id);
3221
+ } else {
3222
+ graphic.removeAttribute("aria-describedby");
3223
+ }
3224
+ if (conf.keyboard && KB_TYPES.has(type)) {
3225
+ graphic.setAttribute("tabindex", "0");
3226
+ graphic.setAttribute("data-icen-chart-kb", "");
3227
+ } else {
3228
+ graphic.removeAttribute("tabindex");
3229
+ graphic.removeAttribute("data-icen-chart-kb");
3230
+ }
3231
+ if (conf.table) body.appendChild(buildChartTable(view, spec, type, formatValue));
3232
+ applyKbCursor();
3233
+ };
2994
3234
  const render = () => {
2995
3235
  if (!current2.type) current2 = { ...current2, type: inferChartType(current2) };
2996
3236
  const spec = current2;
@@ -3071,6 +3311,7 @@ function renderChart(el, raw) {
3071
3311
  }
3072
3312
  }
3073
3313
  if (loading) return;
3314
+ applyChartA11yPlus(view, spec, type, formatValue);
3074
3315
  paintChartMarks(root, view, spec, formatValue, warnedMarks);
3075
3316
  bindZoomGesture();
3076
3317
  };
@@ -3152,6 +3393,7 @@ function renderChart(el, raw) {
3152
3393
  releaseFluidWidth(root);
3153
3394
  hiddenLegendKeys.delete(root);
3154
3395
  zoomDragCleanup?.();
3396
+ root.removeEventListener("keydown", onKbKeydown);
3155
3397
  root.textContent = "";
3156
3398
  root.classList.remove("is-loading");
3157
3399
  unbindEvents();
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@icen.ai/ui",
3
- "version": "0.11.0",
3
+ "version": "0.11.1",
4
4
  "tokens": [
5
5
  "colors.css",
6
6
  "scrollbars.css",
7
7
  "style-profiles.css",
8
8
  "typography.css",
9
+ "motion.css",
9
10
  "design.css",
10
11
  "chinese.css",
11
12
  "soviet.css",
@@ -239,6 +240,7 @@
239
240
  "kb-who-can",
240
241
  "mentions",
241
242
  "modal",
243
+ "motion",
242
244
  "nav",
243
245
  "notification",
244
246
  "panel",