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,4 +1,4 @@
1
- import { action } from "../../core/action.js";
1
+ import { closedAction } from "../../core/action.js";
2
2
  import { cloneAndFreeze, freezeOwned, isPlainObject } from "../../core/immutable.js";
3
3
  import { validateGeneratedItemLimit } from "../../core/validation.js";
4
4
  import {
@@ -63,6 +63,32 @@ function replaceDrawableItems(value, id, type) {
63
63
  });
64
64
  }
65
65
 
66
+ export function replaceGraphicItems(program, target, baseType, items) {
67
+ let next = program.editGraphics({ target, property: "items", value: items });
68
+ const graphic = next.graphicSpec.objects[target];
69
+ if (
70
+ graphic.type !== "collection" ||
71
+ !graphic.items.every(item => item.type === baseType)
72
+ ) return next;
73
+ const normalized = freezeOwned({
74
+ ...graphic,
75
+ type: baseType,
76
+ items: freezeOwned(graphic.items.map(item => freezeOwned({
77
+ id: item.id,
78
+ properties: item.properties
79
+ })))
80
+ });
81
+ return next._clone({
82
+ graphicSpec: freezeOwned({
83
+ ...next.graphicSpec,
84
+ objects: freezeOwned({
85
+ ...next.graphicSpec.objects,
86
+ [target]: normalized
87
+ })
88
+ })
89
+ });
90
+ }
91
+
66
92
  function editGeneratedItem(owner, itemIndex, property, value) {
67
93
  const items = [...owner.items];
68
94
  const item = items[itemIndex];
@@ -136,13 +162,14 @@ function removeGraphicTree(graphicSpec, id) {
136
162
  return freezeOwned({ objects: freezeOwned(objects), order });
137
163
  }
138
164
 
139
- const editGraphics = action(
165
+ const editGraphics = /* @__PURE__ */ closedAction(
140
166
  {
141
167
  op: "editGraphics",
142
168
  description: "Replace or remove concrete graphic state.",
143
169
  scope: "any"
144
- },
145
- function ({ target, property, value, remove = false } = {}) {
170
+ }, ["target", "property", "value", "remove"],
171
+ function (args = {}) {
172
+ const { target, property, value, remove = false } = args;
146
173
  if (typeof target !== "string" || target.length === 0) {
147
174
  throw new TypeError("editGraphics requires a non-empty target string.");
148
175
  }
@@ -0,0 +1,6 @@
1
+ export function editGraphicProperties(program, target, properties) {
2
+ for (const [property, value] of Object.entries(properties)) {
3
+ program = program.editGraphics({ target, property, value });
4
+ }
5
+ return program;
6
+ }
@@ -1,4 +1,5 @@
1
1
  import { createSemanticPrimitiveAction } from "./semanticAction.js";
2
+ import { planResourceRemoval } from "../resources/remove.js";
2
3
  import { validateDatasetTransforms } from "../../grammar/transforms.js";
3
4
  import {
4
5
  validateParallelDimensions,
@@ -17,12 +18,19 @@ function validateParallel(property, value) {
17
18
  }
18
19
  }
19
20
 
20
- export function registerSemanticPrimitiveAction(ProgramClass) {
21
- const validateSemanticValue = createSemanticValueValidator({
21
+ function fullSemanticValidator() {
22
+ return createSemanticValueValidator({
22
23
  validateDatasetTransforms,
23
24
  validateParallel,
24
25
  sourceMarkTypes: ["point", "bar", "line", "rule", "rect", "arc"]
25
26
  });
27
+ }
28
+
29
+ export function validateStoredSemanticValue(program, parsed, value) {
30
+ return fullSemanticValidator()(program, parsed, value);
31
+ }
32
+
33
+ export function registerSemanticPrimitiveAction(ProgramClass) {
26
34
  ProgramClass.prototype.editSemantic =
27
- createSemanticPrimitiveAction(validateSemanticValue);
35
+ createSemanticPrimitiveAction(fullSemanticValidator(), planResourceRemoval);
28
36
  }
@@ -1,5 +1,5 @@
1
1
  import { hasSemanticScaleReferences } from "../../selectors/scales.js";
2
- import { action } from "../../core/action.js";
2
+ import { closedAction } from "../../core/action.js";
3
3
  import {
4
4
  cloneAndFreeze,
5
5
  freezeOwned,
@@ -8,6 +8,7 @@ import {
8
8
  } from "../../core/immutable.js";
9
9
  import { parseSemanticPath } from "../../grammar/schemas/semanticPath.js";
10
10
 
11
+
11
12
  const CONTEXT_KEYS = Object.freeze({
12
13
  dataset: "currentData",
13
14
  layer: "currentMark",
@@ -79,7 +80,7 @@ function removeEntity(spec, parsed) {
79
80
  if (parsed.kind === "scale" && parsed.path.length === 0) {
80
81
  if (hasSemanticScaleReferences(spec, parsed.id)) throw new Error(`Scale "${parsed.id}" is still referenced.`);
81
82
  }
82
- if (["layer", "scale"].includes(parsed.kind) && parsed.path.length === 0) {
83
+ if (["layer", "scale", "coordinate"].includes(parsed.kind) && parsed.path.length === 0) {
83
84
  const nextCollection = collection.filter((_, itemIndex) => itemIndex !== index);
84
85
  return freezeOwned({
85
86
  ...spec,
@@ -87,10 +88,6 @@ function removeEntity(spec, parsed) {
87
88
  });
88
89
  }
89
90
  if (parsed.kind === "dataset" && parsed.path.length === 0) {
90
- const dataset = collection[index];
91
- if (dataset.source === undefined) {
92
- throw new Error(`Source dataset "${parsed.id}" is immutable after creation.`);
93
- }
94
91
  const referenced = spec.layers.some(layer => layer.data === parsed.id) ||
95
92
  spec.datasets.some(candidate => candidate.source === parsed.id);
96
93
  if (referenced) {
@@ -151,14 +148,59 @@ export function withPreviewDatasetValues(program, {
151
148
  return program._clone({ semanticSpec });
152
149
  }
153
150
 
154
- export function createSemanticPrimitiveAction(validateSemanticValue) {
155
- return action(
151
+ // Owned materializers may refresh a generated derived dataset at a stable ID.
152
+ // Keep that privileged immutable transition inside the semantic primitive
153
+ // boundary; public editSemantic still rejects every post-creation dataset edit.
154
+ export function withRematerializedDerivedDataset(program, {
155
+ id,
156
+ source,
157
+ transform,
158
+ values
159
+ }) {
160
+ const semanticSpec = {
161
+ ...program.semanticSpec,
162
+ datasets: program.semanticSpec.datasets.map(dataset =>
163
+ dataset.id === id
164
+ ? { ...dataset, source, transform: [transform], values }
165
+ : dataset
166
+ )
167
+ };
168
+ return program._clone({ semanticSpec });
169
+ }
170
+
171
+ // Final-state planners may need to remove mutually constrained roles before
172
+ // replaying their canonical primitive owners. Keep that speculative clone in
173
+ // the primitive boundary and preserve the caller's layer for later dependency
174
+ // and guide reconciliation.
175
+ export function withoutPreviewLayerEncodings(program, { id, channels }) {
176
+ const removed = new Set(channels);
177
+ const semanticSpec = {
178
+ ...program.semanticSpec,
179
+ layers: program.semanticSpec.layers.map(layer => {
180
+ if (layer.id !== id) return layer;
181
+ return {
182
+ ...layer,
183
+ encoding: Object.fromEntries(
184
+ Object.entries(layer.encoding ?? {}).filter(
185
+ ([channel]) => !removed.has(channel)
186
+ )
187
+ )
188
+ };
189
+ })
190
+ };
191
+ return program._clone({ semanticSpec });
192
+ }
193
+
194
+ export function createSemanticPrimitiveAction(validateSemanticValue, planNamedRemoval) {
195
+ return closedAction(
156
196
  {
157
197
  op: "editSemantic",
158
198
  description: "Create, replace, or remove one semantic property.",
159
199
  scope: "any"
160
- },
161
- function ({ property, value, remove = false } = {}) {
200
+ }, ["property", "value", "remove"],
201
+ function (args = {}) {
202
+
203
+ const { property, value, remove = false } = args;
162
204
  if (typeof remove !== "boolean") {
163
205
  throw new TypeError("editSemantic remove must be a boolean.");
164
206
  }
@@ -170,14 +212,37 @@ export function createSemanticPrimitiveAction(validateSemanticValue) {
170
212
  }
171
213
 
172
214
  const parsed = parseSemanticPath(property, { allowContainer: remove });
215
+ const removesNamedResource = remove && parsed.path.length === 0 &&
216
+ ["dataset", "scale", "coordinate"].includes(parsed.kind);
173
217
  if (this.compositionSpec !== undefined && !(
174
- this.compositionSpec.type === "facet" && parsed.kind === "title"
218
+ this.compositionSpec.type === "facet" &&
219
+ (parsed.kind === "title" || removesNamedResource)
175
220
  )) {
176
221
  throw new Error(
177
- "editSemantic on a composition parent currently supports only facet title state."
222
+ "editSemantic on a composition parent supports only facet title state and unreferenced named resource removal."
178
223
  );
179
224
  }
180
225
  if (remove) {
226
+ // Source/coordinate removal and facet-parent removal use the same full
227
+ // dependency policy as the domain lifecycle. Existing unit derived-data
228
+ // teardown retains its semantic-only primitive contract; its owning
229
+ // action coordinates configuration and graphics cleanup separately.
230
+ const resource = removesNamedResource
231
+ ? this.semanticSpec[parsed.collection].find(item => item.id === parsed.id)
232
+ : undefined;
233
+ if (resource !== undefined && (
234
+ parsed.kind === "coordinate" || this.compositionSpec?.type === "facet" ||
235
+ (parsed.kind === "dataset" && resource.source === undefined)
236
+ )) {
237
+ if (planNamedRemoval === undefined) {
238
+ throw new Error("Named resource removal requires the full ChartProgram.");
239
+ }
240
+ planNamedRemoval(this, {
241
+ kind: parsed.kind === "dataset" ? "data" : parsed.kind,
242
+ id: parsed.id,
243
+ operation: "editSemantic"
244
+ });
245
+ }
181
246
  const semanticSpec = parsed.kind === "guide"
182
247
  ? removeRootProperty(this.semanticSpec, "guides", parsed.path)
183
248
  : parsed.kind === "title"
@@ -192,17 +257,28 @@ export function createSemanticPrimitiveAction(validateSemanticValue) {
192
257
  parsed.kind === "layer" &&
193
258
  parsed.path.length === 0 &&
194
259
  this.context.currentMark === parsed.id;
260
+ const clearsCurrentCoordinate =
261
+ parsed.kind === "coordinate" &&
262
+ parsed.path.length === 0 &&
263
+ this.context.currentCoordinate === parsed.id;
264
+ const clearsCurrentGuide =
265
+ parsed.kind === "guide" &&
266
+ parsed.path.length === 2 &&
267
+ this.context.currentGuide === parsed.id;
195
268
  const removesScale = parsed.kind === "scale" && parsed.path.length === 0;
196
269
  const resolvedScales = removesScale ? Object.fromEntries(Object.entries(this.resolvedScales).filter(([id]) => id !== parsed.id)) : this.resolvedScales;
197
270
  return this._clone({
198
271
  semanticSpec,
199
272
  ...(removesScale ? { resolvedScales: freezeOwned(resolvedScales) } : {}),
200
- ...(clearsCurrentData || clearsCurrentMark || (removesScale && this.context.currentScale === parsed.id)
273
+ ...(clearsCurrentData || clearsCurrentMark || clearsCurrentGuide || clearsCurrentCoordinate ||
274
+ (removesScale && this.context.currentScale === parsed.id)
201
275
  ? { context: freezeOwned({
202
276
  ...this.context,
203
277
  ...(removesScale && this.context.currentScale === parsed.id ? { currentScale: undefined } : {}),
204
278
  ...(clearsCurrentData ? { currentData: undefined } : {}),
205
- ...(clearsCurrentMark ? { currentMark: undefined } : {})
279
+ ...(clearsCurrentMark ? { currentMark: undefined } : {}),
280
+ ...(clearsCurrentCoordinate ? { currentCoordinate: undefined } : {}),
281
+ ...(clearsCurrentGuide ? { currentGuide: undefined } : {})
206
282
  }) }
207
283
  : {})
208
284
  });
@@ -1,5 +1,5 @@
1
1
  import { validateUserId } from "../../../core/identifiers.js";
2
- import { isPlainObject } from "../../../core/immutable.js";
2
+ import { validateDataRows } from "../../../core/validation.js";
3
3
  import { hasDataset } from "../../../selectors/datasets.js";
4
4
 
5
5
  export function validateDatasetSemanticValue(
@@ -10,9 +10,7 @@ export function validateDatasetSemanticValue(
10
10
  ) {
11
11
  const property = parsed.path[0];
12
12
  if (property === "values") {
13
- if (!Array.isArray(value) || !value.every(isPlainObject)) {
14
- throw new TypeError("Dataset values must be an array of plain row objects.");
15
- }
13
+ validateDataRows(value, `Dataset "${parsed.id}"`);
16
14
  return;
17
15
  }
18
16
  if (property === "source") {
@@ -30,7 +30,7 @@ export function validateGuideSemanticValue(_program, parsed, value) {
30
30
  validateParallelAxisTitles(value);
31
31
  return;
32
32
  }
33
- if (["legend.series", "legend.color"].includes(parsed.id) && property === "order") {
33
+ if (["legend.series", "legend.color", "legend.stroke"].includes(parsed.id) && property === "order") {
34
34
  normalizeLegendOrder(value);
35
35
  return;
36
36
  }
@@ -1,4 +1,8 @@
1
- import { validateCoordinateType } from "../../../grammar/coordinates.js";
1
+ import {
2
+ normalizeCoordinateAspect,
3
+ validateCoordinateType
4
+ } from "../../../grammar/coordinates.js";
5
+ import { normalizePolarFrameOptions } from "../../../grammar/polar.js";
2
6
  import { validateScaleSemanticValue } from "./scale.js";
3
7
  import { validateDatasetSemanticValue } from "./dataset.js";
4
8
  import { validateLayerSemanticValue } from "./layer.js";
@@ -27,6 +31,10 @@ export function createSemanticValueValidator({
27
31
  validateScaleSemanticValue(program, parsed, value);
28
32
  } else if (parsed.kind === "coordinate" && parsed.path[0] === "type") {
29
33
  validateCoordinateType(value);
34
+ } else if (parsed.kind === "coordinate" && parsed.path[0] === "aspect") {
35
+ normalizeCoordinateAspect(value);
36
+ } else if (parsed.kind === "coordinate" && parsed.path[0] === "polarFrame") {
37
+ normalizePolarFrameOptions(value);
30
38
  } else if (parsed.kind === "guide") {
31
39
  validateGuideSemanticValue(program, parsed, value);
32
40
  } else if (parsed.kind === "title") {
@@ -17,6 +17,8 @@ import { normalizeCategoryOrder } from "../../../grammar/categoryOrder.js";
17
17
  import { validateSemanticFieldType } from "../../../grammar/scales/index.js";
18
18
  import { findLayer } from "../../../selectors/layers.js";
19
19
  import { validateNonEmptySemanticString } from "./shared.js";
20
+ import { normalizeStatisticalWeight } from
21
+ "../../../grammar/weightedStatistics.js";
20
22
 
21
23
  function validateLayerSource(program, parsed, value, sourceMarkTypes) {
22
24
  validateUserId(value, "Layer source id");
@@ -81,6 +83,9 @@ export function validateLayerSemanticValue(
81
83
  validateParallel?.(property, value);
82
84
  }
83
85
  if (property.endsWith(".aggregate")) validateAggregate(value);
86
+ if (property === "encoding.x.weight") {
87
+ normalizeStatisticalWeight(value, "Histogram weight");
88
+ }
84
89
  if (property.endsWith(".bin.maxBins")) normalizeHistogramBin({ maxBins: value });
85
90
  if (property.endsWith(".bin.step")) validateHistogramBinStep(value);
86
91
  if (property.endsWith(".bin.boundaries")) validateHistogramBinBoundaries(value);
@@ -8,9 +8,22 @@ import {
8
8
  validateSemanticScaleType
9
9
  } from "../../../grammar/scales/index.js";
10
10
  import { findSemanticScale } from "../../../selectors/scales.js";
11
+ import { findScaleConsumers } from "../../scales/consumers/index.js";
11
12
 
12
- function validateOwnedProperty(existing, property) {
13
+ function isOffsetScale(program, id, existing) {
14
+ if (existing?.type !== "ordinal") return false;
15
+ const consumers = findScaleConsumers(program, id);
16
+ return consumers.length > 0 && consumers.every(
17
+ consumer => ["xOffset", "yOffset"].includes(consumer.channel)
18
+ );
19
+ }
20
+
21
+ function validateOwnedProperty(program, id, existing, property) {
13
22
  if (existing?.type !== undefined) {
23
+ if (
24
+ isOffsetScale(program, id, existing) &&
25
+ ["paddingInner", "paddingOuter", "align"].includes(property)
26
+ ) return;
14
27
  validateScalePropertyForType(existing.type, property);
15
28
  }
16
29
  }
@@ -31,7 +44,13 @@ export function validateScaleSemanticValue(program, parsed, value) {
31
44
  "nice", "zero", "clamp", "base", "exponent", "constant", "midpoint",
32
45
  "paddingInner", "paddingOuter", "padding", "align"
33
46
  ]) {
34
- if (existing?.[owned] !== undefined) validateScalePropertyForType(value, owned);
47
+ if (existing?.[owned] === undefined) continue;
48
+ if (
49
+ value === "ordinal" &&
50
+ isOffsetScale(program, parsed.id, existing) &&
51
+ ["paddingInner", "paddingOuter", "align"].includes(owned)
52
+ ) continue;
53
+ validateScalePropertyForType(value, owned);
35
54
  }
36
55
  return;
37
56
  }
@@ -41,7 +60,9 @@ export function validateScaleSemanticValue(program, parsed, value) {
41
60
  if (typeof value !== "boolean") {
42
61
  throw new TypeError(`Scale ${property} must be a boolean.`);
43
62
  }
44
- if (property !== "reverse") validateOwnedProperty(existing, property);
63
+ if (property !== "reverse") {
64
+ validateOwnedProperty(program, parsed.id, existing, property);
65
+ }
45
66
  return;
46
67
  }
47
68
  if (["base", "exponent", "constant"].includes(property)) {
@@ -51,7 +72,7 @@ export function validateScaleSemanticValue(program, parsed, value) {
51
72
  if (property === "base" && value === 1) {
52
73
  throw new RangeError("Scale base must not equal 1.");
53
74
  }
54
- validateOwnedProperty(existing, property);
75
+ validateOwnedProperty(program, parsed.id, existing, property);
55
76
  return;
56
77
  }
57
78
  if (property === "interpolate") {
@@ -64,20 +85,20 @@ export function validateScaleSemanticValue(program, parsed, value) {
64
85
  "Scale paddingInner must be from 0 (inclusive) to 1 (exclusive)."
65
86
  );
66
87
  }
67
- validateOwnedProperty(existing, property);
88
+ validateOwnedProperty(program, parsed.id, existing, property);
68
89
  return;
69
90
  }
70
91
  if (property === "paddingOuter" || property === "padding") {
71
92
  if (!Number.isFinite(value) || value < 0) {
72
93
  throw new RangeError(`Scale ${property} must be a non-negative finite number.`);
73
94
  }
74
- validateOwnedProperty(existing, property);
95
+ validateOwnedProperty(program, parsed.id, existing, property);
75
96
  return;
76
97
  }
77
98
  if (property === "align") {
78
99
  if (!Number.isFinite(value) || value < 0 || value > 1) {
79
100
  throw new RangeError("Scale align must be between 0 and 1.");
80
101
  }
81
- validateOwnedProperty(existing, property);
102
+ validateOwnedProperty(program, parsed.id, existing, property);
82
103
  }
83
104
  }
@@ -1,6 +1,6 @@
1
- import { action } from "../../core/action.js";
1
+ import { closedAction } from "../../core/action.js";
2
2
  import { validateUserId } from "../../core/identifiers.js";
3
- import { validateKeys } from "../../core/validation.js";
3
+
4
4
  import { DEFAULT_COLORS } from "../../theme/defaults.js";
5
5
  import { findDataset } from "../../selectors/datasets.js";
6
6
  import { resolveEligibleLayer } from "../../selectors/layers.js";
@@ -9,19 +9,27 @@ import {
9
9
  REGRESSION_UPPER_FIELD
10
10
  } from "../../grammar/regression/index.js";
11
11
  import { validateAreaCreateOutline } from "../marks/area/index.js";
12
+ import {
13
+ requestedStrokeDetails,
14
+ STROKE_STYLE_PROPERTIES
15
+ } from "../../grammar/strokeStyle.js";
12
16
 
13
17
  const BAND_OPTIONS = Object.freeze([
14
18
  "id", "data", "x", "lower", "upper", "groupBy", "coordinate", "xScale", "yScale",
15
- "color", "opacity", "stroke", "strokeWidth", "curve"
19
+ "color", "opacity", "stroke", "strokeWidth", "curve",
20
+ ...STROKE_STYLE_PROPERTIES
16
21
  ]);
17
22
  const LINE_OPTIONS = Object.freeze([
18
23
  "id", "data", "x", "y", "groupBy", "coordinate", "xScale", "yScale", "colorScale",
19
- "strokeWidth", "curve"
24
+ "strokeWidth", "curve", ...STROKE_STYLE_PROPERTIES
20
25
  ]);
21
26
  const EDIT_BAND_OPTIONS = Object.freeze([
22
- "target", "color", "opacity", "stroke", "strokeWidth", "curve"
27
+ "target", "color", "opacity", "stroke", "strokeWidth", "curve",
28
+ ...STROKE_STYLE_PROPERTIES
29
+ ]);
30
+ const EDIT_LINE_OPTIONS = Object.freeze([
31
+ "target", "strokeWidth", "curve", ...STROKE_STYLE_PROPERTIES
23
32
  ]);
24
- const EDIT_LINE_OPTIONS = Object.freeze(["target", "strokeWidth", "curve"]);
25
33
 
26
34
  function isRegressionLayer(program, layer, type) {
27
35
  const dataset = findDataset(program, layer.data);
@@ -64,13 +72,12 @@ function requireRegressionBandDataset(program, args) {
64
72
  return { dataset, transform };
65
73
  }
66
74
 
67
- export const createRegressionBand = action(
75
+ export const createRegressionBand = /* @__PURE__ */ closedAction(
68
76
  {
69
77
  op: "createRegressionBand",
70
78
  description: "Create and encode a grouped regression confidence band."
71
- },
79
+ }, BAND_OPTIONS,
72
80
  function (args = {}) {
73
- validateKeys(args, BAND_OPTIONS, "createRegressionBand");
74
81
  const id = validateUserId(args.id, "Regression band id");
75
82
  const { transform } = requireRegressionBandDataset(this, args);
76
83
  validateAreaCreateOutline(args, "createRegressionBand");
@@ -93,7 +100,8 @@ export const createRegressionBand = action(
93
100
  coordinate: args.coordinate,
94
101
  fill: args.color ?? DEFAULT_COLORS.regressionBand,
95
102
  opacity: args.opacity ?? 0.18,
96
- ...(args.curve === undefined ? {} : { curve: args.curve })
103
+ ...(args.curve === undefined ? {} : { curve: args.curve }),
104
+ ...requestedStrokeDetails(args, "createRegressionBand")
97
105
  })
98
106
  .editSemantic({
99
107
  property: `layer[${id}].encoding.y.title`,
@@ -112,14 +120,16 @@ export const createRegressionBand = action(
112
120
  }
113
121
  );
114
122
 
115
- export const editRegressionBand = action(
123
+ export const editRegressionBand = /* @__PURE__ */ closedAction(
116
124
  {
117
125
  op: "editRegressionBand",
118
126
  description: "Edit regression-band fill, opacity, and outline."
119
- },
127
+ }, EDIT_BAND_OPTIONS,
120
128
  function (args = {}) {
121
- validateKeys(args, EDIT_BAND_OPTIONS, "editRegressionBand");
122
- const changes = ["color", "opacity", "stroke", "strokeWidth", "curve"];
129
+ const changes = [
130
+ "color", "opacity", "stroke", "strokeWidth", "curve",
131
+ ...STROKE_STYLE_PROPERTIES
132
+ ];
123
133
  if (!changes.some(key => Object.hasOwn(args, key))) {
124
134
  throw new Error(
125
135
  "editRegressionBand requires color, opacity, stroke, strokeWidth, or curve."
@@ -139,21 +149,21 @@ export const editRegressionBand = action(
139
149
  ...(Object.hasOwn(args, "strokeWidth")
140
150
  ? { strokeWidth: args.strokeWidth }
141
151
  : {}),
142
- ...(Object.hasOwn(args, "curve") ? { curve: args.curve } : {})
152
+ ...(Object.hasOwn(args, "curve") ? { curve: args.curve } : {}),
153
+ ...requestedStrokeDetails(args, "editRegressionBand")
143
154
  });
144
155
  }
145
156
  );
146
157
 
147
- export const editRegressionLine = action(
158
+ export const editRegressionLine = /* @__PURE__ */ closedAction(
148
159
  {
149
160
  op: "editRegressionLine",
150
161
  description: "Edit regression-line width or curve."
151
- },
162
+ }, EDIT_LINE_OPTIONS,
152
163
  function (args = {}) {
153
- validateKeys(args, EDIT_LINE_OPTIONS, "editRegressionLine");
154
164
  if (
155
- !Object.hasOwn(args, "strokeWidth") &&
156
- !Object.hasOwn(args, "curve")
165
+ !["strokeWidth", "curve", ...STROKE_STYLE_PROPERTIES]
166
+ .some(property => Object.hasOwn(args, property))
157
167
  ) {
158
168
  throw new Error("editRegressionLine requires strokeWidth or curve.");
159
169
  }
@@ -168,25 +178,26 @@ export const editRegressionLine = action(
168
178
  ...(Object.hasOwn(args, "strokeWidth")
169
179
  ? { strokeWidth: args.strokeWidth }
170
180
  : {}),
171
- ...(Object.hasOwn(args, "curve") ? { curve: args.curve } : {})
181
+ ...(Object.hasOwn(args, "curve") ? { curve: args.curve } : {}),
182
+ ...requestedStrokeDetails(args, "editRegressionLine")
172
183
  });
173
184
  }
174
185
  );
175
186
 
176
- export const createRegressionLine = action(
187
+ export const createRegressionLine = /* @__PURE__ */ closedAction(
177
188
  {
178
189
  op: "createRegressionLine",
179
190
  description: "Create and encode grouped regression line paths."
180
- },
191
+ }, LINE_OPTIONS,
181
192
  function (args = {}) {
182
- validateKeys(args, LINE_OPTIONS, "createRegressionLine");
183
193
  const id = validateUserId(args.id, "Regression line id");
184
194
  let next = this
185
195
  .createLineMark({
186
196
  id,
187
197
  data: args.data,
188
198
  strokeWidth: args.strokeWidth ?? 3,
189
- ...(args.curve === undefined ? {} : { curve: args.curve })
199
+ ...(args.curve === undefined ? {} : { curve: args.curve }),
200
+ ...requestedStrokeDetails(args, "createRegressionLine")
190
201
  })
191
202
  .encodeX({
192
203
  target: id,
@@ -1,6 +1,7 @@
1
- import { action } from "../../core/action.js";
1
+ import { closedAction } from "../../core/action.js";
2
2
  import { validateKeys } from "../../core/validation.js";
3
3
  import { normalizeRegressionParameters } from "../../grammar/regression/index.js";
4
+ import { STROKE_STYLE_PROPERTIES } from "../../grammar/strokeStyle.js";
4
5
  import {
5
6
  findRegressionPoint,
6
7
  inferRegressionGroup,
@@ -13,13 +14,12 @@ const REGRESSION_OPTIONS = Object.freeze([
13
14
  "confidenceMethod", "level", "confidence", "interval", "band", "line"
14
15
  ]);
15
16
 
16
- export const createRegression = action(
17
+ export const createRegression = /* @__PURE__ */ closedAction(
17
18
  {
18
19
  op: "createRegression",
19
20
  description: "Fit and layer regression lines with optional interval bands."
20
- },
21
+ }, REGRESSION_OPTIONS,
21
22
  function (args = {}) {
22
- validateKeys(args, REGRESSION_OPTIONS, "createRegression");
23
23
  const point = findRegressionPoint(this, args.target);
24
24
  const x = args.x === undefined
25
25
  ? point.encoding.x.field
@@ -49,11 +49,18 @@ export const createRegression = action(
49
49
  if (band !== false) {
50
50
  validateKeys(
51
51
  band,
52
- ["color", "opacity", "stroke", "strokeWidth", "curve"],
52
+ [
53
+ "color", "opacity", "stroke", "strokeWidth", "curve",
54
+ ...STROKE_STYLE_PROPERTIES
55
+ ],
53
56
  "regression band"
54
57
  );
55
58
  }
56
- validateKeys(line, ["strokeWidth", "curve"], "regression line");
59
+ validateKeys(
60
+ line,
61
+ ["strokeWidth", "curve", ...STROKE_STYLE_PROPERTIES],
62
+ "regression line"
63
+ );
57
64
  const colorEncoding = point.encoding?.color;
58
65
  const namespace = point.id;
59
66
  const dataId = `${namespace}RegressionData`;