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
@@ -0,0 +1,415 @@
1
+ import {
2
+ action,
3
+ invokeWrappedActionImplementation
4
+ } from "../../core/action.js";
5
+ import { isPlainObject } from "../../core/immutable.js";
6
+ import { validateUserId } from "../../core/identifiers.js";
7
+ import { validateOptionObject } from "../../core/validation.js";
8
+ import { findLayer } from "../../selectors/layers.js";
9
+ import { findSemanticScale } from "../../selectors/scales.js";
10
+ import { findScaleConsumers } from "../scales/consumers/index.js";
11
+ import { resolveScalePreview, validatePendingMeasuredScale } from
12
+ "../scales/preview.js";
13
+ import { isPendingMeasuredRadiusConsumer } from
14
+ "../../materialization/scales/policies/arc.js";
15
+ import {
16
+ getMarkRematerializationStep,
17
+ getScaleConsumerMarkSteps,
18
+ getSourceDependentMarkSteps
19
+ } from "../../materialization/marks/index.js";
20
+ import {
21
+ applyMaterializationPlan,
22
+ planScaleGuideRematerialization
23
+ } from "../../materialization/dependencies.js";
24
+ import { buildMaterializationPlan } from "../../materialization/planner.js";
25
+ import { hasMaterializedLegend } from "../../materialization/legends.js";
26
+ import { rebindPositionGuides } from "./shared.js";
27
+ import { withoutPreviewLayerEncodings } from
28
+ "../primitives/semanticAction.js";
29
+
30
+ export const ENCODING_CHANNEL_ORDER = Object.freeze([
31
+ "x", "y", "x2", "y2", "theta", "r", "xOffset", "yOffset",
32
+ "group", "pathOrder", "color", "stroke", "size", "shape",
33
+ "opacity", "strokeWidth", "strokeDash", "angle", "text"
34
+ ]);
35
+
36
+ const CHANNEL_METHODS = Object.freeze({
37
+ x: "encodeX",
38
+ y: "encodeY",
39
+ x2: "encodeX2",
40
+ y2: "encodeY2",
41
+ theta: "encodeTheta",
42
+ r: "encodeR",
43
+ xOffset: "encodeXOffset",
44
+ yOffset: "encodeYOffset",
45
+ group: "encodeGroup",
46
+ pathOrder: "encodePathOrder",
47
+ color: "encodeColor",
48
+ stroke: "encodeStroke",
49
+ size: "encodeSize",
50
+ shape: "encodeShape",
51
+ opacity: "encodeOpacity",
52
+ strokeWidth: "encodeStrokeWidth",
53
+ strokeDash: "encodeStrokeDash",
54
+ angle: "encodeAngle",
55
+ text: "encodeText"
56
+ });
57
+
58
+ const FORBIDDEN_PAYLOAD_KEYS = Object.freeze(["target", "coordinate", "id"]);
59
+
60
+ const DEFERRED_MARK_OPERATIONS = Object.freeze([
61
+ "materializeEmptyMark",
62
+ "rematerializePointMark",
63
+ "rematerializeTickMark",
64
+ "rematerializeLineMark",
65
+ "rematerializeAreaMark",
66
+ "rematerializeArcMark",
67
+ "rematerializeBarMark",
68
+ "rematerializeRuleMark",
69
+ "rematerializeTextMark",
70
+ "rematerializeRectMark",
71
+ "rematerializeMarkHighlights",
72
+ "materializeLabelLayout"
73
+ ]);
74
+
75
+ const deferredClassByProgram = new WeakMap();
76
+
77
+ function sameSetting(left, right) {
78
+ if (Object.is(left, right)) return true;
79
+ if (Array.isArray(left) && Array.isArray(right)) {
80
+ return left.length === right.length && left.every(
81
+ (value, index) => sameSetting(value, right[index])
82
+ );
83
+ }
84
+ if (isPlainObject(left) && isPlainObject(right)) {
85
+ const leftKeys = Object.keys(left);
86
+ const rightKeys = Object.keys(right);
87
+ return leftKeys.length === rightKeys.length && leftKeys.every(
88
+ key => Object.hasOwn(right, key) && sameSetting(left[key], right[key])
89
+ );
90
+ }
91
+ return false;
92
+ }
93
+
94
+ function assertCompatibleScaleRequests(requests) {
95
+ const patches = new Map();
96
+ for (const { channel, payload } of requests) {
97
+ if (!isPlainObject(payload.scale) || payload.scale.id === undefined) continue;
98
+ const id = payload.scale.id;
99
+ const previous = patches.get(id) ?? new Map();
100
+ for (const [property, value] of Object.entries(payload.scale)) {
101
+ if (property === "id" || value === undefined) continue;
102
+ const existing = previous.get(property);
103
+ if (existing !== undefined && !sameSetting(existing.value, value)) {
104
+ throw new Error(
105
+ `encodeChannels scale "${id}" has conflicting explicit ${property} requests for ${existing.channel} and ${channel}.`
106
+ );
107
+ }
108
+ if (existing === undefined) previous.set(property, { channel, value });
109
+ }
110
+ patches.set(id, previous);
111
+ }
112
+ }
113
+
114
+ export function normalizeEncodeChannelsArgs(args) {
115
+ validateOptionObject(args, ["target", "channels"], "encodeChannels");
116
+ const target = validateUserId(args.target, "Mark id");
117
+ if (!isPlainObject(args.channels)) {
118
+ throw new TypeError("encodeChannels channels must be a plain object.");
119
+ }
120
+ const keys = Object.keys(args.channels);
121
+ if (keys.length === 0) {
122
+ throw new TypeError("encodeChannels channels must contain at least one channel.");
123
+ }
124
+ for (const key of keys) {
125
+ if (!Object.hasOwn(CHANNEL_METHODS, key)) {
126
+ throw new Error(`encodeChannels does not support channel "${key}".`);
127
+ }
128
+ }
129
+ const requests = ENCODING_CHANNEL_ORDER
130
+ .filter(channel => Object.hasOwn(args.channels, channel))
131
+ .map(channel => {
132
+ const payload = args.channels[channel];
133
+ if (!isPlainObject(payload)) {
134
+ throw new TypeError(
135
+ `encodeChannels ${channel} payload must be a plain object.`
136
+ );
137
+ }
138
+ for (const key of FORBIDDEN_PAYLOAD_KEYS) {
139
+ if (Object.hasOwn(payload, key)) {
140
+ throw new Error(
141
+ `encodeChannels ${channel} payload cannot specify ${key}.`
142
+ );
143
+ }
144
+ }
145
+ return Object.freeze({ channel, payload });
146
+ });
147
+ assertCompatibleScaleRequests(requests);
148
+ return Object.freeze({ target, requests: Object.freeze(requests) });
149
+ }
150
+
151
+ function resolveScaleOnly(program, id) {
152
+ const semanticScale = findSemanticScale(program, id);
153
+ if (semanticScale === undefined) {
154
+ throw new Error(`Unknown scale "${id}".`);
155
+ }
156
+ const consumers = findScaleConsumers(program, id);
157
+ if (
158
+ semanticScale.radialMapping !== undefined &&
159
+ consumers.length > 0 &&
160
+ consumers.every(isPendingMeasuredRadiusConsumer)
161
+ ) {
162
+ validatePendingMeasuredScale(program, semanticScale, consumers);
163
+ return program._withoutResolvedScale(id);
164
+ }
165
+ return program._withResolvedScale(
166
+ id,
167
+ resolveScalePreview(program, id).resolvedScale
168
+ );
169
+ }
170
+
171
+ function deferredProgramClass(ProgramClass) {
172
+ const existing = deferredClassByProgram.get(ProgramClass);
173
+ if (existing !== undefined) return existing;
174
+
175
+ class DeferredEncodingProgram extends ProgramClass {
176
+ rematerializeScale({ id } = {}) {
177
+ return resolveScaleOnly(this, id);
178
+ }
179
+
180
+ rematerializeLegend() {
181
+ return this;
182
+ }
183
+ }
184
+ for (const operation of DEFERRED_MARK_OPERATIONS) {
185
+ Object.defineProperty(DeferredEncodingProgram.prototype, operation, {
186
+ configurable: true,
187
+ value() {
188
+ return this;
189
+ }
190
+ });
191
+ }
192
+ deferredClassByProgram.set(ProgramClass, DeferredEncodingProgram);
193
+ return DeferredEncodingProgram;
194
+ }
195
+
196
+ function programState(program) {
197
+ return Object.freeze({
198
+ semanticSpec: program.semanticSpec,
199
+ graphicSpec: program.graphicSpec,
200
+ resolvedScales: program.resolvedScales,
201
+ materializationConfigs: program.materializationConfigs,
202
+ children: program.children,
203
+ compositionSpec: program.compositionSpec,
204
+ context: program.context,
205
+ trace: program.trace,
206
+ actionStack: program.actionStack,
207
+ actionSequence: program._actionSequence
208
+ });
209
+ }
210
+
211
+ function instantiateProgram(state, ProgramClass) {
212
+ return new ProgramClass({
213
+ semanticSpec: state.semanticSpec,
214
+ graphicSpec: state.graphicSpec,
215
+ resolvedScales: state.resolvedScales,
216
+ materializationConfigs: state.materializationConfigs,
217
+ children: state.children,
218
+ compositionSpec: state.compositionSpec,
219
+ context: state.context,
220
+ trace: state.trace,
221
+ actionStack: state.actionStack,
222
+ actionSequence: state.actionSequence
223
+ });
224
+ }
225
+
226
+ function createDeferredProgram(program) {
227
+ return instantiateProgram(
228
+ programState(program),
229
+ deferredProgramClass(program.constructor)
230
+ );
231
+ }
232
+
233
+ function requestedFieldType(request) {
234
+ if (request === undefined) return undefined;
235
+ return request.payload.fieldType ?? "quantitative";
236
+ }
237
+
238
+ function isCategoricalBarType(fieldType) {
239
+ return ["nominal", "ordinal", "temporal"].includes(fieldType);
240
+ }
241
+
242
+ function stageFinalPositionRoles(program, target, requests) {
243
+ const layer = findLayer(program, target);
244
+ if (layer?.mark?.type !== "bar") return program;
245
+ const byChannel = new Map(requests.map(request => [request.channel, request]));
246
+ const nextX = requestedFieldType(byChannel.get("x"));
247
+ const nextY = requestedFieldType(byChannel.get("y"));
248
+ if (nextX === undefined || nextY === undefined) return program;
249
+ const currentX = layer.encoding?.x?.fieldType;
250
+ const currentY = layer.encoding?.y?.fieldType;
251
+ const changesOrientation =
252
+ isCategoricalBarType(currentX) !== isCategoricalBarType(nextX) &&
253
+ isCategoricalBarType(currentY) !== isCategoricalBarType(nextY);
254
+ if (!changesOrientation) return program;
255
+
256
+ return withoutPreviewLayerEncodings(program, {
257
+ id: target,
258
+ channels: ["x", "y"]
259
+ });
260
+ }
261
+
262
+ const INTERNAL_SCALE_CHANNEL = Object.freeze({ r: "radius" });
263
+
264
+ function requestedScaleIds(originalLayer, finalLayer, requests) {
265
+ const requestedChannels = new Set(requests.map(({ channel }) =>
266
+ INTERNAL_SCALE_CHANNEL[channel] ?? channel
267
+ ));
268
+ const ids = [];
269
+ for (const channel of [
270
+ "x", "y", "x2", "y2", "theta", "radius", "xOffset", "yOffset",
271
+ "color", "stroke", "size", "shape", "opacity", "strokeWidth",
272
+ "strokeDash", "angle", "text", "group", "pathOrder"
273
+ ]) {
274
+ if (!requestedChannels.has(channel)) continue;
275
+ for (const layer of [originalLayer, finalLayer]) {
276
+ const id = layer?.encoding?.[channel]?.scale;
277
+ if (id !== undefined && !ids.includes(id)) ids.push(id);
278
+ }
279
+ }
280
+ return ids;
281
+ }
282
+
283
+ function changedScaleIds(before, after) {
284
+ const beforeById = new Map(before.semanticSpec.scales.map(scale => [scale.id, scale]));
285
+ return after.semanticSpec.scales
286
+ .filter(scale => !sameSetting(scale, beforeById.get(scale.id)))
287
+ .map(scale => scale.id)
288
+ .sort();
289
+ }
290
+
291
+ function finalScaleIds(before, after, originalLayer, finalLayer, requests) {
292
+ const ids = [
293
+ ...requestedScaleIds(originalLayer, finalLayer, requests),
294
+ ...changedScaleIds(before, after)
295
+ ];
296
+ return [...new Set(ids)].filter(id =>
297
+ findSemanticScale(after, id) !== undefined &&
298
+ findScaleConsumers(after, id).length > 0
299
+ );
300
+ }
301
+
302
+ const rematerializeEncodingScales = action(
303
+ {
304
+ op: "rematerializeEncodingScales",
305
+ description: "Resolve every scale affected by one atomic encoding assignment."
306
+ },
307
+ function ({ ids } = {}) {
308
+ let next = this;
309
+ for (const id of ids) next = resolveScaleOnly(next, id);
310
+ return next;
311
+ }
312
+ );
313
+
314
+ function finalMaterializationPlan(program, target, scaleIds) {
315
+ const layer = findLayer(program, target);
316
+ const targetStep = layer === undefined
317
+ ? undefined
318
+ : getMarkRematerializationStep(program, layer);
319
+ const directMarks = [
320
+ ...(targetStep === undefined ? [] : [targetStep]),
321
+ ...getScaleConsumerMarkSteps(program, scaleIds)
322
+ ];
323
+ const marks = [
324
+ ...directMarks,
325
+ ...directMarks.flatMap(step =>
326
+ getSourceDependentMarkSteps(program, step.args.id)
327
+ )
328
+ ];
329
+ const guides = scaleIds.flatMap(id =>
330
+ planScaleGuideRematerialization(program, id)
331
+ );
332
+ if (hasMaterializedLegend(program)) {
333
+ guides.push({ op: "rematerializeLegend" });
334
+ }
335
+ return buildMaterializationPlan({ marks, guides });
336
+ }
337
+
338
+ function applyEncodingDraft(program, target, requests) {
339
+ let draft = stageFinalPositionRoles(
340
+ createDeferredProgram(program),
341
+ target,
342
+ requests
343
+ );
344
+ for (const { channel, payload } of requests) {
345
+ const focusedAction = draft[CHANNEL_METHODS[channel]];
346
+ draft = invokeWrappedActionImplementation(
347
+ focusedAction,
348
+ draft,
349
+ { ...payload, target }
350
+ );
351
+ }
352
+ const originalLayer = findLayer(program, target);
353
+ const finalLayer = findLayer(draft, target);
354
+ for (const channel of ["x", "y"]) {
355
+ const previousScale = originalLayer?.encoding?.[channel]?.scale;
356
+ const nextScale = finalLayer?.encoding?.[channel]?.scale;
357
+ if (previousScale !== undefined && nextScale !== undefined) {
358
+ draft = rebindPositionGuides(
359
+ draft,
360
+ channel,
361
+ previousScale,
362
+ nextScale,
363
+ target
364
+ );
365
+ }
366
+ }
367
+ return draft;
368
+ }
369
+
370
+ function planEncodingAssignments(program, target, requests) {
371
+ const originalLayer = findLayer(program, target);
372
+ const draft = applyEncodingDraft(program, target, requests);
373
+ const finalLayer = findLayer(draft, target);
374
+ return Object.freeze({
375
+ target,
376
+ originalLayer,
377
+ finalLayer,
378
+ state: programState(draft),
379
+ scaleIds: Object.freeze(finalScaleIds(
380
+ program,
381
+ draft,
382
+ originalLayer,
383
+ finalLayer,
384
+ requests
385
+ ))
386
+ });
387
+ }
388
+
389
+ function applyEncodingAssignments(program, plan) {
390
+ return instantiateProgram(plan.state, program.constructor);
391
+ }
392
+
393
+ export const encodeChannels = action(
394
+ {
395
+ op: "encodeChannels",
396
+ description: "Atomically assign multiple encoding channels to one mark."
397
+ },
398
+ function (args = {}) {
399
+ const { target, requests } = normalizeEncodeChannelsArgs(args);
400
+ const plan = planEncodingAssignments(this, target, requests);
401
+ let next = applyEncodingAssignments(this, plan);
402
+ const scaleIds = plan.scaleIds;
403
+ if (scaleIds.length > 0) {
404
+ next = rematerializeEncodingScales.call(next, { ids: scaleIds });
405
+ }
406
+ return applyMaterializationPlan(
407
+ next,
408
+ finalMaterializationPlan(next, target, scaleIds)
409
+ );
410
+ }
411
+ );
412
+
413
+ export function registerAtomicEncodingAction(ProgramClass) {
414
+ ProgramClass.prototype.encodeChannels = encodeChannels;
415
+ }
@@ -1,18 +1,11 @@
1
1
  import {
2
2
  readQuantitativeField,
3
3
  readScaleField,
4
- readTemporalField
4
+ readTemporalField,
5
+ resolveTemporalUnit
5
6
  } from "../../../grammar/scales/index.js";
6
- import {
7
- BAR_GRAINS,
8
- resolveBarChannels,
9
- resolveBarGrain
10
- } from "../../../grammar/bars/policy.js";
11
- import {
12
- validateAggregate,
13
- validateAggregateFieldType,
14
- validateAggregateFieldValues
15
- } from "../../../grammar/aggregate.js";
7
+ import { validateContinuousColorConsumer } from "../../../grammar/scales/colorConsumers.js";
8
+ import { validateAggregateFieldValues } from "../../../grammar/aggregate.js";
16
9
  import {
17
10
  resolveQuantitativeColorScaleDefinition
18
11
  } from "../../scales/definitions.js";
@@ -20,7 +13,10 @@ import {
20
13
  resolveReassignmentScaleOptions,
21
14
  resolveTarget
22
15
  } from "../shared.js";
23
- import { applyMaterializationPlan } from "../../../materialization/dependencies.js";
16
+ import {
17
+ applyDetachedScaleRematerialization,
18
+ applyMaterializationPlan
19
+ } from "../../../materialization/dependencies.js";
24
20
  import {
25
21
  planEncodingRematerialization
26
22
  } from "../../../materialization/encodings.js";
@@ -29,6 +25,8 @@ import {
29
25
  resolveColorScaleOptions
30
26
  } from "./policy.js";
31
27
 
28
+ import { applyTemporalUnit } from "../temporal.js";
29
+
32
30
  export function encodeContinuousColor(program, args) {
33
31
  if (!["quantitative", "temporal"].includes(args.fieldType)) {
34
32
  throw new Error(`Unsupported color field type "${args.fieldType}".`);
@@ -43,6 +41,7 @@ export function encodeContinuousColor(program, args) {
43
41
  "continuous color mark"
44
42
  );
45
43
  assertNoConstantColor(program, layer);
44
+ const temporalUnit = resolveTemporalUnit(args, args.fieldType, layer.encoding?.color);
46
45
  const requestedScale = resolveReassignmentScaleOptions(
47
46
  layer.encoding?.color,
48
47
  resolveColorScaleOptions(args)
@@ -52,61 +51,22 @@ export function encodeContinuousColor(program, args) {
52
51
  args.fieldType,
53
52
  requestedScale
54
53
  );
55
- if (Object.hasOwn(scale, "unknown") && layer.mark.type !== "point") {
56
- throw new Error(
57
- "Continuous color scale unknown currently requires a row-owned point mark."
58
- );
59
- }
60
- if (
61
- layer.mark.type === "bar" &&
62
- ["nominal", "ordinal"].includes(layer.encoding?.color?.fieldType)
63
- ) {
64
- throw new Error(
65
- "Continuous bar color cannot replace an existing nominal color layout."
66
- );
67
- }
68
- let aggregate;
69
- if (["point", "rect"].includes(layer.mark.type)) {
70
- if (args.aggregate !== undefined) {
71
- throw new Error(`${layer.mark.type} continuous color does not support aggregate.`);
72
- }
73
- } else {
74
- if (args.fieldType !== "quantitative") {
75
- throw new Error("Aggregate bar color currently requires a quantitative field.");
76
- }
77
- if (resolveBarGrain(layer) !== BAR_GRAINS.aggregate) {
78
- throw new Error(
79
- "Continuous bar color requires a complete categorical aggregate bar."
80
- );
81
- }
82
- const channels = resolveBarChannels(layer);
83
- const measure = layer.encoding?.[channels.measure];
84
- aggregate = args.aggregate ?? (
85
- measure?.field === args.field ? measure.aggregate : undefined
86
- );
87
- if (aggregate === undefined) {
88
- throw new Error(
89
- "Continuous bar color requires aggregate when its field differs from the measure field."
90
- );
91
- }
92
- aggregate = validateAggregate(aggregate);
93
- validateAggregateFieldType(aggregate, args.fieldType);
94
- validateAggregateFieldValues(dataset.values, args.field, args.fieldType);
95
- }
54
+ const aggregate = validateContinuousColorConsumer(layer, args, scale, { inferAggregate: true });
55
+ if (aggregate !== undefined) validateAggregateFieldValues(dataset.values, args.field, args.fieldType);
96
56
  if (layer.mark.type === "rect") {
97
57
  readScaleField(dataset.values, args.field, args.fieldType, {
98
- allowUnknown: true
58
+ allowUnknown: true, temporalUnit
99
59
  });
100
60
  } else if (Object.hasOwn(scale, "unknown")) {
101
61
  readScaleField(dataset.values, args.field, args.fieldType, {
102
- allowUnknown: true
62
+ allowUnknown: true, temporalUnit
103
63
  });
104
64
  } else if (args.fieldType === "temporal") {
105
- readTemporalField(dataset.values, args.field);
65
+ readTemporalField(dataset.values, args.field, temporalUnit);
106
66
  } else {
107
67
  readQuantitativeField(dataset.values, args.field);
108
68
  }
109
- const next = program
69
+ const next = applyTemporalUnit(program, target, "color", temporalUnit, layer.encoding?.color)
110
70
  .editSemantic({
111
71
  property: `layer[${target}].encoding.color.field`,
112
72
  value: args.field
@@ -126,12 +86,12 @@ export function encodeContinuousColor(program, args) {
126
86
  value: aggregate
127
87
  });
128
88
  const scaled = encoded.setQuantitativeColorScale(scale);
129
- return applyMaterializationPlan(
89
+ return applyDetachedScaleRematerialization(applyMaterializationPlan(
130
90
  scaled,
131
91
  planEncodingRematerialization(scaled, {
132
92
  target,
133
93
  channel: "color",
134
94
  scale: scale.id
135
95
  })
136
- );
96
+ ), [layer]);
137
97
  }
@@ -1,7 +1,9 @@
1
+ import { inferSeriesGroup } from "../shared.js";
1
2
  import { action } from "../../../core/action.js";
2
3
  import {
3
4
  readNominalField,
4
5
  readScaleField,
6
+ resolveTemporalUnit,
5
7
  validateCategoricalFieldType
6
8
  } from "../../../grammar/scales/index.js";
7
9
  import {
@@ -12,19 +14,21 @@ import {
12
14
  applyEncodingScale,
13
15
  resolveReassignmentScaleOptions,
14
16
  resolveTarget,
15
- validateLineSeriesCompatibility,
16
17
  validateOptions
17
18
  } from "../shared.js";
18
- import { applyMaterializationPlan } from "../../../materialization/dependencies.js";
19
+ import {
20
+ applyDetachedScaleRematerialization,
21
+ applyMaterializationPlan
22
+ } from "../../../materialization/dependencies.js";
19
23
  import {
20
24
  planEncodingRematerialization
21
25
  } from "../../../materialization/encodings.js";
22
26
  import { findUpstreamTransform } from
23
27
  "../../../materialization/dataProvenance.js";
28
+ import { validatePathSeriesAppearance } from "../../../grammar/pathSeries.js";
24
29
  import { encodeContinuousColor } from "./continuous.js";
25
30
  import {
26
- applyColorLayoutCompanion,
27
- preSynchronizeGroupedOffset
31
+ applyColorLayoutCompanion
28
32
  } from "./layout.js";
29
33
  import {
30
34
  assertNoConstantColor,
@@ -33,6 +37,8 @@ import {
33
37
  resolveColorScaleOptions
34
38
  } from "./policy.js";
35
39
 
40
+ import { applyTemporalUnit } from "../temporal.js";
41
+
36
42
  const encodeColor = action(
37
43
  {
38
44
  op: "encodeColor",
@@ -51,6 +57,7 @@ const encodeColor = action(
51
57
  throw new Error("Categorical color does not support aggregate.");
52
58
  }
53
59
  const fieldType = validateCategoricalFieldType(requestedFieldType);
60
+ resolveTemporalUnit(args, fieldType);
54
61
  const { id: target, dataset, layer } = resolveTarget(
55
62
  this,
56
63
  args.target,
@@ -78,10 +85,13 @@ const encodeColor = action(
78
85
  }
79
86
  const layout = resolveColorLayout(layer, args.layout, barGrain);
80
87
  const createsCenterGroup = layer.mark.type === "area" &&
81
- layout === "center" && layer.encoding?.group?.field === undefined;
88
+ layout === "center" && layer.encoding?.group === undefined;
89
+ const ordinaryGroup = layer.mark.type === "area" && !createsCenterGroup &&
90
+ densityTransform === undefined && horizonTransform === undefined;
82
91
  if (
83
92
  layer.mark.type === "area" &&
84
93
  !createsCenterGroup &&
94
+ !ordinaryGroup &&
85
95
  (layer.encoding?.group?.field === undefined ||
86
96
  layer.encoding.group.field !== args.field) &&
87
97
  !densitySeriesFields.includes(args.field) &&
@@ -91,7 +101,11 @@ const encodeColor = action(
91
101
  "Area color encoding must match an existing group encoding."
92
102
  );
93
103
  }
94
- validateLineSeriesCompatibility(layer, "color", args.field);
104
+ if (layer.mark.type === "line" || ordinaryGroup) {
105
+ validatePathSeriesAppearance(dataset.values, {
106
+ ...layer, encoding: { ...layer.encoding, color: { field: args.field, fieldType } }
107
+ });
108
+ }
95
109
  const requestedScale = resolveReassignmentScaleOptions(
96
110
  layer.encoding?.color,
97
111
  resolveColorScaleOptions(args)
@@ -114,10 +128,10 @@ const encodeColor = action(
114
128
  readNominalField(dataset.values, args.field);
115
129
  }
116
130
 
117
- let next = createsCenterGroup
118
- ? this.encodeGroup({ target, field: args.field })
131
+ let next = layer.mark.type === "bar" || createsCenterGroup
132
+ ? inferSeriesGroup(this, layer, args.field, "color")
119
133
  : this;
120
- next = next.editSemantic({
134
+ next = applyTemporalUnit(next, target, "color", undefined, layer.encoding?.color).editSemantic({
121
135
  property: `layer[${target}].encoding.color.field`,
122
136
  value: args.field
123
137
  })
@@ -129,40 +143,33 @@ const encodeColor = action(
129
143
  property: `layer[${target}].encoding.color.scale`,
130
144
  value: scale.id
131
145
  });
132
- if (layout !== undefined) {
146
+ if (layout !== undefined && (layer.mark.type === "arc" || horizonTransform !== undefined)) {
133
147
  next = next.editSemantic({
134
148
  property: `layer[${target}].encoding.color.layout`,
135
149
  value: layout
136
150
  });
137
151
  }
138
- next = preSynchronizeGroupedOffset(next, {
139
- target,
140
- layer,
141
- layout,
142
- field: args.field,
143
- fieldType,
144
- scale,
145
- requestedScale
146
- });
147
152
  next = applyEncodingScale(next, scale, requestedScale, {
148
- reassignment: layer.encoding?.color?.scale === scale.id
153
+ reassignment: layer.encoding?.color?.scale === scale.id,
154
+ allowTypeChange: true
149
155
  });
150
156
  next = applyColorLayoutCompanion(next, {
151
157
  target,
152
158
  layer,
153
159
  layout,
154
160
  scale,
155
- field: args.field
161
+ field: args.field,
162
+ horizon: horizonTransform !== undefined
156
163
  });
157
164
 
158
- return applyMaterializationPlan(
165
+ return applyDetachedScaleRematerialization(applyMaterializationPlan(
159
166
  next,
160
167
  planEncodingRematerialization(next, {
161
168
  target,
162
169
  channel: "color",
163
170
  scale: scale.id
164
171
  })
165
- );
172
+ ), [layer]);
166
173
  }
167
174
  );
168
175