ggaction 0.0.11 → 0.0.13

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 (289) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/README.md +3 -2
  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 +4 -2
  11. package/src/actions/basic.js +2 -0
  12. package/src/actions/boxPlots/materialize.js +2 -1
  13. package/src/actions/canvas/actions.js +3 -1
  14. package/src/actions/canvas/fitting.js +211 -0
  15. package/src/actions/canvas/index.js +2 -0
  16. package/src/actions/categoryOrder/index.js +3 -2
  17. package/src/actions/charts/area.js +47 -0
  18. package/src/actions/charts/bar.js +11 -5
  19. package/src/actions/charts/beeswarm.js +70 -0
  20. package/src/actions/charts/density.js +44 -0
  21. package/src/actions/charts/ecdf.js +183 -0
  22. package/src/actions/charts/endpoints.js +451 -0
  23. package/src/actions/charts/heatmap.js +2 -2
  24. package/src/actions/charts/histogram.js +1 -1
  25. package/src/actions/charts/horizon.js +71 -0
  26. package/src/actions/charts/index.js +37 -0
  27. package/src/actions/charts/interval-regression.js +169 -0
  28. package/src/actions/charts/line.js +5 -5
  29. package/src/actions/charts/parallel.js +1 -1
  30. package/src/actions/charts/pie.js +44 -0
  31. package/src/actions/charts/polar.js +123 -0
  32. package/src/actions/charts/radar.js +299 -0
  33. package/src/actions/charts/radial.js +42 -0
  34. package/src/actions/charts/raincloud.js +422 -0
  35. package/src/actions/charts/rug-strip.js +279 -0
  36. package/src/actions/charts/scatter.js +9 -3
  37. package/src/actions/charts/shared.js +69 -10
  38. package/src/actions/composition/actions.js +118 -1
  39. package/src/actions/data/bin.js +29 -0
  40. package/src/actions/data/computed.js +24 -0
  41. package/src/actions/data/derived.js +66 -0
  42. package/src/actions/data/ecdf.js +38 -0
  43. package/src/actions/data/filter.js +212 -28
  44. package/src/actions/data/fold.js +24 -0
  45. package/src/actions/data/index.js +24 -0
  46. package/src/actions/data/interval.js +2 -0
  47. package/src/actions/data/intervalEdit.js +86 -4
  48. package/src/actions/data/regression.js +5 -2
  49. package/src/actions/data/stack.js +26 -0
  50. package/src/actions/data/summary.js +26 -0
  51. package/src/actions/data/timeUnit.js +1 -1
  52. package/src/actions/encodings/appearance.js +32 -13
  53. package/src/actions/encodings/barWidth.js +8 -0
  54. package/src/actions/encodings/basic.js +2 -0
  55. package/src/actions/encodings/color/continuous.js +19 -59
  56. package/src/actions/encodings/color/index.js +28 -22
  57. package/src/actions/encodings/color/layout.js +3 -97
  58. package/src/actions/encodings/color/policy.js +5 -8
  59. package/src/actions/encodings/density/resolve.js +37 -5
  60. package/src/actions/encodings/density.js +120 -33
  61. package/src/actions/encodings/horizon/resolve.js +8 -4
  62. package/src/actions/encodings/horizon.js +18 -8
  63. package/src/actions/encodings/index.js +2 -0
  64. package/src/actions/encodings/offset.js +33 -27
  65. package/src/actions/encodings/parallel.js +2 -8
  66. package/src/actions/encodings/position/apply.js +40 -4
  67. package/src/actions/encodings/position/index.js +43 -5
  68. package/src/actions/encodings/position/policies/arc.js +37 -2
  69. package/src/actions/encodings/position/policies/area.js +4 -42
  70. package/src/actions/encodings/position/policies/bar.js +48 -13
  71. package/src/actions/encodings/position/resolve.js +79 -24
  72. package/src/actions/encodings/ranged.js +123 -97
  73. package/src/actions/encodings/remove.js +37 -32
  74. package/src/actions/encodings/ruleAppearance.js +23 -9
  75. package/src/actions/encodings/seriesLayout.js +104 -0
  76. package/src/actions/encodings/shared.js +25 -23
  77. package/src/actions/encodings/strokeDash.js +9 -6
  78. package/src/actions/encodings/temporal.js +7 -0
  79. package/src/actions/encodings/text.js +30 -15
  80. package/src/actions/errorBands/create.js +18 -5
  81. package/src/actions/errorBands/edit.js +341 -21
  82. package/src/actions/errorBars/create.js +153 -11
  83. package/src/actions/errorBars/edit.js +365 -19
  84. package/src/actions/facets/actions.js +410 -12
  85. package/src/actions/facets/derive.js +57 -8
  86. package/src/actions/gradientPlots/components.js +33 -11
  87. package/src/actions/gradientPlots/materialize.js +5 -7
  88. package/src/actions/guides/applicability.js +20 -17
  89. package/src/actions/guides/axes/axes.js +4 -3
  90. package/src/actions/guides/axes/axis.js +18 -18
  91. package/src/actions/guides/axes/components.js +43 -0
  92. package/src/actions/guides/axes/edit.js +16 -51
  93. package/src/actions/guides/axes/labels.js +129 -19
  94. package/src/actions/guides/axes/lines.js +7 -5
  95. package/src/actions/guides/axes/parallel/lifecycle.js +117 -0
  96. package/src/actions/guides/axes/parallel/policy.js +137 -0
  97. package/src/actions/guides/axes/parallel/resolve.js +44 -23
  98. package/src/actions/guides/axes/parallel.js +93 -217
  99. package/src/actions/guides/axes/policy.js +21 -67
  100. package/src/actions/guides/axes/tickGroups.js +14 -8
  101. package/src/actions/guides/axes/ticks.js +5 -4
  102. package/src/actions/guides/axes/titles.js +21 -8
  103. package/src/actions/guides/basic.js +2 -0
  104. package/src/actions/guides/facade.js +106 -0
  105. package/src/actions/guides/facadeAxes.js +200 -0
  106. package/src/actions/guides/facadeLegend.js +63 -0
  107. package/src/actions/guides/grids/grid.js +37 -33
  108. package/src/actions/guides/grids/resolve.js +10 -2
  109. package/src/actions/guides/guides.js +35 -28
  110. package/src/actions/guides/legends/categorical/actions.js +193 -249
  111. package/src/actions/guides/legends/categorical/components.js +4 -5
  112. package/src/actions/guides/legends/categorical/layout.js +36 -349
  113. package/src/actions/guides/legends/categorical/options.js +30 -14
  114. package/src/actions/guides/legends/categorical/recipes.js +14 -0
  115. package/src/actions/guides/legends/categorical/resolve.js +21 -10
  116. package/src/actions/guides/legends/categorical/symbols.js +2 -3
  117. package/src/actions/guides/legends/continuous/common.js +69 -14
  118. package/src/actions/guides/legends/continuous/gradient.js +111 -83
  119. package/src/actions/guides/legends/continuous/index.js +6 -2
  120. package/src/actions/guides/legends/continuous/interval.js +71 -92
  121. package/src/actions/guides/legends/continuous/opacity.js +105 -84
  122. package/src/actions/guides/legends/creation.js +27 -0
  123. package/src/actions/guides/legends/edit.js +198 -66
  124. package/src/actions/guides/legends/focused.js +2 -2
  125. package/src/actions/guides/legends/lane.js +98 -86
  126. package/src/actions/guides/legends/lifecycle.js +132 -0
  127. package/src/actions/guides/legends/remove.js +31 -76
  128. package/src/actions/guides/legends/size.js +149 -105
  129. package/src/actions/guides/legends/strokeWidth.js +106 -64
  130. package/src/actions/guides/legends/target.js +12 -0
  131. package/src/actions/guides/legends/transition.js +43 -0
  132. package/src/actions/guides/polar/axes/facade.js +77 -62
  133. package/src/actions/guides/polar/axes/labels.js +5 -4
  134. package/src/actions/guides/polar/axes/lines.js +2 -1
  135. package/src/actions/guides/polar/axes/shared.js +6 -0
  136. package/src/actions/guides/polar/axes/ticks.js +5 -4
  137. package/src/actions/guides/polar/axes/titles.js +5 -4
  138. package/src/actions/guides/polar/grids.js +7 -7
  139. package/src/actions/guides/reuse.js +44 -0
  140. package/src/actions/guides/tickValues.js +19 -21
  141. package/src/actions/index.js +2 -0
  142. package/src/actions/intervals/resolve.js +35 -15
  143. package/src/actions/marks/arc/actions.js +1 -1
  144. package/src/actions/marks/area/actions.js +18 -4
  145. package/src/actions/marks/area/materialize.js +5 -2
  146. package/src/actions/marks/bar/materialize.js +11 -7
  147. package/src/actions/marks/index.js +5 -0
  148. package/src/actions/marks/line/actions.js +43 -108
  149. package/src/actions/marks/line/materialize.js +34 -52
  150. package/src/actions/marks/point/edit.js +11 -15
  151. package/src/actions/marks/point/index.js +1 -0
  152. package/src/actions/marks/point/jitter.js +3 -0
  153. package/src/actions/marks/point/materialize.js +138 -14
  154. package/src/actions/marks/point/packing.js +68 -0
  155. package/src/actions/marks/references.js +87 -0
  156. package/src/actions/marks/remove.js +17 -4
  157. package/src/actions/marks/rule/actions.js +60 -4
  158. package/src/actions/marks/rule/appearance.js +26 -0
  159. package/src/actions/marks/shared.js +12 -12
  160. package/src/actions/marks/text/actions.js +224 -6
  161. package/src/actions/marks/text/layout.js +2 -2
  162. package/src/actions/primitives/createGraphics.js +3 -1
  163. package/src/actions/primitives/semantic.js +1 -1
  164. package/src/actions/primitives/semanticAction.js +39 -2
  165. package/src/actions/primitives/semanticValidation/guide.js +10 -0
  166. package/src/actions/primitives/semanticValidation/layer.js +18 -1
  167. package/src/actions/primitives/semanticValidation/scale.js +10 -1
  168. package/src/actions/regression/create.js +1 -1
  169. package/src/actions/regression/edit.js +19 -6
  170. package/src/actions/regression/resolve.js +1 -1
  171. package/src/actions/scales/consumers/common.js +20 -53
  172. package/src/actions/scales/consumers/families.js +27 -14
  173. package/src/actions/scales/consumers/index.js +23 -2
  174. package/src/actions/scales/consumers/seriesLayout.js +33 -2
  175. package/src/actions/scales/create.js +4 -4
  176. package/src/actions/scales/definitions.js +19 -3
  177. package/src/actions/scales/edit.js +46 -34
  178. package/src/actions/scales/editPolicy.js +21 -31
  179. package/src/actions/scales/materialize.js +10 -46
  180. package/src/actions/scales/patch.js +1 -1
  181. package/src/actions/scales/preview.js +39 -0
  182. package/src/actions/scales/quantitativeColor.js +9 -5
  183. package/src/actions/theme/actions.js +43 -0
  184. package/src/actions/theme/index.js +9 -0
  185. package/src/actions/theme/reconcile.js +882 -0
  186. package/src/actions/titles/actions.js +11 -10
  187. package/src/actions/titles/resolve.js +1 -40
  188. package/src/actions/violinPlots/create.js +86 -42
  189. package/src/actions/violinPlots/edit.js +191 -0
  190. package/src/actions/violinPlots/index.js +2 -0
  191. package/src/core/ChartProgram.js +7 -0
  192. package/src/core/action.js +22 -2
  193. package/src/core/compositionState.js +136 -8
  194. package/src/core/vocabulary.js +2 -2
  195. package/src/grammar/aggregate.js +66 -23
  196. package/src/grammar/arcs.js +213 -65
  197. package/src/grammar/areaEndpoints.js +46 -0
  198. package/src/grammar/areaSeries.js +87 -41
  199. package/src/grammar/bars/aggregate.js +33 -12
  200. package/src/grammar/bars/policy.js +9 -7
  201. package/src/grammar/bin.js +181 -0
  202. package/src/grammar/categoryOrder.js +34 -0
  203. package/src/grammar/computed.js +150 -0
  204. package/src/grammar/ecdf.js +198 -0
  205. package/src/grammar/facets/index.js +117 -6
  206. package/src/grammar/fold.js +123 -0
  207. package/src/grammar/horizon.js +6 -4
  208. package/src/grammar/interval.js +37 -20
  209. package/src/grammar/lineSeries.js +20 -63
  210. package/src/grammar/markFilter.js +22 -4
  211. package/src/grammar/markLabels.js +93 -0
  212. package/src/grammar/parallelAxisTitles.js +16 -0
  213. package/src/grammar/pathSeries.js +91 -0
  214. package/src/grammar/pointPacking.js +183 -0
  215. package/src/grammar/positionCompatibility.js +1 -1
  216. package/src/grammar/positionDatum.js +12 -0
  217. package/src/grammar/rects.js +9 -1
  218. package/src/grammar/regression/derive.js +4 -0
  219. package/src/grammar/regression/index.js +7 -2
  220. package/src/grammar/regression/models.js +22 -7
  221. package/src/grammar/regression/parameters.js +40 -13
  222. package/src/grammar/rotation.js +29 -0
  223. package/src/grammar/rules.js +9 -21
  224. package/src/grammar/scales/color.js +23 -1
  225. package/src/grammar/scales/colorConsumers.js +38 -0
  226. package/src/grammar/scales/definition.js +15 -1
  227. package/src/grammar/scales/discretized.js +11 -5
  228. package/src/grammar/scales/fields.js +38 -8
  229. package/src/grammar/scales/mapping.js +2 -0
  230. package/src/grammar/scales/ordinal.js +23 -0
  231. package/src/grammar/scales/radial.js +55 -0
  232. package/src/grammar/scales/types.js +1 -0
  233. package/src/grammar/schemas/semanticPath.js +17 -3
  234. package/src/grammar/stack.js +170 -0
  235. package/src/grammar/statistics/confidenceInterval.js +101 -0
  236. package/src/grammar/summary.js +183 -0
  237. package/src/grammar/text.js +22 -16
  238. package/src/grammar/timeUnit.js +21 -11
  239. package/src/grammar/transformTopology.js +6 -0
  240. package/src/grammar/transforms.js +36 -0
  241. package/src/grammar/valueFormat.js +112 -0
  242. package/src/grammar/window.js +13 -7
  243. package/src/layout/facets.js +86 -23
  244. package/src/layout/guideCollisions.js +12 -0
  245. package/src/layout/legend.js +2 -10
  246. package/src/layout/legendItems.js +102 -0
  247. package/src/layout/legendLane.js +100 -11
  248. package/src/materialization/bars/aggregate.js +38 -152
  249. package/src/materialization/bars/histogram.js +24 -0
  250. package/src/materialization/categorySlotOffset.js +40 -0
  251. package/src/materialization/compositionSnapshot.js +1 -1
  252. package/src/materialization/dependencies.js +57 -16
  253. package/src/materialization/encodings.js +1 -1
  254. package/src/materialization/facetGuides/legacyCategorical.js +7 -1
  255. package/src/materialization/facetGuides/placement.js +16 -1
  256. package/src/materialization/facetGuides/preparation.js +26 -1
  257. package/src/materialization/facets.js +11 -2
  258. package/src/materialization/guides/layout.js +57 -0
  259. package/src/materialization/guides/resources.js +2 -2
  260. package/src/materialization/legends.js +10 -1
  261. package/src/materialization/marks/capabilities.js +30 -7
  262. package/src/materialization/marks/grouping.js +52 -0
  263. package/src/materialization/marks/index.js +63 -25
  264. package/src/materialization/marks/policies.js +2 -0
  265. package/src/materialization/rect.js +33 -33
  266. package/src/materialization/rowEncoding.js +70 -14
  267. package/src/materialization/scaleGuideDependencies.js +8 -5
  268. package/src/materialization/scales/map.js +1 -0
  269. package/src/materialization/scales/policies/arc.js +42 -0
  270. package/src/materialization/scales/policies/bar.js +0 -43
  271. package/src/materialization/scales/policies/binnedPosition.js +7 -4
  272. package/src/materialization/scales/policies/offset.js +54 -0
  273. package/src/materialization/scales/policies/series.js +2 -1
  274. package/src/materialization/scales/resolve.js +48 -18
  275. package/src/materialization/selection/compatibility.js +11 -0
  276. package/src/materialization/selection/items/bar.js +12 -68
  277. package/src/materialization/selection/items/common.js +8 -2
  278. package/src/materialization/selection/items/path.js +3 -13
  279. package/src/materialization/selection/items/rect.js +7 -4
  280. package/src/materialization/text.js +64 -5
  281. package/src/mcp/adapter.js +2 -2
  282. package/src/selectors/datasets.js +13 -0
  283. package/src/selectors/layers.js +3 -2
  284. package/src/selectors/scales.js +6 -0
  285. package/src/theme/defaults.js +39 -0
  286. package/types/basic.d.ts +11 -2
  287. package/types/index.d.ts +126 -0
  288. package/types/program.d.ts +1327 -124
  289. package/src/materialization/bars/grouped.js +0 -144
@@ -9,7 +9,7 @@ import { derivedMaterializer } from "./shared.js";
9
9
 
10
10
  const OPTIONS = Object.freeze([
11
11
  "id", "source", "x", "y", "groupBy", "method", "degree", "span",
12
- "confidence", "interval"
12
+ "confidenceMethod", "level", "confidence", "interval"
13
13
  ]);
14
14
 
15
15
  export const materializeRegressionData = derivedMaterializer(
@@ -23,6 +23,8 @@ export const materializeRegressionData = derivedMaterializer(
23
23
  method: transform.method,
24
24
  degree: transform.degree,
25
25
  span: transform.span,
26
+ confidenceMethod: transform.confidenceMethod,
27
+ level: transform.level,
26
28
  confidence: transform.confidence,
27
29
  interval: transform.interval
28
30
  })
@@ -50,7 +52,8 @@ export const createRegressionData = action(
50
52
  ...(parameters.method === "loess"
51
53
  ? { span: parameters.span }
52
54
  : {
53
- confidence: parameters.confidence,
55
+ confidenceMethod: parameters.confidenceMethod,
56
+ level: parameters.level,
54
57
  interval: parameters.interval
55
58
  })
56
59
  };
@@ -0,0 +1,26 @@
1
+ import {
2
+ deriveStackRows,
3
+ normalizeStackTransform
4
+ } from "../../grammar/stack.js";
5
+ import { derivedCreator, derivedMaterializer } from "./shared.js";
6
+
7
+ const OPTIONS = Object.freeze([
8
+ "id", "source", "category", "group", "value", "mode", "as"
9
+ ]);
10
+
11
+ export const materializeStackData = derivedMaterializer(
12
+ "materializeStackData",
13
+ "Materialize one immutable category/series stack dataset.",
14
+ "stack",
15
+ deriveStackRows
16
+ );
17
+
18
+ export const createStackData = derivedCreator(
19
+ "createStackData",
20
+ "Create reusable start, end, value, and share stack rows.",
21
+ OPTIONS,
22
+ "Stack dataset id",
23
+ "Stack source dataset id",
24
+ normalizeStackTransform,
25
+ "materializeStackData"
26
+ );
@@ -0,0 +1,26 @@
1
+ import {
2
+ deriveSummaryRows,
3
+ normalizeSummaryTransform
4
+ } from "../../grammar/summary.js";
5
+ import { derivedCreator, derivedMaterializer } from "./shared.js";
6
+
7
+ const OPTIONS = Object.freeze([
8
+ "id", "source", "groupBy", "aggregates", "members"
9
+ ]);
10
+
11
+ export const materializeSummaryData = derivedMaterializer(
12
+ "materializeSummaryData",
13
+ "Materialize one immutable grouped summary dataset.",
14
+ "summary",
15
+ deriveSummaryRows
16
+ );
17
+
18
+ export const createSummaryData = derivedCreator(
19
+ "createSummaryData",
20
+ "Create reusable grouped aggregate values and provenance.",
21
+ OPTIONS,
22
+ "Summary dataset id",
23
+ "Summary source dataset id",
24
+ normalizeSummaryTransform,
25
+ "materializeSummaryData"
26
+ );
@@ -4,7 +4,7 @@ import {
4
4
  } from "../../grammar/timeUnit.js";
5
5
  import { derivedCreator, derivedMaterializer } from "./shared.js";
6
6
 
7
- const OPTIONS = Object.freeze(["id", "source", "field", "unit", "as"]);
7
+ const OPTIONS = Object.freeze(["id", "source", "field", "unit", "as", "temporalUnit"]);
8
8
 
9
9
  export const materializeTimeUnitData = derivedMaterializer(
10
10
  "materializeTimeUnitData",
@@ -1,4 +1,6 @@
1
1
  import { action } from "../../core/action.js";
2
+ import { validatePathSeriesAppearance } from "../../grammar/pathSeries.js";
3
+ import { assertEncodingSelectionCompatibility } from "../../materialization/selection/compatibility.js";
2
4
  import {
3
5
  readNominalField,
4
6
  readQuantitativeField,
@@ -15,6 +17,7 @@ import {
15
17
  } from "../../selectors/layers.js";
16
18
  import {
17
19
  applyEncodingScale,
20
+ applyDetachedScaleRematerialization,
18
21
  clearMarkGraphic,
19
22
  rematerializeEncoding,
20
23
  resolveReassignmentScaleOptions,
@@ -73,7 +76,7 @@ function encodeAppearanceField(program, channel, args, operation) {
73
76
  next = applyEncodingScale(next, scale, requestedScale, {
74
77
  reassignment: previous?.scale === scale.id
75
78
  });
76
- return rematerializeEncoding(next, target, channel, scale.id);
79
+ return rematerializeEncoding(next, target, channel, scale.id, layer);
77
80
  }
78
81
 
79
82
  const encodeRadius = action(
@@ -119,7 +122,7 @@ const encodePointRadius = action(
119
122
  }
120
123
  );
121
124
 
122
- const removePointRadius = action(
125
+ const removePointRadius = /* @__PURE__ */ action(
123
126
  {
124
127
  op: "removePointRadius",
125
128
  description: "Remove a constant point radius and restore the theme default."
@@ -163,7 +166,7 @@ const encodeShape = action(
163
166
  }
164
167
  );
165
168
 
166
- const clearOpacityEncoding = action(
169
+ const clearOpacityEncoding = /* @__PURE__ */ action(
167
170
  {
168
171
  op: "clearOpacityEncoding",
169
172
  description: "Remove the semantic field-driven opacity assignment."
@@ -178,7 +181,7 @@ const clearOpacityEncoding = action(
178
181
  }
179
182
  );
180
183
 
181
- const encodeOpacity = action(
184
+ const encodeOpacity = /* @__PURE__ */ action(
182
185
  {
183
186
  op: "encodeOpacity",
184
187
  description: "Assign constant or field-driven mark opacity."
@@ -190,26 +193,34 @@ const encodeOpacity = action(
190
193
  if (hasValue === hasField) {
191
194
  throw new Error("encodeOpacity requires exactly one of value or field.");
192
195
  }
196
+ if (hasValue && (args.fieldType !== undefined || args.scale !== undefined)) {
197
+ throw new Error("Constant opacity does not accept fieldType or scale.");
198
+ }
193
199
  const { id: target, dataset, layer } = resolveTarget(
194
200
  this,
195
201
  args.target,
196
- ["point", "rule"],
197
- "point or rule mark"
202
+ ["point", "rule", "line"],
203
+ "point, rule or line mark"
198
204
  );
199
205
  if (hasValue) {
200
206
  validateOpacityValue(args.value, "encodeOpacity");
207
+ assertEncodingSelectionCompatibility(this, target, ["opacity"]);
201
208
  const { opacity, ...config } = this.markConfigs[target] ?? {};
202
209
  void opacity;
203
- const withoutLegend = this.guideConfigs.legend?.opacity === undefined
204
- ? this
205
- : this.removeOpacityLegend();
210
+ const withoutLegend = this.guideConfigs.legend?.opacity?.target === target
211
+ ? this.removeOpacityLegend()
212
+ : this;
206
213
  const next = withoutLegend
207
214
  .clearOpacityEncoding({ target })
208
215
  ._withoutMaterializationConfig(["marks", target, "opacity"])
209
216
  ._withMarkConfig(target, { ...config, opacity: args.value });
210
- return layer.mark.type === "rule"
217
+ if (layer.mark.type === "line") {
218
+ return rematerializeEncoding(next, target, "opacity", undefined, layer);
219
+ }
220
+ const materialized = layer.mark.type === "rule"
211
221
  ? next.rematerializeRuleMark({ id: target })
212
222
  : next.rematerializePointMark({ id: target });
223
+ return applyDetachedScaleRematerialization(materialized, [layer]);
213
224
  }
214
225
  const fieldType = args.fieldType ?? "quantitative";
215
226
  if (fieldType !== "quantitative") {
@@ -221,6 +232,14 @@ const encodeOpacity = action(
221
232
  args.scale ?? {}
222
233
  );
223
234
  const scale = resolveOpacityScaleDefinition(this, requestedScale);
235
+ if (layer.mark.type === "line") {
236
+ if (Object.hasOwn(scale, "unknown")) {
237
+ throw new Error("Line opacity does not support unknown fallback.");
238
+ }
239
+ validatePathSeriesAppearance(dataset.values, {
240
+ ...layer, encoding: { ...layer.encoding, opacity: { field: args.field, fieldType } }
241
+ });
242
+ }
224
243
  if (Object.hasOwn(scale, "unknown")) {
225
244
  readScaleField(dataset.values, args.field, fieldType, {
226
245
  allowUnknown: true
@@ -239,19 +258,19 @@ const encodeOpacity = action(
239
258
  next = applyEncodingScale(next, scale, requestedScale, {
240
259
  reassignment: previous?.scale === scale.id
241
260
  });
242
- return rematerializeEncoding(next, target, "opacity", scale.id);
261
+ return rematerializeEncoding(next, target, "opacity", scale.id, layer);
243
262
  }
244
263
  );
245
264
 
246
265
  export function registerBasicAppearanceEncodingActions(ProgramClass) {
247
266
  ProgramClass.prototype.encodeSize = encodeSize;
248
267
  ProgramClass.prototype.encodeShape = encodeShape;
268
+ ProgramClass.prototype.encodeRadius = encodeRadius;
269
+ ProgramClass.prototype.encodePointRadius = encodePointRadius;
249
270
  }
250
271
 
251
272
  export function registerAppearanceEncodingAction(ProgramClass) {
252
273
  registerBasicAppearanceEncodingActions(ProgramClass);
253
- ProgramClass.prototype.encodeRadius = encodeRadius;
254
- ProgramClass.prototype.encodePointRadius = encodePointRadius;
255
274
  ProgramClass.prototype.removePointRadius = removePointRadius;
256
275
  ProgramClass.prototype.encodeOpacity = encodeOpacity;
257
276
  ProgramClass.prototype.clearOpacityEncoding = clearOpacityEncoding;
@@ -31,6 +31,14 @@ const encodeBarWidth = action(
31
31
  const grouped = layout === "group";
32
32
  const offsetChannel = resolveBarOffsetChannel(layer);
33
33
  const grain = resolveBarGrain(layer);
34
+ if (grain === undefined && this.markConfigs[target]?.boxPlot === undefined && (
35
+ layer.encoding?.x === undefined || layer.encoding?.y === undefined
36
+ )) {
37
+ return this._withMarkConfig(target, { ...this.markConfigs[target], barWidth: width });
38
+ }
39
+ if (grain === BAR_GRAINS.histogram) {
40
+ throw new Error("encodeBarWidth requires an aggregate or ranged category slot, not histogram bins.");
41
+ }
34
42
  if (grain === BAR_GRAINS.ranged) {
35
43
  return this._withMarkConfig(target, { ...this.markConfigs[target], barWidth: width })
36
44
  .rematerializeBarMark({ id: target });
@@ -1,3 +1,4 @@
1
+ import { registerSeriesLayoutAction } from "./seriesLayout.js";
1
2
  import { registerBasicAppearanceEncodingActions } from "./appearance.js";
2
3
  import { registerBarWidthEncodingAction } from "./barWidth.js";
3
4
  import { registerColorEncodingAction } from "./color/index.js";
@@ -10,6 +11,7 @@ import { registerBasicRangedEncodingActions } from "./ranged.js";
10
11
  import { registerStrokeDashEncodingActions } from "./strokeDash.js";
11
12
 
12
13
  export function registerBasicEncodingActions(ProgramClass) {
14
+ registerSeriesLayoutAction(ProgramClass);
13
15
  registerCartesianPositionEncodingActions(ProgramClass);
14
16
  registerBasicRangedEncodingActions(ProgramClass);
15
17
  registerColorEncodingAction(ProgramClass);
@@ -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,21 +143,12 @@ 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
153
  reassignment: layer.encoding?.color?.scale === scale.id
149
154
  });
@@ -152,17 +157,18 @@ const encodeColor = action(
152
157
  layer,
153
158
  layout,
154
159
  scale,
155
- field: args.field
160
+ field: args.field,
161
+ horizon: horizonTransform !== undefined
156
162
  });
157
163
 
158
- return applyMaterializationPlan(
164
+ return applyDetachedScaleRematerialization(applyMaterializationPlan(
159
165
  next,
160
166
  planEncodingRematerialization(next, {
161
167
  target,
162
168
  channel: "color",
163
169
  scale: scale.id
164
170
  })
165
- );
171
+ ), [layer]);
166
172
  }
167
173
  );
168
174
 
@@ -1,101 +1,7 @@
1
- import {
2
- BAR_GRAINS,
3
- resolveBarChannels,
4
- resolveBarGrain,
5
- resolveBarOffsetChannel
6
- } from "../../../grammar/bars/policy.js";
7
- import { resolveOffsetScaleDefinition } from "../../scales/definitions.js";
8
- import { isRangedArea } from "./policy.js";
9
-
10
1
  export function applyColorLayoutCompanion(
11
2
  program,
12
- { target, layer, layout, scale, field }
3
+ { target, layer, layout, horizon }
13
4
  ) {
14
- if (layout === undefined || layer.mark.type === "arc") return program;
15
- if (
16
- layer.mark.type === "bar" &&
17
- resolveBarGrain(layer) === BAR_GRAINS.ranged
18
- ) {
19
- return program;
20
- }
21
- if (
22
- layer.mark.type === "area" &&
23
- [layer.encoding?.x, layer.encoding?.y].some(
24
- encoding => ["nominal", "ordinal"].includes(encoding?.fieldType)
25
- )
26
- ) {
27
- return program;
28
- }
29
- if (isRangedArea(layer)) return program;
30
- const channels = layer.mark.type === "bar"
31
- ? resolveBarChannels(layer)
32
- : undefined;
33
- const measureChannel = channels?.measure ?? "y";
34
- const measure = layer.encoding?.[measureChannel];
35
- if (measure?.field === undefined || measure.scale === undefined) {
36
- throw new Error(`Color layout on mark "${target}" requires a measure encoding.`);
37
- }
38
- const stack = layout === "fill"
39
- ? "normalize"
40
- : layout === "center"
41
- ? "center"
42
- : layout === "overlay" || layout === "group"
43
- ? null
44
- : "zero";
45
- let next = program;
46
- if (layout === "group") {
47
- const offsetChannel = resolveBarOffsetChannel(layer);
48
- const method = offsetChannel === "xOffset" ? "encodeXOffset" : "encodeYOffset";
49
- next = next[method]({
50
- field,
51
- target,
52
- scale: {
53
- ...(layer.encoding?.[offsetChannel]?.scale === undefined
54
- ? {}
55
- : { id: layer.encoding[offsetChannel].scale }),
56
- domain: scale.domain
57
- }
58
- });
59
- }
60
- return next[measureChannel === "x" ? "encodeX" : "encodeY"]({
61
- target,
62
- field: measure.field,
63
- fieldType: measure.fieldType,
64
- ...(measure.aggregate === undefined ? {} : { aggregate: measure.aggregate }),
65
- stack
66
- });
67
- }
68
-
69
- export function preSynchronizeGroupedOffset(
70
- program,
71
- { target, layer, layout, field, fieldType, scale, requestedScale }
72
- ) {
73
- if (
74
- layer.mark.type !== "bar" ||
75
- layout !== "group" ||
76
- layer.encoding?.color?.scale !== scale.id ||
77
- !Object.keys(requestedScale).some(key => key !== "id")
78
- ) {
79
- return program;
80
- }
81
- const offsetChannel = resolveBarOffsetChannel(layer);
82
- const offset = layer.encoding?.[offsetChannel];
83
- if (offset?.scale === undefined) return program;
84
- const offsetScale = resolveOffsetScaleDefinition(program, {
85
- id: offset.scale,
86
- domain: scale.domain
87
- }, offsetChannel);
88
- return program
89
- .editSemantic({
90
- property: `layer[${target}].encoding.${offsetChannel}.field`,
91
- value: field
92
- })
93
- .editSemantic({
94
- property: `layer[${target}].encoding.${offsetChannel}.fieldType`,
95
- value: fieldType
96
- })
97
- .editSemantic({
98
- property: `scale[${offsetScale.id}].domain`,
99
- value: offsetScale.domain
100
- });
5
+ if (layout === undefined || layer.mark.type === "arc" || horizon) return program;
6
+ return program.layoutSeries({ target, mode: layout });
101
7
  }
@@ -5,7 +5,7 @@ import {
5
5
  import { validateColorLayout } from "../../../grammar/seriesLayout.js";
6
6
 
7
7
  export const COLOR_ENCODING_OPTIONS = Object.freeze([
8
- "field", "target", "fieldType", "scale", "palette", "layout", "aggregate"
8
+ "field", "target", "fieldType", "scale", "palette", "layout", "aggregate", "temporalUnit"
9
9
  ]);
10
10
 
11
11
  export function resolveColorScaleOptions(args) {
@@ -24,6 +24,7 @@ export function assertNoConstantColor(program, layer) {
24
24
  const hasConstant = {
25
25
  point: config?.fill !== undefined,
26
26
  line: config?.stroke !== undefined,
27
+ area: config?.errorBand?.fill !== undefined,
27
28
  bar: config?.barAppearance?.fill !== undefined,
28
29
  arc: config?.fill !== undefined,
29
30
  rect: config?.fillExplicit === true
@@ -53,12 +54,7 @@ export function resolveColorLayout(layer, requested, barGrain) {
53
54
  : undefined
54
55
  );
55
56
  if (requested !== undefined) validateColorLayout(requested);
56
- if (existing !== undefined && requested !== undefined && requested !== existing) {
57
- throw new Error(
58
- `Color layout transition from "${existing}" to "${requested}" is not supported.`
59
- );
60
- }
61
- const layout = requested ?? existing ?? (
57
+ const layout = requested ?? layer.layout?.mode ?? existing ?? (
62
58
  layer.mark.type === "bar"
63
59
  ? barGrain === BAR_GRAINS.histogram
64
60
  ? "stack"
@@ -84,7 +80,8 @@ export function resolveColorLayout(layer, requested, barGrain) {
84
80
  ) {
85
81
  throw new Error('Arc color layout currently supports only "overlay".');
86
82
  }
87
- if (isRangedArea(layer) && layout !== "overlay") {
83
+ if (isRangedArea(layer) && layer.encoding?.x2?.datum === undefined && layer.encoding?.y2?.datum === undefined &&
84
+ layer.encoding?.x?.datum === undefined && layer.encoding?.y?.datum === undefined && layout !== "overlay") {
88
85
  throw new Error('Ranged area color encoding supports only "overlay" layout.');
89
86
  }
90
87
  return layout;