ggaction 0.0.12 → 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 (281) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README.md +2 -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 +10 -16
  65. package/src/actions/encodings/parallel.js +2 -8
  66. package/src/actions/encodings/position/apply.js +10 -4
  67. package/src/actions/encodings/position/index.js +43 -5
  68. package/src/actions/encodings/position/policies/arc.js +14 -0
  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 +67 -22
  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 +21 -10
  83. package/src/actions/errorBars/edit.js +355 -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 +106 -2
  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 +34 -3
  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/grammar/aggregate.js +66 -23
  195. package/src/grammar/arcs.js +58 -1
  196. package/src/grammar/areaEndpoints.js +46 -0
  197. package/src/grammar/areaSeries.js +87 -41
  198. package/src/grammar/bars/aggregate.js +33 -12
  199. package/src/grammar/bars/policy.js +9 -7
  200. package/src/grammar/bin.js +181 -0
  201. package/src/grammar/categoryOrder.js +34 -0
  202. package/src/grammar/computed.js +150 -0
  203. package/src/grammar/ecdf.js +198 -0
  204. package/src/grammar/facets/index.js +117 -6
  205. package/src/grammar/fold.js +123 -0
  206. package/src/grammar/horizon.js +6 -4
  207. package/src/grammar/interval.js +37 -20
  208. package/src/grammar/lineSeries.js +20 -63
  209. package/src/grammar/markFilter.js +22 -4
  210. package/src/grammar/markLabels.js +93 -0
  211. package/src/grammar/parallelAxisTitles.js +16 -0
  212. package/src/grammar/pathSeries.js +91 -0
  213. package/src/grammar/pointPacking.js +183 -0
  214. package/src/grammar/positionDatum.js +12 -0
  215. package/src/grammar/rects.js +9 -1
  216. package/src/grammar/regression/derive.js +4 -0
  217. package/src/grammar/regression/index.js +7 -2
  218. package/src/grammar/regression/models.js +22 -7
  219. package/src/grammar/regression/parameters.js +40 -13
  220. package/src/grammar/rotation.js +29 -0
  221. package/src/grammar/rules.js +7 -21
  222. package/src/grammar/scales/color.js +23 -1
  223. package/src/grammar/scales/colorConsumers.js +38 -0
  224. package/src/grammar/scales/definition.js +15 -1
  225. package/src/grammar/scales/discretized.js +11 -5
  226. package/src/grammar/scales/fields.js +38 -8
  227. package/src/grammar/scales/mapping.js +2 -0
  228. package/src/grammar/scales/radial.js +55 -0
  229. package/src/grammar/scales/types.js +1 -0
  230. package/src/grammar/schemas/semanticPath.js +17 -3
  231. package/src/grammar/stack.js +170 -0
  232. package/src/grammar/statistics/confidenceInterval.js +101 -0
  233. package/src/grammar/summary.js +183 -0
  234. package/src/grammar/text.js +22 -16
  235. package/src/grammar/timeUnit.js +21 -11
  236. package/src/grammar/transformTopology.js +6 -0
  237. package/src/grammar/transforms.js +36 -0
  238. package/src/grammar/valueFormat.js +112 -0
  239. package/src/grammar/window.js +13 -7
  240. package/src/layout/facets.js +41 -7
  241. package/src/layout/guideCollisions.js +12 -0
  242. package/src/layout/legend.js +2 -10
  243. package/src/layout/legendItems.js +102 -0
  244. package/src/layout/legendLane.js +100 -11
  245. package/src/materialization/bars/aggregate.js +38 -152
  246. package/src/materialization/bars/histogram.js +24 -0
  247. package/src/materialization/categorySlotOffset.js +40 -0
  248. package/src/materialization/compositionSnapshot.js +1 -1
  249. package/src/materialization/dependencies.js +57 -16
  250. package/src/materialization/encodings.js +1 -1
  251. package/src/materialization/facets.js +8 -1
  252. package/src/materialization/guides/layout.js +57 -0
  253. package/src/materialization/guides/resources.js +2 -2
  254. package/src/materialization/legends.js +10 -1
  255. package/src/materialization/marks/capabilities.js +20 -5
  256. package/src/materialization/marks/grouping.js +52 -0
  257. package/src/materialization/marks/index.js +58 -23
  258. package/src/materialization/marks/policies.js +2 -0
  259. package/src/materialization/rect.js +33 -33
  260. package/src/materialization/rowEncoding.js +13 -13
  261. package/src/materialization/scaleGuideDependencies.js +8 -5
  262. package/src/materialization/scales/map.js +1 -0
  263. package/src/materialization/scales/policies/arc.js +42 -0
  264. package/src/materialization/scales/policies/binnedPosition.js +7 -4
  265. package/src/materialization/scales/policies/series.js +2 -1
  266. package/src/materialization/scales/resolve.js +46 -14
  267. package/src/materialization/selection/compatibility.js +11 -0
  268. package/src/materialization/selection/items/bar.js +12 -68
  269. package/src/materialization/selection/items/common.js +8 -2
  270. package/src/materialization/selection/items/path.js +3 -13
  271. package/src/materialization/selection/items/rect.js +7 -4
  272. package/src/materialization/text.js +64 -5
  273. package/src/mcp/adapter.js +2 -2
  274. package/src/selectors/datasets.js +13 -0
  275. package/src/selectors/layers.js +3 -2
  276. package/src/selectors/scales.js +6 -0
  277. package/src/theme/defaults.js +39 -0
  278. package/types/basic.d.ts +11 -2
  279. package/types/index.d.ts +126 -0
  280. package/types/program.d.ts +1320 -135
  281. package/src/materialization/bars/grouped.js +0 -144
@@ -1,15 +1,13 @@
1
+ import { isSourceOwnedText } from "../../grammar/text.js";
1
2
  import { validateUserId } from "../../core/identifiers.js";
2
3
  import { findDataset } from "../../selectors/datasets.js";
3
4
  import { resolveEligibleLayer } from "../../selectors/layers.js";
4
5
  import { hasMaterializedLegend } from "../../materialization/legends.js";
5
- import { applyMaterializationPlan } from "../../materialization/dependencies.js";
6
+ import { applyDetachedScaleRematerialization, applyMaterializationPlan } from "../../materialization/dependencies.js";
6
7
  import { planEncodingRematerialization } from "../../materialization/encodings.js";
7
8
  import { findSemanticScale } from "../../selectors/scales.js";
8
9
  import { validateOptionObject } from "../../core/validation.js";
9
- import {
10
- getMarkGraphicTypes,
11
- getPositionChannelDefinition
12
- } from "../../core/vocabulary.js";
10
+ import { getMarkGraphicTypes, getPositionChannelDefinition } from "../../core/vocabulary.js";
13
11
 
14
12
  export function validateOptions(args, supported, operation) {
15
13
  validateOptionObject(args, supported, operation);
@@ -31,11 +29,16 @@ export function setEncodingProperties(
31
29
  return next;
32
30
  }
33
31
 
34
- export function rematerializeEncoding(program, target, channel, scale) {
35
- return applyMaterializationPlan(
32
+ export { applyDetachedScaleRematerialization };
33
+
34
+ export function rematerializeEncoding(program, target, channel, scale, previousLayer) {
35
+ const next = applyMaterializationPlan(
36
36
  program,
37
37
  planEncodingRematerialization(program, { target, channel, scale })
38
38
  );
39
+ return previousLayer === undefined
40
+ ? next
41
+ : applyDetachedScaleRematerialization(next, [previousLayer]);
39
42
  }
40
43
 
41
44
  export function clearMarkGraphic(program, target) {
@@ -50,19 +53,6 @@ export function clearMarkGraphic(program, target) {
50
53
  }
51
54
 
52
55
 
53
- export function validateLineSeriesCompatibility(layer, channel, field) {
54
- if (layer.mark?.type !== "line") return;
55
- for (const companion of ["group", "color", "strokeDash"]) {
56
- if (companion === channel) continue;
57
- const companionField = layer.encoding?.[companion]?.field;
58
- if (companionField !== undefined && companionField !== field) {
59
- throw new Error(
60
- `Line ${channel} field "${field}" must match ${companion} field "${companionField}".`
61
- );
62
- }
63
- }
64
- }
65
-
66
56
  export function resolveReassignmentScaleOptions(encoding, options) {
67
57
  if (encoding?.scale === undefined || options?.id !== undefined) return options;
68
58
  return { ...options, id: encoding.scale };
@@ -72,13 +62,14 @@ export function applyEncodingScale(
72
62
  program,
73
63
  definition,
74
64
  options = {},
75
- { reassignment = false } = {}
65
+ { reassignment = false, allowTypeChange = false } = {}
76
66
  ) {
77
67
  const existing = findSemanticScale(program, definition.id);
78
68
  if (existing === undefined) {
79
69
  return program.createScale(definition);
80
70
  }
81
71
  if (existing.type !== definition.type) {
72
+ if (reassignment && allowTypeChange) return program.editScale({ ...definition });
82
73
  throw new Error(
83
74
  `Scale "${definition.id}" cannot change type from "${existing.type}" to "${definition.type}".`
84
75
  );
@@ -88,7 +79,7 @@ export function applyEncodingScale(
88
79
  for (const property of [
89
80
  "domain", "range", "nice", "zero", "clamp", "reverse",
90
81
  "base", "exponent", "constant", "paddingInner", "paddingOuter",
91
- "padding", "align", "unknown", "interpolate"
82
+ "padding", "align", "unknown", "interpolate", "radialMapping"
92
83
  ]) {
93
84
  if (Object.hasOwn(options, property)) patch[property] = definition[property];
94
85
  }
@@ -118,7 +109,7 @@ export function rebindPositionGuides(
118
109
  if (!ownsAxis && !ownsGrid) return program;
119
110
 
120
111
  const remaining = program.semanticSpec.layers.some(layer =>
121
- layer.id !== target && layer.encoding?.[channel]?.scale === previousScale
112
+ layer.id !== target && !isSourceOwnedText(layer) && layer.encoding?.[channel]?.scale === previousScale
122
113
  );
123
114
  if (remaining) {
124
115
  throw new Error(
@@ -195,3 +186,14 @@ export function resolveTarget(
195
186
 
196
187
  return { id, dataset, layer };
197
188
  }
189
+
190
+ export function inferSeriesGroup(program, layer, field, origin) {
191
+ const group = layer.encoding?.group;
192
+ if (group !== undefined && group.inferredFrom !== origin) {
193
+ if (origin === "offset" && (group.fields !== undefined || group.field !== field)) {
194
+ throw new Error("Offset field conflicts with the stored series group.");
195
+ }
196
+ return program;
197
+ }
198
+ return setEncodingProperties(program, layer.id, "group", { field, fieldType: "nominal", inferredFrom: origin });
199
+ }
@@ -11,10 +11,10 @@ import {
11
11
  resolveReassignmentScaleOptions,
12
12
  resolveTarget,
13
13
  setEncodingProperties,
14
- validateLineSeriesCompatibility,
15
14
  validateOptions
16
15
  } from "./shared.js";
17
16
  import { findLayer } from "../../selectors/layers.js";
17
+ import { validatePathSeriesAppearance } from "../../grammar/pathSeries.js";
18
18
 
19
19
  const STROKE_DASH_ENCODING_OPTIONS = Object.freeze([
20
20
  "field", "value", "target", "fieldType", "scale"
@@ -81,6 +81,9 @@ const encodeStrokeDash = action(
81
81
  );
82
82
  if (hasValue) {
83
83
  normalizeStrokeDashPattern(args.value);
84
+ validatePathSeriesAppearance(dataset.values, {
85
+ ...layer, encoding: { ...layer.encoding, strokeDash: { datum: args.value } }
86
+ });
84
87
  let next = layer.encoding?.strokeDash === undefined
85
88
  ? this
86
89
  : this.clearStrokeDashEncoding({ target });
@@ -89,13 +92,13 @@ const encodeStrokeDash = action(
89
92
  value: args.value
90
93
  });
91
94
  next = reconcileLegendAfterDashRemoval(next, target);
92
- return layer.mark.type === "rule"
93
- ? next.rematerializeRuleMark({ id: target })
94
- : next.rematerializeLineMark({ id: target });
95
+ return rematerializeEncoding(next, target, "strokeDash", undefined, layer);
95
96
  }
96
97
 
97
98
  const fieldType = validateNominalFieldType(args.fieldType ?? "nominal");
98
- validateLineSeriesCompatibility(layer, "strokeDash", args.field);
99
+ validatePathSeriesAppearance(dataset.values, {
100
+ ...layer, encoding: { ...layer.encoding, strokeDash: { field: args.field, fieldType } }
101
+ });
99
102
  const previous = layer.encoding?.strokeDash;
100
103
  const requestedScale = previous?.field === args.field
101
104
  ? resolveReassignmentScaleOptions(previous, args.scale ?? {})
@@ -120,7 +123,7 @@ const encodeStrokeDash = action(
120
123
  reassignment: previous?.scale === scale.id
121
124
  });
122
125
 
123
- return rematerializeEncoding(next, target, "strokeDash", scale.id);
126
+ return rematerializeEncoding(next, target, "strokeDash", scale.id, layer);
124
127
  }
125
128
  );
126
129
 
@@ -0,0 +1,7 @@
1
+ export function applyTemporalUnit(program, target, channel, unit, previous) {
2
+ if (unit === previous?.temporalUnit) return program;
3
+ return program.editSemantic({
4
+ property: `layer[${target}].encoding.${channel}.temporalUnit`,
5
+ ...(unit === undefined ? { remove: true } : { value: unit })
6
+ });
7
+ }
@@ -1,6 +1,8 @@
1
1
  import { action } from "../../core/action.js";
2
2
  import { formatTextValue, validateTextFormat } from "../../grammar/text.js";
3
+ import { normalizeMarkLabelContent, resolveMarkLabelValues } from "../../grammar/markLabels.js";
3
4
  import { canMaterializeText } from "../../materialization/marks/index.js";
5
+ import { resolveMarkItems } from "../../materialization/selection/policies/index.js";
4
6
  import { findLayer } from "../../selectors/layers.js";
5
7
  import {
6
8
  resolveTarget,
@@ -8,7 +10,7 @@ import {
8
10
  validateOptions
9
11
  } from "./shared.js";
10
12
 
11
- const OPTIONS = Object.freeze(["target", "field", "value", "format"]);
13
+ const OPTIONS = Object.freeze(["target", "field", "value", "content", "normalizeBy", "format"]);
12
14
 
13
15
  function validateTextField(rows, field, format) {
14
16
  if (typeof field !== "string" || field.length === 0) {
@@ -26,14 +28,18 @@ function validateTextField(rows, field, format) {
26
28
  export const encodeText = action(
27
29
  {
28
30
  op: "encodeText",
29
- description: "Assign field-driven or constant text content."
31
+ description: "Assign field, constant, or source semantic text content."
30
32
  },
31
33
  function (args = {}) {
32
34
  validateOptions(args, OPTIONS, "encodeText");
33
35
  const hasField = Object.hasOwn(args, "field");
34
36
  const hasValue = Object.hasOwn(args, "value");
35
- if (hasField === hasValue) {
36
- throw new Error("encodeText requires exactly one of field or value.");
37
+ const hasContent = Object.hasOwn(args, "content");
38
+ if ([hasField, hasValue, hasContent].filter(Boolean).length !== 1) {
39
+ throw new Error("encodeText requires exactly one of field, value, or content.");
40
+ }
41
+ if (Object.hasOwn(args, "normalizeBy") && (!hasContent || args.content !== "share")) {
42
+ throw new Error("Text normalizeBy is only supported with share content.");
37
43
  }
38
44
  const { id: target, dataset, layer } = resolveTarget(
39
45
  this,
@@ -43,23 +49,32 @@ export const encodeText = action(
43
49
  );
44
50
  const previous = layer.encoding?.text;
45
51
  const format = validateTextFormat(args.format ?? previous?.format ?? "auto");
52
+ const content = hasContent
53
+ ? normalizeMarkLabelContent(findLayer(this, layer.source), args)
54
+ : undefined;
46
55
  if (hasField) validateTextField(dataset.values, args.field, format);
47
- else if (formatTextValue(args.value, format) === undefined) {
56
+ else if (hasValue && formatTextValue(args.value, format) === undefined) {
48
57
  throw new Error("encodeText value must produce non-empty text.");
49
58
  }
50
59
 
60
+ const properties = {
61
+ ...(hasContent ? content : { [hasField ? "field" : "datum"]: hasField ? args.field : args.value }),
62
+ format
63
+ };
64
+ const proposed = { ...layer, encoding: { ...layer.encoding, text: properties } };
65
+ if (hasContent && canMaterializeText(this, proposed)) {
66
+ const source = findLayer(this, layer.source);
67
+ for (const value of resolveMarkLabelValues(source, resolveMarkItems(this, source.id), content)) {
68
+ formatTextValue(value, format);
69
+ }
70
+ }
51
71
  let next = this;
52
- const alternate = hasField ? "datum" : "field";
53
- if (Object.hasOwn(previous ?? {}, alternate)) {
54
- next = next.editSemantic({
55
- property: `layer[${target}].encoding.text.${alternate}`,
56
- remove: true
57
- });
72
+ for (const property of ["field", "datum", "content", "normalizeBy"]) {
73
+ if (Object.hasOwn(previous ?? {}, property) && !Object.hasOwn(properties, property)) {
74
+ next = next.editSemantic({ property: `layer[${target}].encoding.text.${property}`, remove: true });
75
+ }
58
76
  }
59
- next = setEncodingProperties(next, target, "text", {
60
- [hasField ? "field" : "datum"]: hasField ? args.field : args.value,
61
- format
62
- });
77
+ next = setEncodingProperties(next, target, "text", properties);
63
78
  const updated = findLayer(next, target);
64
79
  return canMaterializeText(next, updated)
65
80
  ? next.rematerializeTextMark({ id: target })
@@ -2,6 +2,7 @@ import { action } from "../../core/action.js";
2
2
  import { isPlainObject } from "../../core/immutable.js";
3
3
  import {
4
4
  validateKeys,
5
+ validateNonEmptyString,
5
6
  } from "../../core/validation.js";
6
7
  import { DEFAULT_COLORS } from "../../theme/defaults.js";
7
8
  import { validateCurveInterpolation } from "../../grammar/curveCommands.js";
@@ -23,7 +24,7 @@ const OPTIONS = Object.freeze([
23
24
  "boundaries"
24
25
  ]);
25
26
 
26
- const ERROR_BAND_POLICY = Object.freeze({
27
+ export const ERROR_BAND_POLICY = Object.freeze({
27
28
  operation: "createErrorBand",
28
29
  resourceLabel: "error-band",
29
30
  defaultId: "errorBand",
@@ -40,8 +41,8 @@ const ERROR_BAND_POLICY = Object.freeze({
40
41
  "createErrorBand cannot infer the interval axis when both positions are quantitative; provide an interval option."
41
42
  });
42
43
 
43
- function resolveErrorBand(program, args) {
44
- const resolved = resolveIntervalComposite(program, args, ERROR_BAND_POLICY);
44
+ export function resolveErrorBand(program, args, policy = ERROR_BAND_POLICY) {
45
+ const resolved = resolveIntervalComposite(program, args, policy);
45
46
  if (resolved.groupField === resolved.position.field) {
46
47
  throw new Error(
47
48
  "createErrorBand groupBy must differ from the independent position field."
@@ -71,8 +72,9 @@ function resolveBoundaries(value, areaCurve) {
71
72
  });
72
73
  }
73
74
 
74
- function positionOptions({ target, field, fieldType, coordinate, scale }) {
75
- return { target, field, fieldType, coordinate, scale: { id: scale } };
75
+ function positionOptions({ target, field, fieldType, coordinate, scale, temporalUnit }) {
76
+ return { target, field, fieldType, coordinate, scale: { id: scale },
77
+ ...(temporalUnit === undefined ? {} : { temporalUnit }) };
76
78
  }
77
79
 
78
80
  export const createErrorBandBoundary = action(
@@ -103,6 +105,7 @@ export const createErrorBandBoundary = action(
103
105
  target: id,
104
106
  field: vertical ? bound : position.field,
105
107
  fieldType: vertical ? "quantitative" : position.fieldType,
108
+ temporalUnit: vertical ? undefined : position.temporalUnit,
106
109
  coordinate,
107
110
  scale: vertical ? intervalScale : positionScale
108
111
  }))
@@ -110,6 +113,7 @@ export const createErrorBandBoundary = action(
110
113
  target: id,
111
114
  field: vertical ? position.field : bound,
112
115
  fieldType: vertical ? position.fieldType : "quantitative",
116
+ temporalUnit: vertical ? position.temporalUnit : undefined,
113
117
  coordinate,
114
118
  scale: vertical ? positionScale : intervalScale
115
119
  }));
@@ -132,6 +136,7 @@ function positionArgs(resolved) {
132
136
  target: resolved.id,
133
137
  field: resolved.position.field,
134
138
  fieldType: resolved.position.fieldType,
139
+ ...(resolved.position.temporalUnit === undefined ? {} : { temporalUnit: resolved.position.temporalUnit }),
135
140
  coordinate: resolved.coordinate,
136
141
  scale: resolved.position.scale
137
142
  };
@@ -158,6 +163,7 @@ export const createErrorBand = action(
158
163
  const resolved = resolveErrorBand(this, args);
159
164
  const curve = validateCurveInterpolation(args.curve ?? "linear");
160
165
  const boundaries = resolveBoundaries(args.boundaries, curve);
166
+ if (args.fill !== undefined) validateNonEmptyString(args.fill, "Error-band fill");
161
167
  let next = createResolvedIntervalData(this, resolved);
162
168
  next = next.createAreaMark({
163
169
  id: resolved.id,
@@ -216,6 +222,13 @@ export const createErrorBand = action(
216
222
  return next._withMarkConfig(resolved.id, {
217
223
  ...next.markConfigs[resolved.id],
218
224
  errorBand: {
225
+ ...(args.fill === undefined ? {} : { fill: args.fill }),
226
+ source: resolved.source,
227
+ intervalMode: resolved.interval.mode,
228
+ ...(resolved.interval.mode === "statistical"
229
+ ? { intervalField: resolved.interval.field }
230
+ : { centerField: resolved.fields.center }),
231
+ transformGroupBy: resolved.groupBy,
219
232
  data: resolved.dataId,
220
233
  orientation: resolved.orientation,
221
234
  position: resolved.position,