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
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/axes/morph.ts"],
4
+ "sourcesContent": ["/**\n * Axis motion \u2014 gridlines, tick marks and tick labels glide with the scale.\n *\n * Each axis draw stashes what it drew (see render/chromeMotion). When a morph\n * redraws a chart on a tween view, the axis modules blend the outgoing frame\n * with the incoming one: a tick present in both glides between its positions,\n * a new tick slides in from where its value sat on the old scale while fading\n * in, and a tick that is gone slides toward where its value lands on the new\n * scale while fading out \u2014 so the axis moves exactly with the marks, which\n * interpolate between the same two pixel mappings.\n */\n\nimport type { Rect } from '../types';\nimport type { CartesianModel, Tick } from '../runtime/cartesian';\nimport type { Surface } from '../render/surface';\nimport { chromeMorph, stashChrome, visibleItems } from '../render/chromeMotion';\nimport { crisp } from '../util/math';\n\n/** Chrome stash names shared by the axis, annotation and trendline modules. */\nexport const AXIS_SCALES = 'axes:scales';\nexport const AXIS_UNDERLAY = 'axes:underlay';\nexport const AXIS_LABELS = 'axes:labels';\n\nexport type TickValue = Tick['value'];\n\n/** Value \u2192 pixel mappings of one drawn frame. */\nexport interface ScaleMaps {\n xAt(value: unknown): number | undefined;\n yAt(value: unknown): number | undefined;\n}\n\n/** A frame's plot rect and value \u2192 pixel mappings, for placing the other frame's ticks. */\nexport interface ScaleFrame extends ScaleMaps {\n plot: Rect;\n /**\n * The static frames this one mixes, with weights summing to 1. However many\n * morphs interrupt one another, a blend stays this flat list \u2014 never a chain\n * of nested closures \u2014 and frames whose weight has decayed away are dropped.\n */\n parts: ReadonlyArray<{ weight: number; maps: ScaleMaps }>;\n}\n\n/** Outgoing frames below this weight are dropped from a blend. */\nconst MIN_PART_WEIGHT = 1e-3;\n/** Most static frames a blend mixes (rapid-fire updates keep the heaviest). */\nconst MAX_PARTS = 12;\n\n/** One drawn axis element, keyed by its tick value. */\nexport interface AxisItem {\n key: string;\n value: TickValue;\n /** Pixel position along its axis. */\n pos: number;\n alpha: number;\n}\n\n/** The axis lines one frame drew (see drawAxesUnderlay). */\nexport interface UnderlayFrame {\n orientation: 'vertical' | 'horizontal';\n plot: Rect;\n /** Gridlines at value (y) ticks: horizontal lines, or vertical ones for horizontal bars. */\n valueGrid: AxisItem[];\n /** Vertical gridlines at the labelled x ticks (vertical orientation). */\n xGrid: AxisItem[];\n /** Tick marks on the category/x axis. */\n xMarks: AxisItem[];\n /** Whether the x baseline (or the horizontal-bar spine) is drawn. */\n axisLine: boolean;\n}\n\nexport const lerp = (a: number, b: number, t: number): number => a + (b - a) * t;\n\nexport function lerpRect(a: Rect, b: Rect, t: number): Rect {\n return { x: lerp(a.x, b.x, t), y: lerp(a.y, b.y, t), width: lerp(a.width, b.width, t), height: lerp(a.height, b.height, t) };\n}\n\n/** A stable key for a tick value (numbers and strings never collide). */\nexport function tickKey(value: TickValue): string {\n return `${typeof value === 'number' ? 'n' : 's'}:${value}`;\n}\n\nfunction finiteOr(value: number | undefined, fallback: number): number {\n return value !== undefined && Number.isFinite(value) ? value : fallback;\n}\n\n/**\n * Blend two frames' items: shared ones (same key, and `same` agrees) glide and\n * keep the incoming item; new ones start where their value sat on the outgoing\n * scale and fade in; departing ones head to where their value lands on the\n * incoming scale and fade out. Departing items come first so they paint beneath.\n */\nexport function blendItems<T extends AxisItem>(\n from: readonly T[],\n to: readonly T[],\n t: number,\n fromAt: (value: TickValue) => number | undefined,\n toAt: (value: TickValue) => number | undefined,\n same: (a: T, b: T) => boolean = () => true,\n): Array<{ item: T; pos: number; alpha: number }> {\n const byKey = new Map<string, T>();\n for (const a of from) byKey.set(a.key, a);\n const kept = new Set<T>();\n const entering: Array<{ item: T; pos: number; alpha: number }> = [];\n for (const b of to) {\n const a = byKey.get(b.key);\n if (a && !kept.has(a) && same(a, b)) {\n kept.add(a);\n entering.push({ item: b, pos: lerp(a.pos, b.pos, t), alpha: lerp(a.alpha, b.alpha, t) });\n } else {\n entering.push({ item: b, pos: lerp(finiteOr(fromAt(b.value), b.pos), b.pos, t), alpha: b.alpha * t });\n }\n }\n const leaving = from\n .filter((a) => !kept.has(a))\n .map((a) => ({ item: a, pos: lerp(a.pos, finiteOr(toAt(a.value), a.pos), t), alpha: a.alpha * (1 - t) }));\n return [...leaving, ...entering];\n}\n\n/**\n * A weighted mix of `parts`' mappings for one axis: every part that can place a\n * value contributes by weight; a value only some parts can place lands where\n * they put it, and one only weightless parts can place falls back to the newest.\n */\nfunction mixedAt(parts: ScaleFrame['parts'], axis: 'xAt' | 'yAt'): (value: unknown) => number | undefined {\n if (parts.length === 1) return parts[0].maps[axis];\n return (value) => {\n let sum = 0;\n let total = 0;\n for (const { weight, maps } of parts) {\n if (!(weight > 0)) continue;\n const p = maps[axis](value);\n if (p === undefined || !Number.isFinite(p)) continue;\n sum += weight * p;\n total += weight;\n }\n if (total > 0) return sum / total;\n for (let i = parts.length - 1; i >= 0; i--) {\n const p = parts[i].maps[axis](value);\n if (p !== undefined && Number.isFinite(p)) return p;\n }\n return undefined;\n };\n}\n\n/** The current frame's mappings, as stashed for the next morph. */\nexport function scaleFrameOf(model: CartesianModel): ScaleFrame {\n const maps: ScaleMaps = { xAt: (v) => model.x.pixel(v), yAt: (v) => model.y.pixel(v) };\n return { plot: model.plot, xAt: maps.xAt, yAt: maps.yAt, parts: [{ weight: 1, maps }] };\n}\n\n/** `from` mixed `t` of the way into `model`'s mappings. */\nexport function blendScales(from: ScaleFrame, model: CartesianModel, t: number): ScaleFrame {\n const to = scaleFrameOf(model);\n const w = Math.min(1, Math.max(0, t));\n const incoming = { weight: w, maps: to.parts[0].maps };\n let outgoing = from.parts\n .map((part) => ({ weight: part.weight * (1 - w), maps: part.maps }))\n .filter((part) => part.weight >= MIN_PART_WEIGHT);\n if (outgoing.length > MAX_PARTS - 1) outgoing = outgoing.sort((a, b) => b.weight - a.weight).slice(0, MAX_PARTS - 1);\n const total = outgoing.reduce((sum, part) => sum + part.weight, incoming.weight);\n const parts = total > 0\n ? [...outgoing, incoming].map((part) => ({ weight: part.weight / total, maps: part.maps }))\n : [{ weight: 1, maps: incoming.maps }];\n return { plot: lerpRect(from.plot, to.plot, t), xAt: mixedAt(parts, 'xAt'), yAt: mixedAt(parts, 'yAt'), parts };\n}\n\n/**\n * Stash this frame's mappings and return the outgoing ones while morphing (null\n * otherwise). Call once per axis draw, before anything uses {@link morphedModel}.\n * `name` lets a secondary axis (combo's right axis) track its own scale.\n */\nexport function trackScales(surface: Surface, model: CartesianModel, name = AXIS_SCALES): { from: ScaleFrame; t: number } | null {\n const morph = chromeMorph<ScaleFrame>(surface, name);\n stashChrome(surface, name, morph ? blendScales(morph.from, model, morph.t) : scaleFrameOf(model));\n return morph;\n}\n\n/**\n * `model` with its plot and value \u2192 pixel mappings mixed from the outgoing\n * frame's while a morph is drawing (so annotations and trendlines move with the\n * axes); `model` itself otherwise.\n */\nexport function morphedModel(surface: Surface, model: CartesianModel): CartesianModel {\n const morph = chromeMorph<ScaleFrame>(surface, AXIS_SCALES);\n if (!morph) return model;\n const blended = blendScales(morph.from, model, morph.t);\n return {\n ...model,\n plot: blended.plot,\n x: { ...model.x, pixel: blended.xAt },\n y: {\n ...model.y,\n baseline: lerp(finiteOr(morph.from.yAt(0), model.y.baseline), model.y.baseline, morph.t),\n pixel: (v) => blended.yAt(v) ?? NaN,\n },\n };\n}\n\n/** The axis lines a static draw of `model` produces (`xPlaced` = the labelled x ticks). */\nexport function underlayFrameOf(\n model: CartesianModel,\n xPlaced: ReadonlyArray<{ value: TickValue; pos: number }>,\n grid: { x: boolean; y: boolean },\n): UnderlayFrame {\n const item = (value: TickValue, pos: number): AxisItem => ({ key: tickKey(value), value, pos, alpha: 1 });\n const axisLine = model.spec.axes?.x?.show !== false;\n if (model.orientation === 'horizontal') {\n return {\n orientation: 'horizontal',\n plot: model.plot,\n valueGrid: grid.y ? model.yTicks.map((tick) => item(tick.value, tick.pos)) : [],\n xGrid: [],\n xMarks: axisLine ? model.xTicks.map((tick) => item(tick.value, tick.pos)) : [],\n axisLine,\n };\n }\n return {\n orientation: 'vertical',\n plot: model.plot,\n valueGrid: grid.y ? model.yTicks.map((tick) => item(tick.value, tick.pos)) : [],\n xGrid: grid.x ? xPlaced.map((p) => item(p.value, p.pos)) : [],\n xMarks: axisLine ? xPlaced.map((p) => item(p.value, p.pos)) : [],\n axisLine,\n };\n}\n\n/** `to` blended from `from`: what a tween frame paints (and stashes). */\nexport function blendUnderlay(from: UnderlayFrame, to: UnderlayFrame, t: number, scales: ScaleFrame, model: CartesianModel): UnderlayFrame {\n const xAt = (v: TickValue) => model.x.pixel(v);\n const yAt = (v: TickValue) => model.y.pixel(v);\n const settle = (items: Array<{ item: AxisItem; pos: number; alpha: number }>): AxisItem[] =>\n visibleItems(items.map(({ item, pos, alpha }) => ({ ...item, pos, alpha })));\n // A frame whose orientation flipped has nothing to glide from: cross-fade instead.\n const flipped = from.orientation !== to.orientation;\n const fromOf = (items: AxisItem[]) => (flipped ? [] : items);\n return {\n orientation: to.orientation,\n plot: lerpRect(from.plot, to.plot, t),\n valueGrid: settle(blendItems(fromOf(from.valueGrid), to.valueGrid, t, scales.yAt, yAt)),\n xGrid: settle(blendItems(fromOf(from.xGrid), to.xGrid, t, scales.xAt, xAt)),\n xMarks: settle(blendItems(fromOf(from.xMarks), to.xMarks, t, scales.xAt, xAt)),\n axisLine: to.axisLine,\n };\n}\n\n/**\n * Paint a blended underlay: gridlines clipped to the (moving) plot, each at its\n * own opacity, then the axis line and tick marks.\n */\nexport function paintUnderlay(ctx: CanvasRenderingContext2D, frame: UnderlayFrame, colors: { grid: string; axis: string }, tickSize: number): void {\n const { plot } = frame;\n const x0 = plot.x;\n const x1 = plot.x + plot.width;\n const y0 = plot.y;\n const y1 = plot.y + plot.height;\n const stroke = (alpha: number, color: string, path: () => void) => {\n if (!(alpha > 0.002)) return;\n ctx.globalAlpha = Math.min(1, alpha);\n ctx.strokeStyle = color;\n ctx.beginPath();\n path();\n ctx.stroke();\n };\n const horizontal = frame.orientation === 'horizontal';\n ctx.save();\n ctx.lineWidth = 1;\n ctx.save();\n ctx.beginPath();\n ctx.rect(x0 - 0.5, y0 - 0.5, plot.width + 1, plot.height + 1);\n ctx.clip();\n for (const line of frame.valueGrid) {\n stroke(line.alpha, colors.grid, () => {\n if (horizontal) {\n ctx.moveTo(crisp(line.pos), y0);\n ctx.lineTo(crisp(line.pos), y1);\n } else {\n ctx.moveTo(x0, crisp(line.pos));\n ctx.lineTo(x1, crisp(line.pos));\n }\n });\n }\n for (const line of frame.xGrid) {\n stroke(line.alpha, colors.grid, () => {\n ctx.moveTo(crisp(line.pos), y0);\n ctx.lineTo(crisp(line.pos), y1);\n });\n }\n ctx.restore();\n if (frame.axisLine) {\n stroke(1, colors.axis, () => {\n if (horizontal) {\n ctx.moveTo(crisp(x0), y0);\n ctx.lineTo(crisp(x0), y1);\n } else {\n ctx.moveTo(x0, crisp(y1));\n ctx.lineTo(x1, crisp(y1));\n }\n });\n for (const mark of frame.xMarks) {\n // Tick marks sit outside the plot; keep the ones that have left its span hidden.\n const inside = horizontal ? mark.pos >= y0 - 1 && mark.pos <= y1 + 1 : mark.pos >= x0 - 1 && mark.pos <= x1 + 1;\n if (!inside) continue;\n stroke(mark.alpha, colors.axis, () => {\n if (horizontal) {\n ctx.moveTo(x0 - tickSize, crisp(mark.pos));\n ctx.lineTo(x0, crisp(mark.pos));\n } else {\n ctx.moveTo(crisp(mark.pos), y1);\n ctx.lineTo(crisp(mark.pos), y1 + tickSize);\n }\n });\n }\n }\n ctx.restore();\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"overlay.d.ts","sourceRoot":"","sources":["../../src/axes/overlay.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAGjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAM3D,4EAA4E;AAC5E,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,GAAG,IAAI,CAOzE"}
1
+ {"version":3,"file":"overlay.d.ts","sourceRoot":"","sources":["../../src/axes/overlay.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAIjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAoB3D,4EAA4E;AAC5E,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,GAAG,IAAI,CAOzE"}
@@ -1,37 +1,85 @@
1
1
  import { fontString } from "../render/text.js";
2
2
  import { TICK_SIZE } from "../layout/index.js";
3
+ import { CHROME_VISIBLE, chromeMorph, stashChrome, visibleItems } from "../render/chromeMotion.js";
3
4
  import { drawLegend } from "./legend.js";
4
5
  import { drawAxisTitles, drawTitle } from "./title.js";
5
6
  import { flatLabelBounds, placeXLabels, resolveXLabels, thinGutterTicks } from "./tickLabels.js";
6
7
  import { addText } from "./text.js";
8
+ import { AXIS_LABELS, AXIS_SCALES, blendItems, lerpRect, morphedModel, tickKey } from "./morph.js";
7
9
  function drawOverlay(surface, model) {
8
- drawGutterTickLabels(surface, model);
9
- drawBottomTickLabels(surface, model);
10
- drawAxisTitles(surface, model);
10
+ drawTickLabels(surface, model, [...gutterTickLabels(surface, model), ...bottomTickLabels(surface, model)]);
11
+ drawAxisTitles(surface, morphedModel(surface, model));
11
12
  drawTitle(surface, model.frame, model.spec, model.tokens);
12
13
  recordDroppedLegend(surface, model);
13
14
  if (model.frame.legendItems) drawLegend(surface, model.frame.legendItems, model);
14
15
  }
15
- function drawGutterTickLabels(surface, model) {
16
+ function drawTickLabels(surface, model, items) {
17
+ const morph = model.sketch ? null : chromeMorph(surface, AXIS_LABELS);
18
+ const scales = morph ? chromeMorph(surface, AXIS_SCALES) : null;
19
+ if (!morph || !scales) {
20
+ for (const item of items) addText(surface, item.font, item.opts);
21
+ stashChrome(surface, AXIS_LABELS, items);
22
+ return;
23
+ }
24
+ const same = (a, b) => a.along === b.along && a.opts.text === b.opts.text;
25
+ const blendAxis = (axis) => blendItems(
26
+ morph.from.filter((item) => item.axis === axis),
27
+ items.filter((item) => item.axis === axis),
28
+ morph.t,
29
+ axis === "x" ? scales.from.xAt : scales.from.yAt,
30
+ axis === "x" ? (v) => model.x.pixel(v) : (v) => model.y.pixel(v),
31
+ same
32
+ );
33
+ const plot = lerpRect(scales.from.plot, model.plot, morph.t);
34
+ const within = (item, pos) => {
35
+ const [lo, hi] = item.along === "top" ? [plot.y, plot.y + plot.height] : [plot.x, plot.x + plot.width];
36
+ const outside = pos < lo ? lo - pos : pos > hi ? pos - hi : 0;
37
+ return Math.max(0, 1 - outside / 10);
38
+ };
39
+ const across = (item) => {
40
+ if (item.along === "left") return { top: (item.opts.top ?? 0) + (plot.y + plot.height) - (item.plot.y + item.plot.height) };
41
+ const shift = plot.x - item.plot.x;
42
+ return item.opts.align === "right" && item.opts.width !== void 0 ? { width: Math.max(0, item.opts.width + shift) } : { left: (item.opts.left ?? 0) + shift };
43
+ };
44
+ const drawn = visibleItems([...blendAxis("y"), ...blendAxis("x")].map(({ item, pos, alpha }) => ({
45
+ ...item,
46
+ pos,
47
+ alpha,
48
+ plot,
49
+ opts: { ...item.opts, ...across(item), [item.along]: (item.opts[item.along] ?? 0) + (pos - item.pos), opacity: alpha * within(item, pos) }
50
+ })));
51
+ for (const item of drawn) if ((item.opts.opacity ?? 1) > CHROME_VISIBLE) addText(surface, item.font, item.opts);
52
+ stashChrome(surface, AXIS_LABELS, drawn);
53
+ }
54
+ function gutterTickLabels(surface, model) {
16
55
  const { plot, tokens } = model;
17
56
  const f = tokens.font;
18
57
  const horizontal = model.orientation === "horizontal";
19
58
  const gutterTicks = horizontal ? model.xTicks : model.yTicks;
20
59
  const gutterLabelsOn = horizontal ? model.spec.axes?.x?.labels !== false && model.spec.axes?.x?.show !== false : model.spec.axes?.y?.labels !== false && model.spec.axes?.y?.show !== false;
21
- if (!gutterLabelsOn) return;
60
+ if (!gutterLabelsOn) return [];
22
61
  const smallFont = fontString(f.size.small, f.family, f.weight.normal);
23
62
  const gutterRight = plot.x - TICK_SIZE - 4;
24
63
  const kept = thinGutterTicks(gutterTicks, f.size.small);
64
+ const axis = horizontal ? "x" : "y";
25
65
  surface.labels?.omit({
26
66
  role: "axis-label",
27
67
  reason: "collision",
28
68
  required: horizontal && model.x.kind === "band",
29
69
  count: gutterTicks.length - kept.length,
30
- axis: horizontal ? "x" : "y",
70
+ axis,
31
71
  path: horizontal ? "/axes/x" : "/axes/y"
32
72
  });
33
- for (const t of kept) {
34
- addText(surface, smallFont, {
73
+ return kept.map((t) => ({
74
+ key: tickKey(t.value),
75
+ value: t.value,
76
+ pos: t.pos,
77
+ alpha: 1,
78
+ axis,
79
+ along: "top",
80
+ plot,
81
+ font: smallFont,
82
+ opts: {
35
83
  left: 0,
36
84
  top: t.pos,
37
85
  width: Math.max(0, gutterRight),
@@ -41,18 +89,17 @@ function drawGutterTickLabels(surface, model) {
41
89
  align: "right",
42
90
  transform: "translateY(-50%)",
43
91
  role: "axis-label",
44
- axis: horizontal ? "x" : "y"
45
- });
46
- }
92
+ axis
93
+ }
94
+ }));
47
95
  }
48
- function drawBottomTickLabels(surface, model) {
96
+ function bottomTickLabels(surface, model) {
49
97
  const horizontal = model.orientation === "horizontal";
50
98
  const bottomLabelsOn = horizontal ? model.spec.axes?.y?.labels !== false && model.spec.axes?.y?.show !== false : model.spec.axes?.x?.labels !== false && model.spec.axes?.x?.show !== false;
51
- if (!bottomLabelsOn) return;
52
- if (horizontal) drawHorizontalBottomLabels(surface, model);
53
- else drawVerticalBottomLabels(surface, model);
99
+ if (!bottomLabelsOn) return [];
100
+ return horizontal ? horizontalBottomLabels(surface, model) : verticalBottomLabels(surface, model);
54
101
  }
55
- function drawHorizontalBottomLabels(surface, model) {
102
+ function horizontalBottomLabels(surface, model) {
56
103
  const { plot, tokens, frame } = model;
57
104
  const f = tokens.font;
58
105
  const smallFont = fontString(f.size.small, f.family, f.weight.normal);
@@ -67,8 +114,16 @@ function drawHorizontalBottomLabels(surface, model) {
67
114
  axis: "y",
68
115
  path: "/axes/y"
69
116
  });
70
- for (const p of placed) {
71
- addText(surface, smallFont, {
117
+ return placed.map((p) => ({
118
+ key: tickKey(p.value),
119
+ value: p.value,
120
+ pos: p.pos,
121
+ alpha: 1,
122
+ axis: "y",
123
+ along: "left",
124
+ plot,
125
+ font: smallFont,
126
+ opts: {
72
127
  left: p.left,
73
128
  top,
74
129
  text: p.text,
@@ -78,10 +133,10 @@ function drawHorizontalBottomLabels(surface, model) {
78
133
  maxWidth: frame.xLabelMaxWidth,
79
134
  role: "axis-label",
80
135
  axis: "y"
81
- });
82
- }
136
+ }
137
+ }));
83
138
  }
84
- function drawVerticalBottomLabels(surface, model) {
139
+ function verticalBottomLabels(surface, model) {
85
140
  const { plot, tokens, frame } = model;
86
141
  const f = tokens.font;
87
142
  const smallFont = fontString(f.size.small, f.family, f.weight.normal);
@@ -96,8 +151,16 @@ function drawVerticalBottomLabels(surface, model) {
96
151
  axis: "x",
97
152
  path: "/axes/x"
98
153
  });
99
- for (const p of placed) {
100
- addText(surface, smallFont, {
154
+ return placed.map((p) => ({
155
+ key: tickKey(p.value),
156
+ value: p.value,
157
+ pos: p.pos,
158
+ alpha: 1,
159
+ axis: "x",
160
+ along: "left",
161
+ plot,
162
+ font: smallFont,
163
+ opts: {
101
164
  left: p.left,
102
165
  top,
103
166
  text: p.text,
@@ -109,8 +172,8 @@ function drawVerticalBottomLabels(surface, model) {
109
172
  maxWidth: frame.xLabelMaxWidth,
110
173
  role: "axis-label",
111
174
  axis: "x"
112
- });
113
- }
175
+ }
176
+ }));
114
177
  }
115
178
  function recordDroppedLegend(surface, model) {
116
179
  const { frame } = model;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/axes/overlay.ts"],
4
- "sourcesContent": ["import type { Surface } from '../render/surface';\nimport { fontString } from '../render/text';\nimport { TICK_SIZE } from '../layout';\nimport type { CartesianModel } from '../runtime/cartesian';\nimport { drawLegend } from './legend';\nimport { drawAxisTitles, drawTitle } from './title';\nimport { flatLabelBounds, placeXLabels, resolveXLabels, thinGutterTicks } from './tickLabels';\nimport { addText } from './text';\n\n/** Draw all overlay text: tick labels, axis titles, chart title, legend. */\nexport function drawOverlay(surface: Surface, model: CartesianModel): void {\n drawGutterTickLabels(surface, model);\n drawBottomTickLabels(surface, model);\n drawAxisTitles(surface, model);\n drawTitle(surface, model.frame, model.spec, model.tokens);\n recordDroppedLegend(surface, model);\n if (model.frame.legendItems) drawLegend(surface, model.frame.legendItems, model);\n}\n\n/**\n * Draw the tick labels that live in the left gutter. Horizontal bar charts\n * swap category/value axes, so the required/axis metadata follows the model\n * orientation rather than a hard-coded x/y side.\n */\nfunction drawGutterTickLabels(surface: Surface, model: CartesianModel): void {\n const { plot, tokens } = model;\n const f = tokens.font;\n const horizontal = model.orientation === 'horizontal';\n // Axis tick labels. In horizontal bar mode the category axis runs down the left\n // gutter and the value axis along the bottom, so the two label sets swap sides.\n const gutterTicks = horizontal ? model.xTicks : model.yTicks;\n const gutterLabelsOn = horizontal\n ? model.spec.axes?.x?.labels !== false && model.spec.axes?.x?.show !== false\n : model.spec.axes?.y?.labels !== false && model.spec.axes?.y?.show !== false;\n if (!gutterLabelsOn) return;\n\n const smallFont = fontString(f.size.small, f.family, f.weight.normal);\n const gutterRight = plot.x - TICK_SIZE - 4;\n const kept = thinGutterTicks(gutterTicks, f.size.small);\n surface.labels?.omit({\n role: 'axis-label',\n reason: 'collision',\n required: horizontal && model.x.kind === 'band',\n count: gutterTicks.length - kept.length,\n axis: horizontal ? 'x' : 'y',\n path: horizontal ? '/axes/x' : '/axes/y',\n });\n for (const t of kept) {\n addText(surface, smallFont, {\n left: 0,\n top: t.pos,\n width: Math.max(0, gutterRight),\n text: t.label,\n color: tokens.color.textMuted,\n size: f.size.small,\n align: 'right',\n transform: 'translateY(-50%)',\n role: 'axis-label',\n axis: horizontal ? 'x' : 'y',\n });\n }\n}\n\n/**\n * Draw bottom tick labels after resolving which scale occupies the bottom edge.\n * The horizontal path lays out value ticks, while the vertical path lays out\n * category/time ticks and preserves rotated-label behavior.\n */\nfunction drawBottomTickLabels(surface: Surface, model: CartesianModel): void {\n const horizontal = model.orientation === 'horizontal';\n // Bottom tick labels: category ticks (vertical) or value ticks (horizontal).\n // Resolve each to a measured box, clamp the first/last so they never overflow\n // the surface, then drop any that still collide \u00E2\u20AC\u201D always preserving the final\n // (right-edge) label, which is the most informative on a time/linear axis.\n const bottomLabelsOn = horizontal\n ? model.spec.axes?.y?.labels !== false && model.spec.axes?.y?.show !== false\n : model.spec.axes?.x?.labels !== false && model.spec.axes?.x?.show !== false;\n if (!bottomLabelsOn) return;\n\n if (horizontal) drawHorizontalBottomLabels(surface, model);\n else drawVerticalBottomLabels(surface, model);\n}\n\n/** Bottom value-axis labels for horizontal bar charts. */\nfunction drawHorizontalBottomLabels(surface: Surface, model: CartesianModel): void {\n const { plot, tokens, frame } = model;\n const f = tokens.font;\n const smallFont = fontString(f.size.small, f.family, f.weight.normal);\n const top = plot.y + plot.height + TICK_SIZE + 3;\n const bounds = flatLabelBounds(frame);\n const placed = placeXLabels(model.yTicks, bounds.originX, bounds.width, smallFont);\n surface.labels?.omit({\n role: 'axis-label',\n reason: 'collision',\n required: false,\n count: model.yTicks.length - placed.length,\n axis: 'y',\n path: '/axes/y',\n });\n for (const p of placed) {\n addText(surface, smallFont, {\n left: p.left,\n top,\n text: p.text,\n color: tokens.color.textMuted,\n size: f.size.small,\n transform: p.transform,\n maxWidth: frame.xLabelMaxWidth,\n role: 'axis-label',\n axis: 'y',\n });\n }\n}\n\n/** Bottom x-axis labels for the standard vertical cartesian orientation. */\nfunction drawVerticalBottomLabels(surface: Surface, model: CartesianModel): void {\n const { plot, tokens, frame } = model;\n const f = tokens.font;\n const smallFont = fontString(f.size.small, f.family, f.weight.normal);\n const top = plot.y + plot.height + TICK_SIZE + 3;\n const rotated = frame.xLabelAngle > 0;\n const placed = resolveXLabels(model);\n surface.labels?.omit({\n role: 'axis-label',\n reason: 'collision',\n required: model.x.kind === 'band',\n count: model.xTicks.length - placed.length,\n axis: 'x',\n path: '/axes/x',\n });\n for (const p of placed) {\n addText(surface, smallFont, {\n left: p.left,\n top,\n text: p.text,\n color: tokens.color.textMuted,\n size: f.size.small,\n align: rotated ? 'right' : undefined,\n transform: p.transform,\n transformOrigin: rotated ? '100% 0%' : undefined,\n maxWidth: frame.xLabelMaxWidth,\n role: 'axis-label',\n axis: 'x',\n });\n }\n}\n\n/**\n * If frame compaction dropped the legend entirely, record that omission so\n * reports still explain why identifying series text is missing.\n */\nfunction recordDroppedLegend(surface: Surface, model: CartesianModel): void {\n const { frame } = model;\n if (\n frame.dropped?.includes('legend') &&\n model.spec.legend !== false &&\n !(typeof model.spec.legend === 'object' && model.spec.legend.show === false)\n ) {\n surface.labels?.omit({\n role: 'legend',\n reason: 'frame',\n required: true,\n count: model.series.length,\n path: '/legend',\n });\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { Surface } from '../render/surface';\nimport type { Rect } from '../types';\nimport { fontString } from '../render/text';\nimport { TICK_SIZE } from '../layout';\nimport type { CartesianModel } from '../runtime/cartesian';\nimport { CHROME_VISIBLE, chromeMorph, stashChrome, visibleItems } from '../render/chromeMotion';\nimport { drawLegend } from './legend';\nimport { drawAxisTitles, drawTitle } from './title';\nimport { flatLabelBounds, placeXLabels, resolveXLabels, thinGutterTicks } from './tickLabels';\nimport { addText, type TextOptions } from './text';\nimport { AXIS_LABELS, AXIS_SCALES, blendItems, lerpRect, morphedModel, tickKey, type AxisItem, type ScaleFrame } from './morph';\n\n/** A tick label as drawn, keyed by its tick value so a morph can carry it to its new place. */\ninterface LabelItem extends AxisItem {\n /** The axis whose scale places the label's value. */\n axis: 'x' | 'y';\n /** The text-box coordinate that moves with the tick. */\n along: 'left' | 'top';\n /** The plot the label was laid out against (its gutter edge / baseline). */\n plot: Rect;\n font: string;\n opts: TextOptions;\n}\n\n/** Draw all overlay text: tick labels, axis titles, chart title, legend. */\nexport function drawOverlay(surface: Surface, model: CartesianModel): void {\n drawTickLabels(surface, model, [...gutterTickLabels(surface, model), ...bottomTickLabels(surface, model)]);\n // While a morph draws, axis titles stay centred on the moving plot.\n drawAxisTitles(surface, morphedModel(surface, model));\n drawTitle(surface, model.frame, model.spec, model.tokens);\n recordDroppedLegend(surface, model);\n if (model.frame.legendItems) drawLegend(surface, model.frame.legendItems, model);\n}\n\n/**\n * Paint tick labels. While a morph is drawing, labels glide with their ticks:\n * shared labels move between positions, new ones slide in from where their value\n * sat on the outgoing scale while fading in, and gone ones fade out toward the\n * new scale. Across the axis they follow the moving plot edge, so a gutter that\n * widens (say \"3\" \u2192 \"3,000\") or a baseline that rises carries the labels along.\n */\nfunction drawTickLabels(surface: Surface, model: CartesianModel, items: LabelItem[]): void {\n const morph = model.sketch ? null : chromeMorph<LabelItem[]>(surface, AXIS_LABELS);\n const scales = morph ? chromeMorph<ScaleFrame>(surface, AXIS_SCALES) : null;\n if (!morph || !scales) {\n for (const item of items) addText(surface, item.font, item.opts);\n stashChrome(surface, AXIS_LABELS, items);\n return;\n }\n const same = (a: LabelItem, b: LabelItem) => a.along === b.along && a.opts.text === b.opts.text;\n const blendAxis = (axis: 'x' | 'y') => blendItems(\n morph.from.filter((item) => item.axis === axis),\n items.filter((item) => item.axis === axis),\n morph.t,\n axis === 'x' ? scales.from.xAt : scales.from.yAt,\n axis === 'x' ? (v) => model.x.pixel(v) : (v) => model.y.pixel(v),\n same,\n );\n // Ticks sliding in from beyond the plot (or out past it) stay hidden there, as\n // their clipped gridlines do, instead of drifting over the legend or title.\n const plot = lerpRect(scales.from.plot, model.plot, morph.t);\n const within = (item: LabelItem, pos: number): number => {\n const [lo, hi] = item.along === 'top' ? [plot.y, plot.y + plot.height] : [plot.x, plot.x + plot.width];\n const outside = pos < lo ? lo - pos : pos > hi ? pos - hi : 0;\n return Math.max(0, 1 - outside / 10);\n };\n /** The label's cross-axis coordinate, moved from its own plot edge to the blended one. */\n const across = (item: LabelItem): Partial<TextOptions> => {\n if (item.along === 'left') return { top: (item.opts.top ?? 0) + (plot.y + plot.height) - (item.plot.y + item.plot.height) };\n // Gutter labels are right-aligned against the plot's left edge.\n const shift = plot.x - item.plot.x;\n return item.opts.align === 'right' && item.opts.width !== undefined\n ? { width: Math.max(0, item.opts.width + shift) }\n : { left: (item.opts.left ?? 0) + shift };\n };\n const drawn = visibleItems([...blendAxis('y'), ...blendAxis('x')].map(({ item, pos, alpha }): LabelItem => ({\n ...item,\n pos,\n alpha,\n plot,\n opts: { ...item.opts, ...across(item), [item.along]: (item.opts[item.along] ?? 0) + (pos - item.pos), opacity: alpha * within(item, pos) },\n })));\n for (const item of drawn) if ((item.opts.opacity ?? 1) > CHROME_VISIBLE) addText(surface, item.font, item.opts);\n stashChrome(surface, AXIS_LABELS, drawn);\n}\n\n/**\n * The tick labels that live in the left gutter. Horizontal bar charts swap\n * category/value axes, so the required/axis metadata follows the model\n * orientation rather than a hard-coded x/y side.\n */\nfunction gutterTickLabels(surface: Surface, model: CartesianModel): LabelItem[] {\n const { plot, tokens } = model;\n const f = tokens.font;\n const horizontal = model.orientation === 'horizontal';\n // Axis tick labels. In horizontal bar mode the category axis runs down the left\n // gutter and the value axis along the bottom, so the two label sets swap sides.\n const gutterTicks = horizontal ? model.xTicks : model.yTicks;\n const gutterLabelsOn = horizontal\n ? model.spec.axes?.x?.labels !== false && model.spec.axes?.x?.show !== false\n : model.spec.axes?.y?.labels !== false && model.spec.axes?.y?.show !== false;\n if (!gutterLabelsOn) return [];\n\n const smallFont = fontString(f.size.small, f.family, f.weight.normal);\n const gutterRight = plot.x - TICK_SIZE - 4;\n const kept = thinGutterTicks(gutterTicks, f.size.small);\n const axis = horizontal ? 'x' : 'y';\n surface.labels?.omit({\n role: 'axis-label',\n reason: 'collision',\n required: horizontal && model.x.kind === 'band',\n count: gutterTicks.length - kept.length,\n axis,\n path: horizontal ? '/axes/x' : '/axes/y',\n });\n return kept.map((t) => ({\n key: tickKey(t.value),\n value: t.value,\n pos: t.pos,\n alpha: 1,\n axis,\n along: 'top',\n plot,\n font: smallFont,\n opts: {\n left: 0,\n top: t.pos,\n width: Math.max(0, gutterRight),\n text: t.label,\n color: tokens.color.textMuted,\n size: f.size.small,\n align: 'right',\n transform: 'translateY(-50%)',\n role: 'axis-label',\n axis,\n },\n }));\n}\n\n/**\n * Bottom tick labels after resolving which scale occupies the bottom edge.\n * The horizontal path lays out value ticks, while the vertical path lays out\n * category/time ticks and preserves rotated-label behavior.\n */\nfunction bottomTickLabels(surface: Surface, model: CartesianModel): LabelItem[] {\n const horizontal = model.orientation === 'horizontal';\n // Bottom tick labels: category ticks (vertical) or value ticks (horizontal).\n // Resolve each to a measured box, clamp the first/last so they never overflow\n // the surface, then drop any that still collide \u00E2\u20AC\u201D always preserving the final\n // (right-edge) label, which is the most informative on a time/linear axis.\n const bottomLabelsOn = horizontal\n ? model.spec.axes?.y?.labels !== false && model.spec.axes?.y?.show !== false\n : model.spec.axes?.x?.labels !== false && model.spec.axes?.x?.show !== false;\n if (!bottomLabelsOn) return [];\n\n return horizontal ? horizontalBottomLabels(surface, model) : verticalBottomLabels(surface, model);\n}\n\n/** Bottom value-axis labels for horizontal bar charts. */\nfunction horizontalBottomLabels(surface: Surface, model: CartesianModel): LabelItem[] {\n const { plot, tokens, frame } = model;\n const f = tokens.font;\n const smallFont = fontString(f.size.small, f.family, f.weight.normal);\n const top = plot.y + plot.height + TICK_SIZE + 3;\n const bounds = flatLabelBounds(frame);\n const placed = placeXLabels(model.yTicks, bounds.originX, bounds.width, smallFont);\n surface.labels?.omit({\n role: 'axis-label',\n reason: 'collision',\n required: false,\n count: model.yTicks.length - placed.length,\n axis: 'y',\n path: '/axes/y',\n });\n return placed.map((p) => ({\n key: tickKey(p.value),\n value: p.value,\n pos: p.pos,\n alpha: 1,\n axis: 'y',\n along: 'left',\n plot,\n font: smallFont,\n opts: {\n left: p.left,\n top,\n text: p.text,\n color: tokens.color.textMuted,\n size: f.size.small,\n transform: p.transform,\n maxWidth: frame.xLabelMaxWidth,\n role: 'axis-label',\n axis: 'y',\n },\n }));\n}\n\n/** Bottom x-axis labels for the standard vertical cartesian orientation. */\nfunction verticalBottomLabels(surface: Surface, model: CartesianModel): LabelItem[] {\n const { plot, tokens, frame } = model;\n const f = tokens.font;\n const smallFont = fontString(f.size.small, f.family, f.weight.normal);\n const top = plot.y + plot.height + TICK_SIZE + 3;\n const rotated = frame.xLabelAngle > 0;\n const placed = resolveXLabels(model);\n surface.labels?.omit({\n role: 'axis-label',\n reason: 'collision',\n required: model.x.kind === 'band',\n count: model.xTicks.length - placed.length,\n axis: 'x',\n path: '/axes/x',\n });\n return placed.map((p) => ({\n key: tickKey(p.value),\n value: p.value,\n pos: p.pos,\n alpha: 1,\n axis: 'x',\n along: 'left',\n plot,\n font: smallFont,\n opts: {\n left: p.left,\n top,\n text: p.text,\n color: tokens.color.textMuted,\n size: f.size.small,\n align: rotated ? 'right' : undefined,\n transform: p.transform,\n transformOrigin: rotated ? '100% 0%' : undefined,\n maxWidth: frame.xLabelMaxWidth,\n role: 'axis-label',\n axis: 'x',\n },\n }));\n}\n\n/**\n * If frame compaction dropped the legend entirely, record that omission so\n * reports still explain why identifying series text is missing.\n */\nfunction recordDroppedLegend(surface: Surface, model: CartesianModel): void {\n const { frame } = model;\n if (\n frame.dropped?.includes('legend') &&\n model.spec.legend !== false &&\n !(typeof model.spec.legend === 'object' && model.spec.legend.show === false)\n ) {\n surface.labels?.omit({\n role: 'legend',\n reason: 'frame',\n required: true,\n count: model.series.length,\n path: '/legend',\n });\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -22,6 +22,8 @@ export interface TextOptions {
22
22
  role?: LabelRole;
23
23
  axis?: 'x' | 'y';
24
24
  path?: string;
25
+ /** Opacity while chrome glides between frames (unset for static text). */
26
+ opacity?: number;
25
27
  }
26
28
  export declare function addText(surface: Surface, font: string, o: TextOptions): void;
27
29
  //# sourceMappingURL=text.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../src/axes/text.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EAAmC,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAExF,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IACjC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,IAAI,CAAC,EAAE,GAAG,GAAG,GAAG,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,WAAW,GAAG,IAAI,CA6D5E"}
1
+ {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../src/axes/text.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EAAmC,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAExF,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IACjC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,IAAI,CAAC,EAAE,GAAG,GAAG,GAAG,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,WAAW,GAAG,IAAI,CA8D5E"}
package/dist/axes/text.js CHANGED
@@ -51,6 +51,7 @@ function addText(surface, font, o) {
51
51
  }
52
52
  if (o.transform) el.style.transform = o.transform;
53
53
  if (o.transformOrigin) el.style.transformOrigin = o.transformOrigin;
54
+ if (o.opacity != null && o.opacity < 1) el.style.opacity = String(Math.max(0, o.opacity));
54
55
  el.style.pointerEvents = "none";
55
56
  overlay.appendChild(el);
56
57
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/axes/text.ts"],
4
- "sourcesContent": ["import type { Surface } from '../render/surface';\nimport { overlayTextToCanvasCmd, paintCanvasText } from '../render/overlayText';\nimport { fitLabel, labelBox, recordLabel, type LabelRole } from '../render/labelLedger';\n\nexport interface TextOptions {\n left?: number;\n top?: number;\n width?: number;\n text: string;\n color: string;\n size: number;\n weight?: number;\n align?: 'left' | 'center' | 'right';\n transform?: string;\n transformOrigin?: string;\n whiteSpace?: 'nowrap' | 'normal';\n /**\n * Width the text must fit into, when that differs from the layout box `width`\n * (rotated tick labels have no box but still have a band to stay inside).\n * Text wider than the effective budget is ellipsized rather than overflowing.\n */\n maxWidth?: number;\n /** Classification recorded in the label ledger for diagnostics. */\n role?: LabelRole;\n axis?: 'x' | 'y';\n path?: string;\n}\n\nexport function addText(surface: Surface, font: string, o: TextOptions): void {\n const budget = o.maxWidth ?? o.width;\n const fitted = fitLabel(o.text, budget, font);\n const geom = {\n left: o.left ?? 0,\n top: o.top ?? 0,\n width: o.width,\n text: fitted.text,\n color: o.color,\n size: o.size,\n align: o.align,\n transform: o.transform,\n };\n const { box, angle } = labelBox(geom, font);\n recordLabel(surface.labels, {\n role: o.role ?? 'other',\n text: fitted.text,\n fullText: o.text,\n truncated: fitted.truncated,\n box,\n angle,\n size: o.size,\n budget,\n axis: o.axis,\n color: o.color,\n opacity: 1,\n background: surface.evidence?.background,\n path:\n o.path ??\n (o.axis\n ? `/axes/${o.axis}`\n : o.role === 'title' || o.role === 'subtitle'\n ? '/title'\n : undefined),\n });\n\n if (surface.headless) {\n paintCanvasText(surface.marks.ctx, overlayTextToCanvasCmd(geom, font));\n return;\n }\n const overlay = surface.overlay;\n const el = document.createElement('div');\n el.textContent = fitted.text;\n // Keep the untruncated string reachable on hover and to assistive tech.\n if (fitted.truncated) el.title = o.text;\n el.style.position = 'absolute';\n el.style.font = font;\n el.style.fontSize = `${o.size}px`;\n if (o.weight) el.style.fontWeight = String(o.weight);\n el.style.color = o.color;\n el.style.whiteSpace = o.whiteSpace ?? 'nowrap';\n if (o.left != null) el.style.left = `${o.left}px`;\n if (o.top != null) el.style.top = `${o.top}px`;\n if (o.width != null) {\n el.style.width = `${o.width}px`;\n el.style.textAlign = o.align ?? 'left';\n }\n if (o.transform) el.style.transform = o.transform;\n if (o.transformOrigin) el.style.transformOrigin = o.transformOrigin;\n el.style.pointerEvents = 'none';\n overlay.appendChild(el);\n}\n\n"],
5
- "mappings": "AACA,SAAS,wBAAwB,uBAAuB;AACxD,SAAS,UAAU,UAAU,mBAAmC;AA0BzD,SAAS,QAAQ,SAAkB,MAAc,GAAsB;AAC5E,QAAM,SAAS,EAAE,YAAY,EAAE;AAC/B,QAAM,SAAS,SAAS,EAAE,MAAM,QAAQ,IAAI;AAC5C,QAAM,OAAO;AAAA,IACX,MAAM,EAAE,QAAQ;AAAA,IAChB,KAAK,EAAE,OAAO;AAAA,IACd,OAAO,EAAE;AAAA,IACT,MAAM,OAAO;AAAA,IACb,OAAO,EAAE;AAAA,IACT,MAAM,EAAE;AAAA,IACR,OAAO,EAAE;AAAA,IACT,WAAW,EAAE;AAAA,EACf;AACA,QAAM,EAAE,KAAK,MAAM,IAAI,SAAS,MAAM,IAAI;AAC1C,cAAY,QAAQ,QAAQ;AAAA,IAC1B,MAAM,EAAE,QAAQ;AAAA,IAChB,MAAM,OAAO;AAAA,IACb,UAAU,EAAE;AAAA,IACZ,WAAW,OAAO;AAAA,IAClB;AAAA,IACA;AAAA,IACA,MAAM,EAAE;AAAA,IACR;AAAA,IACA,MAAM,EAAE;AAAA,IACR,OAAO,EAAE;AAAA,IACT,SAAS;AAAA,IACT,YAAY,QAAQ,UAAU;AAAA,IAC9B,MACE,EAAE,SACD,EAAE,OACC,SAAS,EAAE,IAAI,KACf,EAAE,SAAS,WAAW,EAAE,SAAS,aAC/B,WACA;AAAA,EACV,CAAC;AAED,MAAI,QAAQ,UAAU;AACpB,oBAAgB,QAAQ,MAAM,KAAK,uBAAuB,MAAM,IAAI,CAAC;AACrE;AAAA,EACF;AACA,QAAM,UAAU,QAAQ;AACxB,QAAM,KAAK,SAAS,cAAc,KAAK;AACvC,KAAG,cAAc,OAAO;AAExB,MAAI,OAAO,UAAW,IAAG,QAAQ,EAAE;AACnC,KAAG,MAAM,WAAW;AACpB,KAAG,MAAM,OAAO;AAChB,KAAG,MAAM,WAAW,GAAG,EAAE,IAAI;AAC7B,MAAI,EAAE,OAAQ,IAAG,MAAM,aAAa,OAAO,EAAE,MAAM;AACnD,KAAG,MAAM,QAAQ,EAAE;AACnB,KAAG,MAAM,aAAa,EAAE,cAAc;AACtC,MAAI,EAAE,QAAQ,KAAM,IAAG,MAAM,OAAO,GAAG,EAAE,IAAI;AAC7C,MAAI,EAAE,OAAO,KAAM,IAAG,MAAM,MAAM,GAAG,EAAE,GAAG;AAC1C,MAAI,EAAE,SAAS,MAAM;AACnB,OAAG,MAAM,QAAQ,GAAG,EAAE,KAAK;AAC3B,OAAG,MAAM,YAAY,EAAE,SAAS;AAAA,EAClC;AACA,MAAI,EAAE,UAAW,IAAG,MAAM,YAAY,EAAE;AACxC,MAAI,EAAE,gBAAiB,IAAG,MAAM,kBAAkB,EAAE;AACpD,KAAG,MAAM,gBAAgB;AACzB,UAAQ,YAAY,EAAE;AACxB;",
4
+ "sourcesContent": ["import type { Surface } from '../render/surface';\nimport { overlayTextToCanvasCmd, paintCanvasText } from '../render/overlayText';\nimport { fitLabel, labelBox, recordLabel, type LabelRole } from '../render/labelLedger';\n\nexport interface TextOptions {\n left?: number;\n top?: number;\n width?: number;\n text: string;\n color: string;\n size: number;\n weight?: number;\n align?: 'left' | 'center' | 'right';\n transform?: string;\n transformOrigin?: string;\n whiteSpace?: 'nowrap' | 'normal';\n /**\n * Width the text must fit into, when that differs from the layout box `width`\n * (rotated tick labels have no box but still have a band to stay inside).\n * Text wider than the effective budget is ellipsized rather than overflowing.\n */\n maxWidth?: number;\n /** Classification recorded in the label ledger for diagnostics. */\n role?: LabelRole;\n axis?: 'x' | 'y';\n path?: string;\n /** Opacity while chrome glides between frames (unset for static text). */\n opacity?: number;\n}\n\nexport function addText(surface: Surface, font: string, o: TextOptions): void {\n const budget = o.maxWidth ?? o.width;\n const fitted = fitLabel(o.text, budget, font);\n const geom = {\n left: o.left ?? 0,\n top: o.top ?? 0,\n width: o.width,\n text: fitted.text,\n color: o.color,\n size: o.size,\n align: o.align,\n transform: o.transform,\n };\n const { box, angle } = labelBox(geom, font);\n recordLabel(surface.labels, {\n role: o.role ?? 'other',\n text: fitted.text,\n fullText: o.text,\n truncated: fitted.truncated,\n box,\n angle,\n size: o.size,\n budget,\n axis: o.axis,\n color: o.color,\n opacity: 1,\n background: surface.evidence?.background,\n path:\n o.path ??\n (o.axis\n ? `/axes/${o.axis}`\n : o.role === 'title' || o.role === 'subtitle'\n ? '/title'\n : undefined),\n });\n\n if (surface.headless) {\n paintCanvasText(surface.marks.ctx, overlayTextToCanvasCmd(geom, font));\n return;\n }\n const overlay = surface.overlay;\n const el = document.createElement('div');\n el.textContent = fitted.text;\n // Keep the untruncated string reachable on hover and to assistive tech.\n if (fitted.truncated) el.title = o.text;\n el.style.position = 'absolute';\n el.style.font = font;\n el.style.fontSize = `${o.size}px`;\n if (o.weight) el.style.fontWeight = String(o.weight);\n el.style.color = o.color;\n el.style.whiteSpace = o.whiteSpace ?? 'nowrap';\n if (o.left != null) el.style.left = `${o.left}px`;\n if (o.top != null) el.style.top = `${o.top}px`;\n if (o.width != null) {\n el.style.width = `${o.width}px`;\n el.style.textAlign = o.align ?? 'left';\n }\n if (o.transform) el.style.transform = o.transform;\n if (o.transformOrigin) el.style.transformOrigin = o.transformOrigin;\n if (o.opacity != null && o.opacity < 1) el.style.opacity = String(Math.max(0, o.opacity));\n el.style.pointerEvents = 'none';\n overlay.appendChild(el);\n}\n\n"],
5
+ "mappings": "AACA,SAAS,wBAAwB,uBAAuB;AACxD,SAAS,UAAU,UAAU,mBAAmC;AA4BzD,SAAS,QAAQ,SAAkB,MAAc,GAAsB;AAC5E,QAAM,SAAS,EAAE,YAAY,EAAE;AAC/B,QAAM,SAAS,SAAS,EAAE,MAAM,QAAQ,IAAI;AAC5C,QAAM,OAAO;AAAA,IACX,MAAM,EAAE,QAAQ;AAAA,IAChB,KAAK,EAAE,OAAO;AAAA,IACd,OAAO,EAAE;AAAA,IACT,MAAM,OAAO;AAAA,IACb,OAAO,EAAE;AAAA,IACT,MAAM,EAAE;AAAA,IACR,OAAO,EAAE;AAAA,IACT,WAAW,EAAE;AAAA,EACf;AACA,QAAM,EAAE,KAAK,MAAM,IAAI,SAAS,MAAM,IAAI;AAC1C,cAAY,QAAQ,QAAQ;AAAA,IAC1B,MAAM,EAAE,QAAQ;AAAA,IAChB,MAAM,OAAO;AAAA,IACb,UAAU,EAAE;AAAA,IACZ,WAAW,OAAO;AAAA,IAClB;AAAA,IACA;AAAA,IACA,MAAM,EAAE;AAAA,IACR;AAAA,IACA,MAAM,EAAE;AAAA,IACR,OAAO,EAAE;AAAA,IACT,SAAS;AAAA,IACT,YAAY,QAAQ,UAAU;AAAA,IAC9B,MACE,EAAE,SACD,EAAE,OACC,SAAS,EAAE,IAAI,KACf,EAAE,SAAS,WAAW,EAAE,SAAS,aAC/B,WACA;AAAA,EACV,CAAC;AAED,MAAI,QAAQ,UAAU;AACpB,oBAAgB,QAAQ,MAAM,KAAK,uBAAuB,MAAM,IAAI,CAAC;AACrE;AAAA,EACF;AACA,QAAM,UAAU,QAAQ;AACxB,QAAM,KAAK,SAAS,cAAc,KAAK;AACvC,KAAG,cAAc,OAAO;AAExB,MAAI,OAAO,UAAW,IAAG,QAAQ,EAAE;AACnC,KAAG,MAAM,WAAW;AACpB,KAAG,MAAM,OAAO;AAChB,KAAG,MAAM,WAAW,GAAG,EAAE,IAAI;AAC7B,MAAI,EAAE,OAAQ,IAAG,MAAM,aAAa,OAAO,EAAE,MAAM;AACnD,KAAG,MAAM,QAAQ,EAAE;AACnB,KAAG,MAAM,aAAa,EAAE,cAAc;AACtC,MAAI,EAAE,QAAQ,KAAM,IAAG,MAAM,OAAO,GAAG,EAAE,IAAI;AAC7C,MAAI,EAAE,OAAO,KAAM,IAAG,MAAM,MAAM,GAAG,EAAE,GAAG;AAC1C,MAAI,EAAE,SAAS,MAAM;AACnB,OAAG,MAAM,QAAQ,GAAG,EAAE,KAAK;AAC3B,OAAG,MAAM,YAAY,EAAE,SAAS;AAAA,EAClC;AACA,MAAI,EAAE,UAAW,IAAG,MAAM,YAAY,EAAE;AACxC,MAAI,EAAE,gBAAiB,IAAG,MAAM,kBAAkB,EAAE;AACpD,MAAI,EAAE,WAAW,QAAQ,EAAE,UAAU,EAAG,IAAG,MAAM,UAAU,OAAO,KAAK,IAAI,GAAG,EAAE,OAAO,CAAC;AACxF,KAAG,MAAM,gBAAgB;AACzB,UAAQ,YAAY,EAAE;AACxB;",
6
6
  "names": []
7
7
  }
@@ -15,6 +15,8 @@ export declare function thinGutterTicks<T extends {
15
15
  }>(ticks: T[], fontSize: number): T[];
16
16
  interface PlacedLabel {
17
17
  text: string;
18
+ /** The tick value the label names (lets a morph re-project it through another scale). */
19
+ value: CartesianModel['xTicks'][number]['value'];
18
20
  pos: number;
19
21
  left: number;
20
22
  transform: string;
@@ -1 +1 @@
1
- {"version":3,"file":"tickLabels.d.ts","sourceRoot":"","sources":["../../src/axes/tickLabels.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,4EAA4E;AAC5E,wBAAgB,UAAU,CAAC,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,KAAK,CAAC,MAAM,CASxF;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,CAAC,EAAE,CAY5F;AAED,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,cAAc,CAAC,QAAQ,CAAC,EAC/B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,GACX,WAAW,EAAE,CAwCf;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAShF;AAED,iFAAiF;AACjF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,CAqCrG;AAED;uDACuD;AACvD,wBAAgB,cAAc,CAAC,KAAK,EAAE,cAAc,GAAG,WAAW,EAAE,CAQnE"}
1
+ {"version":3,"file":"tickLabels.d.ts","sourceRoot":"","sources":["../../src/axes/tickLabels.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,4EAA4E;AAC5E,wBAAgB,UAAU,CAAC,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,KAAK,CAAC,MAAM,CASxF;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,CAAC,EAAE,CAY5F;AAED,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,yFAAyF;IACzF,KAAK,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC;IACjD,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,cAAc,CAAC,QAAQ,CAAC,EAC/B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,GACX,WAAW,EAAE,CAwCf;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAShF;AAED,iFAAiF;AACjF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,CAqCrG;AAED;uDACuD;AACvD,wBAAgB,cAAc,CAAC,KAAK,EAAE,cAAc,GAAG,WAAW,EAAE,CAQnE"}
@@ -46,7 +46,7 @@ function placeXLabels(ticks, originX, frameWidth, font) {
46
46
  br = hiEdge;
47
47
  bl = br - w;
48
48
  }
49
- return { text: t.label, pos: t.pos, left, transform, bl, br };
49
+ return { text: t.label, value: t.value, pos: t.pos, left, transform, bl, br };
50
50
  });
51
51
  if (boxes.length <= 1) return boxes;
52
52
  const n = boxes.length;
@@ -106,7 +106,7 @@ function placeRotatedXLabels(model, angle, font) {
106
106
  angle: geometry.angle
107
107
  };
108
108
  if (previous && labelsOverlap(previous, record, 2)) continue;
109
- placed.push({ text: tick.label, pos: tick.pos, left: tick.pos + shift, transform });
109
+ placed.push({ text: tick.label, value: tick.value, pos: tick.pos, left: tick.pos + shift, transform });
110
110
  previous = record;
111
111
  }
112
112
  return placed;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/axes/tickLabels.ts"],
4
- "sourcesContent": ["import { fontString, measureText } from '../render/text';\nimport { fitLabel, labelBox, labelsOverlap, type LabelRecord } from '../render/labelLedger';\nimport type { Frame } from '../layout';\nimport type { CartesianModel } from '../runtime/cartesian';\n\n/** Thin out x tick labels that would overlap, keeping a readable subset. */\nexport function thinXTicks(model: CartesianModel, avgCharPx: number): typeof model.xTicks {\n const ticks = model.xTicks;\n if (ticks.length <= 1) return ticks;\n const longest = ticks.reduce((m, t) => Math.max(m, t.label.length), 0);\n const needed = longest * avgCharPx + 10;\n const available = model.plot.width / ticks.length;\n if (available >= needed) return ticks;\n const stride = Math.ceil(needed / available);\n return ticks.filter((_, i) => i % stride === 0);\n}\n\n/**\n * Thin gutter (left-axis) tick labels that would overlap vertically.\n *\n * Unlike the x axis \u00E2\u20AC\u201D where label widths vary and a measured sweep decides what\n * to drop \u00E2\u20AC\u201D gutter ticks are evenly spaced and all one line tall, so a stride\n * yields a readable, regularly-spaced subset. The final tick is re-added when\n * there is room, so the axis still shows the top of its range.\n */\nexport function thinGutterTicks<T extends { pos: number }>(ticks: T[], fontSize: number): T[] {\n if (ticks.length <= 2) return ticks;\n const minGap = fontSize * 1.35;\n const span = Math.abs(ticks[ticks.length - 1].pos - ticks[0].pos);\n const gap = span / (ticks.length - 1);\n if (gap >= minGap) return ticks;\n const stride = Math.ceil(minGap / Math.max(gap, 1e-6));\n const kept = ticks.filter((_, i) => i % stride === 0);\n const last = ticks[ticks.length - 1];\n const tail = kept[kept.length - 1];\n if (tail !== last && Math.abs(last.pos - tail.pos) >= minGap) kept.push(last);\n return kept;\n}\n\ninterface PlacedLabel {\n text: string;\n pos: number;\n left: number;\n transform: string;\n}\n\n/**\n * Resolve thinned x ticks into non-overlapping label boxes. Each label is\n * centered on its tick, except the first/last which are clamped to the surface\n * edges. A left\u00E2\u2020\u2019right sweep then drops any label whose box would collide with\n * the previous kept label or with the final label, so the right-edge label is\n * always preserved without overlap (fixes \"Aug 2023Sep 2023\" style run-ons).\n */\nexport function placeXLabels(\n ticks: CartesianModel['xTicks'],\n originX: number,\n frameWidth: number,\n font: string,\n): PlacedLabel[] {\n const edgePad = 2;\n const minGap = 6;\n const loEdge = originX + edgePad;\n const hiEdge = originX + frameWidth - edgePad;\n const boxes = ticks.map((t) => {\n const w = measureText(t.label, font).width;\n const half = w / 2;\n let left = t.pos;\n let transform = 'translateX(-50%)';\n let bl = t.pos - half;\n let br = t.pos + half;\n if (bl < loEdge) {\n left = loEdge;\n transform = 'none';\n bl = loEdge;\n br = loEdge + w;\n } else if (br > hiEdge) {\n left = hiEdge;\n transform = 'translateX(-100%)';\n br = hiEdge;\n bl = br - w;\n }\n return { text: t.label, pos: t.pos, left, transform, bl, br };\n });\n if (boxes.length <= 1) return boxes;\n\n const n = boxes.length;\n const last = boxes[n - 1];\n const keep = new Array<boolean>(n).fill(false);\n keep[n - 1] = true;\n let prevBr = -Infinity;\n for (let i = 0; i < n - 1; i += 1) {\n const b = boxes[i];\n if (b.bl < prevBr + minGap) continue; // overlaps the previous kept label\n if (b.br + minGap > last.bl) break; // would overlap the final label\n keep[i] = true;\n prevBr = b.br;\n }\n return boxes.filter((_, i) => keep[i]);\n}\n\nexport function flatLabelBounds(frame: Frame): { originX: number; width: number } {\n let left = frame.originX;\n let right = frame.originX + frame.width;\n if (frame.legendRect && frame.legendPosition === 'left') {\n left = Math.max(left, frame.legendRect.x + frame.legendRect.width);\n } else if (frame.legendRect && frame.legendPosition === 'right') {\n right = Math.min(right, frame.legendRect.x);\n }\n return { originX: left, width: Math.max(0, right - left) };\n}\n\n/** Rotation still needs a readable perpendicular gap and finite edge budgets. */\nexport function placeRotatedXLabels(model: CartesianModel, angle: number, font: string): PlacedLabel[] {\n const size = model.tokens.font.size.small;\n const radians = (angle * Math.PI) / 180;\n let widest = 0;\n for (const tick of model.xTicks) {\n const text = fitLabel(tick.label, model.frame.xLabelMaxWidth, font).text;\n widest = Math.max(widest, measureText(text, font).width);\n }\n const spacing = Math.min(size / Math.sin(radians), (widest + 6) / (Math.cos(radians) * 1.35));\n const ticks = thinGutterTicks(model.xTicks, spacing);\n const lo = model.frame.originX + 1;\n const hi = model.frame.originX + model.frame.width - 1;\n const transform = `translateX(-100%) rotate(-${angle}deg)`;\n const placed: PlacedLabel[] = [];\n let previous: LabelRecord | undefined;\n for (const tick of ticks) {\n const fitted = fitLabel(tick.label, model.frame.xLabelMaxWidth, font);\n const geometry = labelBox(\n { left: tick.pos, top: 0, text: fitted.text, color: '', size, transform },\n font,\n );\n const { box } = geometry;\n const shift = box.x < lo ? lo - box.x : Math.min(0, hi - box.x - box.width);\n const record: LabelRecord = {\n role: 'axis-label',\n text: fitted.text,\n fullText: tick.label,\n truncated: fitted.truncated,\n size,\n box: { ...box, x: box.x + shift },\n angle: geometry.angle,\n };\n if (previous && labelsOverlap(previous, record, 2)) continue;\n placed.push({ text: tick.label, pos: tick.pos, left: tick.pos + shift, transform });\n previous = record;\n }\n return placed;\n}\n\n/** Compute readable placed x labels for a model. Shared by the gridline/tick underlay and\n * the label overlay so they stay perfectly aligned. */\nexport function resolveXLabels(model: CartesianModel): PlacedLabel[] {\n const f = model.tokens.font;\n const angle = model.frame.xLabelAngle;\n const smallFont = fontString(f.size.small, f.family, f.weight.normal);\n if (angle > 0) return placeRotatedXLabels(model, angle, smallFont);\n const thinned = thinXTicks(model, f.size.small * 0.58);\n const bounds = flatLabelBounds(model.frame);\n return placeXLabels(thinned, bounds.originX, bounds.width, smallFont);\n}\n\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { fontString, measureText } from '../render/text';\nimport { fitLabel, labelBox, labelsOverlap, type LabelRecord } from '../render/labelLedger';\nimport type { Frame } from '../layout';\nimport type { CartesianModel } from '../runtime/cartesian';\n\n/** Thin out x tick labels that would overlap, keeping a readable subset. */\nexport function thinXTicks(model: CartesianModel, avgCharPx: number): typeof model.xTicks {\n const ticks = model.xTicks;\n if (ticks.length <= 1) return ticks;\n const longest = ticks.reduce((m, t) => Math.max(m, t.label.length), 0);\n const needed = longest * avgCharPx + 10;\n const available = model.plot.width / ticks.length;\n if (available >= needed) return ticks;\n const stride = Math.ceil(needed / available);\n return ticks.filter((_, i) => i % stride === 0);\n}\n\n/**\n * Thin gutter (left-axis) tick labels that would overlap vertically.\n *\n * Unlike the x axis \u00E2\u20AC\u201D where label widths vary and a measured sweep decides what\n * to drop \u00E2\u20AC\u201D gutter ticks are evenly spaced and all one line tall, so a stride\n * yields a readable, regularly-spaced subset. The final tick is re-added when\n * there is room, so the axis still shows the top of its range.\n */\nexport function thinGutterTicks<T extends { pos: number }>(ticks: T[], fontSize: number): T[] {\n if (ticks.length <= 2) return ticks;\n const minGap = fontSize * 1.35;\n const span = Math.abs(ticks[ticks.length - 1].pos - ticks[0].pos);\n const gap = span / (ticks.length - 1);\n if (gap >= minGap) return ticks;\n const stride = Math.ceil(minGap / Math.max(gap, 1e-6));\n const kept = ticks.filter((_, i) => i % stride === 0);\n const last = ticks[ticks.length - 1];\n const tail = kept[kept.length - 1];\n if (tail !== last && Math.abs(last.pos - tail.pos) >= minGap) kept.push(last);\n return kept;\n}\n\ninterface PlacedLabel {\n text: string;\n /** The tick value the label names (lets a morph re-project it through another scale). */\n value: CartesianModel['xTicks'][number]['value'];\n pos: number;\n left: number;\n transform: string;\n}\n\n/**\n * Resolve thinned x ticks into non-overlapping label boxes. Each label is\n * centered on its tick, except the first/last which are clamped to the surface\n * edges. A left\u00E2\u2020\u2019right sweep then drops any label whose box would collide with\n * the previous kept label or with the final label, so the right-edge label is\n * always preserved without overlap (fixes \"Aug 2023Sep 2023\" style run-ons).\n */\nexport function placeXLabels(\n ticks: CartesianModel['xTicks'],\n originX: number,\n frameWidth: number,\n font: string,\n): PlacedLabel[] {\n const edgePad = 2;\n const minGap = 6;\n const loEdge = originX + edgePad;\n const hiEdge = originX + frameWidth - edgePad;\n const boxes = ticks.map((t) => {\n const w = measureText(t.label, font).width;\n const half = w / 2;\n let left = t.pos;\n let transform = 'translateX(-50%)';\n let bl = t.pos - half;\n let br = t.pos + half;\n if (bl < loEdge) {\n left = loEdge;\n transform = 'none';\n bl = loEdge;\n br = loEdge + w;\n } else if (br > hiEdge) {\n left = hiEdge;\n transform = 'translateX(-100%)';\n br = hiEdge;\n bl = br - w;\n }\n return { text: t.label, value: t.value, pos: t.pos, left, transform, bl, br };\n });\n if (boxes.length <= 1) return boxes;\n\n const n = boxes.length;\n const last = boxes[n - 1];\n const keep = new Array<boolean>(n).fill(false);\n keep[n - 1] = true;\n let prevBr = -Infinity;\n for (let i = 0; i < n - 1; i += 1) {\n const b = boxes[i];\n if (b.bl < prevBr + minGap) continue; // overlaps the previous kept label\n if (b.br + minGap > last.bl) break; // would overlap the final label\n keep[i] = true;\n prevBr = b.br;\n }\n return boxes.filter((_, i) => keep[i]);\n}\n\nexport function flatLabelBounds(frame: Frame): { originX: number; width: number } {\n let left = frame.originX;\n let right = frame.originX + frame.width;\n if (frame.legendRect && frame.legendPosition === 'left') {\n left = Math.max(left, frame.legendRect.x + frame.legendRect.width);\n } else if (frame.legendRect && frame.legendPosition === 'right') {\n right = Math.min(right, frame.legendRect.x);\n }\n return { originX: left, width: Math.max(0, right - left) };\n}\n\n/** Rotation still needs a readable perpendicular gap and finite edge budgets. */\nexport function placeRotatedXLabels(model: CartesianModel, angle: number, font: string): PlacedLabel[] {\n const size = model.tokens.font.size.small;\n const radians = (angle * Math.PI) / 180;\n let widest = 0;\n for (const tick of model.xTicks) {\n const text = fitLabel(tick.label, model.frame.xLabelMaxWidth, font).text;\n widest = Math.max(widest, measureText(text, font).width);\n }\n const spacing = Math.min(size / Math.sin(radians), (widest + 6) / (Math.cos(radians) * 1.35));\n const ticks = thinGutterTicks(model.xTicks, spacing);\n const lo = model.frame.originX + 1;\n const hi = model.frame.originX + model.frame.width - 1;\n const transform = `translateX(-100%) rotate(-${angle}deg)`;\n const placed: PlacedLabel[] = [];\n let previous: LabelRecord | undefined;\n for (const tick of ticks) {\n const fitted = fitLabel(tick.label, model.frame.xLabelMaxWidth, font);\n const geometry = labelBox(\n { left: tick.pos, top: 0, text: fitted.text, color: '', size, transform },\n font,\n );\n const { box } = geometry;\n const shift = box.x < lo ? lo - box.x : Math.min(0, hi - box.x - box.width);\n const record: LabelRecord = {\n role: 'axis-label',\n text: fitted.text,\n fullText: tick.label,\n truncated: fitted.truncated,\n size,\n box: { ...box, x: box.x + shift },\n angle: geometry.angle,\n };\n if (previous && labelsOverlap(previous, record, 2)) continue;\n placed.push({ text: tick.label, value: tick.value, pos: tick.pos, left: tick.pos + shift, transform });\n previous = record;\n }\n return placed;\n}\n\n/** Compute readable placed x labels for a model. Shared by the gridline/tick underlay and\n * the label overlay so they stay perfectly aligned. */\nexport function resolveXLabels(model: CartesianModel): PlacedLabel[] {\n const f = model.tokens.font;\n const angle = model.frame.xLabelAngle;\n const smallFont = fontString(f.size.small, f.family, f.weight.normal);\n if (angle > 0) return placeRotatedXLabels(model, angle, smallFont);\n const thinned = thinXTicks(model, f.size.small * 0.58);\n const bounds = flatLabelBounds(model.frame);\n return placeXLabels(thinned, bounds.originX, bounds.width, smallFont);\n}\n\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -9,14 +9,20 @@
9
9
  */
10
10
  import type { Surface } from '../render/surface.js';
11
11
  import type { CartesianModel } from '../runtime/cartesian.js';
12
+ /** Stash name of the annotation labels (an entrance fades them in place; see chromeMotion's fadeChrome). */
13
+ export declare const ANNOTATION_LABELS = "annotations:labels";
12
14
  /**
13
15
  * Paint reference lines and band/zone fills on the marks canvas, clipped to the
14
- * plot. Call after the chart's data marks so reference lines sit on top.
16
+ * plot. Call after the chart's data marks so reference lines sit on top. While a
17
+ * morph is drawing they move with the axes (see axes/morph.ts); during an
18
+ * entrance rules draw across, bands open out from their middle, and points pop
19
+ * in once the marks have mostly arrived.
15
20
  */
16
21
  export declare function drawAnnotations(surface: Surface, model: CartesianModel): void;
17
22
  /**
18
23
  * Append annotation labels to the HTML overlay. Call after `drawOverlay` so the
19
- * labels layer on top of axis text. No-op when there are no labeled annotations.
24
+ * labels layer on top of axis text. Returns the label elements (none when
25
+ * headless) so an entrance can hold them back until their marks have drawn in.
20
26
  */
21
- export declare function drawAnnotationLabels(surface: Surface, model: CartesianModel): void;
27
+ export declare function drawAnnotationLabels(surface: Surface, model: CartesianModel): HTMLElement[];
22
28
  //# sourceMappingURL=annotations.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"annotations.d.ts","sourceRoot":"","sources":["../../src/charts/annotations.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAgE3D;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,GAAG,IAAI,CAkG7E;AAyCD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,GAAG,IAAI,CA4ElF"}
1
+ {"version":3,"file":"annotations.d.ts","sourceRoot":"","sources":["../../src/charts/annotations.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAe3D,4GAA4G;AAC5G,eAAO,MAAM,iBAAiB,uBAA0B,CAAC;AAqGzD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,GAAG,IAAI,CAmH7E;AAyCD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,GAAG,WAAW,EAAE,CAqF3F"}