ggaction 0.0.12 → 0.0.15

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 (378) hide show
  1. package/CHANGELOG.md +127 -0
  2. package/README.md +20 -11
  3. package/knowledge/action-card.schema.json +1 -1
  4. package/knowledge/action-cards.json +1 -1
  5. package/knowledge/action-cards.schema.json +1 -1
  6. package/knowledge/extension-authoring.md +2 -1
  7. package/knowledge/intent-taxonomy.json +1 -1
  8. package/knowledge/mcp-resources.json +1 -1
  9. package/knowledge/task-resolver.js +1 -1
  10. package/package.json +10 -3
  11. package/src/actions/basic.js +4 -2
  12. package/src/actions/boxPlots/components.js +14 -4
  13. package/src/actions/boxPlots/edit.js +31 -5
  14. package/src/actions/boxPlots/materialize.js +13 -4
  15. package/src/actions/boxPlots/options.js +48 -16
  16. package/src/actions/canvas/actions.js +3 -1
  17. package/src/actions/canvas/fitting.js +211 -0
  18. package/src/actions/canvas/index.js +2 -0
  19. package/src/actions/categoryOrder/index.js +18 -6
  20. package/src/actions/charts/area.js +51 -0
  21. package/src/actions/charts/bar.js +13 -6
  22. package/src/actions/charts/beeswarm.js +70 -0
  23. package/src/actions/charts/density.js +48 -0
  24. package/src/actions/charts/ecdf.js +186 -0
  25. package/src/actions/charts/endpoints.js +473 -0
  26. package/src/actions/charts/heatmap.js +4 -3
  27. package/src/actions/charts/histogram.js +5 -4
  28. package/src/actions/charts/horizon.js +73 -0
  29. package/src/actions/charts/index.js +37 -0
  30. package/src/actions/charts/interval-regression.js +172 -0
  31. package/src/actions/charts/line.js +8 -6
  32. package/src/actions/charts/parallel.js +8 -2
  33. package/src/actions/charts/pie.js +52 -0
  34. package/src/actions/charts/polar.js +134 -0
  35. package/src/actions/charts/radar.js +305 -0
  36. package/src/actions/charts/radial.js +50 -0
  37. package/src/actions/charts/raincloud.js +422 -0
  38. package/src/actions/charts/rug-strip.js +283 -0
  39. package/src/actions/charts/scatter.js +11 -3
  40. package/src/actions/charts/shared.js +69 -10
  41. package/src/actions/composition/actions.js +129 -3
  42. package/src/actions/coordinates/actions.js +1 -1
  43. package/src/actions/coordinates/edit.js +76 -0
  44. package/src/actions/data/bin.js +29 -0
  45. package/src/actions/data/bin2d.js +57 -185
  46. package/src/actions/data/complete.js +26 -0
  47. package/src/actions/data/computed.js +24 -0
  48. package/src/actions/data/create.js +2 -2
  49. package/src/actions/data/density.js +12 -20
  50. package/src/actions/data/derived.js +111 -15
  51. package/src/actions/data/ecdf.js +38 -0
  52. package/src/actions/data/edit.js +539 -0
  53. package/src/actions/data/filter.js +226 -30
  54. package/src/actions/data/fold.js +24 -0
  55. package/src/actions/data/impute.js +26 -0
  56. package/src/actions/data/index.js +38 -0
  57. package/src/actions/data/interval.js +2 -0
  58. package/src/actions/data/intervalEdit.js +86 -4
  59. package/src/actions/data/normalize.js +27 -0
  60. package/src/actions/data/regression.js +19 -21
  61. package/src/actions/data/revisionBindings.js +145 -0
  62. package/src/actions/data/shared.js +20 -4
  63. package/src/actions/data/stack.js +26 -0
  64. package/src/actions/data/summary.js +26 -0
  65. package/src/actions/data/timeUnit.js +4 -1
  66. package/src/actions/data/window.js +1 -1
  67. package/src/actions/encodings/appearance.js +32 -13
  68. package/src/actions/encodings/barWidth.js +8 -0
  69. package/src/actions/encodings/basic.js +2 -0
  70. package/src/actions/encodings/channels.js +415 -0
  71. package/src/actions/encodings/color/continuous.js +19 -59
  72. package/src/actions/encodings/color/index.js +30 -23
  73. package/src/actions/encodings/color/layout.js +3 -97
  74. package/src/actions/encodings/color/policy.js +5 -8
  75. package/src/actions/encodings/density/resolve.js +37 -5
  76. package/src/actions/encodings/density.js +124 -34
  77. package/src/actions/encodings/histogram.js +3 -2
  78. package/src/actions/encodings/horizon/resolve.js +8 -4
  79. package/src/actions/encodings/horizon.js +18 -8
  80. package/src/actions/encodings/index.js +6 -0
  81. package/src/actions/encodings/offset.js +30 -29
  82. package/src/actions/encodings/parallel.js +2 -8
  83. package/src/actions/encodings/position/apply.js +63 -11
  84. package/src/actions/encodings/position/index.js +52 -6
  85. package/src/actions/encodings/position/policies/arc.js +14 -0
  86. package/src/actions/encodings/position/policies/area.js +4 -42
  87. package/src/actions/encodings/position/policies/bar.js +57 -13
  88. package/src/actions/encodings/position/resolve.js +88 -26
  89. package/src/actions/encodings/ranged.js +123 -97
  90. package/src/actions/encodings/remove.js +44 -34
  91. package/src/actions/encodings/ruleAppearance.js +24 -32
  92. package/src/actions/encodings/seriesLayout.js +105 -0
  93. package/src/actions/encodings/shared.js +107 -37
  94. package/src/actions/encodings/stroke.js +226 -0
  95. package/src/actions/encodings/strokeDash.js +9 -6
  96. package/src/actions/encodings/temporal.js +7 -0
  97. package/src/actions/encodings/text.js +30 -15
  98. package/src/actions/errorBands/create.js +41 -8
  99. package/src/actions/errorBands/edit.js +360 -25
  100. package/src/actions/errorBars/create.js +56 -17
  101. package/src/actions/errorBars/edit.js +372 -21
  102. package/src/actions/facets/actions.js +619 -41
  103. package/src/actions/facets/derive.js +139 -25
  104. package/src/actions/gradientPlots/components.js +43 -13
  105. package/src/actions/gradientPlots/edit.js +14 -4
  106. package/src/actions/gradientPlots/materialize.js +8 -8
  107. package/src/actions/gradientPlots/options.js +10 -2
  108. package/src/actions/guides/applicability.js +28 -19
  109. package/src/actions/guides/axes/axes.js +4 -3
  110. package/src/actions/guides/axes/axis.js +18 -18
  111. package/src/actions/guides/axes/components.js +43 -0
  112. package/src/actions/guides/axes/edit.js +16 -51
  113. package/src/actions/guides/axes/labels.js +184 -28
  114. package/src/actions/guides/axes/lines.js +13 -7
  115. package/src/actions/guides/axes/parallel/lifecycle.js +117 -0
  116. package/src/actions/guides/axes/parallel/policy.js +137 -0
  117. package/src/actions/guides/axes/parallel/resolve.js +50 -25
  118. package/src/actions/guides/axes/parallel.js +93 -217
  119. package/src/actions/guides/axes/policy.js +21 -67
  120. package/src/actions/guides/axes/tickGroups.js +15 -8
  121. package/src/actions/guides/axes/ticks.js +11 -6
  122. package/src/actions/guides/axes/titles.js +32 -11
  123. package/src/actions/guides/basic.js +2 -0
  124. package/src/actions/guides/facade.js +106 -0
  125. package/src/actions/guides/facadeAxes.js +200 -0
  126. package/src/actions/guides/facadeLegend.js +63 -0
  127. package/src/actions/guides/grids/grid.js +37 -33
  128. package/src/actions/guides/grids/resolve.js +17 -4
  129. package/src/actions/guides/guides.js +54 -31
  130. package/src/actions/guides/legends/blocks.js +426 -0
  131. package/src/actions/guides/legends/categorical/actions.js +309 -279
  132. package/src/actions/guides/legends/categorical/components.js +21 -17
  133. package/src/actions/guides/legends/categorical/index.js +37 -33
  134. package/src/actions/guides/legends/categorical/layout.js +123 -354
  135. package/src/actions/guides/legends/categorical/options.js +32 -16
  136. package/src/actions/guides/legends/categorical/recipes.js +114 -9
  137. package/src/actions/guides/legends/categorical/resolve.js +30 -15
  138. package/src/actions/guides/legends/categorical/symbols.js +87 -33
  139. package/src/actions/guides/legends/continuous/common.js +99 -26
  140. package/src/actions/guides/legends/continuous/gradient.js +120 -89
  141. package/src/actions/guides/legends/continuous/index.js +8 -2
  142. package/src/actions/guides/legends/continuous/interval.js +79 -95
  143. package/src/actions/guides/legends/continuous/opacity.js +113 -89
  144. package/src/actions/guides/legends/continuous/stroke.js +525 -0
  145. package/src/actions/guides/legends/creation.js +40 -0
  146. package/src/actions/guides/legends/edit.js +321 -90
  147. package/src/actions/guides/legends/focused.js +2 -2
  148. package/src/actions/guides/legends/index.js +2 -0
  149. package/src/actions/guides/legends/lane.js +149 -102
  150. package/src/actions/guides/legends/lifecycle.js +143 -0
  151. package/src/actions/guides/legends/remove.js +43 -78
  152. package/src/actions/guides/legends/sampling.js +140 -0
  153. package/src/actions/guides/legends/size.js +213 -116
  154. package/src/actions/guides/legends/strokeWidth.js +121 -73
  155. package/src/actions/guides/legends/target.js +123 -0
  156. package/src/actions/guides/legends/transition.js +192 -0
  157. package/src/actions/guides/polar/axes/facade.js +81 -66
  158. package/src/actions/guides/polar/axes/labels.js +28 -8
  159. package/src/actions/guides/polar/axes/lines.js +12 -5
  160. package/src/actions/guides/polar/axes/shared.js +22 -0
  161. package/src/actions/guides/polar/axes/ticks.js +6 -5
  162. package/src/actions/guides/polar/axes/titles.js +6 -5
  163. package/src/actions/guides/polar/grids.js +8 -8
  164. package/src/actions/guides/polar/resolve.js +12 -8
  165. package/src/actions/guides/reuse.js +44 -0
  166. package/src/actions/guides/tickValues.js +19 -21
  167. package/src/actions/index.js +15 -2
  168. package/src/actions/intervals/resolve.js +35 -15
  169. package/src/actions/marks/arc/actions.js +78 -20
  170. package/src/actions/marks/area/actions.js +51 -12
  171. package/src/actions/marks/area/materialize.js +22 -3
  172. package/src/actions/marks/bar/create.js +11 -2
  173. package/src/actions/marks/bar/edit.js +20 -3
  174. package/src/actions/marks/bar/materialize.js +33 -9
  175. package/src/actions/marks/index.js +14 -0
  176. package/src/actions/marks/line/actions.js +71 -113
  177. package/src/actions/marks/line/materialize.js +57 -56
  178. package/src/actions/marks/point/create.js +8 -2
  179. package/src/actions/marks/point/edit.js +29 -21
  180. package/src/actions/marks/point/index.js +1 -0
  181. package/src/actions/marks/point/jitter.js +3 -0
  182. package/src/actions/marks/point/materialize.js +154 -16
  183. package/src/actions/marks/point/packing.js +68 -0
  184. package/src/actions/marks/rect/actions.js +33 -12
  185. package/src/actions/marks/references.js +658 -0
  186. package/src/actions/marks/remove.js +87 -4
  187. package/src/actions/marks/rule/actions.js +77 -9
  188. package/src/actions/marks/rule/appearance.js +26 -0
  189. package/src/actions/marks/shared.js +13 -13
  190. package/src/actions/marks/text/actions.js +296 -13
  191. package/src/actions/marks/text/index.js +4 -0
  192. package/src/actions/marks/text/layout.js +156 -8
  193. package/src/actions/marks/text/removal.js +173 -0
  194. package/src/actions/marks/text/selection.js +73 -0
  195. package/src/actions/marks/tick/actions.js +22 -5
  196. package/src/actions/primitives/createGraphics.js +3 -1
  197. package/src/actions/primitives/editGraphics.js +26 -0
  198. package/src/actions/primitives/semantic.js +3 -2
  199. package/src/actions/primitives/semanticAction.js +120 -10
  200. package/src/actions/primitives/semanticValidation/guide.js +10 -0
  201. package/src/actions/primitives/semanticValidation/index.js +9 -1
  202. package/src/actions/primitives/semanticValidation/layer.js +23 -1
  203. package/src/actions/primitives/semanticValidation/scale.js +38 -8
  204. package/src/actions/regression/components.js +26 -11
  205. package/src/actions/regression/create.js +11 -3
  206. package/src/actions/regression/edit.js +40 -13
  207. package/src/actions/regression/resolve.js +1 -1
  208. package/src/actions/resources/index.js +7 -0
  209. package/src/actions/resources/remove.js +172 -0
  210. package/src/actions/scales/channels.js +212 -0
  211. package/src/actions/scales/consumers/common.js +21 -54
  212. package/src/actions/scales/consumers/families.js +28 -14
  213. package/src/actions/scales/consumers/index.js +42 -4
  214. package/src/actions/scales/consumers/seriesLayout.js +57 -12
  215. package/src/actions/scales/create.js +58 -26
  216. package/src/actions/scales/definitions.js +109 -39
  217. package/src/actions/scales/edit.js +101 -34
  218. package/src/actions/scales/editPolicy.js +95 -35
  219. package/src/actions/scales/index.js +6 -0
  220. package/src/actions/scales/materialize.js +14 -49
  221. package/src/actions/scales/offset.js +48 -0
  222. package/src/actions/scales/parallel.js +71 -0
  223. package/src/actions/scales/patch.js +1 -1
  224. package/src/actions/scales/preview.js +140 -0
  225. package/src/actions/scales/quantitativeColor.js +9 -5
  226. package/src/actions/selection/actions.js +154 -30
  227. package/src/actions/selection/index.js +3 -0
  228. package/src/actions/theme/actions.js +98 -0
  229. package/src/actions/theme/composition.js +238 -0
  230. package/src/actions/theme/index.js +16 -0
  231. package/src/actions/theme/reconcile.js +1130 -0
  232. package/src/actions/theme/state.js +160 -0
  233. package/src/actions/titles/actions.js +11 -10
  234. package/src/actions/titles/resolve.js +1 -40
  235. package/src/actions/violinPlots/create.js +90 -43
  236. package/src/actions/violinPlots/edit.js +192 -0
  237. package/src/actions/violinPlots/index.js +2 -0
  238. package/src/core/ChartProgram.js +7 -0
  239. package/src/core/action.js +32 -2
  240. package/src/core/compositionState.js +141 -8
  241. package/src/core/programState.js +20 -0
  242. package/src/core/resourceReferences.js +332 -0
  243. package/src/core/vocabulary.js +16 -3
  244. package/src/grammar/aggregate.js +66 -23
  245. package/src/grammar/arcs.js +58 -1
  246. package/src/grammar/areaEndpoints.js +46 -0
  247. package/src/grammar/areaSeries.js +87 -41
  248. package/src/grammar/bars/aggregate.js +37 -13
  249. package/src/grammar/bars/geometry.js +44 -0
  250. package/src/grammar/bars/policy.js +9 -7
  251. package/src/grammar/bin.js +226 -0
  252. package/src/grammar/categoryOrder.js +34 -0
  253. package/src/grammar/complete.js +279 -0
  254. package/src/grammar/computed.js +351 -0
  255. package/src/grammar/coordinates.js +37 -0
  256. package/src/grammar/density.js +239 -37
  257. package/src/grammar/displayLabels.js +57 -0
  258. package/src/grammar/ecdf.js +198 -0
  259. package/src/grammar/facets/dependencies.js +133 -1
  260. package/src/grammar/facets/guides.js +3 -0
  261. package/src/grammar/facets/index.js +123 -52
  262. package/src/grammar/facets/scales.js +15 -18
  263. package/src/grammar/fold.js +123 -0
  264. package/src/grammar/horizon.js +6 -4
  265. package/src/grammar/impute.js +340 -0
  266. package/src/grammar/interval.js +37 -20
  267. package/src/grammar/lineSeries.js +20 -63
  268. package/src/grammar/markFilter.js +22 -4
  269. package/src/grammar/markLabelSelection.js +57 -0
  270. package/src/grammar/markLabels.js +110 -0
  271. package/src/grammar/normalize.js +366 -0
  272. package/src/grammar/parallelAxisTitles.js +16 -0
  273. package/src/grammar/pathSeries.js +109 -0
  274. package/src/grammar/pointPacking.js +183 -0
  275. package/src/grammar/pointShapes.js +13 -4
  276. package/src/grammar/polar.js +90 -8
  277. package/src/grammar/positionDatum.js +12 -0
  278. package/src/grammar/rects.js +9 -1
  279. package/src/grammar/regression/derive.js +4 -0
  280. package/src/grammar/regression/index.js +8 -2
  281. package/src/grammar/regression/models.js +22 -7
  282. package/src/grammar/regression/parameters.js +63 -13
  283. package/src/grammar/rotation.js +29 -0
  284. package/src/grammar/roundedRect.js +180 -0
  285. package/src/grammar/rules.js +8 -21
  286. package/src/grammar/scales/color.js +23 -1
  287. package/src/grammar/scales/colorConsumers.js +57 -0
  288. package/src/grammar/scales/continuous.js +2 -2
  289. package/src/grammar/scales/definition.js +42 -5
  290. package/src/grammar/scales/discretized.js +11 -5
  291. package/src/grammar/scales/fields.js +38 -8
  292. package/src/grammar/scales/index.js +1 -0
  293. package/src/grammar/scales/mapping.js +2 -0
  294. package/src/grammar/scales/ordinal.js +12 -3
  295. package/src/grammar/scales/policies.js +2 -2
  296. package/src/grammar/scales/radial.js +55 -0
  297. package/src/grammar/scales/size.js +456 -0
  298. package/src/grammar/scales/types.js +1 -0
  299. package/src/grammar/schemas/concreteGraphic.js +14 -2
  300. package/src/grammar/schemas/graphic.js +15 -0
  301. package/src/grammar/schemas/graphicBounds.js +179 -30
  302. package/src/grammar/schemas/semanticPath.js +23 -4
  303. package/src/grammar/stack.js +170 -0
  304. package/src/grammar/statisticalReference.js +103 -0
  305. package/src/grammar/statistics/confidenceInterval.js +101 -0
  306. package/src/grammar/strokeStyle.js +77 -0
  307. package/src/grammar/summary.js +245 -0
  308. package/src/grammar/text.js +22 -16
  309. package/src/grammar/timeUnit.js +234 -13
  310. package/src/grammar/transformTopology.js +10 -0
  311. package/src/grammar/transforms.js +418 -16
  312. package/src/grammar/valueFormat.js +112 -0
  313. package/src/grammar/weightedStatistics.js +374 -0
  314. package/src/grammar/window.js +232 -21
  315. package/src/layout/aspect.js +80 -0
  316. package/src/layout/facets.js +100 -12
  317. package/src/layout/guideCollisions.js +12 -0
  318. package/src/layout/labels.js +343 -0
  319. package/src/layout/legend.js +2 -10
  320. package/src/layout/legendItems.js +102 -0
  321. package/src/layout/legendLane.js +100 -11
  322. package/src/materialization/bars/aggregate.js +56 -147
  323. package/src/materialization/bars/histogram.js +104 -20
  324. package/src/materialization/bars/ranged.js +19 -3
  325. package/src/materialization/bars/resolve.js +7 -4
  326. package/src/materialization/categorySlotOffset.js +40 -0
  327. package/src/materialization/composition.js +3 -1
  328. package/src/materialization/compositionSnapshot.js +1 -1
  329. package/src/materialization/coordinateBounds.js +204 -0
  330. package/src/materialization/dependencies.js +93 -16
  331. package/src/materialization/encodings.js +1 -1
  332. package/src/materialization/facetGuides/legacyCategorical.js +3 -0
  333. package/src/materialization/facetGuides/placement.js +8 -2
  334. package/src/materialization/facetGuides/preparation.js +12 -7
  335. package/src/materialization/facetHeaders.js +346 -0
  336. package/src/materialization/facets.js +23 -66
  337. package/src/materialization/guides/layout.js +57 -0
  338. package/src/materialization/guides/resources.js +28 -2
  339. package/src/materialization/legends.js +11 -2
  340. package/src/materialization/marks/capabilities.js +20 -5
  341. package/src/materialization/marks/grouping.js +52 -0
  342. package/src/materialization/marks/index.js +68 -27
  343. package/src/materialization/marks/policies.js +4 -1
  344. package/src/materialization/rect.js +55 -42
  345. package/src/materialization/rectConfig.js +5 -1
  346. package/src/materialization/rowEncoding.js +13 -13
  347. package/src/materialization/scaleGuideDependencies.js +8 -5
  348. package/src/materialization/scales/map.js +6 -3
  349. package/src/materialization/scales/policies/arc.js +42 -0
  350. package/src/materialization/scales/policies/binnedPosition.js +7 -4
  351. package/src/materialization/scales/policies/offset.js +50 -8
  352. package/src/materialization/scales/policies/series.js +5 -1
  353. package/src/materialization/scales/resolve.js +65 -17
  354. package/src/materialization/selection/compatibility.js +11 -0
  355. package/src/materialization/selection/items/arc.js +28 -4
  356. package/src/materialization/selection/items/bar.js +42 -71
  357. package/src/materialization/selection/items/common.js +27 -6
  358. package/src/materialization/selection/items/path.js +3 -13
  359. package/src/materialization/selection/items/rect.js +35 -8
  360. package/src/materialization/selection/path.js +1 -1
  361. package/src/materialization/selection/point.js +3 -1
  362. package/src/materialization/text.js +324 -14
  363. package/src/mcp/adapter.js +2 -2
  364. package/src/renderers/canvas/circle.js +2 -3
  365. package/src/renderers/canvas/line.js +2 -3
  366. package/src/renderers/canvas/native.js +4 -4
  367. package/src/renderers/canvas/path.js +2 -3
  368. package/src/renderers/canvas/rect.js +2 -3
  369. package/src/renderers/canvas/stroke.js +11 -0
  370. package/src/renderers/svg.js +19 -0
  371. package/src/selectors/datasets.js +44 -0
  372. package/src/selectors/layers.js +7 -2
  373. package/src/selectors/scales.js +6 -0
  374. package/src/theme/defaults.js +140 -0
  375. package/types/basic.d.ts +17 -2
  376. package/types/index.d.ts +211 -0
  377. package/types/program.d.ts +2182 -204
  378. package/src/materialization/bars/grouped.js +0 -144
@@ -19,7 +19,11 @@ import { DEFAULT_COLORS } from "../../../theme/defaults.js";
19
19
  import { findDataset } from "../../../selectors/datasets.js";
20
20
  import { findLayer, resolveEligibleLayer } from "../../../selectors/layers.js";
21
21
  import { validateCurveInterpolation } from "../../../grammar/curveCommands.js";
22
- import { resolveGraphicBounds } from "../../../layout/canvas.js";
22
+ import {
23
+ resolveCoordinateBounds,
24
+ resolveCoordinatePolarFrame
25
+ } from "../../../materialization/coordinateBounds.js";
26
+ import { rematerializeExistingLegend } from "../../encodings/shared.js";
23
27
  import { canMaterializeLine } from "../../../materialization/marks/index.js";
24
28
  import { resolveMarkGraphicPlacement } from
25
29
  "../../../materialization/graphicHierarchy.js";
@@ -31,14 +35,20 @@ import {
31
35
  } from "./materialize.js";
32
36
  import { resolveLineBins } from "../../../grammar/lineSeries.js";
33
37
  import { requireSemanticScale } from "../../../selectors/scales.js";
38
+ import {
39
+ requestedStrokeDetails,
40
+ STROKE_STYLE_PROPERTIES
41
+ } from "../../../grammar/strokeStyle.js";
34
42
 
35
43
  const DEFAULT_LINE_STROKE = DEFAULT_COLORS.mark;
36
44
  const DEFAULT_LINE_WIDTH = 2;
37
45
  const CREATE_OPTIONS = Object.freeze([
38
- "id", "data", "stroke", "strokeWidth", "opacity", "curve", "closed"
46
+ "id", "data", "stroke", "strokeWidth", "opacity", "curve", "closed",
47
+ ...STROKE_STYLE_PROPERTIES
39
48
  ]);
40
49
  const EDIT_OPTIONS = Object.freeze([
41
- "target", "stroke", "strokeWidth", "opacity", "curve", "closed"
50
+ "target", "stroke", "strokeWidth", "opacity", "curve", "closed",
51
+ ...STROKE_STYLE_PROPERTIES
42
52
  ]);
43
53
  const REMATERIALIZE_OPTIONS = Object.freeze(["id", "scales"]);
44
54
 
@@ -71,14 +81,15 @@ function validatePolarLineConfig(layer, config) {
71
81
  }
72
82
  }
73
83
 
74
- function applyLineMaterialization(program, id, materialization, opacity) {
84
+ function applyLineMaterialization(program, id, materialization, config) {
75
85
  return editMarkGraphic(program, id, {
76
86
  length: materialization.commands.length,
77
87
  commands: materialization.commands,
78
88
  stroke: materialization.strokes,
79
89
  strokeWidth: materialization.strokeWidths,
80
90
  strokeDash: materialization.strokeDashes,
81
- ...(opacity === undefined ? {} : { opacity })
91
+ ...(materialization.opacities === undefined ? {} : { opacity: materialization.opacities }),
92
+ ...requestedStrokeDetails(config, "Line mark")
82
93
  });
83
94
  }
84
95
 
@@ -89,6 +100,7 @@ const createLineMark = action(
89
100
  },
90
101
  function (args = {}) {
91
102
  validateMarkOptions(args, CREATE_OPTIONS, "createLineMark");
103
+ const strokeDetails = requestedStrokeDetails(args, "createLineMark");
92
104
  const id = resolveMarkId(this, args.id, {
93
105
  defaultId: "line",
94
106
  label: "Line mark id",
@@ -139,7 +151,8 @@ const createLineMark = action(
139
151
  {
140
152
  ...(Object.hasOwn(args, "strokeWidth") ? { strokeWidth } : {}),
141
153
  ...(Object.hasOwn(args, "curve") ? { curve } : {}),
142
- ...(Object.hasOwn(args, "closed") ? { closed } : {})
154
+ ...(Object.hasOwn(args, "closed") ? { closed } : {}),
155
+ ...strokeDetails
143
156
  }
144
157
  );
145
158
  created = materializeInheritedMark(created, id);
@@ -205,71 +218,34 @@ const rematerializeLineMark = action(
205
218
 
206
219
  if (parallel !== undefined) {
207
220
  validateParallelRows(dataset.values, parallel.dimensions, parallel);
208
- let resolved = this;
209
- if (args.scales !== false) {
210
- for (const dimension of parallel.dimensions) {
211
- resolved = resolved.rematerializeScale({ id: dimension.scale });
212
- }
213
- }
214
- for (const channel of args.scales === false
215
- ? []
216
- : ["color", "strokeDash", "strokeWidth"]) {
217
- const scaleId = layer.encoding?.[channel]?.scale;
218
- if (scaleId !== undefined) {
219
- resolved = resolved.rematerializeScale({ id: scaleId });
220
- }
221
- }
222
- const materialization = resolveParallelLineMaterialization({
223
- rows: dataset.values,
224
- parallel,
225
- layer,
226
- resolvedScales: resolved.resolvedScales,
227
- bounds: resolveGraphicBounds(resolved),
228
- config,
229
- existingChildren,
230
- defaults: { stroke: DEFAULT_LINE_STROKE, strokeWidth: DEFAULT_LINE_WIDTH }
231
- });
232
- return applyLineMaterialization(
233
- resolved,
234
- id,
235
- materialization,
236
- config.opacity
237
- );
238
221
  }
239
-
240
- if (
241
- polar
242
- ? thetaScaleId === undefined || radiusScaleId === undefined
243
- : xScaleId === undefined || yScaleId === undefined
244
- ) {
245
- throw new Error(
246
- polar
247
- ? `Line mark "${id}" requires theta and radius scales.`
248
- : `Line mark "${id}" requires x and y scales.`
249
- );
222
+ const positions = parallel !== undefined
223
+ ? parallel.dimensions.map(dimension => dimension.scale)
224
+ : polar ? [thetaScaleId, radiusScaleId] : [xScaleId, yScaleId];
225
+ if (positions.some(scale => scale === undefined)) {
226
+ throw new Error(`Line mark "${id}" requires ${polar ? "theta and radius" : "x and y"} scales.`);
250
227
  }
251
-
252
- let resolved = args.scales === false
253
- ? this
254
- : polar
255
- ? this
256
- .rematerializeScale({ id: thetaScaleId })
257
- .rematerializeScale({ id: radiusScaleId })
258
- : this
259
- .rematerializeScale({ id: xScaleId })
260
- .rematerializeScale({ id: yScaleId });
261
-
262
- for (const channel of args.scales === false ? [] : ["color", "strokeDash", "strokeWidth"]) {
263
- const scaleId = layer.encoding?.[channel]?.scale;
264
- if (scaleId !== undefined) {
265
- resolved = resolved.rematerializeScale({ id: scaleId });
228
+ let resolved = this;
229
+ if (args.scales !== false) {
230
+ const appearance = ["color", "stroke", "strokeDash", "strokeWidth", "opacity"]
231
+ .map(channel => layer.encoding?.[channel]?.scale).filter(scale => scale !== undefined);
232
+ for (const scale of [...positions, ...appearance]) {
233
+ resolved = resolved.rematerializeScale({ id: scale });
266
234
  }
267
235
  }
236
+ const shared = {
237
+ rows: dataset.values, layer, config, existingChildren,
238
+ resolvedScales: resolved.resolvedScales,
239
+ bounds: resolveCoordinateBounds(resolved, layer.coordinate),
240
+ defaults: { stroke: DEFAULT_LINE_STROKE, strokeWidth: DEFAULT_LINE_WIDTH }
241
+ };
242
+ if (parallel !== undefined) {
243
+ return applyLineMaterialization(resolved, id,
244
+ resolveParallelLineMaterialization({ ...shared, parallel }), config);
245
+ }
268
246
 
269
247
  const materialization = resolvePositionedLineMaterialization({
270
- rows: dataset.values,
271
- layer,
272
- resolvedScales: resolved.resolvedScales,
248
+ ...shared,
273
249
  xBinBoundaries: layer.encoding?.x?.bin === undefined
274
250
  ? undefined
275
251
  : resolveLineBins(
@@ -277,18 +253,17 @@ const rematerializeLineMark = action(
277
253
  layer,
278
254
  requireSemanticScale(resolved, xScaleId)
279
255
  ).boundaries,
280
- bounds: resolveGraphicBounds(resolved),
281
- config,
282
- existingChildren,
283
256
  polar,
284
- defaults: { stroke: DEFAULT_LINE_STROKE, strokeWidth: DEFAULT_LINE_WIDTH }
257
+ ...(polar
258
+ ? { polarFrame: resolveCoordinatePolarFrame(resolved, layer.coordinate) }
259
+ : {})
285
260
  });
286
261
 
287
262
  return applyLineMaterialization(
288
263
  resolved,
289
264
  id,
290
265
  materialization,
291
- config.opacity
266
+ config
292
267
  );
293
268
  }
294
269
  );
@@ -300,16 +275,9 @@ const editLineMark = action(
300
275
  },
301
276
  function (args = {}) {
302
277
  validateMarkOptions(args, EDIT_OPTIONS, "editLineMark");
303
- if (
304
- !Object.hasOwn(args, "stroke") &&
305
- !Object.hasOwn(args, "strokeWidth") &&
306
- !Object.hasOwn(args, "opacity") &&
307
- !Object.hasOwn(args, "curve") &&
308
- !Object.hasOwn(args, "closed")
309
- ) {
310
- throw new Error(
311
- "editLineMark requires stroke, strokeWidth, opacity, curve, or closed."
312
- );
278
+ const strokeDetails = requestedStrokeDetails(args, "editLineMark");
279
+ if (!EDIT_OPTIONS.slice(1).some(key => Object.hasOwn(args, key))) {
280
+ throw new Error("editLineMark requires stroke, strokeWidth, opacity, curve, or closed.");
313
281
  }
314
282
  const target = Object.hasOwn(args, "target")
315
283
  ? validateUserId(args.target, "Line mark id")
@@ -319,50 +287,40 @@ const editLineMark = action(
319
287
  predicate: candidate => candidate.mark?.type === "line",
320
288
  label: "line mark"
321
289
  });
322
- if (Object.hasOwn(args, "stroke") && layer.encoding?.color !== undefined) {
290
+ if (Object.hasOwn(args, "stroke") && (
291
+ layer.encoding?.color !== undefined ||
292
+ layer.encoding?.stroke !== undefined
293
+ )) {
323
294
  throw new Error(
324
- "editLineMark stroke cannot be combined with a color encoding."
295
+ "editLineMark stroke cannot be combined with a color encoding or a stroke encoding; use encodeStroke with value to replace it."
325
296
  );
326
297
  }
298
+ for (const channel of ["strokeWidth", "opacity"]) {
299
+ if (Object.hasOwn(args, channel) && layer.encoding?.[channel]?.field !== undefined) {
300
+ throw new Error(`editLineMark ${channel} conflicts with a field encoding; use its encoder with value to replace it.`);
301
+ }
302
+ }
327
303
  if (Object.hasOwn(args, "closed") && args.closed === true &&
328
304
  (layer.encoding?.x !== undefined || isParallelLine(layer))) {
329
305
  throw new Error("Line closed requires theta/radius Polar position encodings.");
330
306
  }
331
- if (Object.hasOwn(args, "curve") && isPolarLine(layer) &&
332
- args.curve !== "linear") {
333
- throw new Error("Polar line position currently requires curve \"linear\".");
334
- }
335
- if (Object.hasOwn(args, "curve") && isParallelLine(layer) &&
336
- args.curve !== "linear") {
337
- throw new Error("Parallel lines require curve \"linear\".");
307
+ const config = { ...this.markConfigs[layer.id] };
308
+ Object.assign(config, strokeDetails);
309
+ for (const [key, validate] of Object.entries({
310
+ stroke: validateNonEmptyString,
311
+ strokeWidth: validateNonNegativeFinite,
312
+ opacity: validateUnitInterval,
313
+ curve: validateCurveInterpolation,
314
+ closed: validateClosed
315
+ })) {
316
+ if (Object.hasOwn(args, key)) config[key] = validate(args[key], `Line ${key}`);
338
317
  }
339
- const config = {
340
- ...this.markConfigs[layer.id],
341
- ...(Object.hasOwn(args, "stroke")
342
- ? { stroke: validateNonEmptyString(args.stroke, "Line stroke") }
343
- : {}),
344
- ...(Object.hasOwn(args, "strokeWidth")
345
- ? {
346
- strokeWidth: validateNonNegativeFinite(
347
- args.strokeWidth,
348
- "Line strokeWidth"
349
- )
350
- }
351
- : {}),
352
- ...(Object.hasOwn(args, "opacity")
353
- ? { opacity: validateUnitInterval(args.opacity, "Line opacity") }
354
- : {}),
355
- ...(Object.hasOwn(args, "curve")
356
- ? { curve: validateCurveInterpolation(args.curve) }
357
- : {}),
358
- ...(Object.hasOwn(args, "closed")
359
- ? { closed: validateClosed(args.closed) }
360
- : {})
361
- };
318
+ validatePolarLineConfig(layer, config);
362
319
  const next = this._withMarkConfig(layer.id, config);
363
- return canMaterializeLine(next, layer)
320
+ const materialized = canMaterializeLine(next, layer)
364
321
  ? next.rematerializeLineMark({ id: layer.id })
365
322
  : next;
323
+ return rematerializeExistingLegend(materialized);
366
324
  }
367
325
  );
368
326
 
@@ -1,10 +1,8 @@
1
1
  import {
2
- deriveLineSeries,
3
- deriveLineSeriesFieldValues
2
+ deriveLineSeries
4
3
  } from "../../../grammar/lineSeries.js";
5
4
  import {
6
5
  mapContinuousScaleValues,
7
- mapOrdinalValues,
8
6
  normalizeStrokeDashPattern
9
7
  } from "../../../grammar/scales/index.js";
10
8
  import { buildCurvePathCommands } from
@@ -12,6 +10,7 @@ import { buildCurvePathCommands } from
12
10
  import { buildPolarLinePathCommands } from
13
11
  "../../../grammar/polarLineCommands.js";
14
12
  import { resolvePolarFrame } from "../../../grammar/polar.js";
13
+ import { derivePathSeriesFieldValues } from "../../../grammar/pathSeries.js";
15
14
  import { materializeParallelRows } from
16
15
  "../../../grammar/parallelCoordinates.js";
17
16
  import { mapScaleConsumerValues } from
@@ -21,6 +20,21 @@ function existingValue(children, index, property, fallback) {
21
20
  return children[index]?.properties[property] ?? fallback;
22
21
  }
23
22
 
23
+ function appearanceMapper(layer, scales, fieldValues) {
24
+ return (channel, fallback) => {
25
+ const encoding = layer.encoding?.[channel];
26
+ if (encoding?.datum !== undefined) {
27
+ return fallback.map(() => normalizeStrokeDashPattern(encoding.datum));
28
+ }
29
+ if (encoding?.scale === undefined) return fallback;
30
+ return mapScaleConsumerValues(
31
+ fieldValues(encoding.field, channel, encoding),
32
+ scales[encoding.scale],
33
+ channel
34
+ );
35
+ };
36
+ }
37
+
24
38
  export function resolveParallelLineMaterialization({
25
39
  rows,
26
40
  parallel,
@@ -39,36 +53,29 @@ export function resolveParallelLineMaterialization({
39
53
  parallel
40
54
  );
41
55
  const sourceRows = items.map(item => rows[item.sourceRowIndex]);
42
- const mapAppearance = (channel, fallback) => {
43
- const encoding = layer.encoding?.[channel];
44
- if (encoding?.datum !== undefined) {
45
- return items.map(() => channel === "strokeDash"
46
- ? normalizeStrokeDashPattern(encoding.datum)
47
- : encoding.datum);
48
- }
49
- if (encoding?.scale === undefined) return items.map(fallback);
50
- return mapScaleConsumerValues(
51
- sourceRows.map(row => row[encoding.field]),
52
- resolvedScales[encoding.scale],
53
- channel
54
- );
55
- };
56
+ const mapAppearance = appearanceMapper(layer, resolvedScales,
57
+ field => sourceRows.map(row => row[field]));
56
58
  return {
57
59
  commands: items.map(item => item.commands),
58
- strokes: mapAppearance(
59
- "color",
60
- (_, index) => config.stroke ??
61
- existingValue(existingChildren, index, "stroke", defaults.stroke)
62
- ),
60
+ strokes: layer.encoding?.stroke?.scale !== undefined
61
+ ? mapAppearance("stroke", items.map(() => defaults.stroke))
62
+ : config.stroke !== undefined
63
+ ? items.map(() => config.stroke)
64
+ : mapAppearance(
65
+ "color",
66
+ items.map((_, index) =>
67
+ existingValue(existingChildren, index, "stroke", defaults.stroke))
68
+ ),
63
69
  strokeWidths: mapAppearance(
64
70
  "strokeWidth",
65
- (_, index) => config.strokeWidth ??
66
- existingValue(existingChildren, index, "strokeWidth", defaults.strokeWidth)
71
+ items.map((_, index) => config.strokeWidth ??
72
+ existingValue(existingChildren, index, "strokeWidth", defaults.strokeWidth))
67
73
  ),
68
74
  strokeDashes: mapAppearance(
69
75
  "strokeDash",
70
- (_, index) => existingValue(existingChildren, index, "strokeDash", [])
71
- )
76
+ items.map((_, index) => existingValue(existingChildren, index, "strokeDash", []))
77
+ ),
78
+ opacities: mapAppearance("opacity", config.opacity)
72
79
  };
73
80
  }
74
81
 
@@ -81,6 +88,7 @@ export function resolvePositionedLineMaterialization({
81
88
  config,
82
89
  existingChildren,
83
90
  polar,
91
+ polarFrame,
84
92
  defaults
85
93
  }) {
86
94
  const xScaleId = layer.encoding?.x?.scale;
@@ -99,7 +107,7 @@ export function resolvePositionedLineMaterialization({
99
107
  thetaFieldType: derived.thetaFieldType,
100
108
  thetaScale: resolvedScales[thetaScaleId],
101
109
  radiusScale: resolvedScales[radiusScaleId],
102
- frame: resolvePolarFrame(bounds),
110
+ frame: polarFrame ?? resolvePolarFrame(bounds),
103
111
  closed: config.closed ?? false
104
112
  }))
105
113
  : derived.series.map(series => {
@@ -116,33 +124,26 @@ export function resolvePositionedLineMaterialization({
116
124
  config.curve ?? "linear"
117
125
  );
118
126
  });
119
- const colorEncoding = layer.encoding?.color;
120
- const dashEncoding = layer.encoding?.strokeDash;
121
- const widthEncoding = layer.encoding?.strokeWidth;
122
- const strokes = colorEncoding?.scale === undefined
123
- ? commands.map((_, index) => config.stroke ??
124
- existingValue(existingChildren, index, "stroke", defaults.stroke))
125
- : mapOrdinalValues(
126
- derived.series.map(series => series.key[colorEncoding.field]),
127
- resolvedScales[colorEncoding.scale].domain,
128
- resolvedScales[colorEncoding.scale].range
129
- );
130
- const strokeWidths = widthEncoding?.scale === undefined
131
- ? commands.map((_, index) => config.strokeWidth ??
132
- existingValue(existingChildren, index, "strokeWidth", defaults.strokeWidth))
133
- : mapContinuousScaleValues(
134
- deriveLineSeriesFieldValues(rows, layer, derived, widthEncoding.field),
135
- resolvedScales[widthEncoding.scale]
136
- );
137
- const strokeDashes = dashEncoding?.datum !== undefined
138
- ? commands.map(() => normalizeStrokeDashPattern(dashEncoding.datum))
139
- : dashEncoding?.scale === undefined
140
- ? commands.map((_, index) =>
141
- existingValue(existingChildren, index, "strokeDash", []))
142
- : mapOrdinalValues(
143
- derived.series.map(series => series.key[dashEncoding.field]),
144
- resolvedScales[dashEncoding.scale].domain,
145
- resolvedScales[dashEncoding.scale].range
146
- );
147
- return { commands, strokes, strokeWidths, strokeDashes };
127
+ const appearance = appearanceMapper(layer, resolvedScales,
128
+ (field, channel, encoding) => derivePathSeriesFieldValues(
129
+ rows,
130
+ derived.series,
131
+ field,
132
+ channel,
133
+ encoding
134
+ ));
135
+ return {
136
+ commands,
137
+ strokes: layer.encoding?.stroke?.scale !== undefined
138
+ ? appearance("stroke", commands.map(() => defaults.stroke))
139
+ : config.stroke !== undefined
140
+ ? commands.map(() => config.stroke)
141
+ : appearance("color", commands.map((_, index) =>
142
+ existingValue(existingChildren, index, "stroke", defaults.stroke))),
143
+ strokeWidths: appearance("strokeWidth", commands.map((_, index) => config.strokeWidth ??
144
+ existingValue(existingChildren, index, "strokeWidth", defaults.strokeWidth))),
145
+ strokeDashes: appearance("strokeDash", commands.map((_, index) =>
146
+ existingValue(existingChildren, index, "strokeDash", []))),
147
+ opacities: appearance("opacity", config.opacity)
148
+ };
148
149
  }
@@ -1,6 +1,10 @@
1
1
  import { action } from "../../../core/action.js";
2
2
  import { validatePointShape } from "../../../grammar/pointShapes.js";
3
3
  import { getPointGraphicType } from "../../../grammar/schemas/mark.js";
4
+ import {
5
+ requestedStrokeDetails,
6
+ STROKE_STYLE_PROPERTIES
7
+ } from "../../../grammar/strokeStyle.js";
4
8
  import { resolveMarkGraphicPlacement } from
5
9
  "../../../materialization/graphicHierarchy.js";
6
10
  import {
@@ -14,7 +18,8 @@ import {
14
18
  } from "../shared.js";
15
19
 
16
20
  const OPTIONS = Object.freeze([
17
- "id", "data", "shape", "fill", "opacity", "stroke", "strokeWidth"
21
+ "id", "data", "shape", "fill", "opacity", "stroke", "strokeWidth",
22
+ ...STROKE_STYLE_PROPERTIES
18
23
  ]);
19
24
 
20
25
  export const createPointMark = action(
@@ -24,6 +29,7 @@ export const createPointMark = action(
24
29
  },
25
30
  function (args = {}) {
26
31
  validateMarkOptions(args, OPTIONS, "createPointMark");
32
+ requestedStrokeDetails(args, "createPointMark");
27
33
  const id = resolveMarkId(this, args.id, {
28
34
  defaultId: "point",
29
35
  label: "Point mark id",
@@ -65,7 +71,7 @@ export const createPointMark = action(
65
71
  ._withMarkConfig(id, { shape });
66
72
  const created = materializeInheritedMark(next, id);
67
73
  const appearance = Object.fromEntries(
68
- ["fill", "opacity", "stroke", "strokeWidth"]
74
+ ["fill", "opacity", "stroke", "strokeWidth", ...STROKE_STYLE_PROPERTIES]
69
75
  .filter(property => Object.hasOwn(args, property))
70
76
  .map(property => [property, args[property]])
71
77
  );
@@ -6,12 +6,17 @@ import {
6
6
  validateUnitInterval
7
7
  } from "../../../core/validation.js";
8
8
  import { validatePointShape } from "../../../grammar/pointShapes.js";
9
- import { findLayer } from "../../../selectors/layers.js";
9
+ import {
10
+ requestedStrokeDetails,
11
+ STROKE_STYLE_PROPERTIES
12
+ } from "../../../grammar/strokeStyle.js";
13
+ import { resolveEligibleLayer } from "../../../selectors/layers.js";
10
14
  import { rematerializeExistingLegend } from "../../encodings/shared.js";
11
15
  import { validateMarkOptions } from "../shared.js";
12
16
 
13
17
  const OPTIONS = Object.freeze([
14
- "target", "shape", "fill", "opacity", "stroke", "strokeWidth"
18
+ "target", "shape", "fill", "opacity", "stroke", "strokeWidth",
19
+ ...STROKE_STYLE_PROPERTIES
15
20
  ]);
16
21
 
17
22
  export const editPointMark = action(
@@ -21,26 +26,23 @@ export const editPointMark = action(
21
26
  },
22
27
  function (args = {}) {
23
28
  validateMarkOptions(args, OPTIONS, "editPointMark");
24
- const editable = ["shape", "fill", "opacity", "stroke", "strokeWidth"];
29
+ const strokeDetails = requestedStrokeDetails(args, "editPointMark");
30
+ const editable = [
31
+ "shape", "fill", "opacity", "stroke", "strokeWidth",
32
+ ...STROKE_STYLE_PROPERTIES
33
+ ];
25
34
  if (!editable.some(property => Object.hasOwn(args, property))) {
26
35
  throw new Error(
27
36
  "editPointMark requires shape, fill, opacity, stroke, or strokeWidth."
28
37
  );
29
38
  }
30
- const candidates = this.semanticSpec.layers.filter(
31
- layer => layer.mark?.type === "point"
32
- );
33
- const current = findLayer(this, this.context.currentMark);
34
- const requested = args.target ??
35
- (current?.mark?.type === "point" ? current.id : undefined);
36
- const inferred = requested ?? (candidates.length === 1
37
- ? candidates[0].id
38
- : undefined);
39
- const id = validateUserId(inferred, "Point mark id");
40
- const layer = findLayer(this, id);
41
- if (layer?.mark?.type !== "point") {
42
- throw new Error(`Unknown point mark "${id}".`);
43
- }
39
+ if (args.target !== undefined) validateUserId(args.target, "Point mark id");
40
+ const layer = resolveEligibleLayer(this, {
41
+ target: args.target,
42
+ predicate: candidate => candidate.mark?.type === "point",
43
+ label: "point mark"
44
+ });
45
+ const id = layer.id;
44
46
  if (Object.hasOwn(args, "shape") && layer.encoding?.shape !== undefined) {
45
47
  throw new Error(
46
48
  "editPointMark shape cannot be combined with a shape encoding."
@@ -51,7 +53,16 @@ export const editPointMark = action(
51
53
  "editPointMark fill cannot be combined with a color encoding."
52
54
  );
53
55
  }
56
+ if (Object.hasOwn(args, "stroke") && layer.encoding?.stroke !== undefined) {
57
+ throw new Error(
58
+ "editPointMark stroke conflicts with a field encoding; use encodeStroke with value to replace it."
59
+ );
60
+ }
61
+ if (Object.hasOwn(args, "opacity") && layer.encoding?.opacity?.field !== undefined) {
62
+ throw new Error("editPointMark opacity conflicts with a field encoding; use encodeOpacity with value to replace it.");
63
+ }
54
64
  const config = { ...this.markConfigs[id] };
65
+ Object.assign(config, strokeDetails);
55
66
  if (Object.hasOwn(args, "shape")) {
56
67
  config.shape = validatePointShape(args.shape);
57
68
  }
@@ -89,9 +100,6 @@ export const editPointMark = action(
89
100
  const next = this
90
101
  ._withMarkConfig(id, config)
91
102
  .rematerializePointMark({ id });
92
- const legend = next.guideConfigs.legend?.series;
93
- return legend?.target === id && legend.channels.includes("shape")
94
- ? rematerializeExistingLegend(next)
95
- : next;
103
+ return rematerializeExistingLegend(next);
96
104
  }
97
105
  );
@@ -3,6 +3,7 @@ import { editPointMark } from "./edit.js";
3
3
  import { rematerializePointMark } from "./materialize.js";
4
4
 
5
5
  export { registerPointJitterActions } from "./jitter.js";
6
+ export { registerPointPackingActions } from "./packing.js";
6
7
 
7
8
  export function registerPointMarkActions(ProgramClass) {
8
9
  ProgramClass.prototype.editPointMark = editPointMark;
@@ -37,6 +37,9 @@ export const jitterPoints = action(
37
37
  validateKeys(args, JITTER_OPTIONS, "jitterPoints");
38
38
  const policy = normalizePointJitterPolicy(args);
39
39
  const layer = resolveJitterTarget(this, args.target, policy.channel);
40
+ if (this.materializationConfigs.pointPacking?.[layer.id] !== undefined) {
41
+ throw new Error(`Point jitter on "${layer.id}" conflicts with point packing.`);
42
+ }
40
43
  return this
41
44
  ._withMaterializationConfig(["jitters", layer.id], policy)
42
45
  .rematerializePointMark({ id: layer.id });