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
@@ -14,9 +14,11 @@ import {
14
14
  isColorScaleType,
15
15
  isContinuousColorScaleType,
16
16
  isDiscretizedColorScaleType,
17
+ normalizeSizeScaleDefinition,
17
18
  normalizeScaleDefinition
18
19
  } from "../../grammar/scales/index.js";
19
20
  import { findSemanticScale } from "../../selectors/scales.js";
21
+ import { findScaleConsumers } from "./consumers/index.js";
20
22
 
21
23
  const CREATE_SCALE_OPTIONS = Object.freeze([
22
24
  "id",
@@ -35,8 +37,8 @@ const CREATE_SCALE_OPTIONS = Object.freeze([
35
37
  "padding",
36
38
  "align",
37
39
  "palette",
38
- "interpolate",
39
- "unknown"
40
+ "interpolate", "midpoint",
41
+ "radialMapping", "unknown"
40
42
  ]);
41
43
 
42
44
  function validateOptions(args) {
@@ -97,28 +99,58 @@ export const createScale = action(
97
99
  const requestedRange = args.palette === undefined
98
100
  ? args.range
99
101
  : { palette: args.palette };
100
- const definition = normalizeScaleDefinition({
101
- type,
102
- patch: {
103
- ...args,
104
- ...(requestedRange === undefined ? {} : { range: requestedRange })
105
- },
106
- validateDomain: (scaleType, value) =>
107
- isDiscretizedColorScaleType(scaleType)
108
- ? validateDiscretizedColorDomain(scaleType, value)
109
- : hasOrdinalDomain(scaleType)
110
- ? validateOrdinalDomain(value)
111
- : validateScaleDomain(value),
112
- validateRange: (scaleType, value) =>
113
- isDiscretizedColorScaleType(scaleType)
114
- ? validateDiscretizedColorRange(value)
115
- : isContinuousColorScaleType(scaleType)
116
- ? validateSequentialColorRange(value)
117
- : scaleType === "ordinal"
118
- ? validateOrdinalRange(value)
119
- : validateScaleRange(value)
120
- });
121
- if (Object.hasOwn(args, "unknown")) definition.unknown = args.unknown;
102
+ const consumers = findScaleConsumers(this, id);
103
+ const offsetScale = consumers.length > 0 && consumers.every(
104
+ consumer => ["xOffset", "yOffset"].includes(consumer.channel)
105
+ );
106
+ const sizeScale = consumers.length > 0 && consumers.every(
107
+ consumer => consumer.channel === "size"
108
+ );
109
+ if (offsetScale && type !== "ordinal") {
110
+ throw new Error(`Scale type "${type}" is not valid for offset positions.`);
111
+ }
112
+ if (
113
+ offsetScale &&
114
+ Object.hasOwn(args, "range") &&
115
+ args.range !== "auto"
116
+ ) {
117
+ throw new Error(
118
+ "Offset scale range is derived from its parent categorical slot."
119
+ );
120
+ }
121
+ const definition = sizeScale
122
+ ? normalizeSizeScaleDefinition({
123
+ patch: {
124
+ ...args,
125
+ type,
126
+ ...(requestedRange === undefined ? {} : { range: requestedRange })
127
+ }
128
+ })
129
+ : normalizeScaleDefinition({
130
+ type,
131
+ patch: {
132
+ ...args,
133
+ ...(requestedRange === undefined ? {} : { range: requestedRange })
134
+ },
135
+ allowOrdinalBandParameters: offsetScale,
136
+ validateDomain: (scaleType, value) =>
137
+ isDiscretizedColorScaleType(scaleType)
138
+ ? validateDiscretizedColorDomain(scaleType, value)
139
+ : hasOrdinalDomain(scaleType)
140
+ ? validateOrdinalDomain(value)
141
+ : validateScaleDomain(value),
142
+ validateRange: (scaleType, value) =>
143
+ isDiscretizedColorScaleType(scaleType)
144
+ ? validateDiscretizedColorRange(value)
145
+ : isContinuousColorScaleType(scaleType)
146
+ ? validateSequentialColorRange(value)
147
+ : scaleType === "ordinal"
148
+ ? validateOrdinalRange(value)
149
+ : validateScaleRange(value)
150
+ });
151
+ if (Object.hasOwn(args, "unknown") && !sizeScale) {
152
+ definition.unknown = args.unknown;
153
+ }
122
154
  const existing = findSemanticScale(this, id);
123
155
 
124
156
  if (existing !== undefined) {
@@ -133,8 +165,8 @@ export const createScale = action(
133
165
 
134
166
  for (const property of [
135
167
  "nice", "zero", "clamp", "reverse", "base", "exponent", "constant",
136
- "paddingInner", "paddingOuter", "padding", "align", "interpolate",
137
- "unknown"
168
+ "paddingInner", "paddingOuter", "padding", "align", "interpolate", "midpoint",
169
+ "radialMapping", "unknown"
138
170
  ]) {
139
171
  if (definition[property] === undefined) continue;
140
172
  next = next.editSemantic({
@@ -1,9 +1,11 @@
1
+ import { prepareScaleEdit } from "./editPolicy.js";
1
2
  import { validateUserId } from "../../core/identifiers.js";
2
3
  import { isPlainObject } from "../../core/immutable.js";
3
4
  import { validateKeys } from "../../core/validation.js";
4
5
  import {
5
6
  validateColorRange,
6
7
  validateContinuousColorInterpolation,
8
+ validateSequentialMidpoint,
7
9
  validateLinearScaleType,
8
10
  validateOpacityRange,
9
11
  validateOrdinalDomain,
@@ -11,7 +13,6 @@ import {
11
13
  validateScaleDomain,
12
14
  validateScaleRange,
13
15
  validateShapeRange,
14
- validateSizeRange,
15
16
  validateStrokeDashRange,
16
17
  validateStrokeWidthRange,
17
18
  validateSequentialColorRange,
@@ -23,6 +24,7 @@ import {
23
24
  validateScalePropertyForType,
24
25
  validateScaleTypeForRole,
25
26
  isDiscretePositionScaleType,
27
+ normalizeSizeScaleDefinition,
26
28
  withScaleUnknown
27
29
  } from "../../grammar/scales/index.js";
28
30
  import {
@@ -30,6 +32,10 @@ import {
30
32
  validateThetaRange
31
33
  } from "../../grammar/polar.js";
32
34
  import { findSemanticScale } from "../../selectors/scales.js";
35
+ import { normalizeOffsetScalePolicy } from "../../grammar/bars/geometry.js";
36
+ import { resolveRequestedOffsetPolicy } from
37
+ "../../materialization/scales/policies/offset.js";
38
+ import { findScaleConsumers } from "./consumers/index.js";
33
39
 
34
40
  const BASE_OPTIONS = ["id", "type", "domain", "range"];
35
41
  const UNKNOWN_OPTIONS = [...BASE_OPTIONS, "unknown"];
@@ -49,13 +55,16 @@ const POSITION_OPTIONS = [
49
55
  const COLOR_OPTIONS = [...BASE_OPTIONS, "palette", "unknown"];
50
56
  const SEQUENTIAL_COLOR_OPTIONS = [
51
57
  ...COLOR_OPTIONS,
52
- "interpolate",
58
+ "interpolate", "midpoint",
53
59
  ...CLAMP_REVERSE
54
60
  ];
55
61
  const OPACITY_OPTIONS = [...BASE_OPTIONS, ...BOOLEAN_OPTIONS, "unknown"];
56
62
  const STROKE_WIDTH_OPTIONS = [
57
63
  ...BASE_OPTIONS, ...BOOLEAN_OPTIONS, ...TRANSFORM_OPTIONS
58
64
  ];
65
+ const SIZE_OPTIONS = [
66
+ ...UNKNOWN_OPTIONS, "clamp", "reverse", "base", "exponent"
67
+ ];
59
68
 
60
69
  function optionsObject(options) {
61
70
  if (!isPlainObject(options)) {
@@ -121,7 +130,7 @@ export function resolvePositionScaleDefinition(
121
130
  ...normalizeScaleDefinition({
122
131
  type,
123
132
  previous: existing,
124
- patch: options,
133
+ patch: { ...options, ...(defaults.radialMapping === undefined ? {} : { radialMapping: defaults.radialMapping }) },
125
134
  defaults: {
126
135
  ...(existing === undefined && defaults.nice !== undefined
127
136
  ? { nice: defaults.nice }
@@ -149,21 +158,22 @@ export function resolvePositionScaleDefinition(
149
158
  return withScaleUnknown(scale, { ...existing, ...options }, channel);
150
159
  }
151
160
 
152
- export function resolveColorScaleDefinition(program, options) {
161
+ export function resolveColorScaleDefinition(program, options, channel = "color") {
153
162
  optionsObject(options);
154
163
  validateKeys(options, COLOR_OPTIONS, "scale");
155
164
  validatePaletteRange(options);
156
- const id = validateUserId(options.id ?? "color", "Scale id");
165
+ const id = validateUserId(options.id ?? channel, "Scale id");
157
166
  const existing = findSemanticScale(program, id);
167
+ const previous = existing?.type === "ordinal" ? existing : undefined;
158
168
  const range = options.palette === undefined
159
169
  ? options.range
160
170
  : { palette: options.palette };
161
171
  return withScaleUnknown({
162
172
  id,
163
- type: validateOrdinalScaleType(options.type ?? existing?.type ?? "ordinal"),
164
- domain: validateOrdinalDomain(options.domain ?? existing?.domain ?? "auto"),
165
- range: validateColorRange(range ?? existing?.range ?? "auto")
166
- }, { ...existing, ...options }, "color");
173
+ type: validateOrdinalScaleType(options.type ?? previous?.type ?? "ordinal"),
174
+ domain: validateOrdinalDomain(options.domain ?? previous?.domain ?? "auto"),
175
+ range: validateColorRange(range ?? previous?.range ?? "auto")
176
+ }, { ...previous, ...options }, channel);
167
177
  }
168
178
 
169
179
  function continuousDomain(value, fieldType) {
@@ -184,14 +194,16 @@ function continuousDomain(value, fieldType) {
184
194
  export function resolveSequentialColorScaleDefinition(
185
195
  program,
186
196
  fieldType,
187
- options
197
+ options,
198
+ channel = "color"
188
199
  ) {
189
200
  optionsObject(options);
190
201
  validateKeys(options, SEQUENTIAL_COLOR_OPTIONS, "scale");
191
202
  validatePaletteRange(options);
192
- const id = validateUserId(options.id ?? "color", "Scale id");
203
+ const id = validateUserId(options.id ?? channel, "Scale id");
193
204
  const existing = findSemanticScale(program, id);
194
- const type = options.type ?? existing?.type ?? "sequential";
205
+ const previous = existing?.type === "sequential" ? existing : undefined;
206
+ const type = options.type ?? previous?.type ?? "sequential";
195
207
  if (type !== "sequential") {
196
208
  throw new Error(`Unsupported continuous color scale type "${type}".`);
197
209
  }
@@ -202,33 +214,52 @@ export function resolveSequentialColorScaleDefinition(
202
214
  const scale = {
203
215
  id,
204
216
  type,
205
- domain: continuousDomain(options.domain ?? existing?.domain ?? "auto", fieldType),
217
+ domain: continuousDomain(options.domain ?? previous?.domain ?? "auto", fieldType),
206
218
  range: validateSequentialColorRange(
207
- requestedRange ?? existing?.range ?? { palette: "viridis" }
219
+ requestedRange ?? previous?.range ?? { palette: "viridis" }
208
220
  ),
209
221
  interpolate: validateContinuousColorInterpolation(
210
- options.interpolate ?? existing?.interpolate ?? "rgb"
222
+ options.interpolate ?? previous?.interpolate ?? "rgb"
211
223
  )
212
224
  };
225
+ const midpoint = validateSequentialMidpoint(
226
+ Object.hasOwn(options, "midpoint") ? options.midpoint : previous?.midpoint, type, scale.domain
227
+ );
228
+ if (midpoint !== undefined) {
229
+ if (fieldType !== "quantitative") {
230
+ throw new Error("Scale midpoint requires quantitative color consumers.");
231
+ }
232
+ scale.midpoint = midpoint;
233
+ }
213
234
  return withScaleUnknown(
214
- assignOptions(scale, options, existing, CLAMP_REVERSE),
215
- { ...existing, ...options },
216
- "color"
235
+ assignOptions(scale, options, previous, CLAMP_REVERSE),
236
+ { ...previous, ...options },
237
+ channel
217
238
  );
218
239
  }
219
240
 
220
241
  export function resolveQuantitativeColorScaleDefinition(
221
242
  program,
222
243
  fieldType,
223
- options
244
+ options,
245
+ channel = "color"
224
246
  ) {
225
247
  optionsObject(options);
226
- const type = options.type ?? findSemanticScale(
227
- program,
228
- options.id ?? "color"
229
- )?.type ?? "sequential";
248
+ const existing = findSemanticScale(program, options.id ?? channel);
249
+ const existingContinuous = existing !== undefined && (
250
+ existing.type === "sequential" ||
251
+ ["quantize", "quantile", "threshold"].includes(existing.type)
252
+ );
253
+ const type = options.type ?? (
254
+ existingContinuous ? existing.type : "sequential"
255
+ );
256
+ if (existingContinuous && existing.type !== type) {
257
+ validateKeys(options, type === "sequential" ? SEQUENTIAL_COLOR_OPTIONS : [...COLOR_OPTIONS, ...CLAMP_REVERSE], "scale");
258
+ if (fieldType !== "quantitative") throw new Error("Color scale type transition requires quantitative color.");
259
+ return { id: existing.id, ...prepareScaleEdit(program, existing, channel, [], options) };
260
+ }
230
261
  if (type === "sequential") {
231
- return resolveSequentialColorScaleDefinition(program, fieldType, options);
262
+ return resolveSequentialColorScaleDefinition(program, fieldType, options, channel);
232
263
  }
233
264
  validateKeys(options, [...COLOR_OPTIONS, ...CLAMP_REVERSE], "scale");
234
265
  if (fieldType !== "quantitative") {
@@ -237,8 +268,7 @@ export function resolveQuantitativeColorScaleDefinition(
237
268
  validateScaleTypeForRole(type, SCALE_ROLES.discretizedColor);
238
269
  validatePaletteRange(options);
239
270
  validateBooleanOptions(options, CLAMP_REVERSE, type);
240
- const id = validateUserId(options.id ?? "color", "Scale id");
241
- const existing = findSemanticScale(program, id);
271
+ const id = validateUserId(options.id ?? channel, "Scale id");
242
272
  const previous = existing?.type === type ? existing : undefined;
243
273
  const requestedRange = options.palette === undefined
244
274
  ? options.range
@@ -257,7 +287,7 @@ export function resolveQuantitativeColorScaleDefinition(
257
287
  return withScaleUnknown(
258
288
  assignOptions(scale, options, previous, CLAMP_REVERSE),
259
289
  { ...existing, ...options },
260
- "color"
290
+ channel
261
291
  );
262
292
  }
263
293
 
@@ -282,21 +312,23 @@ export function resolveStrokeDashScaleDefinition(program, options) {
282
312
 
283
313
  export function resolveAppearanceScaleDefinition(program, channel, options) {
284
314
  optionsObject(options);
315
+ if (channel === "size") {
316
+ validateKeys(options, SIZE_OPTIONS, "scale");
317
+ const id = validateUserId(options.id ?? channel, "Scale id");
318
+ const existing = findSemanticScale(program, id);
319
+ return {
320
+ id,
321
+ ...normalizeSizeScaleDefinition({ previous: existing, patch: options })
322
+ };
323
+ }
285
324
  validateKeys(options, UNKNOWN_OPTIONS, "scale");
286
325
  const id = validateUserId(options.id ?? channel, "Scale id");
287
326
  const existing = findSemanticScale(program, id);
288
- const shape = channel === "shape";
289
327
  return withScaleUnknown({
290
328
  id,
291
- type: shape
292
- ? validateOrdinalScaleType(options.type ?? existing?.type ?? "ordinal")
293
- : validateLinearScaleType(options.type ?? existing?.type ?? "linear"),
294
- domain: shape
295
- ? validateOrdinalDomain(options.domain ?? existing?.domain ?? "auto")
296
- : validateScaleDomain(options.domain ?? existing?.domain ?? "auto"),
297
- range: shape
298
- ? validateShapeRange(options.range ?? existing?.range ?? "auto")
299
- : validateSizeRange(options.range ?? existing?.range ?? "auto")
329
+ type: validateOrdinalScaleType(options.type ?? existing?.type ?? "ordinal"),
330
+ domain: validateOrdinalDomain(options.domain ?? existing?.domain ?? "auto"),
331
+ range: validateShapeRange(options.range ?? existing?.range ?? "auto")
300
332
  }, { ...existing, ...options }, channel);
301
333
  }
302
334
 
@@ -349,7 +381,45 @@ export function resolveStrokeWidthScaleDefinition(program, options) {
349
381
  }
350
382
 
351
383
  export function resolveOffsetScaleDefinition(program, options, channel = "xOffset") {
352
- return resolveOrdinalScaleDefinition(
353
- program, options, channel, validateScaleRange
384
+ optionsObject(options);
385
+ validateKeys(options, [
386
+ "id", "type", "domain", "range", "reverse",
387
+ "padding", "paddingInner", "paddingOuter", "align"
388
+ ], "scale");
389
+ const id = validateUserId(options.id ?? channel, "Scale id");
390
+ const existing = findSemanticScale(program, id);
391
+ const type = options.type ?? existing?.type ?? "ordinal";
392
+ if (type !== "ordinal") {
393
+ throw new Error(`Scale type "${type}" is not valid for ${channel}.`);
394
+ }
395
+ if (Object.hasOwn(options, "range") && options.range !== "auto") {
396
+ throw new Error(
397
+ `${channel} scale range is derived from its parent categorical slot.`
398
+ );
399
+ }
400
+ const consumers = findScaleConsumers(program, id).filter(
401
+ consumer => consumer.channel === channel
354
402
  );
403
+ const currentPolicy = resolveRequestedOffsetPolicy({
404
+ scale: existing,
405
+ consumers,
406
+ markConfigs: program.markConfigs,
407
+ id,
408
+ channel
409
+ });
410
+ const policy = normalizeOffsetScalePolicy(options, currentPolicy, channel);
411
+ const patch = Object.fromEntries(Object.entries(options).filter(
412
+ ([property]) => !["id", "padding"].includes(property)
413
+ ));
414
+ return {
415
+ id,
416
+ ...normalizeScaleDefinition({
417
+ type,
418
+ previous: existing,
419
+ patch: { ...patch, ...policy },
420
+ allowOrdinalBandParameters: true,
421
+ validateDomain: (_scaleType, value) => validateOrdinalDomain(value),
422
+ validateRange: (_scaleType, value) => validateScaleRange(value)
423
+ })
424
+ };
355
425
  }
@@ -1,10 +1,18 @@
1
+ import { withGuideLayoutValidation } from "../../materialization/guides/layout.js";
2
+ import {
3
+ applyColorLegendTransition,
4
+ planColorLegendTransitions
5
+ } from "../guides/legends/transition.js";
1
6
  import { action } from "../../core/action.js";
2
7
  import { validateUserId } from "../../core/identifiers.js";
3
8
  import { validateOptionObject } from "../../core/validation.js";
4
- import { getMarkMaterializationStep } from "../../materialization/marks/index.js";
9
+ import { getMarkMaterializationStep, getSourceDependentMarkSteps } from "../../materialization/marks/index.js";
5
10
  import {
6
- applyMaterializationPlan
11
+ applyMaterializationPlan,
12
+ planCoordinateRematerialization
7
13
  } from "../../materialization/dependencies.js";
14
+ import { findDataAspectCoordinatesForScale } from
15
+ "../../materialization/coordinateBounds.js";
8
16
  import {
9
17
  findSemanticScale,
10
18
  requireSemanticScale
@@ -14,11 +22,13 @@ import {
14
22
  prepareScaleEdit,
15
23
  resolveScaleConsumerChannel
16
24
  } from "./editPolicy.js";
25
+ import { hasExactLegendSamplingForScale } from
26
+ "../guides/legends/sampling.js";
17
27
 
18
28
  const OPTIONS = Object.freeze([
19
29
  "id", "type", "domain", "range", "nice", "zero", "clamp", "reverse",
20
30
  "base", "exponent", "constant", "paddingInner", "paddingOuter", "padding",
21
- "align", "interpolate", "unknown", "palette"
31
+ "align", "interpolate", "midpoint", "unknown", "palette", "radialMapping"
22
32
  ]);
23
33
  const EDITABLE = Object.freeze(
24
34
  OPTIONS.filter(option => !["id", "palette"].includes(option))
@@ -63,7 +73,76 @@ function planMarkRematerialization(program, consumers) {
63
73
  seen.add(consumer.layer.id);
64
74
  plan.push(step);
65
75
  }
66
- return plan;
76
+ return [
77
+ ...plan,
78
+ ...consumers.flatMap(consumer => getSourceDependentMarkSteps(program, consumer.layer.id))
79
+ ];
80
+ }
81
+
82
+ function applyScaleEdit(program, {
83
+ id,
84
+ scale,
85
+ channel,
86
+ consumers,
87
+ definition,
88
+ legendTransitions
89
+ }) {
90
+ let next = program;
91
+ for (const transition of legendTransitions) {
92
+ next = next.removeLegend({
93
+ target: transition.args.target,
94
+ channels: [transition.channel]
95
+ });
96
+ }
97
+ for (const property of EDITABLE) {
98
+ if (
99
+ Object.hasOwn(scale, property) &&
100
+ !Object.hasOwn(definition, property)
101
+ ) {
102
+ next = next.editSemantic({
103
+ property: `scale[${id}].${property}`,
104
+ remove: true
105
+ });
106
+ }
107
+ }
108
+ for (const property of EDITABLE) {
109
+ if (
110
+ !Object.hasOwn(definition, property) ||
111
+ sameValue(scale[property], definition[property])
112
+ ) continue;
113
+ next = next.editSemantic({
114
+ property: `scale[${id}].${property}`,
115
+ value: definition[property]
116
+ });
117
+ }
118
+ if (consumers.length === 0) return next;
119
+
120
+ const aspectCoordinates = findDataAspectCoordinatesForScale(next, id);
121
+ if (aspectCoordinates.length > 0) {
122
+ for (const coordinate of aspectCoordinates) {
123
+ next = applyMaterializationPlan(
124
+ next,
125
+ planCoordinateRematerialization(next, coordinate.id)
126
+ );
127
+ }
128
+ } else {
129
+ next = next.rematerializeScale({ id });
130
+ next = applyMaterializationPlan(
131
+ next,
132
+ planMarkRematerialization(next, consumers)
133
+ );
134
+ }
135
+ if (["xOffset", "yOffset"].includes(channel)) {
136
+ for (const consumer of consumers) {
137
+ next = next._withoutMaterializationConfig([
138
+ "marks", consumer.layer.id, channel
139
+ ]);
140
+ }
141
+ }
142
+ for (const transition of legendTransitions) {
143
+ next = applyColorLegendTransition(next, transition);
144
+ }
145
+ return next;
67
146
  }
68
147
 
69
148
  export const editScale = action(
@@ -71,7 +150,7 @@ export const editScale = action(
71
150
  op: "editScale",
72
151
  description: "Edit an existing scale and rematerialize its consumers."
73
152
  },
74
- function (args = {}) {
153
+ withGuideLayoutValidation(function (args = {}) {
75
154
  validateOptionObject(args, OPTIONS, "editScale");
76
155
  if (!REQUESTED_CHANGES.some(property => Object.hasOwn(args, property))) {
77
156
  throw new Error("editScale requires at least one editable property.");
@@ -82,34 +161,22 @@ export const editScale = action(
82
161
  const channel = resolveScaleConsumerChannel(consumers, id);
83
162
  const definition = prepareScaleEdit(this, scale, channel, consumers, args);
84
163
 
85
- let next = this;
86
- for (const property of EDITABLE) {
87
- if (
88
- Object.hasOwn(scale, property) &&
89
- !Object.hasOwn(definition, property)
90
- ) {
91
- next = next.editSemantic({
92
- property: `scale[${id}].${property}`,
93
- remove: true
94
- });
95
- }
164
+ const legendTransitions = planColorLegendTransitions(this, scale, definition.type);
165
+ const proposal = {
166
+ id,
167
+ scale,
168
+ channel,
169
+ consumers,
170
+ definition,
171
+ legendTransitions
172
+ };
173
+ // Preflight every dependent mark and guide on a discarded immutable branch.
174
+ if (
175
+ scale.type !== definition.type ||
176
+ hasExactLegendSamplingForScale(this, id)
177
+ ) {
178
+ applyScaleEdit(this, proposal);
96
179
  }
97
- for (const property of EDITABLE) {
98
- if (
99
- !Object.hasOwn(definition, property) ||
100
- sameValue(scale[property], definition[property])
101
- ) continue;
102
- next = next.editSemantic({
103
- property: `scale[${id}].${property}`,
104
- value: definition[property]
105
- });
106
- }
107
- if (consumers.length === 0) return next;
108
-
109
- next = next.rematerializeScale({ id });
110
- return applyMaterializationPlan(
111
- next,
112
- planMarkRematerialization(next, consumers)
113
- );
114
- }
180
+ return applyScaleEdit(this, proposal);
181
+ })
115
182
  );