ggaction 0.0.13 → 0.0.16

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 (359) hide show
  1. package/CHANGELOG.md +146 -0
  2. package/README.md +41 -12
  3. package/knowledge/action-cards.json +1 -1
  4. package/knowledge/intent-taxonomy.json +1 -1
  5. package/knowledge/mcp-resources.json +1 -1
  6. package/knowledge/task-packet.schema.json +1 -1
  7. package/knowledge/task-resolver.js +1 -1
  8. package/package.json +39 -7
  9. package/src/BasicChartProgram.js +3 -1
  10. package/src/ChartProgram.js +2 -0
  11. package/src/accessibility.js +157 -0
  12. package/src/actions/basic.js +2 -2
  13. package/src/actions/boxPlots/components.js +20 -12
  14. package/src/actions/boxPlots/create.js +4 -5
  15. package/src/actions/boxPlots/edit.js +34 -9
  16. package/src/actions/boxPlots/materialize.js +12 -4
  17. package/src/actions/boxPlots/options.js +48 -16
  18. package/src/actions/canvas/actions.js +3 -3
  19. package/src/actions/canvas/fitting.js +1 -1
  20. package/src/actions/categoryOrder/index.js +21 -12
  21. package/src/actions/charts/area.js +6 -2
  22. package/src/actions/charts/bar.js +3 -2
  23. package/src/actions/charts/beeswarm.js +1 -1
  24. package/src/actions/charts/density.js +7 -3
  25. package/src/actions/charts/ecdf.js +6 -3
  26. package/src/actions/charts/endpoints.js +29 -7
  27. package/src/actions/charts/heatmap.js +3 -2
  28. package/src/actions/charts/histogram.js +5 -4
  29. package/src/actions/charts/horizon.js +4 -2
  30. package/src/actions/charts/interval-regression.js +7 -4
  31. package/src/actions/charts/line.js +4 -2
  32. package/src/actions/charts/parallel.js +9 -3
  33. package/src/actions/charts/pie.js +11 -3
  34. package/src/actions/charts/polar.js +17 -6
  35. package/src/actions/charts/radar.js +9 -3
  36. package/src/actions/charts/radial.js +12 -4
  37. package/src/actions/charts/raincloud.js +2 -2
  38. package/src/actions/charts/rug-strip.js +8 -4
  39. package/src/actions/charts/scatter.js +4 -2
  40. package/src/actions/composition/actions.js +29 -24
  41. package/src/actions/coordinates/actions.js +2 -2
  42. package/src/actions/coordinates/edit.js +76 -0
  43. package/src/actions/data/basicBin2d.js +4 -5
  44. package/src/actions/data/bin.js +3 -3
  45. package/src/actions/data/bin2d.js +63 -193
  46. package/src/actions/data/bin2dMaterialize.js +4 -5
  47. package/src/actions/data/box.js +4 -4
  48. package/src/actions/data/complete.js +26 -0
  49. package/src/actions/data/computed.js +4 -4
  50. package/src/actions/data/create.js +7 -14
  51. package/src/actions/data/density.js +15 -23
  52. package/src/actions/data/derived.js +57 -31
  53. package/src/actions/data/ecdf.js +2 -2
  54. package/src/actions/data/edit.js +551 -0
  55. package/src/actions/data/filter.js +28 -22
  56. package/src/actions/data/fold.js +2 -2
  57. package/src/actions/data/gradientProfile.js +2 -2
  58. package/src/actions/data/horizon.js +2 -2
  59. package/src/actions/data/impute.js +26 -0
  60. package/src/actions/data/index.js +62 -47
  61. package/src/actions/data/interval.js +2 -2
  62. package/src/actions/data/intervalEdit.js +26 -18
  63. package/src/actions/data/normalize.js +27 -0
  64. package/src/actions/data/regression.js +21 -27
  65. package/src/actions/data/revise.js +48 -0
  66. package/src/actions/data/revisionBindings.js +125 -0
  67. package/src/actions/data/shared.js +20 -4
  68. package/src/actions/data/stack.js +2 -2
  69. package/src/actions/data/summary.js +3 -3
  70. package/src/actions/data/timeUnit.js +6 -3
  71. package/src/actions/data/window.js +3 -3
  72. package/src/actions/encodings/angle.js +1 -1
  73. package/src/actions/encodings/appearance.js +4 -4
  74. package/src/actions/encodings/barWidth.js +1 -1
  75. package/src/actions/encodings/channels.js +415 -0
  76. package/src/actions/encodings/color/index.js +3 -2
  77. package/src/actions/encodings/density.js +7 -4
  78. package/src/actions/encodings/histogram.js +4 -3
  79. package/src/actions/encodings/horizon.js +6 -8
  80. package/src/actions/encodings/index.js +4 -0
  81. package/src/actions/encodings/offset.js +22 -15
  82. package/src/actions/encodings/parallel.js +4 -5
  83. package/src/actions/encodings/pathOrder.js +6 -11
  84. package/src/actions/encodings/position/apply.js +53 -7
  85. package/src/actions/encodings/position/index.js +14 -6
  86. package/src/actions/encodings/position/policies/bar.js +9 -0
  87. package/src/actions/encodings/position/resolve.js +24 -7
  88. package/src/actions/encodings/ranged.js +5 -5
  89. package/src/actions/encodings/remove.js +13 -9
  90. package/src/actions/encodings/ruleAppearance.js +2 -24
  91. package/src/actions/encodings/seriesLayout.js +2 -1
  92. package/src/actions/encodings/shared.js +82 -14
  93. package/src/actions/encodings/stroke.js +226 -0
  94. package/src/actions/encodings/strokeDash.js +2 -2
  95. package/src/actions/encodings/text.js +1 -1
  96. package/src/actions/errorBands/create.js +34 -20
  97. package/src/actions/errorBands/edit.js +34 -137
  98. package/src/actions/errorBars/create.js +43 -20
  99. package/src/actions/errorBars/edit.js +29 -125
  100. package/src/actions/facets/actions.js +267 -73
  101. package/src/actions/facets/derive.js +99 -28
  102. package/src/actions/facets/guides.js +4 -5
  103. package/src/actions/facets/replay.js +4 -5
  104. package/src/actions/gradientPlots/components.js +15 -8
  105. package/src/actions/gradientPlots/create.js +4 -5
  106. package/src/actions/gradientPlots/edit.js +18 -9
  107. package/src/actions/gradientPlots/materialize.js +5 -3
  108. package/src/actions/gradientPlots/options.js +10 -2
  109. package/src/actions/gradientPlots/rebind.js +4 -5
  110. package/src/actions/guides/applicability.js +8 -2
  111. package/src/actions/guides/axes/axes.js +23 -4
  112. package/src/actions/guides/axes/axis.js +2 -2
  113. package/src/actions/guides/axes/edit.js +2 -2
  114. package/src/actions/guides/axes/labels.js +67 -21
  115. package/src/actions/guides/axes/lines.js +15 -9
  116. package/src/actions/guides/axes/parallel/lifecycle.js +13 -13
  117. package/src/actions/guides/axes/parallel/resolve.js +6 -2
  118. package/src/actions/guides/axes/parallel.js +5 -5
  119. package/src/actions/guides/axes/remove.js +4 -4
  120. package/src/actions/guides/axes/tickGroups.js +6 -5
  121. package/src/actions/guides/axes/ticks.js +19 -6
  122. package/src/actions/guides/axes/titles.js +21 -13
  123. package/src/actions/guides/grids/grid.js +18 -29
  124. package/src/actions/guides/grids/resolve.js +7 -2
  125. package/src/actions/guides/guides.js +20 -4
  126. package/src/actions/guides/legends/blocks.js +426 -0
  127. package/src/actions/guides/legends/categorical/actions.js +138 -52
  128. package/src/actions/guides/legends/categorical/components.js +55 -45
  129. package/src/actions/guides/legends/categorical/index.js +37 -33
  130. package/src/actions/guides/legends/categorical/layout.js +98 -16
  131. package/src/actions/guides/legends/categorical/options.js +2 -2
  132. package/src/actions/guides/legends/categorical/recipes.js +111 -20
  133. package/src/actions/guides/legends/categorical/resolve.js +12 -8
  134. package/src/actions/guides/legends/categorical/symbols.js +104 -52
  135. package/src/actions/guides/legends/continuous/common.js +76 -20
  136. package/src/actions/guides/legends/continuous/gradient.js +95 -158
  137. package/src/actions/guides/legends/continuous/index.js +2 -0
  138. package/src/actions/guides/legends/continuous/interval.js +21 -16
  139. package/src/actions/guides/legends/continuous/opacity.js +37 -83
  140. package/src/actions/guides/legends/continuous/stroke.js +225 -0
  141. package/src/actions/guides/legends/creation.js +14 -1
  142. package/src/actions/guides/legends/edit.js +159 -60
  143. package/src/actions/guides/legends/focused.js +5 -5
  144. package/src/actions/guides/legends/index.js +2 -0
  145. package/src/actions/guides/legends/lane.js +59 -24
  146. package/src/actions/guides/legends/lifecycle.js +19 -8
  147. package/src/actions/guides/legends/remove.js +17 -8
  148. package/src/actions/guides/legends/sampling.js +140 -0
  149. package/src/actions/guides/legends/size.js +100 -108
  150. package/src/actions/guides/legends/strokeWidth.js +38 -92
  151. package/src/actions/guides/legends/target.js +112 -1
  152. package/src/actions/guides/legends/transition.js +165 -16
  153. package/src/actions/guides/polar/axes/facade.js +8 -8
  154. package/src/actions/guides/polar/axes/labels.js +29 -10
  155. package/src/actions/guides/polar/axes/lines.js +19 -19
  156. package/src/actions/guides/polar/axes/shared.js +16 -0
  157. package/src/actions/guides/polar/axes/ticks.js +10 -16
  158. package/src/actions/guides/polar/axes/titles.js +6 -6
  159. package/src/actions/guides/polar/grids.js +12 -14
  160. package/src/actions/guides/polar/resolve.js +12 -8
  161. package/src/actions/index.js +16 -3
  162. package/src/actions/intervals/revision.js +126 -0
  163. package/src/actions/marks/arc/actions.js +80 -22
  164. package/src/actions/marks/area/actions.js +38 -13
  165. package/src/actions/marks/area/materialize.js +17 -1
  166. package/src/actions/marks/bar/create.js +12 -3
  167. package/src/actions/marks/bar/edit.js +21 -4
  168. package/src/actions/marks/bar/materialize.js +34 -40
  169. package/src/actions/marks/index.js +10 -1
  170. package/src/actions/marks/line/actions.js +40 -17
  171. package/src/actions/marks/line/materialize.js +29 -10
  172. package/src/actions/marks/point/create.js +9 -3
  173. package/src/actions/marks/point/edit.js +19 -7
  174. package/src/actions/marks/point/jitter.js +6 -8
  175. package/src/actions/marks/point/materialize.js +48 -22
  176. package/src/actions/marks/point/packing.js +6 -8
  177. package/src/actions/marks/rect/actions.js +36 -15
  178. package/src/actions/marks/references.js +602 -33
  179. package/src/actions/marks/remove.js +67 -41
  180. package/src/actions/marks/rule/actions.js +51 -14
  181. package/src/actions/marks/shared.js +1 -1
  182. package/src/actions/marks/text/actions.js +79 -14
  183. package/src/actions/marks/text/index.js +4 -0
  184. package/src/actions/marks/text/layout.js +157 -17
  185. package/src/actions/marks/text/removal.js +173 -0
  186. package/src/actions/marks/text/selection.js +72 -0
  187. package/src/actions/marks/tick/actions.js +25 -8
  188. package/src/actions/primitives/createGraphics.js +5 -4
  189. package/src/actions/primitives/editGraphics.js +31 -4
  190. package/src/actions/primitives/graphicProperties.js +6 -0
  191. package/src/actions/primitives/semantic.js +11 -3
  192. package/src/actions/primitives/semanticAction.js +90 -14
  193. package/src/actions/primitives/semanticValidation/dataset.js +2 -4
  194. package/src/actions/primitives/semanticValidation/guide.js +1 -1
  195. package/src/actions/primitives/semanticValidation/index.js +9 -1
  196. package/src/actions/primitives/semanticValidation/layer.js +5 -0
  197. package/src/actions/primitives/semanticValidation/scale.js +28 -7
  198. package/src/actions/regression/components.js +36 -25
  199. package/src/actions/regression/create.js +13 -6
  200. package/src/actions/regression/edit.js +24 -11
  201. package/src/actions/resources/index.js +7 -0
  202. package/src/actions/resources/remove.js +173 -0
  203. package/src/actions/scales/channels.js +212 -0
  204. package/src/actions/scales/consumers/common.js +1 -1
  205. package/src/actions/scales/consumers/families.js +3 -2
  206. package/src/actions/scales/consumers/index.js +19 -2
  207. package/src/actions/scales/consumers/seriesLayout.js +28 -14
  208. package/src/actions/scales/create.js +55 -23
  209. package/src/actions/scales/definitions.js +94 -40
  210. package/src/actions/scales/edit.js +67 -12
  211. package/src/actions/scales/editPolicy.js +81 -11
  212. package/src/actions/scales/index.js +6 -0
  213. package/src/actions/scales/materialize.js +5 -4
  214. package/src/actions/scales/offset.js +48 -0
  215. package/src/actions/scales/parallel.js +71 -0
  216. package/src/actions/scales/preview.js +111 -10
  217. package/src/actions/scales/quantitativeColor.js +4 -5
  218. package/src/actions/selection/actions.js +179 -66
  219. package/src/actions/selection/index.js +3 -0
  220. package/src/actions/textMetrics/index.js +56 -0
  221. package/src/actions/theme/actions.js +70 -15
  222. package/src/actions/theme/composition.js +238 -0
  223. package/src/actions/theme/index.js +9 -2
  224. package/src/actions/theme/reconcile.js +313 -121
  225. package/src/actions/theme/state.js +160 -0
  226. package/src/actions/titles/actions.js +8 -8
  227. package/src/actions/titles/resolve.js +3 -3
  228. package/src/actions/violinPlots/create.js +6 -3
  229. package/src/actions/violinPlots/edit.js +2 -1
  230. package/src/core/action.js +117 -35
  231. package/src/core/compositionState.js +6 -1
  232. package/src/core/diagnostics.js +16 -0
  233. package/src/core/extensionRegistry.js +27 -0
  234. package/src/core/font.js +18 -0
  235. package/src/core/identifiers.js +5 -4
  236. package/src/core/immutable.js +10 -3
  237. package/src/core/optionRequirements.js +8 -0
  238. package/src/core/programState.js +21 -1
  239. package/src/core/resourceReferences.js +341 -0
  240. package/src/core/textMetricProfile.js +28 -0
  241. package/src/core/textMetrics.js +24 -3
  242. package/src/core/validation.js +22 -10
  243. package/src/core/vocabulary.js +16 -3
  244. package/src/diagnostics.js +1 -0
  245. package/src/grammar/bars/aggregate.js +4 -1
  246. package/src/grammar/bars/geometry.js +44 -0
  247. package/src/grammar/bin.js +54 -9
  248. package/src/grammar/complete.js +279 -0
  249. package/src/grammar/computed.js +265 -64
  250. package/src/grammar/coordinates.js +37 -0
  251. package/src/grammar/density.js +239 -37
  252. package/src/grammar/displayLabels.js +57 -0
  253. package/src/grammar/facets/dependencies.js +133 -1
  254. package/src/grammar/facets/guides.js +3 -0
  255. package/src/grammar/facets/index.js +7 -47
  256. package/src/grammar/facets/scales.js +15 -18
  257. package/src/grammar/impute.js +341 -0
  258. package/src/grammar/markLabelSelection.js +57 -0
  259. package/src/grammar/markLabels.js +18 -1
  260. package/src/grammar/normalize.js +366 -0
  261. package/src/grammar/pathSeries.js +26 -8
  262. package/src/grammar/pointShapes.js +13 -4
  263. package/src/grammar/polar.js +90 -8
  264. package/src/grammar/regression/index.js +1 -0
  265. package/src/grammar/regression/parameters.js +23 -0
  266. package/src/grammar/roundedRect.js +180 -0
  267. package/src/grammar/rules.js +1 -0
  268. package/src/grammar/scales/colorConsumers.js +20 -1
  269. package/src/grammar/scales/continuous.js +2 -2
  270. package/src/grammar/scales/definition.js +27 -4
  271. package/src/grammar/scales/index.js +1 -0
  272. package/src/grammar/scales/ordinal.js +12 -3
  273. package/src/grammar/scales/policies.js +2 -2
  274. package/src/grammar/scales/size.js +456 -0
  275. package/src/grammar/scales/types.js +8 -0
  276. package/src/grammar/schemas/concreteGraphic.js +14 -2
  277. package/src/grammar/schemas/graphic.js +15 -0
  278. package/src/grammar/schemas/graphicBounds.js +190 -41
  279. package/src/grammar/schemas/semanticPath.js +7 -2
  280. package/src/grammar/statisticalReference.js +103 -0
  281. package/src/grammar/strokeStyle.js +77 -0
  282. package/src/grammar/summary.js +78 -16
  283. package/src/grammar/timeUnit.js +216 -5
  284. package/src/grammar/transformTopology.js +4 -0
  285. package/src/grammar/transforms.js +394 -28
  286. package/src/grammar/weightedStatistics.js +374 -0
  287. package/src/grammar/window.js +221 -16
  288. package/src/layout/aspect.js +80 -0
  289. package/src/layout/facets.js +59 -5
  290. package/src/layout/labels.js +351 -7
  291. package/src/layout/legend.js +4 -4
  292. package/src/layout/legendItems.js +6 -6
  293. package/src/layout/text.js +10 -9
  294. package/src/layout/title.js +9 -9
  295. package/src/materialization/bars/aggregate.js +25 -2
  296. package/src/materialization/bars/histogram.js +84 -24
  297. package/src/materialization/bars/ranged.js +19 -3
  298. package/src/materialization/bars/resolve.js +7 -4
  299. package/src/materialization/composition.js +3 -1
  300. package/src/materialization/coordinateBounds.js +204 -0
  301. package/src/materialization/dependencies.js +36 -0
  302. package/src/materialization/facetGuides/legacyCategorical.js +3 -0
  303. package/src/materialization/facetGuides/placement.js +9 -3
  304. package/src/materialization/facetGuides/preparation.js +13 -8
  305. package/src/materialization/facetHeaders.js +347 -0
  306. package/src/materialization/facets.js +15 -65
  307. package/src/materialization/guides/layout.js +3 -3
  308. package/src/materialization/guides/resources.js +26 -0
  309. package/src/materialization/legends.js +1 -1
  310. package/src/materialization/marks/index.js +11 -5
  311. package/src/materialization/marks/policies.js +2 -1
  312. package/src/materialization/rect.js +25 -12
  313. package/src/materialization/rectConfig.js +5 -1
  314. package/src/materialization/scaleGuideDependencies.js +10 -0
  315. package/src/materialization/scales/map.js +5 -3
  316. package/src/materialization/scales/policies/binnedPosition.js +2 -2
  317. package/src/materialization/scales/policies/offset.js +50 -8
  318. package/src/materialization/scales/policies/series.js +4 -1
  319. package/src/materialization/scales/resolve.js +22 -6
  320. package/src/materialization/selection/items/arc.js +28 -4
  321. package/src/materialization/selection/items/bar.js +30 -3
  322. package/src/materialization/selection/items/common.js +20 -5
  323. package/src/materialization/selection/items/rect.js +28 -4
  324. package/src/materialization/selection/path.js +1 -1
  325. package/src/materialization/selection/point.js +3 -1
  326. package/src/materialization/text.js +265 -14
  327. package/src/materialization/typography.js +31 -0
  328. package/src/mcp/cli.js +5 -3
  329. package/src/persistence/codec.js +65 -0
  330. package/src/persistence/validation.js +185 -0
  331. package/src/persistence.js +74 -0
  332. package/src/renderers/canvas/circle.js +2 -3
  333. package/src/renderers/canvas/index.js +5 -4
  334. package/src/renderers/canvas/line.js +2 -3
  335. package/src/renderers/canvas/native.js +4 -4
  336. package/src/renderers/canvas/path.js +2 -3
  337. package/src/renderers/canvas/rect.js +2 -3
  338. package/src/renderers/canvas/stroke.js +11 -0
  339. package/src/renderers/nativeBackend.js +10 -0
  340. package/src/renderers/options.js +13 -0
  341. package/src/renderers/pdf.js +25 -54
  342. package/src/renderers/png.js +22 -17
  343. package/src/renderers/svg.js +29 -14
  344. package/src/renderers/text.js +1 -11
  345. package/src/selectors/coordinates.js +3 -1
  346. package/src/selectors/datasets.js +38 -5
  347. package/src/selectors/layers.js +10 -4
  348. package/src/selectors/markOwners.js +46 -0
  349. package/src/selectors/scales.js +6 -4
  350. package/src/theme/defaults.js +103 -1
  351. package/src/version.js +2 -0
  352. package/types/accessibility.d.ts +37 -0
  353. package/types/basic.d.ts +10 -2
  354. package/types/diagnostics.d.ts +15 -0
  355. package/types/index.d.ts +90 -0
  356. package/types/pdf.d.ts +9 -0
  357. package/types/persistence.d.ts +7 -0
  358. package/types/png.d.ts +9 -0
  359. package/types/program.d.ts +944 -118
@@ -1,10 +1,7 @@
1
- import { action } from "../../../core/action.js";
1
+ import { editGraphicProperties } from "../../primitives/graphicProperties.js";
2
+ import { action, closedAction } from "../../../core/action.js";
2
3
  import { isPlainObject } from "../../../core/immutable.js";
3
- import {
4
- noOptions,
5
- validateKeys,
6
- validateOptionObject
7
- } from "../../../core/validation.js";
4
+ import { noOptions, validateOptionObject } from "../../../core/validation.js";
8
5
  import {
9
6
  gridNames,
10
7
  editGridConfig,
@@ -75,17 +72,10 @@ function makeRematerialize(direction) {
75
72
  value: geometry[property]
76
73
  });
77
74
  }
78
- return next
79
- .editGraphics({
80
- target: operation.graphic,
81
- property: "stroke",
82
- value: config.color
83
- })
84
- .editGraphics({
85
- target: operation.graphic,
86
- property: "strokeWidth",
87
- value: config.lineWidth
88
- })
75
+ return editGraphicProperties(next, operation.graphic, {
76
+ stroke: config.color,
77
+ strokeWidth: config.lineWidth
78
+ })
89
79
  .editGraphics({
90
80
  target: operation.graphic,
91
81
  property: "strokeDash",
@@ -176,14 +166,14 @@ function normalizeDirection(value, direction) {
176
166
  return value;
177
167
  }
178
168
 
179
- const rematerializeHorizontalGrid = makeRematerialize("horizontal");
180
- const rematerializeVerticalGrid = makeRematerialize("vertical");
181
- const createHorizontalGrid = makeCreate("horizontal");
182
- const createVerticalGrid = makeCreate("vertical");
183
- const editHorizontalGrid = makeEdit("horizontal");
184
- const editVerticalGrid = makeEdit("vertical");
169
+ const rematerializeHorizontalGrid = /* @__PURE__ */ makeRematerialize("horizontal");
170
+ const rematerializeVerticalGrid = /* @__PURE__ */ makeRematerialize("vertical");
171
+ const createHorizontalGrid = /* @__PURE__ */ makeCreate("horizontal");
172
+ const createVerticalGrid = /* @__PURE__ */ makeCreate("vertical");
173
+ const editHorizontalGrid = /* @__PURE__ */ makeEdit("horizontal");
174
+ const editVerticalGrid = /* @__PURE__ */ makeEdit("vertical");
185
175
 
186
- const editGrid = action(
176
+ const editGrid = /* @__PURE__ */ action(
187
177
  {
188
178
  op: "editGrid",
189
179
  description: "Edit selected existing Cartesian grid directions."
@@ -253,7 +243,7 @@ export function resolveGridOptions(program, args = {}, layers = program.semantic
253
243
  return { horizontal, vertical, theta, radial };
254
244
  }
255
245
 
256
- const createGrid = action(
246
+ const createGrid = /* @__PURE__ */ action(
257
247
  {
258
248
  op: "createGrid",
259
249
  description: "Create selected Cartesian grid directions."
@@ -273,7 +263,7 @@ const createGrid = action(
273
263
  }
274
264
  );
275
265
 
276
- const rematerializeGrid = action(
266
+ const rematerializeGrid = /* @__PURE__ */ action(
277
267
  {
278
268
  op: "rematerializeGrid",
279
269
  description: "Recompute every existing grid direction."
@@ -305,13 +295,12 @@ const rematerializeGrid = action(
305
295
  }
306
296
  );
307
297
 
308
- const removeGrid = action(
298
+ const removeGrid = /* @__PURE__ */ closedAction(
309
299
  {
310
300
  op: "removeGrid",
311
301
  description: "Remove selected Cartesian grid directions."
312
- },
302
+ }, AGGREGATE_OPTIONS,
313
303
  function (args = {}) {
314
- validateOptionObject(args, AGGREGATE_OPTIONS, "removeGrid");
315
304
  for (const direction of AGGREGATE_OPTIONS) {
316
305
  if (
317
306
  Object.hasOwn(args, direction) &&
@@ -10,7 +10,7 @@ import {
10
10
  isTransformedScaleType,
11
11
  mapContinuousScaleValues
12
12
  } from "../../../grammar/scales/index.js";
13
- import { resolveGraphicBounds } from "../../../layout/canvas.js";
13
+ import { resolveGuideCoordinateBounds } from "../../../materialization/coordinateBounds.js";
14
14
  import { resolveGridLineGeometry } from "../../../layout/grid.js";
15
15
  import { DEFAULT_COLORS } from "../../../theme/defaults.js";
16
16
  import { findCoordinate } from "../../../selectors/coordinates.js";
@@ -248,7 +248,12 @@ export function editGridConfig(previous, args) {
248
248
 
249
249
  export function resolveGridGeometry(program, config) {
250
250
  const scale = program.resolvedScales[config.scale];
251
- const bounds = resolveGraphicBounds(program);
251
+ const channel = config.direction === "horizontal" ? "y" : "x";
252
+ const bounds = resolveGuideCoordinateBounds(program, {
253
+ coordinate: config.coordinate,
254
+ scale: config.scale,
255
+ channel
256
+ });
252
257
  if ((
253
258
  !["linear", "time"].includes(scale?.type) &&
254
259
  !isTransformedScaleType(scale?.type)
@@ -89,6 +89,13 @@ function mergeInferredOptions(inferred, explicit) {
89
89
  return merged;
90
90
  }
91
91
 
92
+ function hasStoredGuideCollection(program, collection) {
93
+ return [
94
+ program.semanticSpec.guides?.[collection],
95
+ program.guideConfigs?.[collection]
96
+ ].some(value => value !== undefined && Object.keys(value).length > 0);
97
+ }
98
+
92
99
  export function resolveGuideOptions(program, args = {}, layers = program.semanticSpec.layers) {
93
100
  validateOptions(args);
94
101
  const applicability = resolveGuideApplicability(program, layers);
@@ -116,7 +123,7 @@ export function resolveGuideOptions(program, args = {}, layers = program.semanti
116
123
  return { axes, grid, legend };
117
124
  }
118
125
 
119
- const createGuides = action(
126
+ const createGuides = /* @__PURE__ */ action(
120
127
  {
121
128
  op: "createGuides",
122
129
  description: "Create applicable axes, grid, and legend."
@@ -128,9 +135,18 @@ const createGuides = action(
128
135
  }
129
136
 
130
137
  let next = this;
131
- if (axes !== undefined) next = next.createAxes(axes);
132
- if (grid !== undefined) next = next.createGrid(grid);
133
- if (legend !== undefined) next = next.createLegend(legend);
138
+ if (
139
+ axes !== undefined &&
140
+ !(args.axes === undefined && hasStoredGuideCollection(this, "axis"))
141
+ ) next = next.createAxes(axes);
142
+ if (
143
+ grid !== undefined &&
144
+ !(args.grid === undefined && hasStoredGuideCollection(this, "grid"))
145
+ ) next = next.createGrid(grid);
146
+ if (
147
+ legend !== undefined &&
148
+ !(args.legend === undefined && hasStoredGuideCollection(this, "legend"))
149
+ ) next = next.createLegend(legend);
134
150
  return next;
135
151
  }
136
152
  );
@@ -0,0 +1,426 @@
1
+ import { action } from "../../../core/action.js";
2
+ import { isPlainObject } from "../../../core/immutable.js";
3
+ import {
4
+ validateNonEmptyString,
5
+ validateNonNegativeFinite,
6
+ validateOptionObject,
7
+ validatePositiveFinite,
8
+ validateUnitInterval
9
+ } from "../../../core/validation.js";
10
+ import { isNominalValue } from "../../../grammar/scales/fields.js";
11
+ import { withGuideLayoutValidation } from "../../../materialization/guides/layout.js";
12
+ import { legendResourcePolicy } from "../../../materialization/guides/resources.js";
13
+ import {
14
+ preserveGraphicPlacement,
15
+ resolveLegendGraphicPlacement
16
+ } from "../../../materialization/graphicHierarchy.js";
17
+ import {
18
+ normalizeLegendSampling,
19
+ validateSamplingAgainstScale
20
+ } from "./sampling.js";
21
+ import {
22
+ legendBlockKey,
23
+ resolveLegendBlock
24
+ } from "./target.js";
25
+ import { validateFontWeight } from "./categorical/validation.js";
26
+ import { normalizeDisplayLabelMap } from "../../../grammar/displayLabels.js";
27
+
28
+ const OPTIONS = Object.freeze([
29
+ "target", "channel", "title", "values", "count", "order", "gap", "text", "symbol",
30
+ "labelMap"
31
+ ]);
32
+ const TEXT_OPTIONS = Object.freeze(["fontSize", "fontFamily", "fontWeight", "color"]);
33
+ const SYMBOL_OPTIONS = Object.freeze(["size", "fill", "stroke", "strokeWidth", "opacity"]);
34
+
35
+ function validatePatchObject(value, keys, label) {
36
+ if (!isPlainObject(value)) throw new TypeError(`${label} must be a plain object.`);
37
+ validateOptionObject(value, keys, label);
38
+ }
39
+
40
+ function normalizeTextPatch(value) {
41
+ validatePatchObject(value, TEXT_OPTIONS, "editLegendBlock text");
42
+ if (value.fontSize !== undefined) {
43
+ validatePositiveFinite(value.fontSize, "editLegendBlock text.fontSize");
44
+ }
45
+ if (value.fontFamily !== undefined) {
46
+ validateNonEmptyString(value.fontFamily, "editLegendBlock text.fontFamily");
47
+ }
48
+ if (value.fontWeight !== undefined) {
49
+ validateFontWeight(value.fontWeight, "editLegendBlock text.fontWeight");
50
+ }
51
+ if (value.color !== undefined) {
52
+ validateNonEmptyString(value.color, "editLegendBlock text.color");
53
+ }
54
+ return { ...value };
55
+ }
56
+
57
+ function categoricalSupports(config, property) {
58
+ const types = new Set(config.symbol.layers.map(layer => layer.type));
59
+ if (property === "size") return types.has("point");
60
+ if (property === "fill") return types.has("point") || types.has("swatch");
61
+ return types.size > 0;
62
+ }
63
+
64
+ function validateSymbolSupport(descriptor, property) {
65
+ const { channels, family, kind, config } = descriptor;
66
+ if (family === "gradient") {
67
+ throw new Error(`editLegendBlock symbol.${property} is unsupported for gradient blocks.`);
68
+ }
69
+ if (family === "categorical") {
70
+ if (property === "fill" && channels.includes("color")) {
71
+ throw new Error("editLegendBlock symbol.fill conflicts with the color mapping.");
72
+ }
73
+ if (property === "stroke" && channels.includes("stroke")) {
74
+ throw new Error("editLegendBlock symbol.stroke conflicts with the stroke mapping.");
75
+ }
76
+ if (!categoricalSupports(config, property)) {
77
+ throw new Error(`editLegendBlock symbol.${property} is unsupported by this categorical recipe.`);
78
+ }
79
+ return;
80
+ }
81
+ if (kind === "size" && property === "size") {
82
+ throw new Error("editLegendBlock symbol.size conflicts with the size mapping.");
83
+ }
84
+ if (kind === "opacity" && property === "opacity") {
85
+ throw new Error("editLegendBlock symbol.opacity conflicts with the opacity mapping.");
86
+ }
87
+ if (kind === "strokeWidth" && property === "strokeWidth") {
88
+ throw new Error("editLegendBlock symbol.strokeWidth conflicts with the strokeWidth mapping.");
89
+ }
90
+ if (kind === "strokeWidth" && ["size", "fill"].includes(property)) {
91
+ throw new Error(`editLegendBlock symbol.${property} is unsupported for strokeWidth blocks.`);
92
+ }
93
+ if (kind === "interval" && property === "fill") {
94
+ throw new Error("editLegendBlock symbol.fill conflicts with the color mapping.");
95
+ }
96
+ if (kind === "strokeInterval" && property === "stroke") {
97
+ throw new Error("editLegendBlock symbol.stroke conflicts with the stroke mapping.");
98
+ }
99
+ if (["interval", "strokeInterval"].includes(kind) && property === "size") {
100
+ throw new Error(`editLegendBlock symbol.size is unsupported for ${kind} blocks.`);
101
+ }
102
+ }
103
+
104
+ function normalizeSymbolPatch(value, descriptor) {
105
+ validatePatchObject(value, SYMBOL_OPTIONS, "editLegendBlock symbol");
106
+ if (value.size !== undefined) {
107
+ validateNonNegativeFinite(value.size, "editLegendBlock symbol.size");
108
+ }
109
+ if (value.fill !== undefined) {
110
+ validateNonEmptyString(value.fill, "editLegendBlock symbol.fill");
111
+ }
112
+ if (value.stroke !== undefined) {
113
+ validateNonEmptyString(value.stroke, "editLegendBlock symbol.stroke");
114
+ }
115
+ if (value.strokeWidth !== undefined) {
116
+ validateNonNegativeFinite(value.strokeWidth, "editLegendBlock symbol.strokeWidth");
117
+ }
118
+ if (value.opacity !== undefined) {
119
+ validateUnitInterval(value.opacity, "editLegendBlock symbol.opacity");
120
+ }
121
+ for (const property of Object.keys(value)) {
122
+ validateSymbolSupport(descriptor, property);
123
+ }
124
+ return { ...value };
125
+ }
126
+
127
+ export function validateLegendBlockOverride(descriptor, override) {
128
+ if (override.symbol !== undefined) normalizeSymbolPatch(override.symbol, descriptor);
129
+ if (override.labelMap !== undefined) {
130
+ if (descriptor.family !== "categorical") {
131
+ throw new Error("editLegendBlock labelMap requires a categorical block.");
132
+ }
133
+ normalizeDisplayLabelMap(override.labelMap, "editLegendBlock labelMap");
134
+ }
135
+ return override;
136
+ }
137
+
138
+ function exactCategoricalOrder(program, descriptor, value) {
139
+ if (descriptor.family !== "categorical") {
140
+ throw new Error("editLegendBlock order requires a categorical block.");
141
+ }
142
+ if (!Array.isArray(value) || value.length === 0) {
143
+ throw new TypeError("editLegendBlock order must be a non-empty array.");
144
+ }
145
+ if (!value.every(isNominalValue)) {
146
+ throw new TypeError("editLegendBlock order must contain categorical values.");
147
+ }
148
+ if (value.some((item, index) =>
149
+ value.slice(0, index).some(previous => Object.is(previous, item)))) {
150
+ throw new Error("editLegendBlock order values must be unique.");
151
+ }
152
+ const domain = program.resolvedScales[descriptor.scaleIds[0]].domain;
153
+ if (domain.length !== value.length ||
154
+ domain.some(item => !value.some(candidate => Object.is(candidate, item)))) {
155
+ throw new Error("editLegendBlock order must be an exact permutation of the current domain.");
156
+ }
157
+ return Object.freeze({ values: Object.freeze([...value]) });
158
+ }
159
+
160
+ function withoutEmptyNested(entry, key, value) {
161
+ if (Object.keys(value).length > 0) return { ...entry, [key]: value };
162
+ const { [key]: _removed, ...rest } = entry;
163
+ return rest;
164
+ }
165
+
166
+ export function patchLegendBlockOverride(config, key, patch) {
167
+ const previous = config.blockOverrides?.[key] ?? {};
168
+ let entry = { ...previous };
169
+ if (Object.hasOwn(patch, "title")) entry.title = patch.title;
170
+ if (Object.hasOwn(patch, "gap")) entry.gap = patch.gap;
171
+ if (Object.hasOwn(patch, "text")) {
172
+ entry = withoutEmptyNested(entry, "text", patch.text);
173
+ }
174
+ if (Object.hasOwn(patch, "symbol")) {
175
+ entry = withoutEmptyNested(entry, "symbol", patch.symbol);
176
+ }
177
+ if (Object.hasOwn(patch, "labelMap")) entry.labelMap = patch.labelMap;
178
+ if (patch.removeLabelMap === true) delete entry.labelMap;
179
+ const blockOverrides = { ...(config.blockOverrides ?? {}) };
180
+ if (Object.keys(entry).length === 0) delete blockOverrides[key];
181
+ else blockOverrides[key] = entry;
182
+ return Object.keys(blockOverrides).length === 0
183
+ ? undefined
184
+ : blockOverrides;
185
+ }
186
+
187
+ function applyCategoricalSymbol(symbol, patch) {
188
+ return {
189
+ ...symbol,
190
+ layers: symbol.layers.map(layer => ({
191
+ ...layer,
192
+ ...(patch.size !== undefined && layer.type === "point"
193
+ ? { size: Math.sqrt(patch.size / Math.PI) }
194
+ : {}),
195
+ ...(patch.fill !== undefined && ["point", "swatch"].includes(layer.type)
196
+ ? { fill: patch.fill }
197
+ : {}),
198
+ ...(patch.stroke !== undefined ? { stroke: patch.stroke } : {}),
199
+ ...(patch.strokeWidth !== undefined
200
+ ? layer.type === "line"
201
+ ? { lineWidth: patch.strokeWidth }
202
+ : { strokeWidth: patch.strokeWidth }
203
+ : {}),
204
+ ...(patch.opacity !== undefined ? { opacity: patch.opacity } : {})
205
+ }))
206
+ };
207
+ }
208
+
209
+ export function resolveEffectiveLegendBlockConfig(program, kind, config) {
210
+ const channels = kind === "series" || kind === "color" || kind === "stroke"
211
+ ? config.channels
212
+ : [{ size: "size", opacity: "opacity", strokeWidth: "strokeWidth",
213
+ gradient: "color", interval: "color", strokeGradient: "stroke",
214
+ strokeInterval: "stroke" }[kind]];
215
+ const override = config.blockOverrides?.[legendBlockKey(channels)] ?? {};
216
+ const effective = {
217
+ ...config,
218
+ ...(override.title === undefined || override.title === ""
219
+ ? {}
220
+ : { title: override.title }),
221
+ ...(override.title === ""
222
+ ? { titleVisible: false }
223
+ : override.title === undefined
224
+ ? {}
225
+ : { titleVisible: true }),
226
+ ...(override.gap === undefined ? {} : { itemGap: override.gap, blockGap: override.gap }),
227
+ ...(override.text === undefined
228
+ ? {}
229
+ : { labels: { ...config.labels, ...override.text } }),
230
+ ...(Object.hasOwn(override, "labelMap")
231
+ ? { labelMap: override.labelMap }
232
+ : {})
233
+ };
234
+ if (override.symbol === undefined) return effective;
235
+ if (["series", "color", "stroke"].includes(kind)) {
236
+ return {
237
+ ...effective,
238
+ symbol: applyCategoricalSymbol(config.symbol, override.symbol),
239
+ blockSymbol: override.symbol
240
+ };
241
+ }
242
+ if (kind === "opacity") {
243
+ return {
244
+ ...effective,
245
+ symbol: {
246
+ ...config.symbol,
247
+ ...(override.symbol.size === undefined
248
+ ? {}
249
+ : { radius: Math.sqrt(override.symbol.size / Math.PI) }),
250
+ ...Object.fromEntries(Object.entries(override.symbol)
251
+ .filter(([key]) => ["fill", "stroke", "strokeWidth"].includes(key)))
252
+ },
253
+ blockSymbol: override.symbol
254
+ };
255
+ }
256
+ if (["interval", "strokeInterval"].includes(kind)) {
257
+ return {
258
+ ...effective,
259
+ symbol: { ...config.symbol, ...override.symbol },
260
+ blockSymbol: override.symbol
261
+ };
262
+ }
263
+ return { ...effective, blockSymbol: override.symbol };
264
+ }
265
+
266
+ function titleGraphicId(kind) {
267
+ return legendResourcePolicy(kind).graphicIds.find(id => id.endsWith("Title"));
268
+ }
269
+
270
+ export function reconcileLegendBlockTitleGraphic(program, kind, visible) {
271
+ const id = titleGraphicId(kind);
272
+ const exists = program.graphicSpec.objects[id] !== undefined;
273
+ if (!visible && exists) return program.editGraphics({ target: id, remove: true });
274
+ if (visible && !exists) {
275
+ const owned = new Set(legendResourcePolicy(kind).graphicIds);
276
+ const anchor = program.graphicSpec.objects.canvas.children
277
+ .filter(childId => childId !== id && owned.has(childId))
278
+ .at(-1);
279
+ if (anchor === undefined) {
280
+ throw new Error(`editLegendBlock cannot place the ${kind} legend title.`);
281
+ }
282
+ return program.createGraphics({
283
+ id,
284
+ type: "text",
285
+ ...resolveLegendGraphicPlacement(program, { after: anchor })
286
+ });
287
+ }
288
+ return program;
289
+ }
290
+
291
+ const SPECIALIZED_SYMBOLS = Object.freeze({
292
+ size: Object.freeze({ id: "sizeLegendSymbols", type: "circle" }),
293
+ opacity: Object.freeze({ id: "opacityLegendSymbols", type: "circle" }),
294
+ strokeWidth: Object.freeze({ id: "strokeWidthLegendSymbols", type: "line" }),
295
+ interval: Object.freeze({ id: "colorLegendSymbols", type: "rect" }),
296
+ strokeInterval: Object.freeze({ id: "strokeIntervalSymbols", type: "rect" })
297
+ });
298
+
299
+ // Symbol patches replace the complete block-local patch. Recreating the concrete
300
+ // symbol owner removes properties that existed only in the previous patch before
301
+ // the ordinary rematerializer writes the new effective appearance.
302
+ function recreateSymbolGraphics(program, descriptor) {
303
+ if (descriptor.family === "gradient") return program;
304
+ if (descriptor.family === "categorical") {
305
+ const symbolIds = legendResourcePolicy(descriptor.kind).graphicIds
306
+ .filter(id => id.includes("Symbol"));
307
+ let next = program;
308
+ for (const id of symbolIds) {
309
+ if (next.graphicSpec.objects[id] !== undefined) {
310
+ next = next.editGraphics({ target: id, remove: true });
311
+ }
312
+ }
313
+ return next.createLegendSymbols({ kind: descriptor.kind });
314
+ }
315
+ const symbol = SPECIALIZED_SYMBOLS[descriptor.kind];
316
+ if (symbol === undefined || program.graphicSpec.objects[symbol.id] === undefined) {
317
+ throw new Error(`editLegendBlock cannot recreate ${descriptor.kind} legend symbols.`);
318
+ }
319
+ const placement = preserveGraphicPlacement(program, symbol.id);
320
+ return program
321
+ .editGraphics({ target: symbol.id, remove: true })
322
+ .createGraphics({ id: symbol.id, type: symbol.type, length: 0, ...placement });
323
+ }
324
+
325
+ function normalizeBlockPatch(args, descriptor) {
326
+ const patch = {};
327
+ if (Object.hasOwn(args, "title")) {
328
+ if (typeof args.title !== "string") {
329
+ throw new TypeError("editLegendBlock title must be a string.");
330
+ }
331
+ patch.title = args.title;
332
+ }
333
+ if (Object.hasOwn(args, "gap")) {
334
+ validateNonNegativeFinite(args.gap, "editLegendBlock gap");
335
+ patch.gap = args.gap;
336
+ }
337
+ if (Object.hasOwn(args, "text")) patch.text = normalizeTextPatch(args.text);
338
+ if (Object.hasOwn(args, "symbol")) {
339
+ patch.symbol = normalizeSymbolPatch(args.symbol, descriptor);
340
+ }
341
+ if (Object.hasOwn(args, "labelMap")) {
342
+ if (descriptor.family !== "categorical") {
343
+ throw new Error("editLegendBlock labelMap requires a categorical block.");
344
+ }
345
+ if (args.labelMap === "auto") patch.removeLabelMap = true;
346
+ else {
347
+ patch.labelMap = normalizeDisplayLabelMap(
348
+ args.labelMap,
349
+ "editLegendBlock labelMap"
350
+ );
351
+ }
352
+ }
353
+ return patch;
354
+ }
355
+
356
+ export const editLegendBlock = /* @__PURE__ */ action(
357
+ {
358
+ op: "editLegendBlock",
359
+ description: "Edit one logical legend block selected by channel."
360
+ },
361
+ withGuideLayoutValidation(function (args = {}) {
362
+ validateOptionObject(args, OPTIONS, "editLegendBlock");
363
+ if (!Object.hasOwn(args, "target") || typeof args.target !== "string") {
364
+ throw new TypeError("editLegendBlock target must be an explicit legend target id.");
365
+ }
366
+ if (!Object.hasOwn(args, "channel") || typeof args.channel !== "string") {
367
+ throw new TypeError("editLegendBlock channel must be explicit.");
368
+ }
369
+ if (!Object.keys(args).some(key => !["target", "channel"].includes(key))) {
370
+ throw new Error("editLegendBlock requires at least one change.");
371
+ }
372
+ const descriptor = resolveLegendBlock(this, args, "editLegendBlock");
373
+ if ((Object.hasOwn(args, "values") || Object.hasOwn(args, "count")) &&
374
+ descriptor.family !== "sampled") {
375
+ throw new Error("editLegendBlock values and count require a sampled legend block.");
376
+ }
377
+ const patch = normalizeBlockPatch(args, descriptor);
378
+ let config = descriptor.config;
379
+ const blockOverrides = patchLegendBlockOverride(config, descriptor.key, patch);
380
+ if (blockOverrides === undefined) {
381
+ const { blockOverrides: _removed, ...rest } = config;
382
+ config = rest;
383
+ } else {
384
+ config = { ...config, blockOverrides };
385
+ }
386
+ if (Object.hasOwn(args, "values") || Object.hasOwn(args, "count")) {
387
+ const sampling = normalizeLegendSampling(args, {
388
+ previous: config,
389
+ operation: "edit",
390
+ label: `editLegendBlock ${descriptor.channel ?? args.channel} legend`
391
+ });
392
+ validateSamplingAgainstScale(sampling, this.resolvedScales[descriptor.scaleIds[0]],
393
+ "editLegendBlock values");
394
+ const { count: _legacyCount, ...withoutCount } = config;
395
+ config = { ...withoutCount, sampling };
396
+ }
397
+ const order = Object.hasOwn(args, "order")
398
+ ? exactCategoricalOrder(this, descriptor, args.order)
399
+ : undefined;
400
+ if (args.title !== undefined && args.title !== "") {
401
+ config = { ...config, title: args.title, inferredTitle: false };
402
+ }
403
+ let next = this._withLegendConfig(descriptor.kind, config);
404
+ const semanticKind = legendResourcePolicy(descriptor.kind).semanticKind;
405
+ if (order !== undefined) {
406
+ next = next.editSemantic({
407
+ property: `guide.legend.${semanticKind}.order`,
408
+ value: order
409
+ });
410
+ }
411
+ if (args.title !== undefined && args.title !== "") {
412
+ next = next.editSemantic({
413
+ property: `guide.legend.${semanticKind}.title`,
414
+ value: args.title
415
+ });
416
+ }
417
+ const effective = resolveEffectiveLegendBlockConfig(next, descriptor.kind,
418
+ next.guideConfigs.legend[descriptor.kind]);
419
+ if (Object.hasOwn(args, "symbol")) {
420
+ next = recreateSymbolGraphics(next, descriptor);
421
+ }
422
+ next = reconcileLegendBlockTitleGraphic(next, descriptor.kind,
423
+ effective.titleVisible !== false);
424
+ return next.rematerializeLegend();
425
+ })
426
+ );