@vanduo-oss/vd3-cbun 1.4.1 → 1.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,62 @@ All notable changes to `@vanduo-oss/vd3-cbun` are documented here. This file
4
4
  tracks the PACKAGE only — never docs-site content. Per-component VERSION
5
5
  constants are listed alongside each bundle release.
6
6
 
7
+ ## 1.4.2 — 2026-09-13
8
+
9
+ Patch release: resolves all 14 Kilo review findings on PR #13 plus two adjacent
10
+ gesture-cancel bugs. The reviewed fixes include additive charts core `role` /
11
+ Vue `svgRole` options and four strengthened tests. Additive and
12
+ backward-compatible; **no
13
+ per-component VERSION constant changes** (no serialization change, no new
14
+ load-bearing default).
15
+
16
+ ### Fixed
17
+
18
+ - **draw** — the rendered DOM paint order now always matches document order.
19
+ Reorders (`bringToFront` / `sendToBack` / `bringForward` / `sendBackward`),
20
+ undo/redo, and `load()` previously updated the model but could leave the SVG
21
+ element order stale, so the visually topmost shape could disagree with
22
+ `toJSON().shapes`. Paint order is normalized inside the incremental render pass.
23
+ A second pointer contacting the canvas now cancels the active gesture
24
+ coherently: an in-progress erase keeps the model intact and restores the shapes
25
+ it had hidden, an in-progress move/resize restores the pre-drag geometry in the
26
+ model and DOM, and an in-progress pan clears `.vd-draw-panning` (also cleared
27
+ when a pinch ends). `setReadonly(false)` rebuilds the toolbar/style panel only
28
+ when it was never built and now syncs their state. The select-mode move cursor
29
+ rule targets the real `.vd-draw-shapes` layer instead of the non-existent
30
+ `.vd-draw-shape-layer`. `VD_DRAW_VERSION` stays `1.1.0`.
31
+
32
+ - **charts** — a visible data table no longer feeds back into responsive sizing.
33
+ The measured height and the `ResizeObserver` comparison now exclude the table's
34
+ layout contribution (hidden `.vd-chart-sr-only` tables contribute zero), so
35
+ growing the table no longer triggers a resize re-render loop, while genuine
36
+ container changes still resize. Multi-series line/area data tables now match
37
+ each row's x value by the same normalized key as the x scale (timestamp /
38
+ number / string) instead of strict reference equality, so per-series `Date` or
39
+ object x values no longer produce blank cells. Adds an additive core `role`
40
+ option and Vue `svgRole` prop (`string`, overrides the default
41
+ `graphics-document document`); the distinct Vue name preserves standard root
42
+ `role` attribute fallthrough. `dataTable`, `ariaRoleDescription`, and
43
+ `svgRole` react after mount. `VD_CHARTS_VERSION` stays `1.1.0`.
44
+
45
+ - **hex-grid** — fast blit frames now report `total`, `visible`, and `drawn: 0`
46
+ in `getRenderStats()` instead of leaving the previous sharp frame's counts, so
47
+ stats stay consistent with the transform in effect during a gesture.
48
+ `VD_HEX_VERSION` stays `1.1.0`.
49
+
50
+ - **tests** — the z-order test now asserts DOM order (and covers reorder,
51
+ undo/redo, send-to-back/forward/backward); the `canUndo` test builds real
52
+ history before disabling it; `autoClose: false` asserts the textarea is
53
+ unchanged with an `autoClose: true` pair-insert control; the pressure-default
54
+ smoothing test blends both missing directions at factor 0.5; the DPR test
55
+ rejects any transform carrying the device ratio; the fast-frame test pins
56
+ `visible` / `drawn`. New coverage: gesture cancellation, per-series `Date` data
57
+ tables, responsive table-growth, and Vue reactivity for `svgRole` /
58
+ `ariaRoleDescription` / `dataTable`.
59
+
60
+ Component versions in this release: **charts 1.1.0, code-editor 1.1.0, draw
61
+ 1.1.0, flowchart 1.2.0, hex-grid 1.1.0, music-player 1.0.1**.
62
+
7
63
  ## 1.4.1 — 2026-09-12
8
64
 
9
65
  Patch release: hex-grid adaptive rendering, draw vector polish, charts
package/README.md CHANGED
@@ -8,12 +8,16 @@ tree-shakeable subpath exports. It replaces the four per-repo publishes
8
8
  (`@vanduo-oss/charts`, `@vanduo-oss/flowchart`, `@vanduo-oss/hex-grid`,
9
9
  `@vanduo-oss/music-player`) of the old line.
10
10
 
11
- **Status: 1.4.1.** Six Vue3-only component families — charts, code-editor, draw,
11
+ **Status: 1.4.2.** Six Vue3-only component families — charts, code-editor, draw,
12
12
  flowchart, hex-grid, music-player — each on its own tree-shakeable subpath, with
13
- the esbuild metafile isolation guard in place. (1.4.1 adds hex-grid DPR/culling,
14
- draw vector polish and multi-touch, charts WAI-ARIA + keyboard + data table, and
15
- a wrap-mode scrollbar guard; 1.4.0 the snippet-safe `./code-editor/highlight`
16
- tokenizer; see the [changelog](./CHANGELOG.md).)
13
+ the esbuild metafile isolation guard in place. (1.4.2 is a review-fix patch: draw
14
+ paint-order and gesture-cancel correctness, charts responsive-table and
15
+ per-series data-table fixes plus additive core `role` / Vue `svgRole` options,
16
+ hex-grid fast-frame stats, and test hardening; 1.4.1 added hex-grid DPR/culling,
17
+ draw vector polish
18
+ and multi-touch, charts WAI-ARIA + keyboard + data table, and a wrap-mode
19
+ scrollbar guard; 1.4.0 the snippet-safe `./code-editor/highlight` tokenizer; see
20
+ the [changelog](./CHANGELOG.md).)
17
21
 
18
22
  ## Install
19
23
 
package/SKILL.md CHANGED
@@ -46,7 +46,9 @@ the scales (`scaleLinear`, `scaleTime`, `scaleBand`, `scalePoint`,
46
46
  CSS ships at `@vanduo-oss/vd3-cbun/charts/css`. Charts expose WAI-ARIA Graphics
47
47
  roles, arrow-key mark navigation, and an accessible data table (`dataTable`:
48
48
  `true` / `false` / `'visible'`; `ariaRoleDescription` overrides the SVG
49
- description). `VD_CHARTS_VERSION` is `1.1.0`.
49
+ description; core `role` / Vue `svgRole` override the default
50
+ `graphics-document document` role without consuming the Vue root `role`
51
+ attribute). `VD_CHARTS_VERSION` is `1.1.0`.
50
52
 
51
53
  ## Code-editor
52
54
 
@@ -109,6 +109,8 @@ export interface BaseChartOptions<T = Row> {
109
109
  dataTable?: boolean | 'visible';
110
110
  /** Custom ARIA role description override for the chart root (defaults to `${kind} chart`). */
111
111
  ariaRoleDescription?: string;
112
+ /** Override the SVG WAI-ARIA graphics role (defaults to `graphics-document document`). */
113
+ role?: string;
112
114
  }
113
115
 
114
116
  export interface CartesianChartOptions<T = Row> extends BaseChartOptions<T> {
@@ -180,9 +180,22 @@ function normalizeMargin(value, fallback = DEFAULT_MARGIN) {
180
180
  left: Number(value?.left ?? fallback.left)
181
181
  };
182
182
  }
183
+ function dataTableContribution(target) {
184
+ if (!isElement(target) || typeof target.querySelectorAll !== "function") return 0;
185
+ let total = 0;
186
+ target.querySelectorAll(".vd-chart-data-table:not(.vd-chart-sr-only)").forEach((table) => {
187
+ const style = typeof getComputedStyle === "function" ? getComputedStyle(table) : null;
188
+ const marginTop = style ? parseFloat(style.marginTop) || 0 : 0;
189
+ const marginBottom = style ? parseFloat(style.marginBottom) || 0 : 0;
190
+ total += (table.offsetHeight || 0) + marginTop + marginBottom;
191
+ });
192
+ return total;
193
+ }
183
194
  function measureTarget(target, options) {
184
195
  const width = Number(options.width) || Math.round(target.clientWidth) || DEFAULT_WIDTH;
185
- const height = Number(options.height) || Math.round(target.clientHeight) || DEFAULT_HEIGHT;
196
+ const targetHeight = Math.round(Number(target.clientHeight));
197
+ const availableHeight = Math.max(0, targetHeight - dataTableContribution(target));
198
+ const height = Number(options.height) || (targetHeight > 0 ? availableHeight : DEFAULT_HEIGHT);
186
199
  return {
187
200
  width: Math.max(160, width),
188
201
  height: Math.max(140, height)
@@ -1464,12 +1477,22 @@ function renderMultiLineChart(instance, mode) {
1464
1477
  }
1465
1478
  });
1466
1479
  setupKeyboardNavigation(svg, markGroup);
1467
- const uniqueX = unique(allRows.map((r) => r.x));
1468
- const tableRows = uniqueX.map((xVal) => {
1480
+ const xKey = (value) => String(xInfo.mapValue(value));
1481
+ const uniqueX = /* @__PURE__ */ new Map();
1482
+ for (const row of allRows) {
1483
+ const key = xKey(row.x);
1484
+ if (!uniqueX.has(key)) uniqueX.set(key, row.x);
1485
+ }
1486
+ const seriesValues = seriesList.map((s) => {
1487
+ const byX = /* @__PURE__ */ new Map();
1488
+ for (const row of s.rows) byX.set(xKey(row.x), row.y);
1489
+ return byX;
1490
+ });
1491
+ const tableRows = [...uniqueX.entries()].map(([key, xVal]) => {
1469
1492
  const rowCells = [formatCategory(xVal)];
1470
- seriesList.forEach((s) => {
1471
- const match = s.rows.find((r) => r.x === xVal);
1472
- rowCells.push(match && isFiniteNumber(match.y) ? formatNumber(match.y) : "");
1493
+ seriesValues.forEach((byX) => {
1494
+ const y = byX.get(key);
1495
+ rowCells.push(isFiniteNumber(y) ? formatNumber(y) : "");
1473
1496
  });
1474
1497
  return rowCells;
1475
1498
  });
@@ -1751,7 +1774,11 @@ var ChartInstance = class {
1751
1774
  }
1752
1775
  render() {
1753
1776
  if (this.destroyed) return this;
1777
+ const tableHeight = dataTableContribution(this.target);
1754
1778
  this.renderer(this);
1779
+ if (!Number(this.options.height) && dataTableContribution(this.target) !== tableHeight) {
1780
+ this.renderer(this);
1781
+ }
1755
1782
  return this;
1756
1783
  }
1757
1784
  update(nextOptions = {}) {
@@ -1765,11 +1792,12 @@ var ChartInstance = class {
1765
1792
  setupResizeObserver() {
1766
1793
  if (this.options.responsive === false || !hasWindow() || typeof ResizeObserver === "undefined")
1767
1794
  return;
1795
+ const chartHeight = () => Math.max(0, this.target.clientHeight - dataTableContribution(this.target));
1768
1796
  let lastWidth = this.target.clientWidth;
1769
- let lastHeight = this.target.clientHeight;
1797
+ let lastHeight = chartHeight();
1770
1798
  this.resizeObserver = new ResizeObserver(() => {
1771
1799
  const width = this.target.clientWidth;
1772
- const height = this.target.clientHeight;
1800
+ const height = chartHeight();
1773
1801
  if (width === lastWidth && height === lastHeight) return;
1774
1802
  lastWidth = width;
1775
1803
  lastHeight = height;
@@ -1887,7 +1915,9 @@ var CHART_PROPS = {
1887
1915
  // Accessible data table: `true` (sr-only, default) / `false` / `'visible'`.
1888
1916
  dataTable: { type: [Boolean, String], default: void 0 },
1889
1917
  // Override the SVG `aria-roledescription` (defaults to `${kind} chart`).
1890
- ariaRoleDescription: { type: String, default: void 0 }
1918
+ ariaRoleDescription: { type: String, default: void 0 },
1919
+ // Avoid colliding with the standard root `role` fallthrough attribute.
1920
+ svgRole: { type: String, default: void 0 }
1891
1921
  };
1892
1922
  function optionsFrom(target, props, emit) {
1893
1923
  return {
@@ -1923,6 +1953,7 @@ function optionsFrom(target, props, emit) {
1923
1953
  yAxis: props.yAxis,
1924
1954
  dataTable: props.dataTable,
1925
1955
  ariaRoleDescription: props.ariaRoleDescription,
1956
+ role: props.svgRole,
1926
1957
  // Forward the core's mark-click callbacks as the Vue emit family. Passing
1927
1958
  // all three unconditionally is safe: the core only fires the callback
1928
1959
  // relevant to the rendered chart type (bars → onBarClick, points →
@@ -1981,7 +2012,10 @@ var VdChart = (0, import_vue.defineComponent)({
1981
2012
  props.xFormat,
1982
2013
  props.yFormat,
1983
2014
  props.xAxis,
1984
- props.yAxis
2015
+ props.yAxis,
2016
+ props.dataTable,
2017
+ props.ariaRoleDescription,
2018
+ props.svgRole
1985
2019
  ],
1986
2020
  () => {
1987
2021
  if (!instance) return;