graphein 0.20.0 → 0.22.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 (319) hide show
  1. package/dist/animation/animate.d.ts +6 -1
  2. package/dist/animation/animate.d.ts.map +1 -1
  3. package/dist/animation/animate.js +2 -2
  4. package/dist/animation/animate.js.map +2 -2
  5. package/dist/animation/env.d.ts +5 -0
  6. package/dist/animation/env.d.ts.map +1 -1
  7. package/dist/animation/env.js +4 -0
  8. package/dist/animation/env.js.map +2 -2
  9. package/dist/animation/index.d.ts +1 -0
  10. package/dist/animation/index.d.ts.map +1 -1
  11. package/dist/animation/index.js +1 -0
  12. package/dist/animation/index.js.map +2 -2
  13. package/dist/animation/stagger.d.ts +13 -0
  14. package/dist/animation/stagger.d.ts.map +1 -0
  15. package/dist/animation/stagger.js +13 -0
  16. package/dist/animation/stagger.js.map +7 -0
  17. package/dist/axes/draw.d.ts +5 -1
  18. package/dist/axes/draw.d.ts.map +1 -1
  19. package/dist/axes/draw.js +13 -1
  20. package/dist/axes/draw.js.map +2 -2
  21. package/dist/axes/morph.d.ts +111 -0
  22. package/dist/axes/morph.d.ts.map +1 -0
  23. package/dist/axes/morph.js +207 -0
  24. package/dist/axes/morph.js.map +7 -0
  25. package/dist/axes/overlay.d.ts.map +1 -1
  26. package/dist/axes/overlay.js +88 -25
  27. package/dist/axes/overlay.js.map +2 -2
  28. package/dist/axes/text.d.ts +2 -0
  29. package/dist/axes/text.d.ts.map +1 -1
  30. package/dist/axes/text.js +1 -0
  31. package/dist/axes/text.js.map +2 -2
  32. package/dist/axes/tickLabels.d.ts +2 -0
  33. package/dist/axes/tickLabels.d.ts.map +1 -1
  34. package/dist/axes/tickLabels.js +2 -2
  35. package/dist/axes/tickLabels.js.map +2 -2
  36. package/dist/charts/annotations.d.ts +9 -3
  37. package/dist/charts/annotations.d.ts.map +1 -1
  38. package/dist/charts/annotations.js +82 -27
  39. package/dist/charts/annotations.js.map +2 -2
  40. package/dist/charts/area.d.ts +37 -1
  41. package/dist/charts/area.d.ts.map +1 -1
  42. package/dist/charts/area.js +35 -17
  43. package/dist/charts/area.js.map +2 -2
  44. package/dist/charts/bar.d.ts +53 -0
  45. package/dist/charts/bar.d.ts.map +1 -1
  46. package/dist/charts/bar.js +117 -83
  47. package/dist/charts/bar.js.map +2 -2
  48. package/dist/charts/box.d.ts +31 -0
  49. package/dist/charts/box.d.ts.map +1 -1
  50. package/dist/charts/box.js +93 -70
  51. package/dist/charts/box.js.map +2 -2
  52. package/dist/charts/bubbleMap.d.ts +20 -0
  53. package/dist/charts/bubbleMap.d.ts.map +1 -0
  54. package/dist/charts/bubbleMap.js +951 -0
  55. package/dist/charts/bubbleMap.js.map +7 -0
  56. package/dist/charts/bullet.d.ts.map +1 -1
  57. package/dist/charts/bullet.js +69 -51
  58. package/dist/charts/bullet.js.map +2 -2
  59. package/dist/charts/calendarHeatmap.d.ts.map +1 -1
  60. package/dist/charts/calendarHeatmap.js +56 -19
  61. package/dist/charts/calendarHeatmap.js.map +2 -2
  62. package/dist/charts/candlestick.d.ts +40 -0
  63. package/dist/charts/candlestick.d.ts.map +1 -0
  64. package/dist/charts/candlestick.js +325 -0
  65. package/dist/charts/candlestick.js.map +7 -0
  66. package/dist/charts/choropleth.d.ts.map +1 -1
  67. package/dist/charts/choropleth.js +46 -67
  68. package/dist/charts/choropleth.js.map +2 -2
  69. package/dist/charts/combo.d.ts.map +1 -1
  70. package/dist/charts/combo.js +127 -27
  71. package/dist/charts/combo.js.map +3 -3
  72. package/dist/charts/dumbbell.d.ts.map +1 -1
  73. package/dist/charts/dumbbell.js +100 -31
  74. package/dist/charts/dumbbell.js.map +2 -2
  75. package/dist/charts/funnel.d.ts.map +1 -1
  76. package/dist/charts/funnel.js +93 -28
  77. package/dist/charts/funnel.js.map +3 -3
  78. package/dist/charts/gantt.d.ts +22 -0
  79. package/dist/charts/gantt.d.ts.map +1 -0
  80. package/dist/charts/gantt.js +564 -0
  81. package/dist/charts/gantt.js.map +7 -0
  82. package/dist/charts/gauge.d.ts.map +1 -1
  83. package/dist/charts/gauge.js +30 -4
  84. package/dist/charts/gauge.js.map +2 -2
  85. package/dist/charts/geo.d.ts +87 -0
  86. package/dist/charts/geo.d.ts.map +1 -0
  87. package/dist/charts/geo.js +140 -0
  88. package/dist/charts/geo.js.map +7 -0
  89. package/dist/charts/geometry.d.ts +20 -0
  90. package/dist/charts/geometry.d.ts.map +1 -0
  91. package/dist/charts/geometry.js +25 -0
  92. package/dist/charts/geometry.js.map +7 -0
  93. package/dist/charts/heatmap.d.ts.map +1 -1
  94. package/dist/charts/heatmap.js +58 -49
  95. package/dist/charts/heatmap.js.map +2 -2
  96. package/dist/charts/histogram.d.ts +1 -1
  97. package/dist/charts/histogram.d.ts.map +1 -1
  98. package/dist/charts/histogram.js +49 -31
  99. package/dist/charts/histogram.js.map +2 -2
  100. package/dist/charts/index.d.ts +35 -1
  101. package/dist/charts/index.d.ts.map +1 -1
  102. package/dist/charts/index.js +12 -0
  103. package/dist/charts/index.js.map +2 -2
  104. package/dist/charts/legibility.d.ts +38 -0
  105. package/dist/charts/legibility.d.ts.map +1 -0
  106. package/dist/charts/legibility.js +72 -0
  107. package/dist/charts/legibility.js.map +7 -0
  108. package/dist/charts/line.d.ts +39 -1
  109. package/dist/charts/line.d.ts.map +1 -1
  110. package/dist/charts/line.js +48 -16
  111. package/dist/charts/line.js.map +2 -2
  112. package/dist/charts/matrix.d.ts +2 -1
  113. package/dist/charts/matrix.d.ts.map +1 -1
  114. package/dist/charts/matrix.js +481 -319
  115. package/dist/charts/matrix.js.map +3 -3
  116. package/dist/charts/morphFrame.d.ts +83 -0
  117. package/dist/charts/morphFrame.d.ts.map +1 -0
  118. package/dist/charts/morphFrame.js +128 -0
  119. package/dist/charts/morphFrame.js.map +7 -0
  120. package/dist/charts/pie.d.ts.map +1 -1
  121. package/dist/charts/pie.js +89 -26
  122. package/dist/charts/pie.js.map +2 -2
  123. package/dist/charts/pieState.d.ts +27 -0
  124. package/dist/charts/pieState.d.ts.map +1 -0
  125. package/dist/charts/pieState.js +55 -0
  126. package/dist/charts/pieState.js.map +7 -0
  127. package/dist/charts/radar.d.ts +62 -0
  128. package/dist/charts/radar.d.ts.map +1 -0
  129. package/dist/charts/radar.js +912 -0
  130. package/dist/charts/radar.js.map +7 -0
  131. package/dist/charts/radialBar.d.ts +60 -0
  132. package/dist/charts/radialBar.d.ts.map +1 -0
  133. package/dist/charts/radialBar.js +708 -0
  134. package/dist/charts/radialBar.js.map +7 -0
  135. package/dist/charts/sankey.d.ts.map +1 -1
  136. package/dist/charts/sankey.js +223 -57
  137. package/dist/charts/sankey.js.map +3 -3
  138. package/dist/charts/scatter.d.ts +31 -0
  139. package/dist/charts/scatter.d.ts.map +1 -1
  140. package/dist/charts/scatter.js +48 -12
  141. package/dist/charts/scatter.js.map +2 -2
  142. package/dist/charts/slope.d.ts.map +1 -1
  143. package/dist/charts/slope.js +55 -3
  144. package/dist/charts/slope.js.map +2 -2
  145. package/dist/charts/sunburst.d.ts +14 -0
  146. package/dist/charts/sunburst.d.ts.map +1 -0
  147. package/dist/charts/sunburst.js +885 -0
  148. package/dist/charts/sunburst.js.map +7 -0
  149. package/dist/charts/table.d.ts +2 -1
  150. package/dist/charts/table.d.ts.map +1 -1
  151. package/dist/charts/table.js +214 -224
  152. package/dist/charts/table.js.map +3 -3
  153. package/dist/charts/tableView/canvas.d.ts +16 -1
  154. package/dist/charts/tableView/canvas.d.ts.map +1 -1
  155. package/dist/charts/tableView/canvas.js +361 -252
  156. package/dist/charts/tableView/canvas.js.map +3 -3
  157. package/dist/charts/tableView/conditional.d.ts +66 -40
  158. package/dist/charts/tableView/conditional.d.ts.map +1 -1
  159. package/dist/charts/tableView/conditional.js +163 -256
  160. package/dist/charts/tableView/conditional.js.map +3 -3
  161. package/dist/charts/tableView/dom.d.ts +26 -2
  162. package/dist/charts/tableView/dom.d.ts.map +1 -1
  163. package/dist/charts/tableView/dom.js +609 -269
  164. package/dist/charts/tableView/dom.js.map +3 -3
  165. package/dist/charts/tableView/layout.d.ts +22 -0
  166. package/dist/charts/tableView/layout.d.ts.map +1 -0
  167. package/dist/charts/tableView/layout.js +246 -0
  168. package/dist/charts/tableView/layout.js.map +7 -0
  169. package/dist/charts/tableView/sketch.d.ts +3 -2
  170. package/dist/charts/tableView/sketch.d.ts.map +1 -1
  171. package/dist/charts/tableView/sketch.js +2 -2
  172. package/dist/charts/tableView/sketch.js.map +2 -2
  173. package/dist/charts/tableView/sketchBar.d.ts +7 -0
  174. package/dist/charts/tableView/sketchBar.d.ts.map +1 -0
  175. package/dist/charts/tableView/sketchBar.js +32 -0
  176. package/dist/charts/tableView/sketchBar.js.map +7 -0
  177. package/dist/charts/tableView/sort.d.ts +9 -0
  178. package/dist/charts/tableView/sort.d.ts.map +1 -0
  179. package/dist/charts/tableView/sort.js +70 -0
  180. package/dist/charts/tableView/sort.js.map +7 -0
  181. package/dist/charts/tableView/sparkline.d.ts +30 -0
  182. package/dist/charts/tableView/sparkline.d.ts.map +1 -0
  183. package/dist/charts/tableView/sparkline.js +63 -0
  184. package/dist/charts/tableView/sparkline.js.map +7 -0
  185. package/dist/charts/tableView/style.d.ts +67 -0
  186. package/dist/charts/tableView/style.d.ts.map +1 -0
  187. package/dist/charts/tableView/style.js +131 -0
  188. package/dist/charts/tableView/style.js.map +7 -0
  189. package/dist/charts/tableView/types.d.ts +69 -46
  190. package/dist/charts/tableView/types.d.ts.map +1 -1
  191. package/dist/charts/tableView/types.js +22 -87
  192. package/dist/charts/tableView/types.js.map +2 -2
  193. package/dist/charts/tableView.d.ts +10 -5
  194. package/dist/charts/tableView.d.ts.map +1 -1
  195. package/dist/charts/tableView.js +12 -8
  196. package/dist/charts/tableView.js.map +2 -2
  197. package/dist/charts/treemap.d.ts.map +1 -1
  198. package/dist/charts/treemap.js +177 -68
  199. package/dist/charts/treemap.js.map +3 -3
  200. package/dist/charts/trendline.d.ts +13 -5
  201. package/dist/charts/trendline.d.ts.map +1 -1
  202. package/dist/charts/trendline.js +205 -56
  203. package/dist/charts/trendline.js.map +2 -2
  204. package/dist/charts/waterfall.d.ts.map +1 -1
  205. package/dist/charts/waterfall.js +76 -5
  206. package/dist/charts/waterfall.js.map +2 -2
  207. package/dist/index.d.ts +1 -1
  208. package/dist/index.js +1 -1
  209. package/dist/index.js.map +1 -1
  210. package/dist/interaction/cartesian.d.ts +4 -1
  211. package/dist/interaction/cartesian.d.ts.map +1 -1
  212. package/dist/interaction/cartesian.js +104 -34
  213. package/dist/interaction/cartesian.js.map +2 -2
  214. package/dist/interaction/controller.d.ts +29 -1
  215. package/dist/interaction/controller.d.ts.map +1 -1
  216. package/dist/interaction/controller.js +131 -8
  217. package/dist/interaction/controller.js.map +2 -2
  218. package/dist/interaction/focus.d.ts +23 -0
  219. package/dist/interaction/focus.d.ts.map +1 -0
  220. package/dist/interaction/focus.js +65 -0
  221. package/dist/interaction/focus.js.map +7 -0
  222. package/dist/interaction/tooltip.d.ts +6 -0
  223. package/dist/interaction/tooltip.d.ts.map +1 -1
  224. package/dist/interaction/tooltip.js +41 -16
  225. package/dist/interaction/tooltip.js.map +2 -2
  226. package/dist/interaction/types.d.ts +19 -0
  227. package/dist/interaction/types.d.ts.map +1 -1
  228. package/dist/interaction/veil.d.ts +25 -0
  229. package/dist/interaction/veil.d.ts.map +1 -0
  230. package/dist/interaction/veil.js +35 -0
  231. package/dist/interaction/veil.js.map +7 -0
  232. package/dist/pivot/pivot.d.ts +18 -0
  233. package/dist/pivot/pivot.d.ts.map +1 -1
  234. package/dist/pivot/pivot.js +46 -34
  235. package/dist/pivot/pivot.js.map +2 -2
  236. package/dist/render/chromeMotion.d.ts +65 -0
  237. package/dist/render/chromeMotion.d.ts.map +1 -0
  238. package/dist/render/chromeMotion.js +79 -0
  239. package/dist/render/chromeMotion.js.map +7 -0
  240. package/dist/render/pathSample.d.ts +24 -0
  241. package/dist/render/pathSample.d.ts.map +1 -0
  242. package/dist/render/pathSample.js +76 -0
  243. package/dist/render/pathSample.js.map +7 -0
  244. package/dist/render/transient.d.ts +18 -0
  245. package/dist/render/transient.d.ts.map +1 -0
  246. package/dist/render/transient.js +19 -0
  247. package/dist/render/transient.js.map +7 -0
  248. package/dist/runtime/candlestick.d.ts +112 -0
  249. package/dist/runtime/candlestick.d.ts.map +1 -0
  250. package/dist/runtime/candlestick.js +531 -0
  251. package/dist/runtime/candlestick.js.map +7 -0
  252. package/dist/runtime/cartesian.d.ts +14 -3
  253. package/dist/runtime/cartesian.d.ts.map +1 -1
  254. package/dist/runtime/cartesian.js.map +2 -2
  255. package/dist/runtime/chartAnimation.d.ts +54 -7
  256. package/dist/runtime/chartAnimation.d.ts.map +1 -1
  257. package/dist/runtime/chartAnimation.js +146 -28
  258. package/dist/runtime/chartAnimation.js.map +2 -2
  259. package/dist/runtime/chartRuntime.d.ts +42 -0
  260. package/dist/runtime/chartRuntime.d.ts.map +1 -1
  261. package/dist/runtime/chartRuntime.js +196 -30
  262. package/dist/runtime/chartRuntime.js.map +2 -2
  263. package/dist/runtime/dashboardDom.d.ts.map +1 -1
  264. package/dist/runtime/dashboardDom.js +45 -0
  265. package/dist/runtime/dashboardDom.js.map +2 -2
  266. package/dist/runtime/facet.d.ts +1 -1
  267. package/dist/runtime/facet.d.ts.map +1 -1
  268. package/dist/runtime/gantt.d.ts +137 -0
  269. package/dist/runtime/gantt.d.ts.map +1 -0
  270. package/dist/runtime/gantt.js +647 -0
  271. package/dist/runtime/gantt.js.map +7 -0
  272. package/dist/runtime/morph.d.ts +33 -0
  273. package/dist/runtime/morph.d.ts.map +1 -0
  274. package/dist/runtime/morph.js +332 -0
  275. package/dist/runtime/morph.js.map +7 -0
  276. package/dist/runtime/radar.d.ts +75 -0
  277. package/dist/runtime/radar.d.ts.map +1 -0
  278. package/dist/runtime/radar.js +201 -0
  279. package/dist/runtime/radar.js.map +7 -0
  280. package/dist/runtime/radialBar.d.ts +81 -0
  281. package/dist/runtime/radialBar.d.ts.map +1 -0
  282. package/dist/runtime/radialBar.js +125 -0
  283. package/dist/runtime/radialBar.js.map +7 -0
  284. package/dist/runtime/report/constants.d.ts +2 -2
  285. package/dist/runtime/report/constants.d.ts.map +1 -1
  286. package/dist/runtime/runtimeShared.d.ts +3 -3
  287. package/dist/runtime/runtimeShared.d.ts.map +1 -1
  288. package/dist/runtime/sunburst.d.ts +74 -0
  289. package/dist/runtime/sunburst.d.ts.map +1 -0
  290. package/dist/runtime/sunburst.js +150 -0
  291. package/dist/runtime/sunburst.js.map +7 -0
  292. package/dist/runtime/wire.d.ts.map +1 -1
  293. package/dist/runtime/wire.js +3 -1
  294. package/dist/runtime/wire.js.map +2 -2
  295. package/dist/shape/arc.d.ts +15 -2
  296. package/dist/shape/arc.d.ts.map +1 -1
  297. package/dist/shape/arc.js +204 -32
  298. package/dist/shape/arc.js.map +2 -2
  299. package/dist/spec/chartDefinitions.d.ts +6 -0
  300. package/dist/spec/chartDefinitions.d.ts.map +1 -1
  301. package/dist/spec/chartDefinitions.js +7 -1
  302. package/dist/spec/chartDefinitions.js.map +2 -2
  303. package/dist/spec/templates.d.ts.map +1 -1
  304. package/dist/spec/templates.js +177 -2
  305. package/dist/spec/templates.js.map +2 -2
  306. package/dist/spec/types.d.ts +258 -6
  307. package/dist/spec/types.d.ts.map +1 -1
  308. package/dist/spec/types.js +6 -0
  309. package/dist/spec/types.js.map +2 -2
  310. package/dist/spec/validate/charts.js +147 -15
  311. package/dist/spec/validate/charts.js.map +2 -2
  312. package/dist/spec/validate/common.d.ts +6 -0
  313. package/dist/spec/validate/common.d.ts.map +1 -1
  314. package/dist/spec/validate/overlays.js +2 -2
  315. package/dist/spec/validate/overlays.js.map +2 -2
  316. package/dist/spec/validate.data.d.ts.map +1 -1
  317. package/dist/spec/validate.data.js +8 -3
  318. package/dist/spec/validate.data.js.map +2 -2
  319. package/package.json +1 -1
@@ -1,299 +1,639 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
1
4
  import { recordDomLabels } from "../../render/labelLedger.js";
2
- import { applyConditionalFormatting, applyNegativeStyle, gridBorderColor, rowBackground, setCellBaseStyles } from "./conditional.js";
3
- import { SKETCH_BORDER_RADIUS, computeFittedWidths, densityMetrics, normalizedHeaderRows, clampInt, setStyles, leftOffset, formatDisplayValue } from "./types.js";
5
+ import { measureText } from "../../render/text.js";
6
+ import {
7
+ BAR_LANE_ALPHA,
8
+ BAR_OVERLAY_ALPHA,
9
+ barFill,
10
+ barTrack,
11
+ buildColumnFormatter,
12
+ negativeTextColor
13
+ } from "./conditional.js";
14
+ import { buildSketchDataBar } from "./sketchBar.js";
15
+ import { createSketchGridCanvas, paintSketchGridOverlay } from "./sketch.js";
16
+ import { columnOffsets, layoutColumns } from "./layout.js";
17
+ import { fontFor, resolveTableStyle, rowBackground, tableStylesheet } from "./style.js";
18
+ import { sparklinePath } from "./sparkline.js";
19
+ import {
20
+ SKETCH_BORDER_RADIUS,
21
+ cellText,
22
+ clampInt,
23
+ defaultRowInfo,
24
+ normalizedHeaderRows,
25
+ setStyles,
26
+ sumWidths
27
+ } from "./types.js";
28
+ const RENDER_ALL_CELLS = 2400;
29
+ const RENDER_ALL_ROWS = 250;
30
+ const BUFFER_ROWS = 6;
31
+ const DEFAULT_FORMAT_KINDS = ["leaf"];
32
+ let scopeSeq = 0;
4
33
  let cachedScrollbarWidth = null;
5
- function buildTable(opts) {
6
- const { container, rect, tokens } = opts;
7
- const reserve = willScrollVertically(opts) ? scrollbarWidth() : 0;
8
- const available = Math.max(0, rect.width - 2 - reserve);
9
- opts.widths = computeFittedWidths(opts.columns, available);
10
- const tableWidth = Math.max(available, sumWidths(opts.widths));
11
- setStyles(container, {
12
- position: "absolute",
13
- left: `${rect.x}px`,
14
- top: `${rect.y}px`,
15
- width: `${Math.max(0, rect.width)}px`,
16
- height: `${Math.max(0, rect.height)}px`,
17
- overflow: "auto",
18
- pointerEvents: "auto",
19
- boxSizing: "border-box",
20
- border: `1px solid ${opts.sketch ? "transparent" : tokens.color.border}`,
21
- borderRadius: opts.sketch ? SKETCH_BORDER_RADIUS : `${tokens.radius.sm}px`,
22
- background: tokens.color.background
23
- });
24
- container.replaceChildren();
25
- const table = document.createElement("table");
26
- table.setAttribute("role", "table");
27
- setStyles(table, {
28
- width: `${tableWidth}px`,
29
- minWidth: "100%",
30
- borderCollapse: "separate",
31
- borderSpacing: "0",
32
- tableLayout: "fixed",
33
- fontFamily: tokens.font.family,
34
- fontSize: `${tokens.font.size.small}px`,
35
- lineHeight: "1.35",
36
- color: tokens.color.text
37
- });
38
- table.appendChild(buildColGroup(opts.widths));
39
- table.appendChild(buildHeader(opts));
40
- table.appendChild(buildBody(opts));
41
- const footer = buildFooter(opts);
42
- if (footer) table.appendChild(footer);
43
- container.appendChild(table);
44
- opts.refreshEvidence = () => recordTableDom(opts, table);
45
- opts.refreshEvidence();
34
+ function mountTable(opts) {
35
+ return new TableDom(opts);
46
36
  }
47
- function recordTableDom(opts, table) {
48
- if (!opts.evidence) return;
49
- const metrics = densityMetrics(opts.density);
50
- const host = opts.container.getBoundingClientRect();
51
- const origin = { x: host.left - opts.rect.x, y: host.top - opts.rect.y };
52
- const rowHeight = opts.visibleRange?.rowHeight ?? metrics.rowHeight;
53
- const headerHeight = normalizedHeaderRows(opts).length * metrics.headerHeight;
54
- const first = opts.visibleRange ? clampInt(opts.visibleRange.start, 0, opts.rowCount) : 0;
55
- const last = opts.visibleRange ? clampInt(opts.visibleRange.end, first, opts.rowCount) : opts.rowCount;
56
- recordTableDataEvidence(opts, first, last);
57
- const bodyRows = table.tBodies[0]?.rows;
58
- if (!bodyRows) return;
59
- let rowIndex = first;
60
- for (let i = 0; i < bodyRows.length; i++) {
61
- const tr = bodyRows[i];
62
- if (tr.getAttribute("aria-hidden") === "true") continue;
63
- let x = opts.rect.x - opts.container.scrollLeft;
64
- for (let colIndex = 0; colIndex < tr.cells.length; colIndex++) {
65
- const td = tr.cells[colIndex];
66
- const actual = td.getBoundingClientRect();
67
- const width = opts.widths?.[colIndex] ?? 0;
68
- const measured = actual.width > 0 && actual.height > 0;
69
- if (!measured) opts.evidence.partial("geometry", "text");
70
- const box = measured ? { x: actual.left - origin.x, y: actual.top - origin.y, width: actual.width, height: actual.height } : { x, y: opts.rect.y + headerHeight + rowIndex * rowHeight - opts.container.scrollTop, width, height: rowHeight };
71
- const color = td.style.color || opts.tokens.color.text;
72
- const background = td.style.backgroundColor || opts.tokens.color.background;
73
- const value = opts.getCell(rowIndex, colIndex);
74
- if (opts.dataRepresentation !== "aggregate" || colIndex >= (opts.rowHeaderSpan ?? 0) && value.raw != null && Number.isFinite(value.raw)) {
75
- opts.evidence.mark("cell", box.x, box.y, box.width, box.height, color, 1, opts.rect, 1, background);
76
- }
77
- if (measured) recordDomLabels(opts.labels, td, origin, background, "data-label", void 0, opts.rect);
78
- x += width;
79
- }
80
- rowIndex++;
37
+ class TableDom {
38
+ constructor(opts) {
39
+ __publicField(this, "host");
40
+ __publicField(this, "widths", []);
41
+ __publicField(this, "opts");
42
+ __publicField(this, "scope", `gtv-${++scopeSeq}`);
43
+ __publicField(this, "style");
44
+ __publicField(this, "formatters", []);
45
+ __publicField(this, "layout");
46
+ __publicField(this, "offsets", []);
47
+ __publicField(this, "baseClass", []);
48
+ __publicField(this, "table");
49
+ __publicField(this, "tbody");
50
+ __publicField(this, "topSpacer");
51
+ __publicField(this, "bottomSpacer");
52
+ __publicField(this, "pool", []);
53
+ __publicField(this, "start", 0);
54
+ __publicField(this, "end", 0);
55
+ __publicField(this, "windowed", false);
56
+ __publicField(this, "frame", 0);
57
+ __publicField(this, "mounted", false);
58
+ __publicField(this, "headerRowCount", 1);
59
+ __publicField(this, "hierarchy", false);
60
+ __publicField(this, "bodyFont", "");
61
+ __publicField(this, "boldFont", "");
62
+ __publicField(this, "textWidths", /* @__PURE__ */ new Map());
63
+ __publicField(this, "sketchCanvas", null);
64
+ this.opts = opts;
65
+ this.host = document.createElement("div");
66
+ this.host.className = `graphein-table ${this.scope}`;
67
+ opts.container.appendChild(this.host);
68
+ this.host.addEventListener("scroll", () => this.onScroll(), { passive: true });
69
+ this.build(true);
81
70
  }
82
- for (const th of Array.from(table.querySelectorAll("th"))) {
83
- if (!recordDomLabels(opts.labels, th, origin, opts.tokens.color.surface, "axis-label", "/columns", opts.rect)) opts.evidence.partial("text");
71
+ update(next, relayout = false) {
72
+ this.opts = next;
73
+ this.build(relayout || next.columns.length !== this.widths.length);
84
74
  }
85
- }
86
- function recordTableDataEvidence(opts, first, last) {
87
- if (!opts.evidence) return;
88
- if (opts.dataRepresentation === "rows") {
89
- opts.evidence.data(
90
- opts.columns.length ? last - first : 0,
91
- "rows",
92
- 0,
93
- opts.columns.length ? opts.rowCount - (last - first) : 0
94
- );
95
- } else if (opts.visibleRange && opts.dataRepresentation === "aggregate") {
96
- const deferred = opts.columns.length ? Math.max(0, opts.rowCount - (last - first)) : 0;
97
- opts.evidence.data(opts.sourceRepresentedRows ?? Math.max(0, opts.evidence.snapshot().data.inputRows - deferred), "aggregate", 0, deferred);
75
+ build(relayout) {
76
+ const o = this.opts;
77
+ this.style = resolveTableStyle(o.tokens, o.density, o.gridlines);
78
+ const m = this.style.metrics;
79
+ this.bodyFont = fontFor(this.style, this.style.weight.normal);
80
+ this.boldFont = fontFor(this.style, this.style.weight.bold);
81
+ this.formatters = o.formatters ?? o.columns.map((column, i) => buildColumnFormatter(column.conditionalFormat, o.conditionalDomains?.[i] ?? null, o.tokens, this.style.color.background));
82
+ const headerRows = normalizedHeaderRows(o);
83
+ this.headerRowCount = headerRows.length;
84
+ this.hierarchy = this.hasHierarchy();
85
+ const contentHeight = headerRows.length * m.headerHeight + o.rowCount * m.rowHeight + (o.footerRow ? m.rowHeight : 0) + 2;
86
+ const scrollsY = contentHeight > o.rect.height + 1;
87
+ if (relayout || !this.layout) {
88
+ const available = Math.max(0, o.rect.width - 2 - (scrollsY ? scrollbarWidth() : 0));
89
+ const measureInput = o.measure ? { ...o, ...o.measure } : o;
90
+ this.layout = layoutColumns(measureInput, this.style, this.formatters, available, "scroll");
91
+ }
92
+ this.widths = this.layout.widths;
93
+ o.widths = this.widths;
94
+ this.offsets = columnOffsets(this.widths);
95
+ this.windowed = o.rowCount > RENDER_ALL_ROWS || o.rowCount * Math.max(1, o.columns.length) > RENDER_ALL_CELLS;
96
+ const frozen = Math.min(o.frozenColumns ?? 0, o.columns.length);
97
+ this.baseClass = o.columns.map((column, i) => columnClass(column, i, frozen, this.windowed));
98
+ setStyles(this.host, {
99
+ position: "absolute",
100
+ left: `${o.rect.x}px`,
101
+ top: `${o.rect.y}px`,
102
+ width: `${Math.max(0, o.rect.width)}px`,
103
+ maxHeight: `${Math.max(0, o.rect.height)}px`,
104
+ overflow: "auto",
105
+ pointerEvents: "auto",
106
+ boxSizing: "border-box",
107
+ border: `1px solid ${o.sketch ? "transparent" : o.tokens.color.border}`,
108
+ borderRadius: o.sketch ? SKETCH_BORDER_RADIUS : `${this.style.radius}px`,
109
+ background: this.style.color.background,
110
+ overscrollBehavior: "contain"
111
+ });
112
+ const sheet = document.createElement("style");
113
+ sheet.textContent = tableStylesheet(this.style, {
114
+ scope: this.scope,
115
+ stickyHeader: o.stickyHeader !== false,
116
+ frozenOffsets: this.offsets.slice(0, frozen),
117
+ sketch: Boolean(o.sketch)
118
+ });
119
+ const table = document.createElement("table");
120
+ table.setAttribute("role", "table");
121
+ table.style.width = `${sumWidths(this.widths)}px`;
122
+ if (this.windowed) table.setAttribute("aria-rowcount", String(headerRows.length + o.rowCount + (o.footerRow ? 1 : 0)));
123
+ table.appendChild(this.buildColgroup());
124
+ table.appendChild(this.buildHeader(headerRows, frozen));
125
+ this.tbody = document.createElement("tbody");
126
+ this.topSpacer = spacerRow(o.columns.length);
127
+ this.bottomSpacer = spacerRow(o.columns.length);
128
+ this.tbody.addEventListener("click", (event) => this.onBodyClick(event));
129
+ this.tbody.addEventListener("keydown", (event) => this.onBodyKey(event));
130
+ this.host.classList.toggle("gtv-pick", Boolean(o.onRowClick));
131
+ table.appendChild(this.tbody);
132
+ const footer = this.buildFooter(headerRows.length);
133
+ if (footer) table.appendChild(footer);
134
+ this.table = table;
135
+ this.pool = [];
136
+ this.start = 0;
137
+ this.end = 0;
138
+ const restore = this.mounted ? { top: this.host.scrollTop || 0, left: this.host.scrollLeft || 0 } : { top: o.scrollTop ?? 0, left: o.scrollLeft ?? 0 };
139
+ this.mounted = true;
140
+ this.host.replaceChildren(sheet, table);
141
+ this.renderWindow(true, restore.top);
142
+ if (this.host.scrollTop !== restore.top) this.host.scrollTop = restore.top;
143
+ if (this.host.scrollLeft !== restore.left) this.host.scrollLeft = restore.left;
144
+ this.mountSketch(Math.min(o.rect.height, contentHeight));
145
+ this.recordEvidence();
98
146
  }
99
- }
100
- function scrollbarWidth() {
101
- if (cachedScrollbarWidth != null) return cachedScrollbarWidth;
102
- if (typeof document === "undefined" || !document.body) return 0;
103
- const probe = document.createElement("div");
104
- probe.style.cssText = "position:absolute;top:-9999px;width:100px;height:100px;overflow:scroll;visibility:hidden";
105
- document.body.appendChild(probe);
106
- cachedScrollbarWidth = probe.offsetWidth - probe.clientWidth;
107
- probe.remove();
108
- return cachedScrollbarWidth;
109
- }
110
- function contentHeight(opts) {
111
- const headerRows = opts.headerRows?.length ?? 1;
112
- const metrics = densityMetrics(opts.density);
113
- const rowHeight = opts.visibleRange?.rowHeight ?? metrics.rowHeight;
114
- return headerRows * metrics.headerHeight + opts.rowCount * rowHeight + (opts.footerRow ? metrics.rowHeight : 0);
115
- }
116
- function willScrollVertically(opts) {
117
- return contentHeight(opts) > opts.rect.height + 1;
118
- }
119
- function sumWidths(widths) {
120
- return widths.reduce((sum, w) => sum + w, 0);
121
- }
122
- function buildColGroup(widths) {
123
- const colgroup = document.createElement("colgroup");
124
- for (const width of widths) {
125
- const col = document.createElement("col");
126
- col.style.width = `${width}px`;
127
- colgroup.appendChild(col);
147
+ buildColgroup() {
148
+ const colgroup = document.createElement("colgroup");
149
+ for (const width of this.widths) {
150
+ const col = document.createElement("col");
151
+ col.style.width = `${width}px`;
152
+ colgroup.appendChild(col);
153
+ }
154
+ return colgroup;
128
155
  }
129
- return colgroup;
130
- }
131
- function buildHeader(opts) {
132
- const thead = document.createElement("thead");
133
- if (opts.headerRows) {
134
- opts.headerRows.forEach((row, rowIndex) => {
135
- const tr2 = document.createElement("tr");
156
+ buildHeader(headerRows, frozen) {
157
+ const o = this.opts;
158
+ const m = this.style.metrics;
159
+ const thead = document.createElement("thead");
160
+ headerRows.forEach((row, rowIndex) => {
161
+ const tr = document.createElement("tr");
162
+ let cursor = 0;
136
163
  for (const cell of row) {
137
- const th = makeHeaderCell(opts, cell.title, cell.colIndex, cell.align, rowIndex);
138
- if (cell.colSpan && cell.colSpan > 1) th.colSpan = cell.colSpan;
139
- if (cell.rowSpan && cell.rowSpan > 1) th.rowSpan = cell.rowSpan;
140
- tr2.appendChild(th);
164
+ const start = cell.colIndex ?? cursor;
165
+ const span = Math.max(1, cell.colSpan ?? 1);
166
+ cursor = start + span;
167
+ const th = document.createElement("th");
168
+ th.scope = span > 1 ? "colgroup" : "col";
169
+ if (span > 1) th.colSpan = span;
170
+ if ((cell.rowSpan ?? 1) > 1) th.rowSpan = cell.rowSpan;
171
+ const classes = [];
172
+ const align = cell.align ?? "left";
173
+ if (align !== "left") classes.push(align === "right" ? "a-r" : "a-c");
174
+ if (cell.colIndex == null && span > 1) classes.push("gtv-hg");
175
+ if (start + span <= frozen) classes.push("fz", `fz${start}`);
176
+ if (start > 0 && o.columns[start]?.groupStart) classes.push("gs");
177
+ th.style.top = o.stickyHeader === false ? "" : `${rowIndex * m.headerHeight}px`;
178
+ if ((cell.rowSpan ?? 1) > 1) th.style.height = `${(cell.rowSpan ?? 1) * m.headerHeight}px`;
179
+ th.title = cell.title;
180
+ const colIndex = cell.colIndex;
181
+ const sorted = colIndex != null && o.sortState?.col === colIndex;
182
+ if (sorted) {
183
+ classes.push("sorted");
184
+ th.setAttribute("aria-sort", o.sortState?.dir === "desc" ? "descending" : "ascending");
185
+ }
186
+ th.className = classes.join(" ");
187
+ if (colIndex != null && o.onSort && o.columns[colIndex]?.sortable !== false) {
188
+ const button = document.createElement("button");
189
+ button.type = "button";
190
+ button.className = "gtv-sort";
191
+ button.addEventListener("click", () => o.onSort?.(colIndex));
192
+ const label = document.createElement("span");
193
+ label.className = "gtv-lbl";
194
+ label.textContent = cell.title;
195
+ const glyph = document.createElement("span");
196
+ glyph.className = "gtv-glyph";
197
+ glyph.setAttribute("aria-hidden", "true");
198
+ glyph.textContent = sorted ? o.sortState?.dir === "desc" ? "\u2193" : "\u2191" : "";
199
+ button.append(label, glyph);
200
+ th.appendChild(button);
201
+ } else {
202
+ th.textContent = cell.title;
203
+ }
204
+ tr.appendChild(th);
141
205
  }
142
- thead.appendChild(tr2);
206
+ thead.appendChild(tr);
143
207
  });
144
208
  return thead;
145
209
  }
146
- const tr = document.createElement("tr");
147
- opts.columns.forEach((column, colIndex) => {
148
- tr.appendChild(makeHeaderCell(opts, column.title, colIndex, column.align));
149
- });
150
- thead.appendChild(tr);
151
- return thead;
152
- }
153
- function makeHeaderCell(opts, title, colIndex, align, rowIndex = 0) {
154
- const th = document.createElement("th");
155
- const metrics = densityMetrics(opts.density);
156
- th.scope = "col";
157
- th.textContent = title;
158
- const isSorted = colIndex != null && opts.sortState?.col === colIndex;
159
- if (isSorted) th.setAttribute("aria-sort", opts.sortState?.dir === "desc" ? "descending" : "ascending");
160
- setCellBaseStyles(th, opts.tokens, align ?? "left", opts.stickyHeader !== false, metrics.padding, gridBorderColor(opts));
161
- setStyles(th, {
162
- top: `${rowIndex * metrics.headerHeight}px`,
163
- zIndex: "4",
164
- background: opts.tokens.color.surface,
165
- color: opts.tokens.color.text,
166
- fontWeight: String(opts.tokens.font.weight.bold),
167
- borderBottom: `1px solid ${gridBorderColor(opts)}`,
168
- userSelect: "none",
169
- height: `${metrics.headerHeight}px`
170
- });
171
- if (colIndex != null && opts.onSort && opts.columns[colIndex]?.sortable !== false) {
172
- th.textContent = "";
173
- const button = document.createElement("button");
174
- button.type = "button";
175
- button.addEventListener("click", () => opts.onSort?.(colIndex));
176
- setStyles(button, {
177
- all: "unset",
178
- boxSizing: "border-box",
179
- display: "inline-flex",
180
- alignItems: "center",
181
- justifyContent: align === "right" ? "flex-end" : align === "center" ? "center" : "flex-start",
182
- gap: "5px",
183
- width: "100%",
184
- cursor: "pointer",
185
- color: "inherit",
186
- font: "inherit"
210
+ buildFooter(headerRowCount) {
211
+ const o = this.opts;
212
+ if (!o.footerRow) return null;
213
+ const tfoot = document.createElement("tfoot");
214
+ const tr = document.createElement("tr");
215
+ if (this.windowed) tr.setAttribute("aria-rowindex", String(headerRowCount + o.rowCount + 1));
216
+ const kind = o.footerRow.kind === "grandtotal" ? "grandtotal" : "subtotal";
217
+ o.columns.forEach((column, col) => {
218
+ const el = document.createElement(column.rowHeader ? "th" : "td");
219
+ if (column.rowHeader) el.scope = "row";
220
+ const slot = { el, kind: "text", styled: false, titled: false };
221
+ const cell = o.footerRow?.cells[col] ?? { value: null, raw: null };
222
+ this.bindCell(slot, -1, col, { kind, depth: 0 }, cell);
223
+ tr.appendChild(el);
187
224
  });
188
- const label = document.createElement("span");
189
- label.textContent = title;
190
- label.style.overflow = "hidden";
191
- label.style.textOverflow = "ellipsis";
192
- const glyph = document.createElement("span");
193
- glyph.textContent = isSorted ? opts.sortState?.dir === "desc" ? "\u2193" : "\u2191" : "";
194
- glyph.setAttribute("aria-hidden", "true");
195
- glyph.style.color = opts.tokens.color.textMuted;
196
- button.append(label, glyph);
197
- th.appendChild(button);
225
+ tfoot.appendChild(tr);
226
+ return tfoot;
198
227
  }
199
- applyStickyColumn(th, opts, colIndex, true);
200
- return th;
201
- }
202
- function buildBody(opts) {
203
- const tbody = document.createElement("tbody");
204
- const range = opts.visibleRange;
205
- const start = range ? clampInt(range.start, 0, opts.rowCount) : 0;
206
- const end = range ? clampInt(range.end, start, opts.rowCount) : opts.rowCount;
207
- if (range && start > 0) tbody.appendChild(makeSpacerRow(opts, start * range.rowHeight));
208
- for (let rowIndex = start; rowIndex < end; rowIndex += 1) {
209
- tbody.appendChild(makeBodyRow(opts, rowIndex, range?.rowHeight));
228
+ onScroll() {
229
+ this.opts.onScroll?.(this.host.scrollTop || 0, this.host.scrollLeft || 0);
230
+ if (this.frame !== 0) return;
231
+ let ran = false;
232
+ const run = () => {
233
+ ran = true;
234
+ this.frame = 0;
235
+ this.renderWindow(false);
236
+ this.paintSketch();
237
+ };
238
+ const id = typeof requestAnimationFrame === "function" ? requestAnimationFrame(run) : setTimeout(run, 16);
239
+ if (!ran) this.frame = id || -1;
210
240
  }
211
- if (range && end < opts.rowCount) {
212
- tbody.appendChild(makeSpacerRow(opts, (opts.rowCount - end) * range.rowHeight));
241
+ visibleRange(scrollTop = this.host.scrollTop || 0) {
242
+ const o = this.opts;
243
+ if (!this.windowed) return [0, o.rowCount];
244
+ const m = this.style.metrics;
245
+ const header = this.headerRowCount * m.headerHeight;
246
+ const viewport = o.rect.height;
247
+ const top = Math.max(0, scrollTop - header);
248
+ const start = Math.max(0, Math.floor(top / m.rowHeight) - BUFFER_ROWS);
249
+ const end = Math.min(o.rowCount, Math.ceil((top + viewport) / m.rowHeight) + BUFFER_ROWS);
250
+ return [start, Math.max(start, end)];
213
251
  }
214
- return tbody;
215
- }
216
- function makeBodyRow(opts, rowIndex, rowHeight) {
217
- const tr = document.createElement("tr");
218
- const cls = opts.rowClass?.(rowIndex) ?? "normal";
219
- const metrics = densityMetrics(opts.density);
220
- if (rowHeight) tr.style.height = `${rowHeight}px`;
221
- opts.columns.forEach((column, colIndex) => {
222
- const td = document.createElement("td");
223
- const cell = opts.getCell(rowIndex, colIndex);
224
- const rowBg = rowBackground(opts, rowIndex, cls);
225
- setCellBaseStyles(td, opts.tokens, column.align, false, metrics.padding, gridBorderColor(opts));
226
- setStyles(td, {
227
- position: colIndex < (opts.rowHeaderSpan ?? 0) ? "sticky" : "relative",
228
- background: rowBg,
229
- fontWeight: cls === "normal" ? String(opts.tokens.font.weight.normal) : String(opts.tokens.font.weight.bold),
230
- color: opts.tokens.color.text,
231
- borderBottom: `1px solid ${gridBorderColor(opts)}`,
232
- height: rowHeight ? `${rowHeight}px` : `${metrics.rowHeight}px`,
233
- whiteSpace: column.wrap === true ? "normal" : "nowrap"
252
+ renderWindow(force, scrollTop) {
253
+ const [start, end] = this.visibleRange(scrollTop);
254
+ if (!force && start === this.start && end === this.end) return;
255
+ this.start = start;
256
+ this.end = end;
257
+ const count = end - start;
258
+ while (this.pool.length < count) this.pool.push(this.createRow());
259
+ while (this.pool.length > count) this.pool.pop().tr.remove();
260
+ const rh = this.style.metrics.rowHeight;
261
+ const info = this.opts.rowInfo ?? defaultRowInfo;
262
+ for (let i = 0; i < count; i++) this.bindRow(this.pool[i], start + i, info(start + i));
263
+ this.setSpacer(this.topSpacer, start * rh, true);
264
+ this.setSpacer(this.bottomSpacer, (this.opts.rowCount - end) * rh, false);
265
+ }
266
+ /** Spacer rows stand in for windowed-out rows and exist only while they have height. */
267
+ setSpacer(tr, height, top) {
268
+ if (height <= 0) {
269
+ tr.remove();
270
+ return;
271
+ }
272
+ const td = tr.firstElementChild;
273
+ const h = `${height}px`;
274
+ if (td.style.height !== h) td.style.height = h;
275
+ if (tr.parentNode !== this.tbody) {
276
+ if (top) this.tbody.insertBefore(tr, this.tbody.firstChild);
277
+ else this.tbody.appendChild(tr);
278
+ }
279
+ }
280
+ createRow() {
281
+ const tr = document.createElement("tr");
282
+ if (this.opts.onRowClick) tr.tabIndex = 0;
283
+ const cells = this.opts.columns.map((column) => {
284
+ const el = document.createElement(column.rowHeader ? "th" : "td");
285
+ if (column.rowHeader) el.scope = "row";
286
+ tr.appendChild(el);
287
+ return { el, kind: "text", styled: false, titled: false };
234
288
  });
235
- const indent = opts.cellIndent?.(rowIndex, colIndex) ?? 0;
236
- if (indent > 0) td.style.paddingLeft = `${metrics.paddingX + indent}px`;
237
- applyConditionalFormatting(td, opts, column, colIndex, cell, rowBg, rowIndex);
238
- if (td.childNodes.length === 0) {
239
- td.textContent = formatDisplayValue(cell.value, column);
240
- applyNegativeStyle(td, cell.value, column);
289
+ this.tbody.insertBefore(tr, this.bottomSpacer.parentNode === this.tbody ? this.bottomSpacer : null);
290
+ return { tr, cells, row: -1 };
291
+ }
292
+ bindRow(slot, row, info) {
293
+ const o = this.opts;
294
+ slot.row = row;
295
+ const tr = slot.tr;
296
+ let cls = "gtv-r";
297
+ if (info.kind === "group") cls += " k-group";
298
+ else if (info.kind === "subtotal") cls += " k-sub";
299
+ else if (info.kind === "grandtotal") cls += " k-grand";
300
+ else if (o.striped && row % 2 === 1) cls += " s";
301
+ if (o.highlighted?.(row)) cls += " hl";
302
+ if (tr.className !== cls) tr.className = cls;
303
+ tr.dataset.row = String(row);
304
+ if (this.windowed) tr.setAttribute("aria-rowindex", String(this.headerRowCount + row + 1));
305
+ for (let col = 0; col < slot.cells.length; col++) this.bindCell(slot.cells[col], row, col, info, o.getCell(row, col));
306
+ }
307
+ bindCell(slot, row, col, info, cell) {
308
+ const o = this.opts;
309
+ const column = o.columns[col];
310
+ const el = slot.el;
311
+ const formatter = o.rowFormatters ? row >= 0 ? o.rowFormatters(row)?.[col] ?? null : null : (o.formatKinds ?? DEFAULT_FORMAT_KINDS).includes(info.kind) ? this.formatters[col] : null;
312
+ const paint = formatter ? formatter(cell) : null;
313
+ const text = paint?.showValue === false ? "" : cellText(cell, column);
314
+ const className = this.baseClass[col];
315
+ if (el.className !== className) el.className = className;
316
+ if (slot.styled) {
317
+ el.style.cssText = "";
318
+ slot.styled = false;
241
319
  }
242
- applyStickyColumn(td, opts, colIndex, false);
243
- tr.appendChild(td);
244
- });
245
- return tr;
246
- }
247
- function buildFooter(opts) {
248
- if (!opts.footerRow) return null;
249
- const tfoot = document.createElement("tfoot");
250
- const tr = document.createElement("tr");
251
- const metrics = densityMetrics(opts.density);
252
- opts.columns.forEach((column, colIndex) => {
253
- const td = document.createElement("td");
254
- const cell = opts.footerRow?.cells[colIndex] ?? { value: null, raw: null };
255
- setCellBaseStyles(td, opts.tokens, column.align, false, metrics.padding, gridBorderColor(opts));
256
- setStyles(td, {
257
- position: colIndex < (opts.rowHeaderSpan ?? 0) ? "sticky" : "sticky",
258
- bottom: "0",
259
- zIndex: colIndex < (opts.rowHeaderSpan ?? 0) ? "5" : "4",
260
- background: opts.tokens.color.surface,
261
- color: opts.tokens.color.text,
262
- fontWeight: String(opts.tokens.font.weight.bold),
263
- borderTop: `1px solid ${gridBorderColor(opts)}`,
264
- borderBottom: `1px solid ${gridBorderColor(opts)}`,
265
- height: `${metrics.rowHeight}px`,
266
- whiteSpace: column.wrap === true ? "normal" : "nowrap"
320
+ const color = paint?.color ?? negativeTextColor(cell.value, column, "", this.style.color.negative);
321
+ if (paint?.background) this.setStyle(slot, "background", paint.background);
322
+ if (color) this.setStyle(slot, "color", color);
323
+ if (paint?.weight) this.setStyle(slot, "fontWeight", String(paint.weight));
324
+ if (column.rowHeader) this.bindRowHeader(slot, info, text, row);
325
+ else if (column.sparkline && Array.isArray(cell.value)) this.bindSpark(slot, cell.value, column);
326
+ else if (paint?.bar) this.bindBar(slot, paint, text, col, row);
327
+ else if (paint?.icon) this.bindIcon(slot, paint, text);
328
+ else {
329
+ slot.kind = "text";
330
+ if (el.firstChild?.nodeType !== 3 || el.childNodes.length !== 1 || el.firstChild.nodeValue !== text) el.textContent = text;
331
+ }
332
+ this.syncTitle(slot, text, col, info.kind !== "leaf" || Boolean(paint?.weight), info.depth);
333
+ }
334
+ setStyle(slot, key, value) {
335
+ slot.el.style[key] = value;
336
+ slot.styled = true;
337
+ }
338
+ bindRowHeader(slot, info, text, row) {
339
+ const m = this.style.metrics;
340
+ if (slot.kind !== "rh") {
341
+ slot.el.replaceChildren();
342
+ slot.toggle = document.createElement("button");
343
+ slot.toggle.type = "button";
344
+ slot.toggle.className = "gtv-tog";
345
+ slot.toggle.innerHTML = '<svg width="10" height="10" viewBox="0 0 10 10" aria-hidden="true"><path d="M2 3.5 5 6.5 8 3.5" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg>';
346
+ slot.pad = document.createElement("span");
347
+ slot.pad.className = "gtv-pad";
348
+ slot.val = document.createElement("span");
349
+ slot.el.append(slot.toggle, slot.pad, slot.val);
350
+ slot.kind = "rh";
351
+ }
352
+ const indent = m.paddingX + info.depth * m.indent;
353
+ if (indent !== m.paddingX) this.setStyle(slot, "paddingLeft", `${indent}px`);
354
+ const expandable = Boolean(info.expandable) && row >= 0 && Boolean(this.opts.onToggle);
355
+ slot.toggle.style.display = expandable ? "" : "none";
356
+ slot.pad.style.display = expandable || !this.hierarchy ? "none" : "";
357
+ if (expandable) {
358
+ slot.toggle.setAttribute("aria-expanded", String(info.expanded !== false));
359
+ slot.toggle.setAttribute("aria-label", `${info.expanded !== false ? "Collapse" : "Expand"} ${text}`);
360
+ }
361
+ if (slot.val.textContent !== text) slot.val.textContent = text;
362
+ }
363
+ hasHierarchy() {
364
+ const info = this.opts.measure?.rowInfo ?? this.opts.rowInfo;
365
+ if (!info) return false;
366
+ const count = this.opts.measure?.rowCount ?? this.opts.rowCount;
367
+ for (let i = 0; i < Math.min(count, 200); i++) if (info(i).expandable) return true;
368
+ return false;
369
+ }
370
+ bindBar(slot, paint, text, col, row) {
371
+ const o = this.opts;
372
+ const bar = paint.bar;
373
+ const column = o.columns[col];
374
+ const track = barTrack(this.widths[col] ?? 0, this.style.metrics.paddingX, this.layout.valueWidths[col] ?? 0, column.align, paint.showValue !== false);
375
+ const left = track.left + bar.from * track.width;
376
+ const width = Math.max(0, (bar.to - bar.from) * track.width);
377
+ const fill = barFill(bar.color, track.overlay ? BAR_OVERLAY_ALPHA : BAR_LANE_ALPHA);
378
+ if (slot.kind !== "bar") {
379
+ slot.el.replaceChildren();
380
+ slot.bar = document.createElement("div");
381
+ slot.zero = document.createElement("div");
382
+ slot.zero.className = "gtv-zero";
383
+ slot.val = document.createElement("span");
384
+ slot.val.className = "gtv-val";
385
+ slot.el.append(slot.bar, slot.zero, slot.val);
386
+ slot.kind = "bar";
387
+ }
388
+ if (o.sketch) {
389
+ const seed = (o.sketch.seed ^ Math.imul(row + 2, 374761393) ^ Math.imul(col + 1, 668265263)) >>> 0;
390
+ const m = this.style.metrics;
391
+ const next = buildSketchDataBar(o.sketch, fill, left, width, m.barHeight, seed) ?? document.createElement("div");
392
+ slot.bar.replaceWith(next);
393
+ slot.bar = next;
394
+ } else {
395
+ const s = slot.bar.style;
396
+ if (slot.bar.className !== "gtv-bar") slot.bar.className = "gtv-bar";
397
+ s.left = `${left}px`;
398
+ s.width = `${width}px`;
399
+ s.background = fill;
400
+ }
401
+ slot.zero.style.display = bar.zero === void 0 ? "none" : "";
402
+ if (bar.zero !== void 0) slot.zero.style.left = `${Math.round(track.left + bar.zero * track.width)}px`;
403
+ if (slot.val.textContent !== text) slot.val.textContent = text;
404
+ }
405
+ bindIcon(slot, paint, text) {
406
+ if (slot.kind !== "icon") {
407
+ slot.el.replaceChildren();
408
+ const wrap2 = document.createElement("span");
409
+ wrap2.className = "gtv-flex";
410
+ slot.icon = document.createElement("span");
411
+ slot.icon.className = "gtv-icon";
412
+ slot.icon.setAttribute("aria-hidden", "true");
413
+ slot.val = document.createElement("span");
414
+ slot.val.className = "gtv-val";
415
+ wrap2.append(slot.icon, slot.val);
416
+ slot.el.appendChild(wrap2);
417
+ slot.kind = "icon";
418
+ }
419
+ const wrap = slot.icon.parentElement;
420
+ if (paint.iconPosition === "right") {
421
+ if (wrap.firstChild !== slot.val) wrap.insertBefore(slot.val, slot.icon);
422
+ } else if (wrap.firstChild !== slot.icon) wrap.insertBefore(slot.icon, slot.val);
423
+ slot.icon.textContent = paint.icon ?? "";
424
+ slot.icon.style.color = paint.iconColor ?? "";
425
+ if (slot.val.textContent !== text) slot.val.textContent = text;
426
+ }
427
+ bindSpark(slot, values, column) {
428
+ const spark = column.sparkline;
429
+ if (slot.kind !== "spark") {
430
+ slot.el.replaceChildren();
431
+ slot.el.innerHTML = '<svg class="gtv-spark" viewBox="0 0 100 20" preserveAspectRatio="none" aria-hidden="true"><path vector-effect="non-scaling-stroke"/><path vector-effect="non-scaling-stroke"/></svg>';
432
+ slot.kind = "spark";
433
+ }
434
+ const color = spark.color ?? this.opts.tokens.color.accent;
435
+ const shape = sparklinePath(values, spark, 100, 20);
436
+ const [fillPath, linePath] = Array.from(slot.el.querySelectorAll("path"));
437
+ fillPath.setAttribute("d", shape.area);
438
+ fillPath.setAttribute("fill", spark.type === "line" ? "none" : barFill(color, spark.type === "bar" ? 0.85 : 0.18));
439
+ fillPath.setAttribute("stroke", "none");
440
+ linePath.setAttribute("d", shape.line);
441
+ linePath.setAttribute("fill", "none");
442
+ linePath.setAttribute("stroke", spark.type === "bar" ? "none" : color);
443
+ linePath.setAttribute("stroke-width", "1.5");
444
+ linePath.setAttribute("stroke-linejoin", "round");
445
+ slot.el.setAttribute("aria-label", shape.summary);
446
+ }
447
+ /** Full text on hover for anything the column width cuts short. */
448
+ syncTitle(slot, text, col, strong, depth) {
449
+ const column = this.opts.columns[col];
450
+ let clipped = false;
451
+ if (text && !column.wrap) {
452
+ const m = this.style.metrics;
453
+ const font = strong ? this.boldFont : this.bodyFont;
454
+ const key = `${strong ? "b" : "n"}${text}`;
455
+ let w = this.textWidths.get(key);
456
+ if (w === void 0) {
457
+ w = measureText(text, font).width;
458
+ if (this.textWidths.size < 8192) this.textWidths.set(key, w);
459
+ }
460
+ const budget = (this.widths[col] ?? 0) - m.paddingX * 2 - (column.rowHeader ? depth * m.indent + m.toggle + 4 : 0);
461
+ clipped = w > budget + 0.5;
462
+ }
463
+ if (clipped) {
464
+ if (slot.el.title !== text) slot.el.title = text;
465
+ slot.titled = true;
466
+ } else if (slot.titled) {
467
+ slot.el.removeAttribute("title");
468
+ slot.titled = false;
469
+ }
470
+ }
471
+ onBodyClick(event) {
472
+ const target = event.target;
473
+ const tr = target?.closest("tr");
474
+ const row = tr?.dataset.row == null ? -1 : Number(tr.dataset.row);
475
+ if (row < 0 || !Number.isFinite(row)) return;
476
+ if (target?.closest(".gtv-tog")) {
477
+ this.opts.onToggle?.(row);
478
+ return;
479
+ }
480
+ const cell = target?.closest("td,th");
481
+ if (cell && this.opts.onRowClick) this.opts.onRowClick(row, cell.cellIndex, event);
482
+ }
483
+ /** Selectable rows are focusable; Enter or Space selects like a click. */
484
+ onBodyKey(event) {
485
+ if (event.key !== "Enter" && event.key !== " ") return;
486
+ const tr = event.target;
487
+ if (!tr || tr.tagName !== "TR" || !this.opts.onRowClick) return;
488
+ const row = Number(tr.dataset.row);
489
+ if (!Number.isFinite(row) || row < 0) return;
490
+ event.preventDefault();
491
+ this.opts.onRowClick(row, 0, event);
492
+ }
493
+ mountSketch(height) {
494
+ const o = this.opts;
495
+ this.sketchCanvas?.canvas.remove();
496
+ this.sketchCanvas = null;
497
+ if (!o.sketch) return;
498
+ const overlay = createSketchGridCanvas({ ...o.rect, height });
499
+ o.container.appendChild(overlay.canvas);
500
+ this.sketchCanvas = overlay;
501
+ this.paintSketch();
502
+ }
503
+ paintSketch() {
504
+ const o = this.opts;
505
+ const canvas = this.sketchCanvas;
506
+ if (!o.sketch || !canvas?.ctx) return;
507
+ const height = parseFloat(canvas.canvas.style.height) || o.rect.height;
508
+ paintSketchGridOverlay(canvas.ctx, {
509
+ sketch: o.sketch,
510
+ tokens: o.tokens,
511
+ rect: { ...o.rect, height },
512
+ dpr: canvas.dpr,
513
+ columns: o.columns,
514
+ widths: this.widths,
515
+ rowCount: o.rowCount,
516
+ headerRowCount: this.headerRowCount,
517
+ hasFooter: Boolean(o.footerRow),
518
+ rowHeaderSpan: o.frozenColumns ?? 0,
519
+ metrics: this.style.metrics,
520
+ scrollLeft: this.host.scrollLeft || 0,
521
+ scrollTop: this.host.scrollTop || 0
267
522
  });
268
- td.textContent = cell.label ? String(cell.value ?? "") : formatDisplayValue(cell.value, column);
269
- applyNegativeStyle(td, cell.value, column);
270
- applyStickyColumn(td, opts, colIndex, false);
271
- tr.appendChild(td);
272
- });
273
- tfoot.appendChild(tr);
274
- return tfoot;
523
+ }
524
+ recordEvidence() {
525
+ const o = this.opts;
526
+ const evidence = o.evidence;
527
+ if (!evidence) return;
528
+ const m = this.style.metrics;
529
+ const first = clampInt(this.start, 0, o.rowCount);
530
+ const last = clampInt(this.end, first, o.rowCount);
531
+ recordDataEvidence(o, first, last);
532
+ const host = this.host.getBoundingClientRect();
533
+ const origin = { x: host.left - o.rect.x, y: host.top - o.rect.y };
534
+ const headerHeight = this.headerRowCount * m.headerHeight;
535
+ const viewTop = this.host.scrollTop || 0;
536
+ const viewBottom = viewTop + (this.host.clientHeight || o.rect.height);
537
+ const info = o.rowInfo ?? defaultRowInfo;
538
+ for (const slot of this.pool) {
539
+ const row = slot.row;
540
+ if (row < first || row >= last) continue;
541
+ const kind = info(row).kind;
542
+ const y = headerHeight + row * m.rowHeight;
543
+ const inView = y + m.rowHeight >= viewTop - m.rowHeight && y <= viewBottom + m.rowHeight;
544
+ let x = o.rect.x - (this.host.scrollLeft || 0);
545
+ for (let col = 0; col < slot.cells.length; col++) {
546
+ const el = slot.cells[col].el;
547
+ const width = this.widths[col] ?? 0;
548
+ const cell = o.getCell(row, col);
549
+ const background = el.style.background || rowBackground(this.style, kind, o.striped === true && row % 2 === 1);
550
+ const color = el.style.color || this.style.color.text;
551
+ const actual = inView ? el.getBoundingClientRect() : null;
552
+ const measured = Boolean(actual && actual.width > 0 && actual.height > 0);
553
+ if (inView && !measured) evidence.partial("geometry", "text");
554
+ const box = measured && actual ? { x: actual.left - origin.x, y: actual.top - origin.y, width: actual.width, height: actual.height } : { x, y: o.rect.y + y - viewTop, width, height: m.rowHeight };
555
+ if (countsAsMark(o, col, cell)) evidence.mark("cell", box.x, box.y, box.width, box.height, color, 1, o.rect, 1, background);
556
+ if (measured) recordDomLabels(o.labels, el, origin, background, "data-label", void 0, o.rect);
557
+ x += width;
558
+ }
559
+ }
560
+ for (const th of Array.from(this.table.tHead?.querySelectorAll("th") ?? [])) {
561
+ if (!recordDomLabels(o.labels, th, origin, this.style.color.headerBg, "axis-label", "/columns", o.rect)) evidence.partial("text");
562
+ }
563
+ const foot = this.table.tFoot?.rows[0];
564
+ if (foot) {
565
+ for (let col = 0; col < foot.cells.length; col++) {
566
+ const el = foot.cells[col];
567
+ const actual = el.getBoundingClientRect();
568
+ if (actual.width <= 0 || actual.height <= 0) continue;
569
+ if (o.dataRepresentation === "aggregate" && countsAsMark(o, col, o.footerRow?.cells[col] ?? { value: null, raw: null })) {
570
+ evidence.mark(
571
+ "cell",
572
+ actual.left - origin.x,
573
+ actual.top - origin.y,
574
+ actual.width,
575
+ actual.height,
576
+ el.style.color || this.style.color.text,
577
+ 1,
578
+ o.rect,
579
+ 1,
580
+ el.style.background || this.style.color.totalBg
581
+ );
582
+ }
583
+ recordDomLabels(o.labels, el, origin, el.style.background || this.style.color.totalBg, "data-label", void 0, o.rect);
584
+ }
585
+ }
586
+ }
587
+ }
588
+ function countsAsMark(o, col, cell) {
589
+ if (o.dataRepresentation !== "aggregate") return true;
590
+ return !o.columns[col]?.rowHeader && typeof cell.raw === "number" && Number.isFinite(cell.raw);
591
+ }
592
+ function recordDataEvidence(o, first, last) {
593
+ const evidence = o.evidence;
594
+ if (!evidence) return;
595
+ const rendered = o.columns.length ? last - first : 0;
596
+ const deferred = o.columns.length ? Math.max(0, o.rowCount - rendered) : 0;
597
+ if (o.dataRepresentation === "rows") {
598
+ evidence.data(rendered, "rows", 0, deferred);
599
+ } else if (o.dataRepresentation === "aggregate") {
600
+ evidence.data(o.sourceRepresentedRows ?? Math.max(0, evidence.snapshot().data.inputRows - deferred), "aggregate", 0, deferred);
601
+ }
275
602
  }
276
- function makeSpacerRow(opts, height) {
603
+ function columnClass(column, index, frozen, windowed) {
604
+ const classes = [];
605
+ if (column.align === "right") classes.push("a-r");
606
+ else if (column.align === "center") classes.push("a-c");
607
+ if (column.isMeasure || column.type === "quantitative") classes.push("num");
608
+ if (index < frozen) classes.push("fz", `fz${index}`);
609
+ if (index === frozen - 1) classes.push("fz-edge");
610
+ if (index > 0 && column.groupStart) classes.push("gs");
611
+ if (column.total) classes.push("tc");
612
+ if (column.wrap && !windowed) classes.push("wrap");
613
+ return classes.join(" ");
614
+ }
615
+ function spacerRow(columns) {
277
616
  const tr = document.createElement("tr");
617
+ tr.className = "gtv-sp";
278
618
  tr.setAttribute("aria-hidden", "true");
279
619
  const td = document.createElement("td");
280
- td.colSpan = Math.max(1, opts.columns.length);
281
- setStyles(td, {
282
- height: `${Math.max(0, height)}px`,
283
- padding: "0",
284
- border: "0",
285
- background: opts.tokens.color.background
286
- });
620
+ td.colSpan = Math.max(1, columns);
287
621
  tr.appendChild(td);
288
622
  return tr;
289
623
  }
290
- function applyStickyColumn(cell, opts, colIndex, isHeader) {
291
- if (colIndex == null || colIndex >= (opts.rowHeaderSpan ?? 0)) return;
292
- cell.style.position = "sticky";
293
- cell.style.left = `${leftOffset(opts, colIndex)}px`;
294
- cell.style.zIndex = isHeader ? "6" : "3";
624
+ function scrollbarWidth() {
625
+ if (cachedScrollbarWidth != null) return cachedScrollbarWidth;
626
+ if (typeof document === "undefined" || !document.body) return 0;
627
+ const probe = document.createElement("div");
628
+ probe.style.cssText = "position:absolute;top:-9999px;width:100px;height:100px;overflow:scroll;visibility:hidden";
629
+ document.body.appendChild(probe);
630
+ cachedScrollbarWidth = probe.offsetWidth - probe.clientWidth;
631
+ probe.remove();
632
+ return cachedScrollbarWidth;
295
633
  }
296
634
  export {
297
- buildTable
635
+ RENDER_ALL_CELLS,
636
+ RENDER_ALL_ROWS,
637
+ mountTable
298
638
  };
299
639
  //# sourceMappingURL=dom.js.map