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,13 +1,28 @@
1
- import { ordinalColorScale, sequentialColorScale, sequential, rgbaToCss, parseColor } from "../color/index.js";
1
+ import { ordinalColorScale, sequentialColorScale, sequential, rgbaToCss, parseColor, contrastRatio } from "../color/index.js";
2
2
  import { formatValue } from "../format/index.js";
3
3
  import { fontString, measureText, ellipsize } from "../render/text.js";
4
+ import { roundedRect } from "../shape/index.js";
4
5
  import { accessor, toNumber, toKey } from "../util/data.js";
5
6
  import { RoughPen } from "../rough/index.js";
6
7
  import { resolveSketch } from "../spec/sketch.js";
7
8
  import { addOverlayText, drawTitleBlock } from "./chrome.js";
9
+ import { compositeOver, contrastTarget, inkOn, knownBackdrop, toneForContrast } from "./legibility.js";
10
+ import { FOCUS_VEIL, hoverFocusEnabled, paintCutoutVeil } from "../interaction/veil.js";
8
11
  import { addCenteredEmptyMessage } from "./emptyOverlay.js";
12
+ import { lerp, lerpRect, lerpValue, mixFill, morphInput, morphKeyed, rampAt, stashMorphFrame, switchAt } from "./morphFrame.js";
9
13
  const MIN_LABEL_WIDTH = 38;
10
14
  const MIN_LABEL_HEIGHT = 24;
15
+ const TILE_GUTTER = 2;
16
+ const TILE_RADIUS = 3;
17
+ const PALE_TILE_CONTRAST = 1.6;
18
+ const TEXT_CONTRAST_MIN = 4.5;
19
+ function tilePaintRect(cell, gutter) {
20
+ const inset = Math.min(gutter / 2, cell.width / 4, cell.height / 4);
21
+ return insetRect(cell, inset);
22
+ }
23
+ function tileRadius(paint) {
24
+ return Math.max(0, Math.min(TILE_RADIUS, paint.width / 4, paint.height / 4));
25
+ }
11
26
  function cleanRect(rect) {
12
27
  return {
13
28
  x: Number.isFinite(rect.x) ? rect.x : 0,
@@ -158,6 +173,7 @@ function colorLeaves(leaves, treemap, tokens) {
158
173
  leaf.colorNoData = true;
159
174
  } else {
160
175
  fill = continuous.map(leaf.colorNumber);
176
+ leaf.ramp = { scheme: treemap.scheme ?? "teal", at: rampAt(domain, leaf.colorNumber) };
161
177
  }
162
178
  } else fill = ordinal.map(toKey(leaf.colorValue));
163
179
  } else if (treemap.encoding.group && leaf.groupKey !== void 0) {
@@ -191,12 +207,18 @@ function tileProbe(treemap, tile) {
191
207
  if (groupField) probe[groupField] = tile.group;
192
208
  return probe;
193
209
  }
194
- function drawRect(ctx, pen, rect, fill, fillCss, stroke) {
210
+ function paleTileBoundary(tile, backdrop, tokens, opacity) {
211
+ if (!backdrop || contrastRatio(compositeOver(tile.fill, 1, backdrop), backdrop) >= PALE_TILE_CONTRAST) return void 0;
212
+ const color = toneForContrast(tile.fillCss, contrastTarget(backdrop, tokens), backdrop, PALE_TILE_CONTRAST);
213
+ return { color, opacity, width: 1 };
214
+ }
215
+ function drawTileShape(ctx, pen, tile, stroke, boundary) {
216
+ const rect = tile.paint;
195
217
  if (rect.width <= 0 || rect.height <= 0) return;
196
218
  if (pen) {
197
- pen.rect(rect.x, rect.y, rect.width, rect.height, { fill: fillCss, fillStyle: "solid" });
219
+ pen.rect(rect.x, rect.y, rect.width, rect.height, { fill: tile.fillCss, fillStyle: "solid" });
198
220
  pen.rect(rect.x, rect.y, rect.width, rect.height, {
199
- fill: textureShade(fill),
221
+ fill: textureShade(tile.fill),
200
222
  fillStyle: "hachure",
201
223
  fillAlpha: 0.4,
202
224
  hachureGap: 7,
@@ -204,81 +226,124 @@ function drawRect(ctx, pen, rect, fill, fillCss, stroke) {
204
226
  });
205
227
  return;
206
228
  }
207
- ctx.fillStyle = fillCss;
208
- ctx.fillRect(rect.x, rect.y, rect.width, rect.height);
209
- ctx.strokeStyle = stroke;
210
- ctx.lineWidth = 1;
211
- ctx.strokeRect(rect.x + 0.5, rect.y + 0.5, Math.max(0, rect.width - 1), Math.max(0, rect.height - 1));
229
+ ctx.fillStyle = tile.fillCss;
230
+ ctx.beginPath();
231
+ roundedRect(ctx, rect.x, rect.y, rect.width, rect.height, tile.radius);
232
+ ctx.fill();
233
+ if (boundary && rect.width > 1 && rect.height > 1) {
234
+ ctx.strokeStyle = boundary.color;
235
+ ctx.lineWidth = boundary.width;
236
+ ctx.beginPath();
237
+ roundedRect(ctx, rect.x + 0.5, rect.y + 0.5, rect.width - 1, rect.height - 1, Math.max(0, tile.radius - 0.5));
238
+ ctx.stroke();
239
+ }
212
240
  }
213
- function addTileLabels(surface, tokens, tile, valueFormat) {
214
- const compact = tile.rect.width < MIN_LABEL_WIDTH || tile.rect.height < MIN_LABEL_HEIGHT;
215
- const pad = compact ? 2 : 5;
216
- const padX = compact ? 3 : 6;
217
- const padY = compact ? 1 : 2;
241
+ function addTileLabels(surface, tokens, tile, backdrop, valueFormat) {
242
+ const rect = tile.paint;
243
+ const compact = rect.width < MIN_LABEL_WIDTH || rect.height < MIN_LABEL_HEIGHT;
244
+ const padX = compact ? 3 : 7;
245
+ const padY = compact ? 2 : 6;
218
246
  const labelSize = tokens.font.size.small;
219
- if (tile.rect.width <= pad * 2 + padX * 2 + 2 || tile.rect.height < labelSize + padY * 2 + pad * 2 + 2) {
247
+ const nameH = labelSize * 1.2;
248
+ if (rect.width <= padX * 2 + 4 || rect.height < nameH + padY * 2) {
220
249
  surface.labels?.omit({ role: "data-label", reason: "space", required: true, example: tile.categoryLabel, path: "/labels" });
221
250
  return;
222
251
  }
223
252
  const valueSize = tokens.font.size.tiny;
224
253
  const labelFont = fontString(labelSize, tokens.font.family, tokens.font.weight.bold);
225
254
  const valueFont = fontString(valueSize, tokens.font.family);
226
- const maxTextWidth = tile.rect.width - pad * 2 - padX * 2 - 2;
255
+ const maxTextWidth = rect.width - padX * 2;
227
256
  const labelText = ellipsize(tile.categoryLabel, maxTextWidth, labelFont);
228
257
  if (labelText === "" || labelText === "\u2026" || compact && labelText.length / Math.max(1, tile.categoryLabel.length) < 0.5) {
229
258
  surface.labels?.omit({ role: "data-label", reason: "unreadable", required: true, example: tile.categoryLabel, path: "/labels" });
230
259
  return;
231
260
  }
232
- const pill = { background: tokens.color.surface, border: tokens.color.border, padX, padY };
261
+ const under = backdrop && (tile.alpha < 1 || tile.fill.a < 1) ? compositeOver(tile.fill, tile.alpha, backdrop) : { ...tile.fill, a: 1 };
262
+ const ink = inkOn(under, tokens, TEXT_CONTRAST_MIN);
263
+ const background = rgbaToCss(under);
264
+ const inkRgba = parseColor(ink);
265
+ const valueOpacity = inkRgba && contrastRatio(compositeOver(inkRgba, 0.82, under), under) >= TEXT_CONTRAST_MIN + 0.25 ? 0.82 : 1;
233
266
  addOverlayText(surface, tokens, {
234
- left: tile.rect.x + pad + 2,
235
- top: tile.rect.y + pad,
267
+ left: rect.x + padX,
268
+ top: rect.y + padY,
236
269
  text: tile.categoryLabel,
237
270
  maxWidth: maxTextWidth,
238
- color: tokens.color.text,
271
+ color: ink,
239
272
  size: labelSize,
240
273
  weight: tokens.font.weight.bold,
241
- pill
274
+ background
242
275
  });
243
- const labelPillH = labelSize + padY * 2 + 2;
244
- const valuePillH = valueSize + padY * 2 + 2;
245
276
  const valueText = formatValue(tile.value, valueFormat);
246
- if (tile.rect.height >= pad * 2 + labelPillH + 2 + valuePillH && measureText(valueText, valueFont).width <= maxTextWidth) {
277
+ if (rect.height >= padY * 2 + nameH + 1 + valueSize * 1.2 && measureText(valueText, valueFont).width <= maxTextWidth) {
247
278
  addOverlayText(surface, tokens, {
248
- left: tile.rect.x + pad + 2,
249
- top: tile.rect.y + pad + labelPillH + 2,
279
+ left: rect.x + padX,
280
+ top: rect.y + padY + nameH + 1,
250
281
  text: valueText,
251
- color: tokens.color.textMuted,
282
+ color: ink,
252
283
  size: valueSize,
253
- pill
284
+ opacity: valueOpacity === 1 ? void 0 : valueOpacity,
285
+ background
254
286
  });
255
287
  } else {
256
288
  surface.labels?.omit({ role: "data-label", reason: "space", required: false, example: valueText, path: "/labels" });
257
289
  }
258
290
  }
291
+ function scaledRect(rect, k) {
292
+ return {
293
+ x: rect.x + rect.width * (1 - k) / 2,
294
+ y: rect.y + rect.height * (1 - k) / 2,
295
+ width: rect.width * k,
296
+ height: rect.height * k
297
+ };
298
+ }
299
+ function grownTile(item, k, alpha) {
300
+ const { tile } = item;
301
+ return { ...item, tile: { ...tile, rect: scaledRect(tile.rect, k), paint: scaledRect(tile.paint, k), radius: tile.radius * k }, alpha };
302
+ }
303
+ const TREEMAP_MORPH = {
304
+ lerp: (a, b, t) => {
305
+ const { fill: fillCss, ramp } = mixFill({ fill: a.tile.fillCss, ramp: a.tile.ramp }, { fill: b.tile.fillCss, ramp: b.tile.ramp }, t);
306
+ return {
307
+ key: b.key,
308
+ tile: {
309
+ ...b.tile,
310
+ rect: lerpRect(a.tile.rect, b.tile.rect, t),
311
+ paint: lerpRect(a.tile.paint, b.tile.paint, t),
312
+ radius: lerp(a.tile.radius, b.tile.radius, t),
313
+ fill: fillCss === b.tile.fillCss ? b.tile.fill : parseColor(fillCss) ?? b.tile.fill,
314
+ fillCss,
315
+ ramp,
316
+ value: lerpValue(a.tile.value, b.tile.value, t),
317
+ alpha: lerp(a.tile.alpha, b.tile.alpha, t)
318
+ },
319
+ alpha: lerp(a.alpha, b.alpha, t)
320
+ };
321
+ },
322
+ enter: (b, t) => grownTile(b, t, t),
323
+ exit: (a, t) => grownTile(a, 1 - t, a.alpha * (1 - t))
324
+ };
325
+ const TREEMAP_GROUP_MORPH = {
326
+ lerp: (a, b, t) => ({
327
+ key: b.key,
328
+ group: { group: b.group.group, rect: lerpRect(a.group.rect, b.group.rect, t), headerH: switchAt(a.group.headerH, b.group.headerH, t) },
329
+ alpha: lerp(a.alpha, b.alpha, t)
330
+ }),
331
+ enter: (b, t) => ({ ...b, alpha: t }),
332
+ exit: (a, t) => ({ ...a, alpha: a.alpha * (1 - t) })
333
+ };
334
+ const fadedTile = (item) => ({ ...item.tile, alpha: item.tile.alpha * item.alpha });
259
335
  function drawNoPositiveTreemap(surface, tokens, content) {
260
336
  addCenteredEmptyMessage(surface, tokens, content, "No positive values");
261
337
  }
262
- function drawTreemapTiles(surface, treemap, tokens, content, leaves, context) {
263
- const ctx = surface.marks.ctx;
338
+ function layoutTreemapTiles(treemap, tokens, content, leaves, sketched, context) {
264
339
  const emphasis = context?.emphasis ?? null;
265
- const sketch = resolveSketch(treemap);
266
- const pen = sketch ? new RoughPen(ctx, sketch) : null;
267
340
  const tiles = [];
268
341
  const groupRects = [];
269
- const stroke = tokens.color.textMuted;
270
- let represented = 0;
271
- const drawTile = (tile) => {
272
- const alpha = emphasis ? emphasis.match(tileProbe(treemap, tile)) ? 1 : emphasis.dim : 1;
273
- if (alpha !== 1) ctx.globalAlpha = alpha;
274
- drawRect(ctx, pen, tile.rect, tile.fill, tile.fillCss, stroke);
275
- if (tile.rect.width > 0 && tile.rect.height > 0) {
276
- surface.evidence?.mark("tile", tile.rect.x, tile.rect.y, tile.rect.width, tile.rect.height, tile.fillCss, alpha, void 0, 1, void 0, alpha < 1, { color: stroke, opacity: 0.65, width: 1 });
277
- represented += tile.sourceRows;
278
- }
279
- if (alpha !== 1) ctx.globalAlpha = 1;
342
+ const addTile = (leaf, cell) => {
343
+ const alpha = emphasis ? emphasis.match(tileProbe(treemap, leaf)) ? 1 : emphasis.dim : 1;
344
+ const paint = sketched ? cell : tilePaintRect(cell, TILE_GUTTER);
345
+ tiles.push({ ...leaf, rect: cell, paint, radius: sketched ? 0 : tileRadius(paint), alpha });
280
346
  };
281
- ctx.save();
282
347
  if (treemap.encoding.group) {
283
348
  const groups = buildGroups(leaves);
284
349
  for (const groupNode of layoutTreemap(groups, content)) {
@@ -289,36 +354,41 @@ function drawTreemapTiles(surface, treemap, tokens, content, leaves, context) {
289
354
  { x: gRect.x, y: gRect.y + headerH, width: gRect.width, height: Math.max(0, gRect.height - headerH) },
290
355
  2
291
356
  );
292
- for (const leafNode of layoutTreemap(groupNode.item.leaves, inner)) {
293
- const tile = { ...leafNode.item, rect: leafNode.rect };
294
- tiles.push(tile);
295
- drawTile(tile);
296
- }
357
+ for (const leafNode of layoutTreemap(groupNode.item.leaves, inner)) addTile(leafNode.item, leafNode.rect);
297
358
  }
298
359
  } else {
299
- for (const leafNode of layoutTreemap(leaves, content)) {
300
- const tile = { ...leafNode.item, rect: leafNode.rect };
301
- tiles.push(tile);
302
- drawTile(tile);
360
+ for (const leafNode of layoutTreemap(leaves, content)) addTile(leafNode.item, leafNode.rect);
361
+ }
362
+ return { tiles, groupRects };
363
+ }
364
+ function paintTreemapTiles(surface, tokens, tiles, backdrop, pen) {
365
+ const ctx = surface.marks.ctx;
366
+ const stroke = tokens.color.textMuted;
367
+ let represented = 0;
368
+ ctx.save();
369
+ for (const tile of tiles) {
370
+ const { paint, alpha } = tile;
371
+ const boundary = pen ? { color: stroke, opacity: 0.65, width: 1 } : paleTileBoundary(tile, backdrop, tokens, alpha);
372
+ if (alpha !== 1) ctx.globalAlpha = alpha;
373
+ drawTileShape(ctx, pen, tile, stroke, boundary);
374
+ if (paint.width > 0 && paint.height > 0) {
375
+ surface.evidence?.mark("tile", paint.x, paint.y, paint.width, paint.height, tile.fillCss, alpha, void 0, 1, void 0, alpha < 1, boundary);
376
+ represented += tile.sourceRows;
303
377
  }
378
+ if (alpha !== 1) ctx.globalAlpha = 1;
304
379
  }
305
380
  ctx.restore();
306
- return { tiles, groupRects, represented, pen, sketch };
381
+ return represented;
307
382
  }
308
383
  function drawGroupFrames(surface, tokens, treemap, groups, pen, sketch) {
309
- const ctx = surface.marks.ctx;
310
384
  const stroke = tokens.color.background;
311
385
  for (const { group, rect, headerH } of groups) {
312
386
  if (pen) pen.rect(rect.x, rect.y, rect.width, rect.height, { stroke, roughness: sketch.roughness * 0.6 });
313
- else {
314
- ctx.strokeStyle = stroke;
315
- ctx.lineWidth = 1;
316
- ctx.strokeRect(rect.x + 0.5, rect.y + 0.5, Math.max(0, rect.width - 1), Math.max(0, rect.height - 1));
317
- }
318
387
  if (headerH > 0) {
319
- const headerW = Math.max(0, rect.width - 10);
388
+ const inset = pen ? 5 : 2 + TILE_GUTTER / 2;
389
+ const headerW = Math.max(0, rect.width - inset * 2);
320
390
  addOverlayText(surface, tokens, {
321
- left: rect.x + 5,
391
+ left: rect.x + inset,
322
392
  top: rect.y + 4,
323
393
  width: headerW,
324
394
  text: group.label,
@@ -333,7 +403,7 @@ function drawGroupFrames(surface, tokens, treemap, groups, pen, sketch) {
333
403
  }
334
404
  }
335
405
  }
336
- function buildTreemapInteraction(treemap, content, tiles, total) {
406
+ function buildTreemapInteraction(treemap, tokens, content, tiles, total) {
337
407
  const valueLabel = treemap.encoding.value.title ?? treemap.encoding.value.field;
338
408
  const colorLabel = treemap.encoding.color?.title ?? treemap.encoding.color?.field;
339
409
  const categoryField = treemap.encoding.category.field;
@@ -345,6 +415,25 @@ function buildTreemapInteraction(treemap, content, tiles, total) {
345
415
  }
346
416
  return null;
347
417
  };
418
+ const focusOn = hoverFocusEnabled(treemap);
419
+ const veil = treemap.background ?? tokens.color.background;
420
+ const paintTileHover = (ictx, tile, strength) => {
421
+ const p = tile.paint;
422
+ if (p.width <= 0 || p.height <= 0) return;
423
+ if (focusOn) {
424
+ paintCutoutVeil(ictx, content, veil, FOCUS_VEIL * strength, (c) => roundedRect(c, p.x, p.y, p.width, p.height, tile.radius));
425
+ }
426
+ ictx.save();
427
+ ictx.globalAlpha = strength;
428
+ for (const [width, color] of [[3, tokens.color.background], [1.5, tokens.color.text]]) {
429
+ ictx.beginPath();
430
+ roundedRect(ictx, p.x, p.y, p.width, p.height, tile.radius);
431
+ ictx.lineWidth = width;
432
+ ictx.strokeStyle = color;
433
+ ictx.stroke();
434
+ }
435
+ ictx.restore();
436
+ };
348
437
  return {
349
438
  region: { x: content.x, y: content.y, width: content.width, height: content.height },
350
439
  hitTest: (px, py) => {
@@ -365,7 +454,9 @@ function buildTreemapInteraction(treemap, content, tiles, total) {
365
454
  }] : [],
366
455
  { label: "share", value: `${(tile.value / total * 100).toFixed(1)}%`, muted: true }
367
456
  ]
368
- }
457
+ },
458
+ paint: (ictx, state) => paintTileHover(ictx, tile, state.focus),
459
+ draw: (ictx) => paintTileHover(ictx, tile, 1)
369
460
  };
370
461
  },
371
462
  pick: (px, py) => {
@@ -384,18 +475,36 @@ function drawTreemap(surface, spec, tokens, size, context) {
384
475
  colorLeaves(leaves, treemap, tokens);
385
476
  const total = leaves.reduce((sum, leaf) => sum + leaf.value, 0);
386
477
  if (leaves.length === 0 || total <= 0 || content.width <= 0 || content.height <= 0) {
478
+ const morph2 = morphInput(context);
479
+ const leaving = morph2 ? morphKeyed(morph2.from.tiles, [], morph2.t, TREEMAP_MORPH).exits : [];
480
+ stashMorphFrame(surface, "treemap", { tiles: leaving, groups: [] });
481
+ if (leaving.length) paintTreemapTiles(surface, tokens, leaving.map(fadedTile), knownBackdrop(surface, tokens), null);
387
482
  drawNoPositiveTreemap(surface, tokens, content);
388
483
  return;
389
484
  }
390
- const { tiles, groupRects, represented, pen, sketch } = drawTreemapTiles(surface, treemap, tokens, content, leaves, context);
485
+ const backdrop = knownBackdrop(surface, tokens);
486
+ const sketch = resolveSketch(treemap);
487
+ const pen = sketch ? new RoughPen(surface.marks.ctx, sketch) : null;
488
+ const { tiles, groupRects } = layoutTreemapTiles(treemap, tokens, content, leaves, pen !== null, context);
489
+ const finalFrame = {
490
+ tiles: tiles.map((tile) => ({ key: tile.key, tile, alpha: 1 })),
491
+ groups: groupRects.map((group) => ({ key: group.group.key, group, alpha: 1 }))
492
+ };
493
+ const morph = morphInput(context);
494
+ const moved = morph && morphKeyed(morph.from.tiles, finalFrame.tiles, morph.t, TREEMAP_MORPH);
495
+ const movedGroups = morph && morphKeyed(morph.from.groups, finalFrame.groups, morph.t, TREEMAP_GROUP_MORPH);
496
+ stashMorphFrame(surface, "treemap", moved && movedGroups ? { tiles: [...moved.exits, ...moved.items], groups: [...movedGroups.exits, ...movedGroups.items] } : finalFrame);
497
+ const represented = paintTreemapTiles(surface, tokens, moved ? [...moved.exits, ...moved.items].map(fadedTile) : tiles, backdrop, pen);
391
498
  surface.evidence?.data(represented, "aggregate");
392
499
  if (treemap.encoding.group) {
393
- drawGroupFrames(surface, tokens, treemap, groupRects, pen, sketch);
500
+ const groups = movedGroups ? movedGroups.items.filter((item) => item.alpha >= 0.5).map((item) => item.group) : groupRects;
501
+ drawGroupFrames(surface, tokens, treemap, groups, pen, sketch);
394
502
  }
395
503
  if (treemap.labels !== false) {
396
- for (const tile of tiles) addTileLabels(surface, tokens, tile, treemap.encoding.value.format);
504
+ const labelled = moved ? moved.items.filter((item) => item.alpha >= 0.5).map(fadedTile) : tiles;
505
+ for (const tile of labelled) addTileLabels(surface, tokens, tile, backdrop, treemap.encoding.value.format);
397
506
  }
398
- return buildTreemapInteraction(treemap, content, tiles, total);
507
+ return buildTreemapInteraction(treemap, tokens, content, tiles, total);
399
508
  }
400
509
  export {
401
510
  drawTreemap,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/charts/treemap.ts"],
4
- "sourcesContent": ["import { ordinalColorScale, sequentialColorScale, sequential, rgbaToCss, parseColor } from '../color';\nimport { formatValue } from '../format';\nimport { fontString, measureText, ellipsize } from '../render/text';\nimport { accessor, toNumber, toKey } from '../util/data';\nimport { RoughPen } from '../rough';\nimport { resolveSketch } from '../spec/sketch';\nimport { addOverlayText, drawTitleBlock } from './chrome';\nimport type { ChartSpec, TreemapSpec } from '../spec/types';\nimport type { Surface } from '../render/surface';\nimport type { ThemeTokens } from '../theme';\nimport type { InteractionModel } from '../interaction/types';\nimport type { RenderContext } from './index';\nimport type { Datum, Rect, RGBA, Size } from '../types';\nimport { addCenteredEmptyMessage } from './emptyOverlay';\n\nexport interface TreemapLayoutInput {\n value: number;\n}\n\nexport interface TreemapLayoutNode<T extends TreemapLayoutInput = TreemapLayoutInput> {\n item: T;\n rect: Rect;\n}\n\ninterface Leaf extends TreemapLayoutInput {\n key: string;\n category: unknown;\n categoryLabel: string;\n value: number;\n group?: unknown;\n groupKey?: string;\n groupLabel?: string;\n colorValue?: unknown;\n colorNumber?: number;\n colorNoData?: boolean;\n fill: RGBA;\n fillCss: string;\n sourceRows: number;\n}\n\ninterface GroupItem extends TreemapLayoutInput {\n key: string;\n label: string;\n value: number;\n leaves: Leaf[];\n}\n\ninterface Tile extends Leaf {\n rect: Rect;\n}\n\nconst MIN_LABEL_WIDTH = 38;\nconst MIN_LABEL_HEIGHT = 24;\n\nfunction cleanRect(rect: Rect): Rect {\n return {\n x: Number.isFinite(rect.x) ? rect.x : 0,\n y: Number.isFinite(rect.y) ? rect.y : 0,\n width: Math.max(0, Number.isFinite(rect.width) ? rect.width : 0),\n height: Math.max(0, Number.isFinite(rect.height) ? rect.height : 0),\n };\n}\n\nfunction worst(row: readonly number[], side: number): number {\n if (row.length === 0 || side <= 0) return Infinity;\n let sum = 0;\n let min = Infinity;\n let max = 0;\n for (const area of row) {\n sum += area;\n min = Math.min(min, area);\n max = Math.max(max, area);\n }\n if (sum <= 0 || min <= 0) return Infinity;\n const side2 = side * side;\n return Math.max((side2 * max) / (sum * sum), (sum * sum) / (side2 * min));\n}\n\nfunction layoutRow<T extends TreemapLayoutInput>(\n row: readonly { item: T; area: number }[],\n rect: Rect,\n out: TreemapLayoutNode<T>[],\n): Rect {\n const area = row.reduce((sum, d) => sum + d.area, 0);\n if (area <= 0 || rect.width <= 0 || rect.height <= 0) return rect;\n\n if (rect.height >= rect.width) {\n const h = Math.min(rect.height, area / rect.width);\n let x = rect.x;\n for (let i = 0; i < row.length; i++) {\n const w = i === row.length - 1 ? rect.x + rect.width - x : row[i].area / h;\n out.push({ item: row[i].item, rect: { x, y: rect.y, width: Math.max(0, w), height: h } });\n x += w;\n }\n return { x: rect.x, y: rect.y + h, width: rect.width, height: Math.max(0, rect.height - h) };\n }\n\n const w = Math.min(rect.width, area / rect.height);\n let y = rect.y;\n for (let i = 0; i < row.length; i++) {\n const h = i === row.length - 1 ? rect.y + rect.height - y : row[i].area / w;\n out.push({ item: row[i].item, rect: { x: rect.x, y, width: w, height: Math.max(0, h) } });\n y += h;\n }\n return { x: rect.x + w, y: rect.y, width: Math.max(0, rect.width - w), height: rect.height };\n}\n\n/** Squarified treemap layout. Input order is preserved for deterministic agent output. */\nexport function layoutTreemap<T extends TreemapLayoutInput>(items: readonly T[], rect: Rect): TreemapLayoutNode<T>[] {\n const box = cleanRect(rect);\n const total = items.reduce((sum, item) => (Number.isFinite(item.value) && item.value > 0 ? sum + item.value : sum), 0);\n const area = box.width * box.height;\n if (total <= 0 || area <= 0) return [];\n\n const pending = items\n .filter((item) => Number.isFinite(item.value) && item.value > 0)\n .map((item) => ({ item, area: (item.value / total) * area }));\n const out: TreemapLayoutNode<T>[] = [];\n let rest = box;\n let row: { item: T; area: number }[] = [];\n let rowAreas: number[] = [];\n\n const flush = () => {\n if (row.length === 0) return;\n rest = layoutRow(row, rest, out);\n row = [];\n rowAreas = [];\n };\n\n for (const next of pending) {\n const side = Math.min(rest.width, rest.height);\n if (row.length === 0 || worst([...rowAreas, next.area], side) <= worst(rowAreas, side)) {\n row.push(next);\n rowAreas.push(next.area);\n } else {\n flush();\n row.push(next);\n rowAreas.push(next.area);\n }\n }\n flush();\n return out;\n}\n\nfunction insetRect(rect: Rect, pad: number): Rect {\n return {\n x: rect.x + pad,\n y: rect.y + pad,\n width: Math.max(0, rect.width - pad * 2),\n height: Math.max(0, rect.height - pad * 2),\n };\n}\n\nfunction labelFor(value: unknown): string {\n const key = toKey(value);\n return key === '' ? '(blank)' : key;\n}\n\n/**\n * A hand-drawn hachure shade for a tile fill: shifted *away* from the fill's own\n * luminance (darker for light tiles, lighter for dark tiles) so the \"marker\"\n * streaks stay visible on any colour in the sequential/ordinal scale.\n */\nfunction textureShade(fill: RGBA): string {\n const lum = (0.299 * fill.r + 0.587 * fill.g + 0.114 * fill.b) / 255;\n const k = lum > 0.5 ? 0.68 : 1.4;\n const c = (n: number) => Math.max(0, Math.min(255, Math.round(n * k)));\n return rgbaToCss({ r: c(fill.r), g: c(fill.g), b: c(fill.b), a: 1 });\n}\n\nfunction buildLeaves(treemap: TreemapSpec): Leaf[] {\n const readCategory = accessor(treemap.encoding.category.field);\n const readValue = accessor(treemap.encoding.value.field);\n const readGroup = treemap.encoding.group ? accessor(treemap.encoding.group.field) : null;\n const readColor = treemap.encoding.color ? accessor(treemap.encoding.color.field) : null;\n const order: string[] = [];\n const totals = new Map<string, Leaf>();\n\n for (const row of treemap.data ?? []) {\n const rawCategory = readCategory(row);\n const categoryKey = toKey(rawCategory);\n const rawGroup = readGroup ? readGroup(row) : undefined;\n const groupKey = readGroup ? toKey(rawGroup) : undefined;\n const key = groupKey === undefined ? categoryKey : `${groupKey}\\u0000${categoryKey}`;\n const value = toNumber(readValue(row));\n if (!Number.isFinite(value) || value <= 0) continue;\n\n let leaf = totals.get(key);\n if (!leaf) {\n const colorValue = readColor ? readColor(row) : undefined;\n const colorNumber = colorValue === undefined ? undefined : toNumber(colorValue);\n leaf = {\n key,\n category: rawCategory,\n categoryLabel: labelFor(rawCategory),\n value: 0,\n group: rawGroup,\n groupKey,\n groupLabel: groupKey === undefined ? undefined : labelFor(rawGroup),\n colorValue,\n colorNumber: Number.isFinite(colorNumber) ? colorNumber : undefined,\n fill: { r: 0, g: 0, b: 0, a: 1 },\n fillCss: '#000',\n sourceRows: 0,\n };\n totals.set(key, leaf);\n order.push(key);\n }\n leaf.value += value;\n leaf.sourceRows++;\n }\n\n return order.map((key) => totals.get(key)!);\n}\n\nfunction colorLeaves(leaves: Leaf[], treemap: TreemapSpec, tokens: ThemeTokens): void {\n const fallback = parseColor(tokens.color.surface) ?? parseColor(tokens.color.palette[0] ?? '#14b8a6') ?? { r: 20, g: 184, b: 166, a: 1 };\n const color = treemap.encoding.color;\n const ordinal = ordinalColorScale({ palette: tokens.color.palette });\n const numericValues = color ? leaves.map((leaf) => leaf.colorNumber).filter((n): n is number => n !== undefined) : [];\n const numericColor = Boolean(color && (color.type === 'quantitative' || (numericValues.length > 0 && numericValues.length === leaves.length)));\n const domain: [number, number] = numericValues.length\n ? [Math.min(...numericValues), Math.max(...numericValues)]\n : [0, 1];\n const continuous = sequentialColorScale({ domain, interpolator: sequential(treemap.scheme ?? 'teal') });\n\n for (const leaf of leaves) {\n let fill: RGBA = fallback;\n if (color) {\n if (numericColor) {\n if (leaf.colorNumber === undefined) {\n fill = parseColor(tokens.color.border) ?? fallback;\n leaf.colorNoData = true;\n } else {\n fill = continuous.map(leaf.colorNumber);\n }\n }\n else fill = ordinal.map(toKey(leaf.colorValue));\n } else if (treemap.encoding.group && leaf.groupKey !== undefined) {\n fill = ordinal.map(leaf.groupKey);\n } else {\n fill = ordinal.map(leaf.categoryLabel);\n }\n leaf.fill = fill;\n leaf.fillCss = rgbaToCss(fill);\n }\n}\n\nfunction buildGroups(leaves: readonly Leaf[]): GroupItem[] {\n const order: string[] = [];\n const groups = new Map<string, GroupItem>();\n for (const leaf of leaves) {\n const key = leaf.groupKey ?? '';\n let group = groups.get(key);\n if (!group) {\n group = { key, label: leaf.groupLabel ?? '(blank)', value: 0, leaves: [] };\n groups.set(key, group);\n order.push(key);\n }\n group.value += leaf.value;\n group.leaves.push(leaf);\n }\n return order.map((key) => groups.get(key)!);\n}\n\nfunction tileProbe(treemap: TreemapSpec, tile: Tile): Datum {\n const probe: Datum = { [treemap.encoding.category.field]: tile.category };\n const groupField = treemap.encoding.group?.field;\n if (groupField) probe[groupField] = tile.group;\n return probe;\n}\n\nfunction drawRect(ctx: CanvasRenderingContext2D, pen: RoughPen | null, rect: Rect, fill: RGBA, fillCss: string, stroke: string): void {\n if (rect.width <= 0 || rect.height <= 0) return;\n if (pen) {\n // A solid base keeps the value-encoding colour crisp (light tiles stay\n // legible on a dark page), while a same-family hachure overlay adds\n // hand-drawn \"marker shading\" so the tile doesn't read as a flat digital\n // block trapped inside a wobbly outline. The overlay carries the outline.\n pen.rect(rect.x, rect.y, rect.width, rect.height, { fill: fillCss, fillStyle: 'solid' });\n pen.rect(rect.x, rect.y, rect.width, rect.height, {\n fill: textureShade(fill),\n fillStyle: 'hachure',\n fillAlpha: 0.4,\n hachureGap: 7,\n stroke,\n });\n return;\n }\n ctx.fillStyle = fillCss;\n ctx.fillRect(rect.x, rect.y, rect.width, rect.height);\n ctx.strokeStyle = stroke;\n ctx.lineWidth = 1;\n ctx.strokeRect(rect.x + 0.5, rect.y + 0.5, Math.max(0, rect.width - 1), Math.max(0, rect.height - 1));\n}\n\nfunction addTileLabels(surface: Surface, tokens: ThemeTokens, tile: Tile, valueFormat?: string): void {\n const compact = tile.rect.width < MIN_LABEL_WIDTH || tile.rect.height < MIN_LABEL_HEIGHT;\n const pad = compact ? 2 : 5;\n const padX = compact ? 3 : 6;\n const padY = compact ? 1 : 2;\n const labelSize = tokens.font.size.small;\n if (tile.rect.width <= pad * 2 + padX * 2 + 2 ||\n tile.rect.height < labelSize + padY * 2 + pad * 2 + 2) {\n surface.labels?.omit({ role: 'data-label', reason: 'space', required: true, example: tile.categoryLabel, path: '/labels' });\n return;\n }\n const valueSize = tokens.font.size.tiny;\n const labelFont = fontString(labelSize, tokens.font.family, tokens.font.weight.bold);\n const valueFont = fontString(valueSize, tokens.font.family);\n // Text budget inside the tile padding and the pill's own padding + 1px border\n // on each side, so the rounded badge never spills past the tile edge.\n const maxTextWidth = tile.rect.width - pad * 2 - padX * 2 - 2;\n const labelText = ellipsize(tile.categoryLabel, maxTextWidth, labelFont);\n if (labelText === '' || labelText === '\u2026' || (compact && labelText.length / Math.max(1, tile.categoryLabel.length) < 0.5)) {\n surface.labels?.omit({ role: 'data-label', reason: 'unreadable', required: true, example: tile.categoryLabel, path: '/labels' });\n return;\n }\n\n // A themed pill (solid fill + hairline border) keeps every label legible on\n // any tile colour, so labels read identically across fills \u2014 like the funnel.\n const pill = { background: tokens.color.surface, border: tokens.color.border, padX, padY } as const;\n\n addOverlayText(surface, tokens, {\n left: tile.rect.x + pad + 2,\n top: tile.rect.y + pad,\n text: tile.categoryLabel,\n maxWidth: maxTextWidth,\n color: tokens.color.text,\n size: labelSize,\n weight: tokens.font.weight.bold,\n pill,\n });\n\n const labelPillH = labelSize + padY * 2 + 2;\n const valuePillH = valueSize + padY * 2 + 2;\n const valueText = formatValue(tile.value, valueFormat);\n // Stack the value in its own pill only when the whole number fits (a truncated\n // number would mislead) and the tile is tall enough for a second row.\n if (\n tile.rect.height >= pad * 2 + labelPillH + 2 + valuePillH &&\n measureText(valueText, valueFont).width <= maxTextWidth\n ) {\n addOverlayText(surface, tokens, {\n left: tile.rect.x + pad + 2,\n top: tile.rect.y + pad + labelPillH + 2,\n text: valueText,\n color: tokens.color.textMuted,\n size: valueSize,\n pill,\n });\n } else {\n surface.labels?.omit({ role: 'data-label', reason: 'space', required: false, example: valueText, path: '/labels' });\n }\n}\n\ninterface GroupRect {\n group: GroupItem;\n rect: Rect;\n headerH: number;\n}\n\nfunction drawNoPositiveTreemap(\n surface: Surface,\n tokens: ThemeTokens,\n content: Rect,\n): void {\n addCenteredEmptyMessage(surface, tokens, content, 'No positive values');\n}\n\nfunction drawTreemapTiles(\n surface: Surface,\n treemap: TreemapSpec,\n tokens: ThemeTokens,\n content: Rect,\n leaves: Leaf[],\n context?: RenderContext,\n): { tiles: Tile[]; groupRects: GroupRect[]; represented: number; pen: RoughPen | null; sketch: ReturnType<typeof resolveSketch> } {\n const ctx = surface.marks.ctx;\n const emphasis = context?.emphasis ?? null;\n const sketch = resolveSketch(treemap);\n const pen = sketch ? new RoughPen(ctx, sketch) : null;\n const tiles: Tile[] = [];\n const groupRects: GroupRect[] = [];\n const stroke = tokens.color.textMuted;\n let represented = 0;\n const drawTile = (tile: Tile) => {\n const alpha = emphasis ? (emphasis.match(tileProbe(treemap, tile)) ? 1 : emphasis.dim) : 1;\n if (alpha !== 1) ctx.globalAlpha = alpha;\n drawRect(ctx, pen, tile.rect, tile.fill, tile.fillCss, stroke);\n if (tile.rect.width > 0 && tile.rect.height > 0) {\n surface.evidence?.mark('tile', tile.rect.x, tile.rect.y, tile.rect.width, tile.rect.height, tile.fillCss, alpha, undefined, 1, undefined, alpha < 1, { color: stroke, opacity: 0.65, width: 1 });\n represented += tile.sourceRows;\n }\n if (alpha !== 1) ctx.globalAlpha = 1;\n };\n\n ctx.save();\n if (treemap.encoding.group) {\n const groups = buildGroups(leaves);\n for (const groupNode of layoutTreemap(groups, content)) {\n const gRect = groupNode.rect;\n const headerH = gRect.width >= 46 && gRect.height >= 40 ? tokens.font.size.tiny + 8 : 0;\n groupRects.push({ group: groupNode.item, rect: gRect, headerH });\n const inner = insetRect(\n { x: gRect.x, y: gRect.y + headerH, width: gRect.width, height: Math.max(0, gRect.height - headerH) },\n 2,\n );\n for (const leafNode of layoutTreemap(groupNode.item.leaves, inner)) {\n const tile = { ...leafNode.item, rect: leafNode.rect };\n tiles.push(tile);\n drawTile(tile);\n }\n }\n } else {\n for (const leafNode of layoutTreemap(leaves, content)) {\n const tile = { ...leafNode.item, rect: leafNode.rect };\n tiles.push(tile);\n drawTile(tile);\n }\n }\n ctx.restore();\n\n return { tiles, groupRects, represented, pen, sketch };\n}\n\nfunction drawGroupFrames(\n surface: Surface,\n tokens: ThemeTokens,\n treemap: TreemapSpec,\n groups: readonly GroupRect[],\n pen: RoughPen | null,\n sketch: ReturnType<typeof resolveSketch>,\n): void {\n const ctx = surface.marks.ctx;\n const stroke = tokens.color.background;\n for (const { group, rect, headerH } of groups) {\n if (pen) pen.rect(rect.x, rect.y, rect.width, rect.height, { stroke, roughness: sketch!.roughness * 0.6 });\n else {\n ctx.strokeStyle = stroke;\n ctx.lineWidth = 1;\n ctx.strokeRect(rect.x + 0.5, rect.y + 0.5, Math.max(0, rect.width - 1), Math.max(0, rect.height - 1));\n }\n if (headerH > 0) {\n const headerW = Math.max(0, rect.width - 10);\n addOverlayText(surface, tokens, {\n left: rect.x + 5,\n top: rect.y + 4,\n width: headerW,\n text: group.label,\n maxWidth: headerW,\n color: tokens.color.text,\n size: tokens.font.size.tiny,\n weight: tokens.font.weight.bold,\n opacity: 0.78,\n });\n } else if (treemap.labels !== false) {\n surface.labels?.omit({ role: 'data-label', reason: 'space', required: true, example: group.label, path: '/encoding/group' });\n }\n }\n}\n\nfunction buildTreemapInteraction(\n treemap: TreemapSpec,\n content: Rect,\n tiles: readonly Tile[],\n total: number,\n): InteractionModel {\n const valueLabel = treemap.encoding.value.title ?? treemap.encoding.value.field;\n const colorLabel = treemap.encoding.color?.title ?? treemap.encoding.color?.field;\n const categoryField = treemap.encoding.category.field;\n const groupField = treemap.encoding.group?.field;\n const hitTile = (px: number, py: number): Tile | null => {\n for (let i = tiles.length - 1; i >= 0; i--) {\n const tile = tiles[i];\n if (px >= tile.rect.x && px <= tile.rect.x + tile.rect.width && py >= tile.rect.y && py <= tile.rect.y + tile.rect.height) return tile;\n }\n return null;\n };\n\n return {\n region: { x: content.x, y: content.y, width: content.width, height: content.height },\n hitTest: (px, py) => {\n const tile = hitTile(px, py);\n if (!tile) return null;\n return {\n key: tile.key,\n anchorX: px,\n anchorY: py,\n content: {\n title: groupField ? `${tile.groupLabel ?? '(blank)'} \u25B8 ${tile.categoryLabel}` : tile.categoryLabel,\n rows: [\n { swatch: tile.fillCss, label: valueLabel, value: formatValue(tile.value, treemap.encoding.value.format) },\n ...(colorLabel\n ? [{\n label: colorLabel,\n value: tile.colorNoData ? 'no data' : formatValue(tile.colorValue, treemap.encoding.color?.format),\n muted: true,\n }]\n : []),\n { label: 'share', value: `${((tile.value / total) * 100).toFixed(1)}%`, muted: true },\n ],\n },\n };\n },\n pick: (px, py) => {\n const tile = hitTile(px, py);\n if (!tile) return null;\n return groupField\n ? { kind: 'point', fields: [categoryField, groupField], tuples: [[tile.category, tile.group]] }\n : { kind: 'point', fields: [categoryField], tuples: [[tile.category]] };\n },\n };\n}\n\nexport function drawTreemap(\n surface: Surface,\n spec: ChartSpec,\n tokens: ThemeTokens,\n size: Size,\n context?: RenderContext,\n): InteractionModel | void {\n surface.evidence?.begin('aggregate');\n surface.evidence?.data(0, 'aggregate');\n const treemap = spec as TreemapSpec;\n const content = drawTitleBlock(surface, tokens, size, treemap.title);\n const leaves = buildLeaves(treemap);\n colorLeaves(leaves, treemap, tokens);\n const total = leaves.reduce((sum, leaf) => sum + leaf.value, 0);\n\n if (leaves.length === 0 || total <= 0 || content.width <= 0 || content.height <= 0) {\n drawNoPositiveTreemap(surface, tokens, content);\n return;\n }\n\n const { tiles, groupRects, represented, pen, sketch } = drawTreemapTiles(surface, treemap, tokens, content, leaves, context);\n surface.evidence?.data(represented, 'aggregate');\n\n if (treemap.encoding.group) {\n drawGroupFrames(surface, tokens, treemap, groupRects, pen, sketch);\n }\n\n if (treemap.labels !== false) {\n for (const tile of tiles) addTileLabels(surface, tokens, tile, treemap.encoding.value.format);\n }\n\n return buildTreemapInteraction(treemap, content, tiles, total);\n}\n"],
5
- "mappings": "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",
6
- "names": ["w"]
4
+ "sourcesContent": ["import { ordinalColorScale, sequentialColorScale, sequential, rgbaToCss, parseColor, contrastRatio } from '../color';\nimport { formatValue } from '../format';\nimport { fontString, measureText, ellipsize } from '../render/text';\nimport type { MarkBoundary } from '../render/evidence';\nimport { roundedRect } from '../shape';\nimport { accessor, toNumber, toKey } from '../util/data';\nimport { RoughPen } from '../rough';\nimport { resolveSketch } from '../spec/sketch';\nimport { addOverlayText, drawTitleBlock } from './chrome';\nimport { compositeOver, contrastTarget, inkOn, knownBackdrop, toneForContrast } from './legibility';\nimport type { ChartSpec, TreemapSpec } from '../spec/types';\nimport type { Surface } from '../render/surface';\nimport type { ThemeTokens } from '../theme';\nimport type { HoverPaintState, InteractionModel } from '../interaction/types';\nimport { FOCUS_VEIL, hoverFocusEnabled, paintCutoutVeil } from '../interaction/veil';\nimport type { RenderContext } from './index';\nimport type { Datum, Rect, RGBA, Size } from '../types';\nimport { addCenteredEmptyMessage } from './emptyOverlay';\nimport { lerp, lerpRect, lerpValue, mixFill, morphInput, morphKeyed, rampAt, stashMorphFrame, switchAt, type MorphPlan, type RampStop } from './morphFrame';\n\nexport interface TreemapLayoutInput {\n value: number;\n}\n\nexport interface TreemapLayoutNode<T extends TreemapLayoutInput = TreemapLayoutInput> {\n item: T;\n rect: Rect;\n}\n\ninterface Leaf extends TreemapLayoutInput {\n key: string;\n category: unknown;\n categoryLabel: string;\n value: number;\n group?: unknown;\n groupKey?: string;\n groupLabel?: string;\n colorValue?: unknown;\n colorNumber?: number;\n colorNoData?: boolean;\n fill: RGBA;\n fillCss: string;\n /** Where a quantitative color sits on the scheme's ramp, so a morph can travel along it. */\n ramp?: RampStop;\n sourceRows: number;\n}\n\ninterface GroupItem extends TreemapLayoutInput {\n key: string;\n label: string;\n value: number;\n leaves: Leaf[];\n}\n\ninterface Tile extends Leaf {\n /** Layout cell: the hit target, including this tile's share of the gutter. */\n rect: Rect;\n /** Painted tile: the cell inset by half the gutter on each side. */\n paint: Rect;\n /** Corner radius of the painted tile. */\n radius: number;\n /** Emphasis opacity the tile was painted with. */\n alpha: number;\n}\n\nconst MIN_LABEL_WIDTH = 38;\nconst MIN_LABEL_HEIGHT = 24;\n/** Background shows through this gap between neighbouring tiles. */\nconst TILE_GUTTER = 2;\nconst TILE_RADIUS = 3;\n/** Tiles fainter than this against the backdrop get a tone-on-tone hairline (the report's mark floor is 1.5). */\nconst PALE_TILE_CONTRAST = 1.6;\nconst TEXT_CONTRAST_MIN = 4.5;\n\n/** The painted tile inside a layout cell; tiny cells keep a sliver rather than vanishing. */\nfunction tilePaintRect(cell: Rect, gutter: number): Rect {\n const inset = Math.min(gutter / 2, cell.width / 4, cell.height / 4);\n return insetRect(cell, inset);\n}\n\nfunction tileRadius(paint: Rect): number {\n return Math.max(0, Math.min(TILE_RADIUS, paint.width / 4, paint.height / 4));\n}\n\nfunction cleanRect(rect: Rect): Rect {\n return {\n x: Number.isFinite(rect.x) ? rect.x : 0,\n y: Number.isFinite(rect.y) ? rect.y : 0,\n width: Math.max(0, Number.isFinite(rect.width) ? rect.width : 0),\n height: Math.max(0, Number.isFinite(rect.height) ? rect.height : 0),\n };\n}\n\nfunction worst(row: readonly number[], side: number): number {\n if (row.length === 0 || side <= 0) return Infinity;\n let sum = 0;\n let min = Infinity;\n let max = 0;\n for (const area of row) {\n sum += area;\n min = Math.min(min, area);\n max = Math.max(max, area);\n }\n if (sum <= 0 || min <= 0) return Infinity;\n const side2 = side * side;\n return Math.max((side2 * max) / (sum * sum), (sum * sum) / (side2 * min));\n}\n\nfunction layoutRow<T extends TreemapLayoutInput>(\n row: readonly { item: T; area: number }[],\n rect: Rect,\n out: TreemapLayoutNode<T>[],\n): Rect {\n const area = row.reduce((sum, d) => sum + d.area, 0);\n if (area <= 0 || rect.width <= 0 || rect.height <= 0) return rect;\n\n if (rect.height >= rect.width) {\n const h = Math.min(rect.height, area / rect.width);\n let x = rect.x;\n for (let i = 0; i < row.length; i++) {\n const w = i === row.length - 1 ? rect.x + rect.width - x : row[i].area / h;\n out.push({ item: row[i].item, rect: { x, y: rect.y, width: Math.max(0, w), height: h } });\n x += w;\n }\n return { x: rect.x, y: rect.y + h, width: rect.width, height: Math.max(0, rect.height - h) };\n }\n\n const w = Math.min(rect.width, area / rect.height);\n let y = rect.y;\n for (let i = 0; i < row.length; i++) {\n const h = i === row.length - 1 ? rect.y + rect.height - y : row[i].area / w;\n out.push({ item: row[i].item, rect: { x: rect.x, y, width: w, height: Math.max(0, h) } });\n y += h;\n }\n return { x: rect.x + w, y: rect.y, width: Math.max(0, rect.width - w), height: rect.height };\n}\n\n/** Squarified treemap layout. Input order is preserved for deterministic agent output. */\nexport function layoutTreemap<T extends TreemapLayoutInput>(items: readonly T[], rect: Rect): TreemapLayoutNode<T>[] {\n const box = cleanRect(rect);\n const total = items.reduce((sum, item) => (Number.isFinite(item.value) && item.value > 0 ? sum + item.value : sum), 0);\n const area = box.width * box.height;\n if (total <= 0 || area <= 0) return [];\n\n const pending = items\n .filter((item) => Number.isFinite(item.value) && item.value > 0)\n .map((item) => ({ item, area: (item.value / total) * area }));\n const out: TreemapLayoutNode<T>[] = [];\n let rest = box;\n let row: { item: T; area: number }[] = [];\n let rowAreas: number[] = [];\n\n const flush = () => {\n if (row.length === 0) return;\n rest = layoutRow(row, rest, out);\n row = [];\n rowAreas = [];\n };\n\n for (const next of pending) {\n const side = Math.min(rest.width, rest.height);\n if (row.length === 0 || worst([...rowAreas, next.area], side) <= worst(rowAreas, side)) {\n row.push(next);\n rowAreas.push(next.area);\n } else {\n flush();\n row.push(next);\n rowAreas.push(next.area);\n }\n }\n flush();\n return out;\n}\n\nfunction insetRect(rect: Rect, pad: number): Rect {\n return {\n x: rect.x + pad,\n y: rect.y + pad,\n width: Math.max(0, rect.width - pad * 2),\n height: Math.max(0, rect.height - pad * 2),\n };\n}\n\nfunction labelFor(value: unknown): string {\n const key = toKey(value);\n return key === '' ? '(blank)' : key;\n}\n\n/**\n * A hand-drawn hachure shade for a tile fill: shifted *away* from the fill's own\n * luminance (darker for light tiles, lighter for dark tiles) so the \"marker\"\n * streaks stay visible on any colour in the sequential/ordinal scale.\n */\nfunction textureShade(fill: RGBA): string {\n const lum = (0.299 * fill.r + 0.587 * fill.g + 0.114 * fill.b) / 255;\n const k = lum > 0.5 ? 0.68 : 1.4;\n const c = (n: number) => Math.max(0, Math.min(255, Math.round(n * k)));\n return rgbaToCss({ r: c(fill.r), g: c(fill.g), b: c(fill.b), a: 1 });\n}\n\nfunction buildLeaves(treemap: TreemapSpec): Leaf[] {\n const readCategory = accessor(treemap.encoding.category.field);\n const readValue = accessor(treemap.encoding.value.field);\n const readGroup = treemap.encoding.group ? accessor(treemap.encoding.group.field) : null;\n const readColor = treemap.encoding.color ? accessor(treemap.encoding.color.field) : null;\n const order: string[] = [];\n const totals = new Map<string, Leaf>();\n\n for (const row of treemap.data ?? []) {\n const rawCategory = readCategory(row);\n const categoryKey = toKey(rawCategory);\n const rawGroup = readGroup ? readGroup(row) : undefined;\n const groupKey = readGroup ? toKey(rawGroup) : undefined;\n const key = groupKey === undefined ? categoryKey : `${groupKey}\\u0000${categoryKey}`;\n const value = toNumber(readValue(row));\n if (!Number.isFinite(value) || value <= 0) continue;\n\n let leaf = totals.get(key);\n if (!leaf) {\n const colorValue = readColor ? readColor(row) : undefined;\n const colorNumber = colorValue === undefined ? undefined : toNumber(colorValue);\n leaf = {\n key,\n category: rawCategory,\n categoryLabel: labelFor(rawCategory),\n value: 0,\n group: rawGroup,\n groupKey,\n groupLabel: groupKey === undefined ? undefined : labelFor(rawGroup),\n colorValue,\n colorNumber: Number.isFinite(colorNumber) ? colorNumber : undefined,\n fill: { r: 0, g: 0, b: 0, a: 1 },\n fillCss: '#000',\n sourceRows: 0,\n };\n totals.set(key, leaf);\n order.push(key);\n }\n leaf.value += value;\n leaf.sourceRows++;\n }\n\n return order.map((key) => totals.get(key)!);\n}\n\nfunction colorLeaves(leaves: Leaf[], treemap: TreemapSpec, tokens: ThemeTokens): void {\n const fallback = parseColor(tokens.color.surface) ?? parseColor(tokens.color.palette[0] ?? '#14b8a6') ?? { r: 20, g: 184, b: 166, a: 1 };\n const color = treemap.encoding.color;\n const ordinal = ordinalColorScale({ palette: tokens.color.palette });\n const numericValues = color ? leaves.map((leaf) => leaf.colorNumber).filter((n): n is number => n !== undefined) : [];\n const numericColor = Boolean(color && (color.type === 'quantitative' || (numericValues.length > 0 && numericValues.length === leaves.length)));\n const domain: [number, number] = numericValues.length\n ? [Math.min(...numericValues), Math.max(...numericValues)]\n : [0, 1];\n const continuous = sequentialColorScale({ domain, interpolator: sequential(treemap.scheme ?? 'teal') });\n\n for (const leaf of leaves) {\n let fill: RGBA = fallback;\n if (color) {\n if (numericColor) {\n if (leaf.colorNumber === undefined) {\n fill = parseColor(tokens.color.border) ?? fallback;\n leaf.colorNoData = true;\n } else {\n fill = continuous.map(leaf.colorNumber);\n leaf.ramp = { scheme: treemap.scheme ?? 'teal', at: rampAt(domain, leaf.colorNumber) };\n }\n }\n else fill = ordinal.map(toKey(leaf.colorValue));\n } else if (treemap.encoding.group && leaf.groupKey !== undefined) {\n fill = ordinal.map(leaf.groupKey);\n } else {\n fill = ordinal.map(leaf.categoryLabel);\n }\n leaf.fill = fill;\n leaf.fillCss = rgbaToCss(fill);\n }\n}\n\nfunction buildGroups(leaves: readonly Leaf[]): GroupItem[] {\n const order: string[] = [];\n const groups = new Map<string, GroupItem>();\n for (const leaf of leaves) {\n const key = leaf.groupKey ?? '';\n let group = groups.get(key);\n if (!group) {\n group = { key, label: leaf.groupLabel ?? '(blank)', value: 0, leaves: [] };\n groups.set(key, group);\n order.push(key);\n }\n group.value += leaf.value;\n group.leaves.push(leaf);\n }\n return order.map((key) => groups.get(key)!);\n}\n\nfunction tileProbe(treemap: TreemapSpec, tile: Leaf): Datum {\n const probe: Datum = { [treemap.encoding.category.field]: tile.category };\n const groupField = treemap.encoding.group?.field;\n if (groupField) probe[groupField] = tile.group;\n return probe;\n}\n\n/** A pale tile's identifying hairline: its own hue, deepened just enough to read. */\nfunction paleTileBoundary(tile: Leaf, backdrop: RGBA | null, tokens: ThemeTokens, opacity: number): MarkBoundary | undefined {\n if (!backdrop || contrastRatio(compositeOver(tile.fill, 1, backdrop), backdrop) >= PALE_TILE_CONTRAST) return undefined;\n const color = toneForContrast(tile.fillCss, contrastTarget(backdrop, tokens), backdrop, PALE_TILE_CONTRAST);\n return { color, opacity, width: 1 };\n}\n\nfunction drawTileShape(\n ctx: CanvasRenderingContext2D,\n pen: RoughPen | null,\n tile: Tile,\n stroke: string,\n boundary: MarkBoundary | undefined,\n): void {\n const rect = tile.paint;\n if (rect.width <= 0 || rect.height <= 0) return;\n if (pen) {\n // A solid base keeps the value-encoding colour crisp (light tiles stay\n // legible on a dark page), while a same-family hachure overlay adds\n // hand-drawn \"marker shading\" so the tile doesn't read as a flat digital\n // block trapped inside a wobbly outline. The overlay carries the outline.\n pen.rect(rect.x, rect.y, rect.width, rect.height, { fill: tile.fillCss, fillStyle: 'solid' });\n pen.rect(rect.x, rect.y, rect.width, rect.height, {\n fill: textureShade(tile.fill),\n fillStyle: 'hachure',\n fillAlpha: 0.4,\n hachureGap: 7,\n stroke,\n });\n return;\n }\n ctx.fillStyle = tile.fillCss;\n ctx.beginPath();\n roundedRect(ctx, rect.x, rect.y, rect.width, rect.height, tile.radius);\n ctx.fill();\n if (boundary && rect.width > 1 && rect.height > 1) {\n ctx.strokeStyle = boundary.color;\n ctx.lineWidth = boundary.width;\n ctx.beginPath();\n roundedRect(ctx, rect.x + 0.5, rect.y + 0.5, rect.width - 1, rect.height - 1, Math.max(0, tile.radius - 0.5));\n ctx.stroke();\n }\n}\n\n/**\n * Name (bold) and value painted straight onto the tile in whichever neutral ink\n * contrasts more with the tile's own (possibly dimmed) colour \u2014 no badges.\n */\nfunction addTileLabels(\n surface: Surface,\n tokens: ThemeTokens,\n tile: Tile,\n backdrop: RGBA | null,\n valueFormat?: string,\n): void {\n const rect = tile.paint;\n const compact = rect.width < MIN_LABEL_WIDTH || rect.height < MIN_LABEL_HEIGHT;\n const padX = compact ? 3 : 7;\n const padY = compact ? 2 : 6;\n const labelSize = tokens.font.size.small;\n const nameH = labelSize * 1.2;\n if (rect.width <= padX * 2 + 4 || rect.height < nameH + padY * 2) {\n surface.labels?.omit({ role: 'data-label', reason: 'space', required: true, example: tile.categoryLabel, path: '/labels' });\n return;\n }\n const valueSize = tokens.font.size.tiny;\n const labelFont = fontString(labelSize, tokens.font.family, tokens.font.weight.bold);\n const valueFont = fontString(valueSize, tokens.font.family);\n const maxTextWidth = rect.width - padX * 2;\n const labelText = ellipsize(tile.categoryLabel, maxTextWidth, labelFont);\n if (labelText === '' || labelText === '\u2026' || (compact && labelText.length / Math.max(1, tile.categoryLabel.length) < 0.5)) {\n surface.labels?.omit({ role: 'data-label', reason: 'unreadable', required: true, example: tile.categoryLabel, path: '/labels' });\n return;\n }\n\n // The colour actually behind the text: the fill, composited when dimmed.\n const under = backdrop && (tile.alpha < 1 || tile.fill.a < 1)\n ? compositeOver(tile.fill, tile.alpha, backdrop)\n : { ...tile.fill, a: 1 };\n const ink = inkOn(under, tokens, TEXT_CONTRAST_MIN);\n const background = rgbaToCss(under);\n const inkRgba = parseColor(ink);\n const valueOpacity = inkRgba && contrastRatio(compositeOver(inkRgba, 0.82, under), under) >= TEXT_CONTRAST_MIN + 0.25 ? 0.82 : 1;\n\n addOverlayText(surface, tokens, {\n left: rect.x + padX,\n top: rect.y + padY,\n text: tile.categoryLabel,\n maxWidth: maxTextWidth,\n color: ink,\n size: labelSize,\n weight: tokens.font.weight.bold,\n background,\n });\n\n const valueText = formatValue(tile.value, valueFormat);\n // Show the value only when the whole number fits (a truncated number would\n // mislead) and the tile is tall enough for a second line.\n if (\n rect.height >= padY * 2 + nameH + 1 + valueSize * 1.2 &&\n measureText(valueText, valueFont).width <= maxTextWidth\n ) {\n addOverlayText(surface, tokens, {\n left: rect.x + padX,\n top: rect.y + padY + nameH + 1,\n text: valueText,\n color: ink,\n size: valueSize,\n opacity: valueOpacity === 1 ? undefined : valueOpacity,\n background,\n });\n } else {\n surface.labels?.omit({ role: 'data-label', reason: 'space', required: false, example: valueText, path: '/labels' });\n }\n}\n\ninterface GroupRect {\n group: GroupItem;\n rect: Rect;\n headerH: number;\n}\n\n/** A tile as drawn, keyed by group + category so an update can carry it to its new cell. */\ninterface TreemapMorphTile {\n key: string;\n /** The tile in pixel space; its `alpha` is the highlight, so emphasis changes fade instead of snapping. */\n tile: Tile;\n /** Fade for tiles arriving or leaving mid-morph. */\n alpha: number;\n}\n\n/** A group's frame and header, keyed by group. */\ninterface TreemapMorphGroup {\n key: string;\n group: GroupRect;\n alpha: number;\n}\n\ninterface TreemapMorphFrame {\n tiles: TreemapMorphTile[];\n groups: TreemapMorphGroup[];\n}\n\n/** `rect` scaled by `k` about its center. */\nfunction scaledRect(rect: Rect, k: number): Rect {\n return {\n x: rect.x + (rect.width * (1 - k)) / 2,\n y: rect.y + (rect.height * (1 - k)) / 2,\n width: rect.width * k,\n height: rect.height * k,\n };\n}\n\n/** `item` grown to fraction `k` of its size, about its center. */\nfunction grownTile(item: TreemapMorphTile, k: number, alpha: number): TreemapMorphTile {\n const { tile } = item;\n return { ...item, tile: { ...tile, rect: scaledRect(tile.rect, k), paint: scaledRect(tile.paint, k), radius: tile.radius * k }, alpha };\n}\n\nconst TREEMAP_MORPH: MorphPlan<TreemapMorphTile> = {\n lerp: (a, b, t) => {\n const { fill: fillCss, ramp } = mixFill({ fill: a.tile.fillCss, ramp: a.tile.ramp }, { fill: b.tile.fillCss, ramp: b.tile.ramp }, t);\n return {\n key: b.key,\n tile: {\n ...b.tile,\n rect: lerpRect(a.tile.rect, b.tile.rect, t),\n paint: lerpRect(a.tile.paint, b.tile.paint, t),\n radius: lerp(a.tile.radius, b.tile.radius, t),\n fill: fillCss === b.tile.fillCss ? b.tile.fill : (parseColor(fillCss) ?? b.tile.fill),\n fillCss,\n ramp,\n value: lerpValue(a.tile.value, b.tile.value, t),\n alpha: lerp(a.tile.alpha, b.tile.alpha, t),\n },\n alpha: lerp(a.alpha, b.alpha, t),\n };\n },\n enter: (b, t) => grownTile(b, t, t),\n exit: (a, t) => grownTile(a, 1 - t, a.alpha * (1 - t)),\n};\n\nconst TREEMAP_GROUP_MORPH: MorphPlan<TreemapMorphGroup> = {\n lerp: (a, b, t) => ({\n key: b.key,\n group: { group: b.group.group, rect: lerpRect(a.group.rect, b.group.rect, t), headerH: switchAt(a.group.headerH, b.group.headerH, t) },\n alpha: lerp(a.alpha, b.alpha, t),\n }),\n enter: (b, t) => ({ ...b, alpha: t }),\n exit: (a, t) => ({ ...a, alpha: a.alpha * (1 - t) }),\n};\n\n/** A morph item as painted: its highlight scaled by its enter/exit fade. */\nconst fadedTile = (item: TreemapMorphTile): Tile => ({ ...item.tile, alpha: item.tile.alpha * item.alpha });\n\nfunction drawNoPositiveTreemap(\n surface: Surface,\n tokens: ThemeTokens,\n content: Rect,\n): void {\n addCenteredEmptyMessage(surface, tokens, content, 'No positive values');\n}\n\nfunction layoutTreemapTiles(\n treemap: TreemapSpec,\n tokens: ThemeTokens,\n content: Rect,\n leaves: Leaf[],\n sketched: boolean,\n context?: RenderContext,\n): { tiles: Tile[]; groupRects: GroupRect[] } {\n const emphasis = context?.emphasis ?? null;\n const tiles: Tile[] = [];\n const groupRects: GroupRect[] = [];\n // Modern tiles are separated by gutters and softly rounded; the hand-drawn\n // sketch style keeps its abutting tiles and wobbly outlines.\n const addTile = (leaf: Leaf, cell: Rect) => {\n const alpha = emphasis ? (emphasis.match(tileProbe(treemap, leaf)) ? 1 : emphasis.dim) : 1;\n const paint = sketched ? cell : tilePaintRect(cell, TILE_GUTTER);\n tiles.push({ ...leaf, rect: cell, paint, radius: sketched ? 0 : tileRadius(paint), alpha });\n };\n\n if (treemap.encoding.group) {\n const groups = buildGroups(leaves);\n for (const groupNode of layoutTreemap(groups, content)) {\n const gRect = groupNode.rect;\n const headerH = gRect.width >= 46 && gRect.height >= 40 ? tokens.font.size.tiny + 8 : 0;\n groupRects.push({ group: groupNode.item, rect: gRect, headerH });\n const inner = insetRect(\n { x: gRect.x, y: gRect.y + headerH, width: gRect.width, height: Math.max(0, gRect.height - headerH) },\n 2,\n );\n for (const leafNode of layoutTreemap(groupNode.item.leaves, inner)) addTile(leafNode.item, leafNode.rect);\n }\n } else {\n for (const leafNode of layoutTreemap(leaves, content)) addTile(leafNode.item, leafNode.rect);\n }\n return { tiles, groupRects };\n}\n\n/** Paint tiles at their own alpha; returns the source rows they represent. */\nfunction paintTreemapTiles(\n surface: Surface,\n tokens: ThemeTokens,\n tiles: readonly Tile[],\n backdrop: RGBA | null,\n pen: RoughPen | null,\n): number {\n const ctx = surface.marks.ctx;\n const stroke = tokens.color.textMuted;\n let represented = 0;\n ctx.save();\n for (const tile of tiles) {\n const { paint, alpha } = tile;\n const boundary = pen ? { color: stroke, opacity: 0.65, width: 1 } : paleTileBoundary(tile, backdrop, tokens, alpha);\n if (alpha !== 1) ctx.globalAlpha = alpha;\n drawTileShape(ctx, pen, tile, stroke, boundary);\n if (paint.width > 0 && paint.height > 0) {\n surface.evidence?.mark('tile', paint.x, paint.y, paint.width, paint.height, tile.fillCss, alpha, undefined, 1, undefined, alpha < 1, boundary);\n represented += tile.sourceRows;\n }\n if (alpha !== 1) ctx.globalAlpha = 1;\n }\n ctx.restore();\n return represented;\n}\n\nfunction drawGroupFrames(\n surface: Surface,\n tokens: ThemeTokens,\n treemap: TreemapSpec,\n groups: readonly GroupRect[],\n pen: RoughPen | null,\n sketch: ReturnType<typeof resolveSketch>,\n): void {\n const stroke = tokens.color.background;\n for (const { group, rect, headerH } of groups) {\n // Gutters already separate groups; only the hand-drawn style frames them.\n if (pen) pen.rect(rect.x, rect.y, rect.width, rect.height, { stroke, roughness: sketch!.roughness * 0.6 });\n if (headerH > 0) {\n const inset = pen ? 5 : 2 + TILE_GUTTER / 2;\n const headerW = Math.max(0, rect.width - inset * 2);\n addOverlayText(surface, tokens, {\n left: rect.x + inset,\n top: rect.y + 4,\n width: headerW,\n text: group.label,\n maxWidth: headerW,\n color: tokens.color.text,\n size: tokens.font.size.tiny,\n weight: tokens.font.weight.bold,\n opacity: 0.78,\n });\n } else if (treemap.labels !== false) {\n surface.labels?.omit({ role: 'data-label', reason: 'space', required: true, example: group.label, path: '/encoding/group' });\n }\n }\n}\n\nfunction buildTreemapInteraction(\n treemap: TreemapSpec,\n tokens: ThemeTokens,\n content: Rect,\n tiles: readonly Tile[],\n total: number,\n): InteractionModel {\n const valueLabel = treemap.encoding.value.title ?? treemap.encoding.value.field;\n const colorLabel = treemap.encoding.color?.title ?? treemap.encoding.color?.field;\n const categoryField = treemap.encoding.category.field;\n const groupField = treemap.encoding.group?.field;\n const hitTile = (px: number, py: number): Tile | null => {\n for (let i = tiles.length - 1; i >= 0; i--) {\n const tile = tiles[i];\n if (px >= tile.rect.x && px <= tile.rect.x + tile.rect.width && py >= tile.rect.y && py <= tile.rect.y + tile.rect.height) return tile;\n }\n return null;\n };\n // Focus recedes every other tile; the hovered tile keeps its outline on top.\n const focusOn = hoverFocusEnabled(treemap);\n const veil = treemap.background ?? tokens.color.background;\n const paintTileHover = (ictx: CanvasRenderingContext2D, tile: Tile, strength: number): void => {\n const p = tile.paint;\n if (p.width <= 0 || p.height <= 0) return;\n if (focusOn) {\n paintCutoutVeil(ictx, content, veil, FOCUS_VEIL * strength, (c) => roundedRect(c, p.x, p.y, p.width, p.height, tile.radius));\n }\n // Outline the painted (rounded) tile: a background halo, then a crisp ink ring.\n ictx.save();\n ictx.globalAlpha = strength;\n for (const [width, color] of [[3, tokens.color.background], [1.5, tokens.color.text]] as const) {\n ictx.beginPath();\n roundedRect(ictx, p.x, p.y, p.width, p.height, tile.radius);\n ictx.lineWidth = width;\n ictx.strokeStyle = color;\n ictx.stroke();\n }\n ictx.restore();\n };\n\n return {\n region: { x: content.x, y: content.y, width: content.width, height: content.height },\n hitTest: (px, py) => {\n const tile = hitTile(px, py);\n if (!tile) return null;\n return {\n key: tile.key,\n anchorX: px,\n anchorY: py,\n content: {\n title: groupField ? `${tile.groupLabel ?? '(blank)'} \u25B8 ${tile.categoryLabel}` : tile.categoryLabel,\n rows: [\n { swatch: tile.fillCss, label: valueLabel, value: formatValue(tile.value, treemap.encoding.value.format) },\n ...(colorLabel\n ? [{\n label: colorLabel,\n value: tile.colorNoData ? 'no data' : formatValue(tile.colorValue, treemap.encoding.color?.format),\n muted: true,\n }]\n : []),\n { label: 'share', value: `${((tile.value / total) * 100).toFixed(1)}%`, muted: true },\n ],\n },\n paint: (ictx: CanvasRenderingContext2D, state: HoverPaintState) => paintTileHover(ictx, tile, state.focus),\n draw: (ictx: CanvasRenderingContext2D) => paintTileHover(ictx, tile, 1),\n };\n },\n pick: (px, py) => {\n const tile = hitTile(px, py);\n if (!tile) return null;\n return groupField\n ? { kind: 'point', fields: [categoryField, groupField], tuples: [[tile.category, tile.group]] }\n : { kind: 'point', fields: [categoryField], tuples: [[tile.category]] };\n },\n };\n}\n\nexport function drawTreemap(\n surface: Surface,\n spec: ChartSpec,\n tokens: ThemeTokens,\n size: Size,\n context?: RenderContext,\n): InteractionModel | void {\n surface.evidence?.begin('aggregate');\n surface.evidence?.data(0, 'aggregate');\n const treemap = spec as TreemapSpec;\n const content = drawTitleBlock(surface, tokens, size, treemap.title);\n const leaves = buildLeaves(treemap);\n colorLeaves(leaves, treemap, tokens);\n const total = leaves.reduce((sum, leaf) => sum + leaf.value, 0);\n\n if (leaves.length === 0 || total <= 0 || content.width <= 0 || content.height <= 0) {\n // Tiles on screen shrink away; a later update grows its tiles in.\n const morph = morphInput<TreemapMorphFrame>(context);\n const leaving = morph ? morphKeyed(morph.from.tiles, [], morph.t, TREEMAP_MORPH).exits : [];\n stashMorphFrame<TreemapMorphFrame>(surface, 'treemap', { tiles: leaving, groups: [] });\n if (leaving.length) paintTreemapTiles(surface, tokens, leaving.map(fadedTile), knownBackdrop(surface, tokens), null);\n drawNoPositiveTreemap(surface, tokens, content);\n return;\n }\n\n // Morph frames paint on a transient view; its owner's ledger knows the backdrop.\n const backdrop = knownBackdrop(surface, tokens);\n const sketch = resolveSketch(treemap);\n const pen = sketch ? new RoughPen(surface.marks.ctx, sketch) : null;\n const { tiles, groupRects } = layoutTreemapTiles(treemap, tokens, content, leaves, pen !== null, context);\n // Update morph: tiles slide, resize and recolor from the ones on screen (new\n // ones grow out of their centers, removed ones shrink away), values count to\n // their new totals, and a changed highlight fades rather than snapping.\n const finalFrame: TreemapMorphFrame = {\n tiles: tiles.map((tile) => ({ key: tile.key, tile, alpha: 1 })),\n groups: groupRects.map((group) => ({ key: group.group.key, group, alpha: 1 })),\n };\n const morph = morphInput<TreemapMorphFrame>(context);\n const moved = morph && morphKeyed(morph.from.tiles, finalFrame.tiles, morph.t, TREEMAP_MORPH);\n const movedGroups = morph && morphKeyed(morph.from.groups, finalFrame.groups, morph.t, TREEMAP_GROUP_MORPH);\n stashMorphFrame<TreemapMorphFrame>(surface, 'treemap', moved && movedGroups\n ? { tiles: [...moved.exits, ...moved.items], groups: [...movedGroups.exits, ...movedGroups.items] }\n : finalFrame);\n\n const represented = paintTreemapTiles(surface, tokens, moved ? [...moved.exits, ...moved.items].map(fadedTile) : tiles, backdrop, pen);\n surface.evidence?.data(represented, 'aggregate');\n\n if (treemap.encoding.group) {\n const groups = movedGroups ? movedGroups.items.filter((item) => item.alpha >= 0.5).map((item) => item.group) : groupRects;\n drawGroupFrames(surface, tokens, treemap, groups, pen, sketch);\n }\n\n if (treemap.labels !== false) {\n const labelled = moved ? moved.items.filter((item) => item.alpha >= 0.5).map(fadedTile) : tiles;\n for (const tile of labelled) addTileLabels(surface, tokens, tile, backdrop, treemap.encoding.value.format);\n }\n\n return buildTreemapInteraction(treemap, tokens, content, tiles, total);\n}\n"],
5
+ "mappings": "AAAA,SAAS,mBAAmB,sBAAsB,YAAY,WAAW,YAAY,qBAAqB;AAC1G,SAAS,mBAAmB;AAC5B,SAAS,YAAY,aAAa,iBAAiB;AAEnD,SAAS,mBAAmB;AAC5B,SAAS,UAAU,UAAU,aAAa;AAC1C,SAAS,gBAAgB;AACzB,SAAS,qBAAqB;AAC9B,SAAS,gBAAgB,sBAAsB;AAC/C,SAAS,eAAe,gBAAgB,OAAO,eAAe,uBAAuB;AAKrF,SAAS,YAAY,mBAAmB,uBAAuB;AAG/D,SAAS,+BAA+B;AACxC,SAAS,MAAM,UAAU,WAAW,SAAS,YAAY,YAAY,QAAQ,iBAAiB,gBAA+C;AA+C7I,MAAM,kBAAkB;AACxB,MAAM,mBAAmB;AAEzB,MAAM,cAAc;AACpB,MAAM,cAAc;AAEpB,MAAM,qBAAqB;AAC3B,MAAM,oBAAoB;AAG1B,SAAS,cAAc,MAAY,QAAsB;AACvD,QAAM,QAAQ,KAAK,IAAI,SAAS,GAAG,KAAK,QAAQ,GAAG,KAAK,SAAS,CAAC;AAClE,SAAO,UAAU,MAAM,KAAK;AAC9B;AAEA,SAAS,WAAW,OAAqB;AACvC,SAAO,KAAK,IAAI,GAAG,KAAK,IAAI,aAAa,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,CAAC;AAC7E;AAEA,SAAS,UAAU,MAAkB;AACnC,SAAO;AAAA,IACL,GAAG,OAAO,SAAS,KAAK,CAAC,IAAI,KAAK,IAAI;AAAA,IACtC,GAAG,OAAO,SAAS,KAAK,CAAC,IAAI,KAAK,IAAI;AAAA,IACtC,OAAO,KAAK,IAAI,GAAG,OAAO,SAAS,KAAK,KAAK,IAAI,KAAK,QAAQ,CAAC;AAAA,IAC/D,QAAQ,KAAK,IAAI,GAAG,OAAO,SAAS,KAAK,MAAM,IAAI,KAAK,SAAS,CAAC;AAAA,EACpE;AACF;AAEA,SAAS,MAAM,KAAwB,MAAsB;AAC3D,MAAI,IAAI,WAAW,KAAK,QAAQ,EAAG,QAAO;AAC1C,MAAI,MAAM;AACV,MAAI,MAAM;AACV,MAAI,MAAM;AACV,aAAW,QAAQ,KAAK;AACtB,WAAO;AACP,UAAM,KAAK,IAAI,KAAK,IAAI;AACxB,UAAM,KAAK,IAAI,KAAK,IAAI;AAAA,EAC1B;AACA,MAAI,OAAO,KAAK,OAAO,EAAG,QAAO;AACjC,QAAM,QAAQ,OAAO;AACrB,SAAO,KAAK,IAAK,QAAQ,OAAQ,MAAM,MAAO,MAAM,OAAQ,QAAQ,IAAI;AAC1E;AAEA,SAAS,UACP,KACA,MACA,KACM;AACN,QAAM,OAAO,IAAI,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,MAAM,CAAC;AACnD,MAAI,QAAQ,KAAK,KAAK,SAAS,KAAK,KAAK,UAAU,EAAG,QAAO;AAE7D,MAAI,KAAK,UAAU,KAAK,OAAO;AAC7B,UAAM,IAAI,KAAK,IAAI,KAAK,QAAQ,OAAO,KAAK,KAAK;AACjD,QAAI,IAAI,KAAK;AACb,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,YAAMA,KAAI,MAAM,IAAI,SAAS,IAAI,KAAK,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,EAAE,OAAO;AACzE,UAAI,KAAK,EAAE,MAAM,IAAI,CAAC,EAAE,MAAM,MAAM,EAAE,GAAG,GAAG,KAAK,GAAG,OAAO,KAAK,IAAI,GAAGA,EAAC,GAAG,QAAQ,EAAE,EAAE,CAAC;AACxF,WAAKA;AAAA,IACP;AACA,WAAO,EAAE,GAAG,KAAK,GAAG,GAAG,KAAK,IAAI,GAAG,OAAO,KAAK,OAAO,QAAQ,KAAK,IAAI,GAAG,KAAK,SAAS,CAAC,EAAE;AAAA,EAC7F;AAEA,QAAM,IAAI,KAAK,IAAI,KAAK,OAAO,OAAO,KAAK,MAAM;AACjD,MAAI,IAAI,KAAK;AACb,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,UAAM,IAAI,MAAM,IAAI,SAAS,IAAI,KAAK,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,EAAE,OAAO;AAC1E,QAAI,KAAK,EAAE,MAAM,IAAI,CAAC,EAAE,MAAM,MAAM,EAAE,GAAG,KAAK,GAAG,GAAG,OAAO,GAAG,QAAQ,KAAK,IAAI,GAAG,CAAC,EAAE,EAAE,CAAC;AACxF,SAAK;AAAA,EACP;AACA,SAAO,EAAE,GAAG,KAAK,IAAI,GAAG,GAAG,KAAK,GAAG,OAAO,KAAK,IAAI,GAAG,KAAK,QAAQ,CAAC,GAAG,QAAQ,KAAK,OAAO;AAC7F;AAGO,SAAS,cAA4C,OAAqB,MAAoC;AACnH,QAAM,MAAM,UAAU,IAAI;AAC1B,QAAM,QAAQ,MAAM,OAAO,CAAC,KAAK,SAAU,OAAO,SAAS,KAAK,KAAK,KAAK,KAAK,QAAQ,IAAI,MAAM,KAAK,QAAQ,KAAM,CAAC;AACrH,QAAM,OAAO,IAAI,QAAQ,IAAI;AAC7B,MAAI,SAAS,KAAK,QAAQ,EAAG,QAAO,CAAC;AAErC,QAAM,UAAU,MACb,OAAO,CAAC,SAAS,OAAO,SAAS,KAAK,KAAK,KAAK,KAAK,QAAQ,CAAC,EAC9D,IAAI,CAAC,UAAU,EAAE,MAAM,MAAO,KAAK,QAAQ,QAAS,KAAK,EAAE;AAC9D,QAAM,MAA8B,CAAC;AACrC,MAAI,OAAO;AACX,MAAI,MAAmC,CAAC;AACxC,MAAI,WAAqB,CAAC;AAE1B,QAAM,QAAQ,MAAM;AAClB,QAAI,IAAI,WAAW,EAAG;AACtB,WAAO,UAAU,KAAK,MAAM,GAAG;AAC/B,UAAM,CAAC;AACP,eAAW,CAAC;AAAA,EACd;AAEA,aAAW,QAAQ,SAAS;AAC1B,UAAM,OAAO,KAAK,IAAI,KAAK,OAAO,KAAK,MAAM;AAC7C,QAAI,IAAI,WAAW,KAAK,MAAM,CAAC,GAAG,UAAU,KAAK,IAAI,GAAG,IAAI,KAAK,MAAM,UAAU,IAAI,GAAG;AACtF,UAAI,KAAK,IAAI;AACb,eAAS,KAAK,KAAK,IAAI;AAAA,IACzB,OAAO;AACL,YAAM;AACN,UAAI,KAAK,IAAI;AACb,eAAS,KAAK,KAAK,IAAI;AAAA,IACzB;AAAA,EACF;AACA,QAAM;AACN,SAAO;AACT;AAEA,SAAS,UAAU,MAAY,KAAmB;AAChD,SAAO;AAAA,IACL,GAAG,KAAK,IAAI;AAAA,IACZ,GAAG,KAAK,IAAI;AAAA,IACZ,OAAO,KAAK,IAAI,GAAG,KAAK,QAAQ,MAAM,CAAC;AAAA,IACvC,QAAQ,KAAK,IAAI,GAAG,KAAK,SAAS,MAAM,CAAC;AAAA,EAC3C;AACF;AAEA,SAAS,SAAS,OAAwB;AACxC,QAAM,MAAM,MAAM,KAAK;AACvB,SAAO,QAAQ,KAAK,YAAY;AAClC;AAOA,SAAS,aAAa,MAAoB;AACxC,QAAM,OAAO,QAAQ,KAAK,IAAI,QAAQ,KAAK,IAAI,QAAQ,KAAK,KAAK;AACjE,QAAM,IAAI,MAAM,MAAM,OAAO;AAC7B,QAAM,IAAI,CAAC,MAAc,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,KAAK,MAAM,IAAI,CAAC,CAAC,CAAC;AACrE,SAAO,UAAU,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,GAAG,EAAE,KAAK,CAAC,GAAG,GAAG,EAAE,KAAK,CAAC,GAAG,GAAG,EAAE,CAAC;AACrE;AAEA,SAAS,YAAY,SAA8B;AACjD,QAAM,eAAe,SAAS,QAAQ,SAAS,SAAS,KAAK;AAC7D,QAAM,YAAY,SAAS,QAAQ,SAAS,MAAM,KAAK;AACvD,QAAM,YAAY,QAAQ,SAAS,QAAQ,SAAS,QAAQ,SAAS,MAAM,KAAK,IAAI;AACpF,QAAM,YAAY,QAAQ,SAAS,QAAQ,SAAS,QAAQ,SAAS,MAAM,KAAK,IAAI;AACpF,QAAM,QAAkB,CAAC;AACzB,QAAM,SAAS,oBAAI,IAAkB;AAErC,aAAW,OAAO,QAAQ,QAAQ,CAAC,GAAG;AACpC,UAAM,cAAc,aAAa,GAAG;AACpC,UAAM,cAAc,MAAM,WAAW;AACrC,UAAM,WAAW,YAAY,UAAU,GAAG,IAAI;AAC9C,UAAM,WAAW,YAAY,MAAM,QAAQ,IAAI;AAC/C,UAAM,MAAM,aAAa,SAAY,cAAc,GAAG,QAAQ,KAAS,WAAW;AAClF,UAAM,QAAQ,SAAS,UAAU,GAAG,CAAC;AACrC,QAAI,CAAC,OAAO,SAAS,KAAK,KAAK,SAAS,EAAG;AAE3C,QAAI,OAAO,OAAO,IAAI,GAAG;AACzB,QAAI,CAAC,MAAM;AACT,YAAM,aAAa,YAAY,UAAU,GAAG,IAAI;AAChD,YAAM,cAAc,eAAe,SAAY,SAAY,SAAS,UAAU;AAC9E,aAAO;AAAA,QACL;AAAA,QACA,UAAU;AAAA,QACV,eAAe,SAAS,WAAW;AAAA,QACnC,OAAO;AAAA,QACP,OAAO;AAAA,QACP;AAAA,QACA,YAAY,aAAa,SAAY,SAAY,SAAS,QAAQ;AAAA,QAClE;AAAA,QACA,aAAa,OAAO,SAAS,WAAW,IAAI,cAAc;AAAA,QAC1D,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,EAAE;AAAA,QAC/B,SAAS;AAAA,QACT,YAAY;AAAA,MACd;AACA,aAAO,IAAI,KAAK,IAAI;AACpB,YAAM,KAAK,GAAG;AAAA,IAChB;AACA,SAAK,SAAS;AACd,SAAK;AAAA,EACP;AAEA,SAAO,MAAM,IAAI,CAAC,QAAQ,OAAO,IAAI,GAAG,CAAE;AAC5C;AAEA,SAAS,YAAY,QAAgB,SAAsB,QAA2B;AACpF,QAAM,WAAW,WAAW,OAAO,MAAM,OAAO,KAAK,WAAW,OAAO,MAAM,QAAQ,CAAC,KAAK,SAAS,KAAK,EAAE,GAAG,IAAI,GAAG,KAAK,GAAG,KAAK,GAAG,EAAE;AACvI,QAAM,QAAQ,QAAQ,SAAS;AAC/B,QAAM,UAAU,kBAAkB,EAAE,SAAS,OAAO,MAAM,QAAQ,CAAC;AACnE,QAAM,gBAAgB,QAAQ,OAAO,IAAI,CAAC,SAAS,KAAK,WAAW,EAAE,OAAO,CAAC,MAAmB,MAAM,MAAS,IAAI,CAAC;AACpH,QAAM,eAAe,QAAQ,UAAU,MAAM,SAAS,kBAAmB,cAAc,SAAS,KAAK,cAAc,WAAW,OAAO,OAAQ;AAC7I,QAAM,SAA2B,cAAc,SAC3C,CAAC,KAAK,IAAI,GAAG,aAAa,GAAG,KAAK,IAAI,GAAG,aAAa,CAAC,IACvD,CAAC,GAAG,CAAC;AACT,QAAM,aAAa,qBAAqB,EAAE,QAAQ,cAAc,WAAW,QAAQ,UAAU,MAAM,EAAE,CAAC;AAEtG,aAAW,QAAQ,QAAQ;AACzB,QAAI,OAAa;AACjB,QAAI,OAAO;AACT,UAAI,cAAc;AAChB,YAAI,KAAK,gBAAgB,QAAW;AAClC,iBAAO,WAAW,OAAO,MAAM,MAAM,KAAK;AAC1C,eAAK,cAAc;AAAA,QACrB,OAAO;AACL,iBAAO,WAAW,IAAI,KAAK,WAAW;AACtC,eAAK,OAAO,EAAE,QAAQ,QAAQ,UAAU,QAAQ,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE;AAAA,QACvF;AAAA,MACF,MACK,QAAO,QAAQ,IAAI,MAAM,KAAK,UAAU,CAAC;AAAA,IAChD,WAAW,QAAQ,SAAS,SAAS,KAAK,aAAa,QAAW;AAChE,aAAO,QAAQ,IAAI,KAAK,QAAQ;AAAA,IAClC,OAAO;AACL,aAAO,QAAQ,IAAI,KAAK,aAAa;AAAA,IACvC;AACA,SAAK,OAAO;AACZ,SAAK,UAAU,UAAU,IAAI;AAAA,EAC/B;AACF;AAEA,SAAS,YAAY,QAAsC;AACzD,QAAM,QAAkB,CAAC;AACzB,QAAM,SAAS,oBAAI,IAAuB;AAC1C,aAAW,QAAQ,QAAQ;AACzB,UAAM,MAAM,KAAK,YAAY;AAC7B,QAAI,QAAQ,OAAO,IAAI,GAAG;AAC1B,QAAI,CAAC,OAAO;AACV,cAAQ,EAAE,KAAK,OAAO,KAAK,cAAc,WAAW,OAAO,GAAG,QAAQ,CAAC,EAAE;AACzE,aAAO,IAAI,KAAK,KAAK;AACrB,YAAM,KAAK,GAAG;AAAA,IAChB;AACA,UAAM,SAAS,KAAK;AACpB,UAAM,OAAO,KAAK,IAAI;AAAA,EACxB;AACA,SAAO,MAAM,IAAI,CAAC,QAAQ,OAAO,IAAI,GAAG,CAAE;AAC5C;AAEA,SAAS,UAAU,SAAsB,MAAmB;AAC1D,QAAM,QAAe,EAAE,CAAC,QAAQ,SAAS,SAAS,KAAK,GAAG,KAAK,SAAS;AACxE,QAAM,aAAa,QAAQ,SAAS,OAAO;AAC3C,MAAI,WAAY,OAAM,UAAU,IAAI,KAAK;AACzC,SAAO;AACT;AAGA,SAAS,iBAAiB,MAAY,UAAuB,QAAqB,SAA2C;AAC3H,MAAI,CAAC,YAAY,cAAc,cAAc,KAAK,MAAM,GAAG,QAAQ,GAAG,QAAQ,KAAK,mBAAoB,QAAO;AAC9G,QAAM,QAAQ,gBAAgB,KAAK,SAAS,eAAe,UAAU,MAAM,GAAG,UAAU,kBAAkB;AAC1G,SAAO,EAAE,OAAO,SAAS,OAAO,EAAE;AACpC;AAEA,SAAS,cACP,KACA,KACA,MACA,QACA,UACM;AACN,QAAM,OAAO,KAAK;AAClB,MAAI,KAAK,SAAS,KAAK,KAAK,UAAU,EAAG;AACzC,MAAI,KAAK;AAKP,QAAI,KAAK,KAAK,GAAG,KAAK,GAAG,KAAK,OAAO,KAAK,QAAQ,EAAE,MAAM,KAAK,SAAS,WAAW,QAAQ,CAAC;AAC5F,QAAI,KAAK,KAAK,GAAG,KAAK,GAAG,KAAK,OAAO,KAAK,QAAQ;AAAA,MAChD,MAAM,aAAa,KAAK,IAAI;AAAA,MAC5B,WAAW;AAAA,MACX,WAAW;AAAA,MACX,YAAY;AAAA,MACZ;AAAA,IACF,CAAC;AACD;AAAA,EACF;AACA,MAAI,YAAY,KAAK;AACrB,MAAI,UAAU;AACd,cAAY,KAAK,KAAK,GAAG,KAAK,GAAG,KAAK,OAAO,KAAK,QAAQ,KAAK,MAAM;AACrE,MAAI,KAAK;AACT,MAAI,YAAY,KAAK,QAAQ,KAAK,KAAK,SAAS,GAAG;AACjD,QAAI,cAAc,SAAS;AAC3B,QAAI,YAAY,SAAS;AACzB,QAAI,UAAU;AACd,gBAAY,KAAK,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,QAAQ,GAAG,KAAK,SAAS,GAAG,KAAK,IAAI,GAAG,KAAK,SAAS,GAAG,CAAC;AAC5G,QAAI,OAAO;AAAA,EACb;AACF;AAMA,SAAS,cACP,SACA,QACA,MACA,UACA,aACM;AACN,QAAM,OAAO,KAAK;AAClB,QAAM,UAAU,KAAK,QAAQ,mBAAmB,KAAK,SAAS;AAC9D,QAAM,OAAO,UAAU,IAAI;AAC3B,QAAM,OAAO,UAAU,IAAI;AAC3B,QAAM,YAAY,OAAO,KAAK,KAAK;AACnC,QAAM,QAAQ,YAAY;AAC1B,MAAI,KAAK,SAAS,OAAO,IAAI,KAAK,KAAK,SAAS,QAAQ,OAAO,GAAG;AAChE,YAAQ,QAAQ,KAAK,EAAE,MAAM,cAAc,QAAQ,SAAS,UAAU,MAAM,SAAS,KAAK,eAAe,MAAM,UAAU,CAAC;AAC1H;AAAA,EACF;AACA,QAAM,YAAY,OAAO,KAAK,KAAK;AACnC,QAAM,YAAY,WAAW,WAAW,OAAO,KAAK,QAAQ,OAAO,KAAK,OAAO,IAAI;AACnF,QAAM,YAAY,WAAW,WAAW,OAAO,KAAK,MAAM;AAC1D,QAAM,eAAe,KAAK,QAAQ,OAAO;AACzC,QAAM,YAAY,UAAU,KAAK,eAAe,cAAc,SAAS;AACvE,MAAI,cAAc,MAAM,cAAc,YAAQ,WAAW,UAAU,SAAS,KAAK,IAAI,GAAG,KAAK,cAAc,MAAM,IAAI,KAAM;AACzH,YAAQ,QAAQ,KAAK,EAAE,MAAM,cAAc,QAAQ,cAAc,UAAU,MAAM,SAAS,KAAK,eAAe,MAAM,UAAU,CAAC;AAC/H;AAAA,EACF;AAGA,QAAM,QAAQ,aAAa,KAAK,QAAQ,KAAK,KAAK,KAAK,IAAI,KACvD,cAAc,KAAK,MAAM,KAAK,OAAO,QAAQ,IAC7C,EAAE,GAAG,KAAK,MAAM,GAAG,EAAE;AACzB,QAAM,MAAM,MAAM,OAAO,QAAQ,iBAAiB;AAClD,QAAM,aAAa,UAAU,KAAK;AAClC,QAAM,UAAU,WAAW,GAAG;AAC9B,QAAM,eAAe,WAAW,cAAc,cAAc,SAAS,MAAM,KAAK,GAAG,KAAK,KAAK,oBAAoB,OAAO,OAAO;AAE/H,iBAAe,SAAS,QAAQ;AAAA,IAC9B,MAAM,KAAK,IAAI;AAAA,IACf,KAAK,KAAK,IAAI;AAAA,IACd,MAAM,KAAK;AAAA,IACX,UAAU;AAAA,IACV,OAAO;AAAA,IACP,MAAM;AAAA,IACN,QAAQ,OAAO,KAAK,OAAO;AAAA,IAC3B;AAAA,EACF,CAAC;AAED,QAAM,YAAY,YAAY,KAAK,OAAO,WAAW;AAGrD,MACE,KAAK,UAAU,OAAO,IAAI,QAAQ,IAAI,YAAY,OAClD,YAAY,WAAW,SAAS,EAAE,SAAS,cAC3C;AACA,mBAAe,SAAS,QAAQ;AAAA,MAC9B,MAAM,KAAK,IAAI;AAAA,MACf,KAAK,KAAK,IAAI,OAAO,QAAQ;AAAA,MAC7B,MAAM;AAAA,MACN,OAAO;AAAA,MACP,MAAM;AAAA,MACN,SAAS,iBAAiB,IAAI,SAAY;AAAA,MAC1C;AAAA,IACF,CAAC;AAAA,EACH,OAAO;AACL,YAAQ,QAAQ,KAAK,EAAE,MAAM,cAAc,QAAQ,SAAS,UAAU,OAAO,SAAS,WAAW,MAAM,UAAU,CAAC;AAAA,EACpH;AACF;AA8BA,SAAS,WAAW,MAAY,GAAiB;AAC/C,SAAO;AAAA,IACL,GAAG,KAAK,IAAK,KAAK,SAAS,IAAI,KAAM;AAAA,IACrC,GAAG,KAAK,IAAK,KAAK,UAAU,IAAI,KAAM;AAAA,IACtC,OAAO,KAAK,QAAQ;AAAA,IACpB,QAAQ,KAAK,SAAS;AAAA,EACxB;AACF;AAGA,SAAS,UAAU,MAAwB,GAAW,OAAiC;AACrF,QAAM,EAAE,KAAK,IAAI;AACjB,SAAO,EAAE,GAAG,MAAM,MAAM,EAAE,GAAG,MAAM,MAAM,WAAW,KAAK,MAAM,CAAC,GAAG,OAAO,WAAW,KAAK,OAAO,CAAC,GAAG,QAAQ,KAAK,SAAS,EAAE,GAAG,MAAM;AACxI;AAEA,MAAM,gBAA6C;AAAA,EACjD,MAAM,CAAC,GAAG,GAAG,MAAM;AACjB,UAAM,EAAE,MAAM,SAAS,KAAK,IAAI,QAAQ,EAAE,MAAM,EAAE,KAAK,SAAS,MAAM,EAAE,KAAK,KAAK,GAAG,EAAE,MAAM,EAAE,KAAK,SAAS,MAAM,EAAE,KAAK,KAAK,GAAG,CAAC;AACnI,WAAO;AAAA,MACL,KAAK,EAAE;AAAA,MACP,MAAM;AAAA,QACJ,GAAG,EAAE;AAAA,QACL,MAAM,SAAS,EAAE,KAAK,MAAM,EAAE,KAAK,MAAM,CAAC;AAAA,QAC1C,OAAO,SAAS,EAAE,KAAK,OAAO,EAAE,KAAK,OAAO,CAAC;AAAA,QAC7C,QAAQ,KAAK,EAAE,KAAK,QAAQ,EAAE,KAAK,QAAQ,CAAC;AAAA,QAC5C,MAAM,YAAY,EAAE,KAAK,UAAU,EAAE,KAAK,OAAQ,WAAW,OAAO,KAAK,EAAE,KAAK;AAAA,QAChF;AAAA,QACA;AAAA,QACA,OAAO,UAAU,EAAE,KAAK,OAAO,EAAE,KAAK,OAAO,CAAC;AAAA,QAC9C,OAAO,KAAK,EAAE,KAAK,OAAO,EAAE,KAAK,OAAO,CAAC;AAAA,MAC3C;AAAA,MACA,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC;AAAA,IACjC;AAAA,EACF;AAAA,EACA,OAAO,CAAC,GAAG,MAAM,UAAU,GAAG,GAAG,CAAC;AAAA,EAClC,MAAM,CAAC,GAAG,MAAM,UAAU,GAAG,IAAI,GAAG,EAAE,SAAS,IAAI,EAAE;AACvD;AAEA,MAAM,sBAAoD;AAAA,EACxD,MAAM,CAAC,GAAG,GAAG,OAAO;AAAA,IAClB,KAAK,EAAE;AAAA,IACP,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,MAAM,SAAS,EAAE,MAAM,MAAM,EAAE,MAAM,MAAM,CAAC,GAAG,SAAS,SAAS,EAAE,MAAM,SAAS,EAAE,MAAM,SAAS,CAAC,EAAE;AAAA,IACrI,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC;AAAA,EACjC;AAAA,EACA,OAAO,CAAC,GAAG,OAAO,EAAE,GAAG,GAAG,OAAO,EAAE;AAAA,EACnC,MAAM,CAAC,GAAG,OAAO,EAAE,GAAG,GAAG,OAAO,EAAE,SAAS,IAAI,GAAG;AACpD;AAGA,MAAM,YAAY,CAAC,UAAkC,EAAE,GAAG,KAAK,MAAM,OAAO,KAAK,KAAK,QAAQ,KAAK,MAAM;AAEzG,SAAS,sBACP,SACA,QACA,SACM;AACN,0BAAwB,SAAS,QAAQ,SAAS,oBAAoB;AACxE;AAEA,SAAS,mBACP,SACA,QACA,SACA,QACA,UACA,SAC4C;AAC5C,QAAM,WAAW,SAAS,YAAY;AACtC,QAAM,QAAgB,CAAC;AACvB,QAAM,aAA0B,CAAC;AAGjC,QAAM,UAAU,CAAC,MAAY,SAAe;AAC1C,UAAM,QAAQ,WAAY,SAAS,MAAM,UAAU,SAAS,IAAI,CAAC,IAAI,IAAI,SAAS,MAAO;AACzF,UAAM,QAAQ,WAAW,OAAO,cAAc,MAAM,WAAW;AAC/D,UAAM,KAAK,EAAE,GAAG,MAAM,MAAM,MAAM,OAAO,QAAQ,WAAW,IAAI,WAAW,KAAK,GAAG,MAAM,CAAC;AAAA,EAC5F;AAEA,MAAI,QAAQ,SAAS,OAAO;AAC1B,UAAM,SAAS,YAAY,MAAM;AACjC,eAAW,aAAa,cAAc,QAAQ,OAAO,GAAG;AACtD,YAAM,QAAQ,UAAU;AACxB,YAAM,UAAU,MAAM,SAAS,MAAM,MAAM,UAAU,KAAK,OAAO,KAAK,KAAK,OAAO,IAAI;AACtF,iBAAW,KAAK,EAAE,OAAO,UAAU,MAAM,MAAM,OAAO,QAAQ,CAAC;AAC/D,YAAM,QAAQ;AAAA,QACZ,EAAE,GAAG,MAAM,GAAG,GAAG,MAAM,IAAI,SAAS,OAAO,MAAM,OAAO,QAAQ,KAAK,IAAI,GAAG,MAAM,SAAS,OAAO,EAAE;AAAA,QACpG;AAAA,MACF;AACA,iBAAW,YAAY,cAAc,UAAU,KAAK,QAAQ,KAAK,EAAG,SAAQ,SAAS,MAAM,SAAS,IAAI;AAAA,IAC1G;AAAA,EACF,OAAO;AACL,eAAW,YAAY,cAAc,QAAQ,OAAO,EAAG,SAAQ,SAAS,MAAM,SAAS,IAAI;AAAA,EAC7F;AACA,SAAO,EAAE,OAAO,WAAW;AAC7B;AAGA,SAAS,kBACP,SACA,QACA,OACA,UACA,KACQ;AACR,QAAM,MAAM,QAAQ,MAAM;AAC1B,QAAM,SAAS,OAAO,MAAM;AAC5B,MAAI,cAAc;AAClB,MAAI,KAAK;AACT,aAAW,QAAQ,OAAO;AACxB,UAAM,EAAE,OAAO,MAAM,IAAI;AACzB,UAAM,WAAW,MAAM,EAAE,OAAO,QAAQ,SAAS,MAAM,OAAO,EAAE,IAAI,iBAAiB,MAAM,UAAU,QAAQ,KAAK;AAClH,QAAI,UAAU,EAAG,KAAI,cAAc;AACnC,kBAAc,KAAK,KAAK,MAAM,QAAQ,QAAQ;AAC9C,QAAI,MAAM,QAAQ,KAAK,MAAM,SAAS,GAAG;AACvC,cAAQ,UAAU,KAAK,QAAQ,MAAM,GAAG,MAAM,GAAG,MAAM,OAAO,MAAM,QAAQ,KAAK,SAAS,OAAO,QAAW,GAAG,QAAW,QAAQ,GAAG,QAAQ;AAC7I,qBAAe,KAAK;AAAA,IACtB;AACA,QAAI,UAAU,EAAG,KAAI,cAAc;AAAA,EACrC;AACA,MAAI,QAAQ;AACZ,SAAO;AACT;AAEA,SAAS,gBACP,SACA,QACA,SACA,QACA,KACA,QACM;AACN,QAAM,SAAS,OAAO,MAAM;AAC5B,aAAW,EAAE,OAAO,MAAM,QAAQ,KAAK,QAAQ;AAE7C,QAAI,IAAK,KAAI,KAAK,KAAK,GAAG,KAAK,GAAG,KAAK,OAAO,KAAK,QAAQ,EAAE,QAAQ,WAAW,OAAQ,YAAY,IAAI,CAAC;AACzG,QAAI,UAAU,GAAG;AACf,YAAM,QAAQ,MAAM,IAAI,IAAI,cAAc;AAC1C,YAAM,UAAU,KAAK,IAAI,GAAG,KAAK,QAAQ,QAAQ,CAAC;AAClD,qBAAe,SAAS,QAAQ;AAAA,QAC9B,MAAM,KAAK,IAAI;AAAA,QACf,KAAK,KAAK,IAAI;AAAA,QACd,OAAO;AAAA,QACP,MAAM,MAAM;AAAA,QACZ,UAAU;AAAA,QACV,OAAO,OAAO,MAAM;AAAA,QACpB,MAAM,OAAO,KAAK,KAAK;AAAA,QACvB,QAAQ,OAAO,KAAK,OAAO;AAAA,QAC3B,SAAS;AAAA,MACX,CAAC;AAAA,IACH,WAAW,QAAQ,WAAW,OAAO;AACnC,cAAQ,QAAQ,KAAK,EAAE,MAAM,cAAc,QAAQ,SAAS,UAAU,MAAM,SAAS,MAAM,OAAO,MAAM,kBAAkB,CAAC;AAAA,IAC7H;AAAA,EACF;AACF;AAEA,SAAS,wBACP,SACA,QACA,SACA,OACA,OACkB;AAClB,QAAM,aAAa,QAAQ,SAAS,MAAM,SAAS,QAAQ,SAAS,MAAM;AAC1E,QAAM,aAAa,QAAQ,SAAS,OAAO,SAAS,QAAQ,SAAS,OAAO;AAC5E,QAAM,gBAAgB,QAAQ,SAAS,SAAS;AAChD,QAAM,aAAa,QAAQ,SAAS,OAAO;AAC3C,QAAM,UAAU,CAAC,IAAY,OAA4B;AACvD,aAAS,IAAI,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK;AAC1C,YAAM,OAAO,MAAM,CAAC;AACpB,UAAI,MAAM,KAAK,KAAK,KAAK,MAAM,KAAK,KAAK,IAAI,KAAK,KAAK,SAAS,MAAM,KAAK,KAAK,KAAK,MAAM,KAAK,KAAK,IAAI,KAAK,KAAK,OAAQ,QAAO;AAAA,IACpI;AACA,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,kBAAkB,OAAO;AACzC,QAAM,OAAO,QAAQ,cAAc,OAAO,MAAM;AAChD,QAAM,iBAAiB,CAAC,MAAgC,MAAY,aAA2B;AAC7F,UAAM,IAAI,KAAK;AACf,QAAI,EAAE,SAAS,KAAK,EAAE,UAAU,EAAG;AACnC,QAAI,SAAS;AACX,sBAAgB,MAAM,SAAS,MAAM,aAAa,UAAU,CAAC,MAAM,YAAY,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,KAAK,MAAM,CAAC;AAAA,IAC7H;AAEA,SAAK,KAAK;AACV,SAAK,cAAc;AACnB,eAAW,CAAC,OAAO,KAAK,KAAK,CAAC,CAAC,GAAG,OAAO,MAAM,UAAU,GAAG,CAAC,KAAK,OAAO,MAAM,IAAI,CAAC,GAAY;AAC9F,WAAK,UAAU;AACf,kBAAY,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,KAAK,MAAM;AAC1D,WAAK,YAAY;AACjB,WAAK,cAAc;AACnB,WAAK,OAAO;AAAA,IACd;AACA,SAAK,QAAQ;AAAA,EACf;AAEA,SAAO;AAAA,IACL,QAAQ,EAAE,GAAG,QAAQ,GAAG,GAAG,QAAQ,GAAG,OAAO,QAAQ,OAAO,QAAQ,QAAQ,OAAO;AAAA,IACnF,SAAS,CAAC,IAAI,OAAO;AACnB,YAAM,OAAO,QAAQ,IAAI,EAAE;AAC3B,UAAI,CAAC,KAAM,QAAO;AAClB,aAAO;AAAA,QACL,KAAK,KAAK;AAAA,QACV,SAAS;AAAA,QACT,SAAS;AAAA,QACT,SAAS;AAAA,UACP,OAAO,aAAa,GAAG,KAAK,cAAc,SAAS,WAAM,KAAK,aAAa,KAAK,KAAK;AAAA,UACrF,MAAM;AAAA,YACJ,EAAE,QAAQ,KAAK,SAAS,OAAO,YAAY,OAAO,YAAY,KAAK,OAAO,QAAQ,SAAS,MAAM,MAAM,EAAE;AAAA,YACzG,GAAI,aACA,CAAC;AAAA,cACC,OAAO;AAAA,cACP,OAAO,KAAK,cAAc,YAAY,YAAY,KAAK,YAAY,QAAQ,SAAS,OAAO,MAAM;AAAA,cACjG,OAAO;AAAA,YACT,CAAC,IACD,CAAC;AAAA,YACL,EAAE,OAAO,SAAS,OAAO,IAAK,KAAK,QAAQ,QAAS,KAAK,QAAQ,CAAC,CAAC,KAAK,OAAO,KAAK;AAAA,UACtF;AAAA,QACF;AAAA,QACA,OAAO,CAAC,MAAgC,UAA2B,eAAe,MAAM,MAAM,MAAM,KAAK;AAAA,QACzG,MAAM,CAAC,SAAmC,eAAe,MAAM,MAAM,CAAC;AAAA,MACxE;AAAA,IACF;AAAA,IACA,MAAM,CAAC,IAAI,OAAO;AAChB,YAAM,OAAO,QAAQ,IAAI,EAAE;AAC3B,UAAI,CAAC,KAAM,QAAO;AAClB,aAAO,aACH,EAAE,MAAM,SAAS,QAAQ,CAAC,eAAe,UAAU,GAAG,QAAQ,CAAC,CAAC,KAAK,UAAU,KAAK,KAAK,CAAC,EAAE,IAC5F,EAAE,MAAM,SAAS,QAAQ,CAAC,aAAa,GAAG,QAAQ,CAAC,CAAC,KAAK,QAAQ,CAAC,EAAE;AAAA,IAC1E;AAAA,EACF;AACF;AAEO,SAAS,YACd,SACA,MACA,QACA,MACA,SACyB;AACzB,UAAQ,UAAU,MAAM,WAAW;AACnC,UAAQ,UAAU,KAAK,GAAG,WAAW;AACrC,QAAM,UAAU;AAChB,QAAM,UAAU,eAAe,SAAS,QAAQ,MAAM,QAAQ,KAAK;AACnE,QAAM,SAAS,YAAY,OAAO;AAClC,cAAY,QAAQ,SAAS,MAAM;AACnC,QAAM,QAAQ,OAAO,OAAO,CAAC,KAAK,SAAS,MAAM,KAAK,OAAO,CAAC;AAE9D,MAAI,OAAO,WAAW,KAAK,SAAS,KAAK,QAAQ,SAAS,KAAK,QAAQ,UAAU,GAAG;AAElF,UAAMC,SAAQ,WAA8B,OAAO;AACnD,UAAM,UAAUA,SAAQ,WAAWA,OAAM,KAAK,OAAO,CAAC,GAAGA,OAAM,GAAG,aAAa,EAAE,QAAQ,CAAC;AAC1F,oBAAmC,SAAS,WAAW,EAAE,OAAO,SAAS,QAAQ,CAAC,EAAE,CAAC;AACrF,QAAI,QAAQ,OAAQ,mBAAkB,SAAS,QAAQ,QAAQ,IAAI,SAAS,GAAG,cAAc,SAAS,MAAM,GAAG,IAAI;AACnH,0BAAsB,SAAS,QAAQ,OAAO;AAC9C;AAAA,EACF;AAGA,QAAM,WAAW,cAAc,SAAS,MAAM;AAC9C,QAAM,SAAS,cAAc,OAAO;AACpC,QAAM,MAAM,SAAS,IAAI,SAAS,QAAQ,MAAM,KAAK,MAAM,IAAI;AAC/D,QAAM,EAAE,OAAO,WAAW,IAAI,mBAAmB,SAAS,QAAQ,SAAS,QAAQ,QAAQ,MAAM,OAAO;AAIxG,QAAM,aAAgC;AAAA,IACpC,OAAO,MAAM,IAAI,CAAC,UAAU,EAAE,KAAK,KAAK,KAAK,MAAM,OAAO,EAAE,EAAE;AAAA,IAC9D,QAAQ,WAAW,IAAI,CAAC,WAAW,EAAE,KAAK,MAAM,MAAM,KAAK,OAAO,OAAO,EAAE,EAAE;AAAA,EAC/E;AACA,QAAM,QAAQ,WAA8B,OAAO;AACnD,QAAM,QAAQ,SAAS,WAAW,MAAM,KAAK,OAAO,WAAW,OAAO,MAAM,GAAG,aAAa;AAC5F,QAAM,cAAc,SAAS,WAAW,MAAM,KAAK,QAAQ,WAAW,QAAQ,MAAM,GAAG,mBAAmB;AAC1G,kBAAmC,SAAS,WAAW,SAAS,cAC5D,EAAE,OAAO,CAAC,GAAG,MAAM,OAAO,GAAG,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,YAAY,OAAO,GAAG,YAAY,KAAK,EAAE,IAChG,UAAU;AAEd,QAAM,cAAc,kBAAkB,SAAS,QAAQ,QAAQ,CAAC,GAAG,MAAM,OAAO,GAAG,MAAM,KAAK,EAAE,IAAI,SAAS,IAAI,OAAO,UAAU,GAAG;AACrI,UAAQ,UAAU,KAAK,aAAa,WAAW;AAE/C,MAAI,QAAQ,SAAS,OAAO;AAC1B,UAAM,SAAS,cAAc,YAAY,MAAM,OAAO,CAAC,SAAS,KAAK,SAAS,GAAG,EAAE,IAAI,CAAC,SAAS,KAAK,KAAK,IAAI;AAC/G,oBAAgB,SAAS,QAAQ,SAAS,QAAQ,KAAK,MAAM;AAAA,EAC/D;AAEA,MAAI,QAAQ,WAAW,OAAO;AAC5B,UAAM,WAAW,QAAQ,MAAM,MAAM,OAAO,CAAC,SAAS,KAAK,SAAS,GAAG,EAAE,IAAI,SAAS,IAAI;AAC1F,eAAW,QAAQ,SAAU,eAAc,SAAS,QAAQ,MAAM,UAAU,QAAQ,SAAS,MAAM,MAAM;AAAA,EAC3G;AAEA,SAAO,wBAAwB,SAAS,QAAQ,SAAS,OAAO,KAAK;AACvE;",
6
+ "names": ["w", "morph"]
7
7
  }
@@ -10,16 +10,24 @@
10
10
  */
11
11
  import type { Surface } from '../render/surface.js';
12
12
  import type { CartesianModel } from '../runtime/cartesian.js';
13
+ /** Stash name of the R² badges (an entrance fades them in place; see chromeMotion's fadeChrome). */
14
+ export declare const TREND_LABELS = "trendlines:labels";
13
15
  /**
14
16
  * Stroke the fitted line(s) on the marks canvas, clipped to the plot. Call after
15
17
  * the chart's data marks so the trendline sits on top. No-op unless the spec
16
- * opts in via `trendline` and the x-axis is continuous/temporal.
18
+ * opts in via `trendline` and the x-axis is continuous/temporal. While a morph
19
+ * is drawing, each group's line glides to its new fit (lines that appear or go
20
+ * away fade); during an entrance the lines draw on once the marks are mostly in.
17
21
  */
18
22
  export declare function drawTrendlines(surface: Surface, model: CartesianModel): void;
19
23
  /**
20
- * Append `R²` labels for each fitted line to the HTML overlay (or paint them to
21
- * canvas when headless). Call after `drawAnnotationLabels`. No-op unless
22
- * `trendline.label` is set.
24
+ * Add `R²` badges for each fitted line to the HTML overlay (or paint them to
25
+ * canvas when headless). Each sits on a knockout pill in the plot background,
26
+ * in its series hue darkened/lightened just enough to read, and badges are
27
+ * de-collided and recorded in the label ledger. Call after
28
+ * `drawAnnotationLabels`. No-op unless `trendline.label` is set. Badges glide
29
+ * with their lines during a morph; the returned elements (none when headless)
30
+ * let an entrance hold them back until the lines have drawn on.
23
31
  */
24
- export declare function drawTrendlineLabels(surface: Surface, model: CartesianModel): void;
32
+ export declare function drawTrendlineLabels(surface: Surface, model: CartesianModel): HTMLElement[];
25
33
  //# sourceMappingURL=trendline.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"trendline.d.ts","sourceRoot":"","sources":["../../src/charts/trendline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AA+E3D;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,GAAG,IAAI,CAyC5E;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,GAAG,IAAI,CAiDjF"}
1
+ {"version":3,"file":"trendline.d.ts","sourceRoot":"","sources":["../../src/charts/trendline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AA4B3D,oGAAoG;AACpG,eAAO,MAAM,YAAY,sBAAsB,CAAC;AA0IhD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,GAAG,IAAI,CA+C5E;AA8GD;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,GAAG,WAAW,EAAE,CAgE1F"}