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
@@ -1,3 +1,5 @@
1
+ import { inferSeriesGroup } from "./shared.js";
2
+ import { findSemanticScale } from "../../selectors/scales.js";
1
3
  import { action } from "../../core/action.js";
2
4
  import {
3
5
  readNominalField,
@@ -13,11 +15,9 @@ import {
13
15
  validateOptions
14
16
  } from "./shared.js";
15
17
  import {
16
- resolveBarColorLayout,
17
18
  resolveBarChannels,
18
19
  resolveBarGrain
19
20
  } from "../../grammar/bars/policy.js";
20
- import { normalizeOffsetPadding } from "../../grammar/bars/geometry.js";
21
21
 
22
22
  const ENCODING_OPTIONS = Object.freeze([
23
23
  "field", "target", "fieldType", "scale", "paddingInner", "paddingOuter"
@@ -50,15 +50,7 @@ function createOffsetEncoding(channel) {
50
50
  `${operation} requires a complete bar with a ${parentChannel} category encoding.`
51
51
  );
52
52
  }
53
- if (
54
- layer.encoding?.color !== undefined &&
55
- (resolveBarColorLayout(layer) !== "group" ||
56
- layer.encoding.color.field !== args.field)
57
- ) {
58
- throw new Error(
59
- `${operation} field must match a grouped bar color field.`
60
- );
61
- }
53
+
62
54
  } else {
63
55
  const parent = layer.encoding?.[parentChannel];
64
56
  if (
@@ -76,35 +68,44 @@ function createOffsetEncoding(channel) {
76
68
  layer.encoding?.[channel],
77
69
  args.scale ?? {}
78
70
  );
79
- const scale = resolveOffsetScaleDefinition(this, requestedScale, channel);
71
+ const scaleOptions = {
72
+ ...requestedScale,
73
+ ...Object.fromEntries(
74
+ ["paddingInner", "paddingOuter"]
75
+ .filter(property => Object.hasOwn(args, property))
76
+ .map(property => [property, args[property]])
77
+ )
78
+ };
79
+ const scale = resolveOffsetScaleDefinition(this, scaleOptions, channel);
80
80
  if (Object.hasOwn(scale, "unknown")) {
81
81
  throw new Error(
82
82
  `${channel} scale unknown is not supported for offset positions.`
83
83
  );
84
84
  }
85
- const padding = normalizeOffsetPadding(
86
- args,
87
- this.markConfigs[target]?.[channel],
88
- channel
89
- );
90
85
 
91
- let next = setEncodingProperties(this, target, channel, {
86
+ const grouped = layer.mark.type === "bar" ? inferSeriesGroup(this, layer, args.field, "offset") : this;
87
+ let next = setEncodingProperties(grouped, target, channel, {
92
88
  field: args.field,
93
89
  fieldType,
94
90
  scale: scale.id
95
- })._withMarkConfig(target, {
96
- ...this.markConfigs[target],
97
- [channel]: padding
98
- });
99
- next = applyEncodingScale(next, scale, requestedScale, {
100
- reassignment: layer.encoding?.[channel]?.scale === scale.id
101
91
  });
102
- if (layer.mark.type === "bar" && layer.encoding?.color === undefined) {
103
- return next
104
- .rematerializeScale({ id: scale.id })
105
- .editGraphics({ target, property: "length", value: 0 });
92
+ next = applyEncodingScale(next, scale, {
93
+ ...Object.fromEntries(Object.entries(scaleOptions).filter(
94
+ ([property]) => property !== "range"
95
+ )),
96
+ paddingInner: scale.paddingInner,
97
+ paddingOuter: scale.paddingOuter,
98
+ align: scale.align
99
+ }, {
100
+ reassignment: findSemanticScale(this, scale.id) !== undefined
101
+ });
102
+ next = next._withoutMaterializationConfig(["marks", target, channel]);
103
+ if (layer.mark.type === "bar") {
104
+ if (findSemanticScale(this, scale.id) === undefined && args.scale?.id === undefined) next = next._withMarkConfig(target, { ...next.markConfigs[target], seriesOffsetScale: scale.id });
105
+ if (layer.layout?.mode !== "group" || layer.encoding?.group?.field !== args.field) return next.layoutSeries({ target, mode: "group" });
106
+ return rematerializeEncoding(next, target, channel, scale.id, layer);
106
107
  }
107
- return rematerializeEncoding(next, target, channel, scale.id);
108
+ return rematerializeEncoding(next, target, channel, scale.id, layer);
108
109
  }
109
110
  );
110
111
  }
@@ -13,6 +13,7 @@ import {
13
13
  setEncodingProperties
14
14
  } from "./shared.js";
15
15
  import { resolveParallelCoordinate } from "../coordinates/parallel.js";
16
+ import { applyLayerDataRematerialization } from "../../materialization/dependencies.js";
16
17
 
17
18
  const OPTIONS = Object.freeze([
18
19
  "target", "coordinate", "dimensions", "key", "missing"
@@ -98,14 +99,7 @@ export const encodeParallelCoordinates = action(
98
99
  for (let index = 0; index < definitions.length; index += 1) {
99
100
  next = applyEncodingScale(next, definitions[index], dimensions[index].scaleOptions);
100
101
  }
101
- for (const definition of definitions) {
102
- next = next.rematerializeScale({
103
- id: definition.id,
104
- guides: false,
105
- marks: false
106
- });
107
- }
108
- return next.rematerializeLineMark({ id: target, scales: false });
102
+ return applyLayerDataRematerialization(next, target);
109
103
  }
110
104
  );
111
105
 
@@ -1,3 +1,5 @@
1
+ import { applyTemporalUnit } from "../temporal.js";
2
+
1
3
  function replaceAlternateBinding(program, target, channel, previous, usesField) {
2
4
  if (previous === undefined) return program;
3
5
  const alternate = usesField ? "datum" : "field";
@@ -64,18 +66,35 @@ function applyBin(program, target, channel, previous, bin) {
64
66
  });
65
67
  }
66
68
 
67
- function applyArcThetaWeight(program, target, channel, previous, aggregate, weight) {
68
- if (channel !== "theta") return program;
69
- if (aggregate !== "sum") {
69
+ function applyPositionWeight(
70
+ program,
71
+ target,
72
+ channel,
73
+ previous,
74
+ bin,
75
+ aggregate,
76
+ weight
77
+ ) {
78
+ const histogram = channel === "x" && bin !== undefined;
79
+ const arcSum = channel === "theta" && aggregate === "sum";
80
+ if (!histogram && !arcSum) {
81
+ return previous?.weight === undefined
82
+ ? program
83
+ : program.editSemantic({
84
+ property: `layer[${target}].encoding.${channel}.weight`,
85
+ remove: true
86
+ });
87
+ }
88
+ if (weight === undefined) {
70
89
  return previous?.weight === undefined
71
90
  ? program
72
91
  : program.editSemantic({
73
- property: `layer[${target}].encoding.theta.weight`,
92
+ property: `layer[${target}].encoding.${channel}.weight`,
74
93
  remove: true
75
94
  });
76
95
  }
77
96
  return program.editSemantic({
78
- property: `layer[${target}].encoding.theta.weight`,
97
+ property: `layer[${target}].encoding.${channel}.weight`,
79
98
  value: weight
80
99
  });
81
100
  }
@@ -102,6 +121,26 @@ function removeStaleArcThetaAggregate(
102
121
  });
103
122
  }
104
123
 
124
+ function removeMeasuredRadiusAggregate(
125
+ program,
126
+ target,
127
+ channel,
128
+ previous,
129
+ clearRadialMapping
130
+ ) {
131
+ if (
132
+ channel !== "radius" ||
133
+ !clearRadialMapping ||
134
+ previous?.aggregate === undefined
135
+ ) {
136
+ return program;
137
+ }
138
+ return program.editSemantic({
139
+ property: `layer[${target}].encoding.radius.aggregate`,
140
+ remove: true
141
+ });
142
+ }
143
+
105
144
  export function applyPositionSemantics(program, {
106
145
  target,
107
146
  channel,
@@ -111,10 +150,12 @@ export function applyPositionSemantics(program, {
111
150
  datum,
112
151
  hasField,
113
152
  fieldType,
153
+ temporalUnit,
114
154
  bin,
115
155
  aggregate,
116
156
  stack,
117
- weight
157
+ weight,
158
+ clearRadialMapping
118
159
  }) {
119
160
  let next = reconcileInheritedRulePosition(program, {
120
161
  target,
@@ -128,16 +169,19 @@ export function applyPositionSemantics(program, {
128
169
  channel,
129
170
  previous,
130
171
  hasField
131
- )
132
- .editSemantic({
172
+ );
173
+ if (hasField || datum !== undefined) {
174
+ next = next.editSemantic({
133
175
  property: `layer[${target}].encoding.${channel}.${hasField ? "field" : "datum"}`,
134
176
  value: hasField ? field : datum
135
- })
136
- .editSemantic({
177
+ });
178
+ }
179
+ next = next.editSemantic({
137
180
  property: `layer[${target}].encoding.${channel}.fieldType`,
138
181
  value: fieldType
139
182
  });
140
183
 
184
+ next = applyTemporalUnit(next, target, channel, temporalUnit, previous);
141
185
  next = applyBin(next, target, channel, previous, bin);
142
186
  for (const [property, value] of Object.entries({ aggregate, stack })) {
143
187
  if (value === undefined) continue;
@@ -154,11 +198,19 @@ export function applyPositionSemantics(program, {
154
198
  fieldType,
155
199
  aggregate
156
200
  );
157
- return applyArcThetaWeight(
201
+ next = removeMeasuredRadiusAggregate(
202
+ next,
203
+ target,
204
+ channel,
205
+ previous,
206
+ clearRadialMapping
207
+ );
208
+ return applyPositionWeight(
158
209
  next,
159
210
  target,
160
211
  channel,
161
212
  previous,
213
+ bin,
162
214
  aggregate,
163
215
  weight
164
216
  );
@@ -1,12 +1,16 @@
1
+ import { stackLayoutMode } from "../seriesLayout.js";
2
+ import { resolveBarGrain } from "../../../grammar/bars/policy.js";
1
3
  import { action } from "../../../core/action.js";
2
4
  import {
3
5
  getPositionEncodingMaterializationSteps
4
6
  } from "../../../materialization/marks/index.js";
5
7
  import { resolvePositionEncoding } from "./resolve.js";
6
8
  import { findLayer } from "../../../selectors/layers.js";
9
+ import { findSemanticScale } from "../../../selectors/scales.js";
7
10
  import { applyPositionSemantics } from "./apply.js";
8
11
  import {
9
12
  applyEncodingScale,
13
+ applyDetachedScaleRematerialization,
10
14
  rebindPositionGuides
11
15
  } from "../shared.js";
12
16
 
@@ -20,12 +24,15 @@ function encodePosition(program, channel, args, operation) {
20
24
  datum,
21
25
  hasField,
22
26
  fieldType,
27
+ temporalUnit,
23
28
  scale,
24
29
  coordinate,
25
30
  bin,
26
31
  aggregate,
27
32
  stack,
28
- weight
33
+ weight,
34
+ companion,
35
+ clearRadialMapping
29
36
  } = resolvePositionEncoding(program, channel, args, operation);
30
37
 
31
38
  let next = program
@@ -43,19 +50,31 @@ function encodePosition(program, channel, args, operation) {
43
50
  datum,
44
51
  hasField,
45
52
  fieldType,
53
+ temporalUnit,
46
54
  bin,
47
55
  aggregate,
48
56
  stack,
49
- weight
57
+ weight,
58
+ clearRadialMapping
50
59
  });
51
60
 
52
61
  next = next.editSemantic({
53
62
  property: `layer[${target}].encoding.${channel}.scale`,
54
63
  value: scale.id
55
64
  });
65
+ if (
66
+ clearRadialMapping &&
67
+ findSemanticScale(next, scale.id)?.radialMapping !== undefined
68
+ ) {
69
+ next = next.editScale({ id: scale.id, radialMapping: undefined });
70
+ }
56
71
  next = applyEncodingScale(next, scale, requestedScale, {
57
- reassignment: previous?.scale === scale.id
72
+ reassignment: previous?.scale === scale.id,
73
+ allowTypeChange: layer.mark.type === "area"
58
74
  });
75
+ if (["bar", "area"].includes(layer.mark.type) && Object.hasOwn(args, "stack") && args.stack !== undefined) {
76
+ next = next.editSemantic({ property: `layer[${target}].layout.mode`, value: stackLayoutMode(args.stack) });
77
+ }
59
78
  next = rebindPositionGuides(
60
79
  next,
61
80
  channel,
@@ -64,13 +83,30 @@ function encodePosition(program, channel, args, operation) {
64
83
  target
65
84
  );
66
85
 
86
+ if (companion !== undefined) {
87
+ const encoding = companion.encoding;
88
+ const storedScale = findSemanticScale(next, encoding.scale);
89
+ next = next[companion.channel === "x" ? "encodeX" : "encodeY"]({
90
+ target,
91
+ field: encoding.field,
92
+ fieldType: encoding.fieldType,
93
+ aggregate: encoding.aggregate,
94
+ ...(layer.layout?.mode === undefined ? {} : { stack: encoding.stack }),
95
+ scale: {
96
+ id: encoding.scale,
97
+ ...(storedScale?.zero === undefined ? { zero: encoding.stack !== null } : {})
98
+ }
99
+ });
100
+ }
101
+
67
102
  if (layer.mark.type === "bar") {
68
103
  const updated = findLayer(next, target);
69
104
  const pendingBox = next.markConfigs[target]?.boxPlot;
70
105
  if (pendingBox !== undefined && !pendingBox.materialized) {
71
- return updated.encoding?.x !== undefined && updated.encoding?.y !== undefined
106
+ const materialized = updated.encoding?.x !== undefined && updated.encoding?.y !== undefined
72
107
  ? next.materializeBoxPlot({ id: target })
73
108
  : next;
109
+ return applyDetachedScaleRematerialization(materialized, [layer]);
74
110
  }
75
111
  }
76
112
 
@@ -78,13 +114,23 @@ function encodePosition(program, channel, args, operation) {
78
114
  const updated = findLayer(next, target);
79
115
  const pendingGradient = next.markConfigs[target]?.gradientPlot;
80
116
  if (pendingGradient !== undefined && !pendingGradient.materialized) {
81
- return updated.encoding?.x !== undefined && updated.encoding?.y !== undefined
117
+ const materialized = updated.encoding?.x !== undefined && updated.encoding?.y !== undefined
82
118
  ? next.materializeGradientPlot({ id: target })
83
119
  : next;
120
+ return applyDetachedScaleRematerialization(materialized, [layer]);
84
121
  }
85
122
  }
86
123
 
87
124
  const updated = findLayer(next, target);
125
+ if (["bar", "area"].includes(layer.mark.type) && next.markConfigs[target]?.boxPlot === undefined &&
126
+ updated.encoding?.x?.scale !== undefined && updated.encoding?.y?.scale !== undefined) {
127
+ const requested = Object.hasOwn(args, "stack") ? args.stack : undefined;
128
+ const mode = requested !== undefined ? stackLayoutMode(requested) : updated.layout?.mode ??
129
+ (layer.mark.type === "bar" && resolveBarGrain(updated) === "histogram" ? stackLayoutMode(updated.encoding.y.stack) : undefined);
130
+ if (mode !== undefined && (layer.mark.type === "area" || resolveBarGrain(updated) !== undefined)) {
131
+ return applyDetachedScaleRematerialization(next.layoutSeries({ target, mode }), [layer]);
132
+ }
133
+ }
88
134
  for (const step of getPositionEncodingMaterializationSteps(
89
135
  next,
90
136
  updated,
@@ -92,7 +138,7 @@ function encodePosition(program, channel, args, operation) {
92
138
  )) {
93
139
  next = next[step.op](step.args);
94
140
  }
95
- return next;
141
+ return applyDetachedScaleRematerialization(next, [layer]);
96
142
  }
97
143
 
98
144
  const encodeX = action(
@@ -1,3 +1,4 @@
1
+ import { validateRadialMapping } from "../../../../grammar/scales/radial.js";
1
2
  import { validateAggregate } from "../../../../grammar/aggregate.js";
2
3
  import { validateNonEmptyString } from "../../../../core/validation.js";
3
4
  import { emptyPositionPolicy } from "./common.js";
@@ -15,6 +16,19 @@ export function resolveArcPositionPolicy({ channel, args, fieldType, layer }) {
15
16
  "Arc radius encoding cannot be combined with quantitative theta."
16
17
  );
17
18
  }
19
+ if (args.mapping !== undefined) {
20
+ validateRadialMapping(args.mapping);
21
+ if (fieldType !== "quantitative" || !["count", "sum"].includes(args.aggregate)) {
22
+ throw new Error("Measured radius requires quantitative count or sum aggregation.");
23
+ }
24
+ if (args.aggregate === "count" && Object.hasOwn(args, "field")) {
25
+ throw new Error("Measured radius count does not accept a field.");
26
+ }
27
+ if (layer.encoding?.theta?.aggregate !== undefined) {
28
+ throw new Error("Measured radius requires equal-angle theta without aggregation.");
29
+ }
30
+ return { ...emptyPositionPolicy(), aggregate: args.aggregate };
31
+ }
18
32
  if (args.aggregate !== undefined) {
19
33
  throw new Error("Arc radius encoding does not support aggregate.");
20
34
  }
@@ -1,6 +1,4 @@
1
1
  import { validateStack, emptyPositionPolicy } from "./common.js";
2
- import { deriveCenteredAreaSeries } from "../../../../grammar/areaSeries.js";
3
- import { readQuantitativeField } from "../../../../grammar/scales/index.js";
4
2
 
5
3
  export function isCategoricalDensityPosition({
6
4
  layer,
@@ -50,44 +48,8 @@ export function resolveAreaPositionPolicy({
50
48
  throw new Error("Area position encoding does not support aggregate or bin.");
51
49
  }
52
50
  if (args.stack === undefined) return emptyPositionPolicy();
53
- const stack = validateStack(args.stack, "Area y encoding");
54
- if (stack === "center") {
55
- if (channel !== "y") {
56
- throw new Error("Area center stack requires a y encoding.");
57
- }
58
- if (layer.encoding?.group?.fieldType !== "nominal") {
59
- throw new Error("Area center stack requires a nominal group encoding.");
60
- }
61
- if (
62
- layer.encoding?.color?.layout !== undefined &&
63
- layer.encoding.color.layout !== "center"
64
- ) {
65
- throw new Error("Area center stack requires a matching center color layout.");
66
- }
67
- const values = readQuantitativeField(dataset.values, field);
68
- if (values.some(value => value < 0)) {
69
- throw new RangeError("Area center stack requires non-negative values.");
70
- }
71
- if (layer.encoding?.x !== undefined) {
72
- deriveCenteredAreaSeries(dataset.values, {
73
- ...layer,
74
- encoding: {
75
- ...layer.encoding,
76
- y: { field, fieldType, stack: "center" }
77
- }
78
- });
79
- }
80
- return { bin: undefined, aggregate: undefined, stack };
81
- }
82
- if (
83
- channel !== "y" ||
84
- density === undefined
85
- ) {
86
- throw new Error("Area stack currently requires a density y encoding.");
87
- }
88
- return {
89
- bin: undefined,
90
- aggregate: undefined,
91
- stack
92
- };
51
+ const stack = validateStack(args.stack, "Area position encoding");
52
+ if (fieldType !== "quantitative") throw new Error("Area stack requires a quantitative measure.");
53
+ if (stack === "center" && channel !== "y") throw new Error("Area center stack requires a y encoding.");
54
+ return { bin: undefined, aggregate: undefined, stack };
93
55
  }
@@ -4,12 +4,16 @@ import {
4
4
  } from "../../../../grammar/aggregate.js";
5
5
  import {
6
6
  BAR_ORIENTATIONS,
7
+ BAR_GRAINS,
8
+ resolveBarGrain,
7
9
  resolveBarOffsetChannel,
8
10
  resolveBarOrientation
9
11
  } from "../../../../grammar/bars/policy.js";
10
12
  import { resolveBin, validateStack } from "./common.js";
13
+ import { normalizeStatisticalWeight } from
14
+ "../../../../grammar/weightedStatistics.js";
11
15
 
12
- export function resolveBarPositionPolicy({
16
+ function resolveBarChannelPolicy({
13
17
  program,
14
18
  layer,
15
19
  channel,
@@ -35,6 +39,10 @@ export function resolveBarPositionPolicy({
35
39
  throw new Error("Binned bar x encoding does not support aggregate or stack.");
36
40
  }
37
41
  bin = resolveBin(args.bin);
42
+ const weight = args.weight === undefined
43
+ ? undefined
44
+ : normalizeStatisticalWeight(args.weight, "Histogram weight");
45
+ return { bin, aggregate, stack, weight };
38
46
  } else if (
39
47
  fieldType === "quantitative" &&
40
48
  channel === "y" &&
@@ -72,17 +80,12 @@ export function resolveBarPositionPolicy({
72
80
  if (pendingBoxRange && args.aggregate === undefined) {
73
81
  return { bin, aggregate, stack };
74
82
  }
75
- if (aggregate === undefined) {
76
- throw new Error(
77
- channel === "x"
78
- ? "Quantitative bar x encoding requires bin or aggregate."
79
- : "Bar y encoding requires a binned quantitative or ordinal x category, temporal x category, or aggregate."
80
- );
83
+ stack = Object.hasOwn(args, "stack") ? args.stack : opposite === undefined ? undefined : null;
84
+ if (aggregate !== undefined) {
85
+ aggregate = validateAggregate(aggregate);
86
+ validateAggregateFieldType(aggregate, fieldType);
81
87
  }
82
- stack = Object.hasOwn(args, "stack") ? args.stack : null;
83
- aggregate = validateAggregate(aggregate);
84
- validateAggregateFieldType(aggregate, fieldType);
85
- stack = validateStack(stack, `Bar ${channel} encoding`);
88
+ if (stack !== undefined) stack = validateStack(stack, `Bar ${channel} encoding`);
86
89
  if (stack === "center") {
87
90
  throw new Error("Centered bars are not supported.");
88
91
  }
@@ -92,13 +95,48 @@ export function resolveBarPositionPolicy({
92
95
  );
93
96
  }
94
97
 
98
+ if (args.weight !== undefined) {
99
+ throw new Error("Bar weight is supported only for a binned x encoding.");
100
+ }
101
+ return { bin, aggregate, stack };
102
+ }
103
+
104
+ export function resolveBarPositionPolicy(context) {
105
+ const { program, layer, channel, field, fieldType } = context;
106
+ const policy = resolveBarChannelPolicy(context);
107
+ const oppositeChannel = channel === "x" ? "y" : "x";
108
+ const opposite = layer.encoding?.[oppositeChannel];
109
+ const pendingBoxRange = program.markConfigs[layer.id]?.boxPlot !== undefined;
95
110
  const candidate = {
96
111
  ...layer,
97
112
  encoding: {
98
113
  ...layer.encoding,
99
- [channel]: { field, fieldType, bin, aggregate, stack }
114
+ [channel]: { field, fieldType, ...policy }
100
115
  }
101
116
  };
117
+ // The same channel policy owns a measure whether it is authored first or last.
118
+ // Absence of an aggregate/stack is sufficient to represent an unresolved role.
119
+ let companion;
120
+ if (
121
+ !pendingBoxRange && resolveBarGrain(layer) === undefined &&
122
+ opposite?.fieldType === "quantitative" && opposite.bin === undefined
123
+ ) {
124
+ const resolved = resolveBarChannelPolicy({
125
+ ...context,
126
+ layer: candidate,
127
+ channel: oppositeChannel,
128
+ field: opposite.field,
129
+ fieldType: opposite.fieldType,
130
+ args: opposite
131
+ });
132
+ candidate.encoding[oppositeChannel] = { ...opposite, ...resolved };
133
+ if (
134
+ (opposite.aggregate === undefined && resolved.aggregate !== undefined) ||
135
+ opposite.stack !== resolved.stack
136
+ ) {
137
+ companion = { channel: oppositeChannel, encoding: candidate.encoding[oppositeChannel] };
138
+ }
139
+ }
102
140
  const orientation = resolveBarOrientation(candidate);
103
141
  if (opposite !== undefined && orientation === undefined && !pendingBoxRange) {
104
142
  throw new Error(
@@ -116,5 +154,11 @@ export function resolveBarPositionPolicy({
116
154
  `${orientation} bars require ${expectedOffset}; remove the incompatible ${incompatibleOffset} encoding.`
117
155
  );
118
156
  }
119
- return { bin, aggregate, stack };
157
+ if (
158
+ program.markConfigs[layer.id]?.barWidth !== undefined &&
159
+ resolveBarGrain(candidate) === BAR_GRAINS.histogram
160
+ ) {
161
+ throw new Error("A saved Bar width requires an aggregate or ranged category slot and cannot be applied to histogram bins.");
162
+ }
163
+ return companion === undefined ? policy : { ...policy, companion };
120
164
  }