ggaction 0.0.10 → 0.0.12

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 (224) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/README.md +5 -3
  3. package/knowledge/action-card.schema.json +1 -0
  4. package/knowledge/action-cards.json +1 -10941
  5. package/knowledge/action-cards.schema.json +1 -0
  6. package/knowledge/intent-taxonomy.json +1 -183
  7. package/knowledge/intent-taxonomy.schema.json +1 -0
  8. package/knowledge/mcp-resources.json +1 -95
  9. package/knowledge/mcp-resources.schema.json +1 -0
  10. package/knowledge/task-packet.schema.json +1 -159
  11. package/knowledge/task-resolver.js +1 -1230
  12. package/package.json +18 -2
  13. package/src/actions/boxPlots/edit.js +45 -298
  14. package/src/actions/boxPlots/options.js +46 -77
  15. package/src/actions/boxPlots/resolve.js +1 -1
  16. package/src/actions/charts/bar.js +3 -0
  17. package/src/actions/charts/heatmap.js +73 -83
  18. package/src/actions/charts/index.js +1 -1
  19. package/src/actions/charts/parallel.js +35 -1
  20. package/src/actions/charts/shared.js +20 -0
  21. package/src/actions/composition/actions.js +46 -49
  22. package/src/actions/data/bin2d.js +105 -2
  23. package/src/actions/data/box.js +17 -17
  24. package/src/actions/data/density.js +42 -78
  25. package/src/actions/data/gradientProfile.js +22 -52
  26. package/src/actions/data/horizon.js +16 -41
  27. package/src/actions/data/index.js +10 -10
  28. package/src/actions/data/interval.js +16 -38
  29. package/src/actions/data/intervalEdit.js +55 -0
  30. package/src/actions/data/regression.js +7 -17
  31. package/src/actions/data/shared.js +47 -0
  32. package/src/actions/data/timeUnit.js +14 -38
  33. package/src/actions/data/window.js +14 -38
  34. package/src/actions/distributions/revision.js +154 -34
  35. package/src/actions/encodings/angle.js +9 -10
  36. package/src/actions/encodings/appearance.js +17 -51
  37. package/src/actions/encodings/density/resolve.js +3 -1
  38. package/src/actions/encodings/density.js +1 -15
  39. package/src/actions/encodings/horizon/resolve.js +13 -3
  40. package/src/actions/encodings/horizon.js +8 -7
  41. package/src/actions/encodings/index.js +9 -9
  42. package/src/actions/encodings/offset.js +41 -45
  43. package/src/actions/encodings/parallel.js +9 -10
  44. package/src/actions/encodings/pathOrder.js +6 -13
  45. package/src/actions/encodings/position/apply.js +30 -0
  46. package/src/actions/encodings/position/policies/arc.js +23 -2
  47. package/src/actions/encodings/position/policies/index.js +4 -4
  48. package/src/actions/encodings/position/policies/line.js +47 -17
  49. package/src/actions/encodings/position/resolve.js +36 -7
  50. package/src/actions/encodings/ranged.js +18 -23
  51. package/src/actions/encodings/remove.js +15 -25
  52. package/src/actions/encodings/ruleAppearance.js +9 -24
  53. package/src/actions/encodings/shared.js +37 -0
  54. package/src/actions/encodings/strokeDash.js +8 -23
  55. package/src/actions/encodings/text.js +9 -10
  56. package/src/actions/errorBands/create.js +105 -39
  57. package/src/actions/errorBands/edit.js +89 -93
  58. package/src/actions/errorBands/index.js +1 -2
  59. package/src/actions/errorBars/create.js +212 -30
  60. package/src/actions/errorBars/edit.js +91 -48
  61. package/src/actions/errorBars/index.js +1 -2
  62. package/src/actions/facets/actions.js +45 -69
  63. package/src/actions/facets/derive.js +38 -53
  64. package/src/actions/facets/replay.js +0 -1
  65. package/src/actions/gradientPlots/components.js +59 -48
  66. package/src/actions/gradientPlots/edit.js +35 -216
  67. package/src/actions/gradientPlots/index.js +11 -10
  68. package/src/actions/gradientPlots/materialize.js +21 -35
  69. package/src/actions/gradientPlots/options.js +5 -7
  70. package/src/actions/gradientPlots/paint.js +9 -6
  71. package/src/actions/gradientPlots/resolve.js +1 -1
  72. package/src/actions/guides/axes/index.js +3 -3
  73. package/src/actions/guides/axes/labels.js +68 -26
  74. package/src/actions/guides/axes/lines.js +22 -2
  75. package/src/actions/guides/axes/policy.js +28 -8
  76. package/src/actions/guides/axes/ticks.js +27 -9
  77. package/src/actions/guides/axes/titles.js +113 -65
  78. package/src/actions/guides/grids/resolve.js +14 -0
  79. package/src/actions/guides/guides.js +2 -4
  80. package/src/actions/guides/index.js +2 -2
  81. package/src/actions/guides/legends/categorical/actions.js +44 -68
  82. package/src/actions/guides/legends/categorical/components.js +6 -1
  83. package/src/actions/guides/legends/categorical/index.js +2 -2
  84. package/src/actions/guides/legends/categorical/layout.js +148 -175
  85. package/src/actions/guides/legends/categorical/recipes.js +5 -0
  86. package/src/actions/guides/legends/categorical/resolve.js +8 -1
  87. package/src/actions/guides/legends/continuous/common.js +117 -66
  88. package/src/actions/guides/legends/continuous/gradient.js +38 -37
  89. package/src/actions/guides/legends/continuous/interval.js +97 -76
  90. package/src/actions/guides/legends/continuous/opacity.js +28 -38
  91. package/src/actions/guides/legends/edit.js +14 -1
  92. package/src/actions/guides/legends/index.js +1 -1
  93. package/src/actions/guides/legends/lane.js +2 -1
  94. package/src/actions/guides/legends/remove.js +8 -0
  95. package/src/actions/guides/legends/size.js +36 -43
  96. package/src/actions/guides/legends/strokeWidth.js +20 -26
  97. package/src/actions/guides/polar/axes/labels.js +17 -1
  98. package/src/actions/guides/polar/axes/shared.js +7 -1
  99. package/src/actions/guides/polar/axes/titles.js +19 -2
  100. package/src/actions/guides/polar/grids.js +4 -0
  101. package/src/actions/guides/polar/resolve.js +6 -0
  102. package/src/actions/index.js +13 -13
  103. package/src/actions/intervals/resolve.js +87 -117
  104. package/src/actions/marks/arc/actions.js +31 -36
  105. package/src/actions/marks/area/actions.js +23 -31
  106. package/src/actions/marks/index.js +6 -6
  107. package/src/actions/marks/line/actions.js +33 -52
  108. package/src/actions/marks/line/materialize.js +7 -5
  109. package/src/actions/marks/point/materialize.js +38 -16
  110. package/src/actions/marks/remove.js +21 -0
  111. package/src/actions/marks/rule/actions.js +79 -54
  112. package/src/actions/marks/shared.js +8 -4
  113. package/src/actions/marks/text/actions.js +14 -6
  114. package/src/actions/marks/text/layout.js +21 -45
  115. package/src/actions/marks/tick/actions.js +22 -21
  116. package/src/actions/primitives/createGraphics.js +4 -0
  117. package/src/actions/primitives/editGraphics.js +4 -0
  118. package/src/actions/primitives/index.js +3 -3
  119. package/src/actions/primitives/semantic.js +1 -1
  120. package/src/actions/primitives/semanticValidation/index.js +2 -2
  121. package/src/actions/primitives/semanticValidation/layer.js +1 -1
  122. package/src/actions/regression/edit.js +3 -43
  123. package/src/actions/scales/consumers/families.js +24 -4
  124. package/src/actions/scales/consumers/index.js +1 -1
  125. package/src/actions/scales/consumers/seriesLayout.js +17 -0
  126. package/src/actions/scales/definitions.js +100 -103
  127. package/src/actions/scales/materialize.js +5 -0
  128. package/src/actions/selection/actions.js +79 -121
  129. package/src/actions/titles/actions.js +48 -94
  130. package/src/actions/titles/resolve.js +14 -27
  131. package/src/actions/violinPlots/create.js +37 -44
  132. package/src/core/textMetrics.js +74 -37
  133. package/src/core/validation.js +18 -0
  134. package/src/core/vocabulary.js +2 -2
  135. package/src/grammar/aggregate.js +48 -28
  136. package/src/grammar/arcs.js +170 -32
  137. package/src/grammar/areaSeries.js +14 -16
  138. package/src/grammar/bin2d.js +37 -23
  139. package/src/grammar/boxPlot.js +14 -13
  140. package/src/grammar/categoricalDensity.js +14 -14
  141. package/src/grammar/categoryOrder.js +8 -14
  142. package/src/grammar/curveCommands.js +87 -20
  143. package/src/grammar/density.js +162 -141
  144. package/src/grammar/facets/index.js +14 -0
  145. package/src/grammar/facets/scales.js +7 -4
  146. package/src/grammar/gradientProfile.js +12 -15
  147. package/src/grammar/histogram.js +145 -83
  148. package/src/grammar/horizon.js +60 -23
  149. package/src/grammar/interval.js +14 -13
  150. package/src/grammar/lineSeries.js +91 -61
  151. package/src/grammar/numeric.js +290 -0
  152. package/src/grammar/paint.js +3 -4
  153. package/src/grammar/palettes.js +21 -32
  154. package/src/grammar/pathCommands.js +6 -0
  155. package/src/grammar/polarPaths.js +5 -5
  156. package/src/grammar/positionCompatibility.js +1 -1
  157. package/src/grammar/regression/derive.js +48 -9
  158. package/src/grammar/regression/models.js +248 -69
  159. package/src/grammar/regression/parameters.js +8 -0
  160. package/src/grammar/rules.js +2 -0
  161. package/src/grammar/scales/color.js +35 -26
  162. package/src/grammar/scales/continuous.js +71 -24
  163. package/src/grammar/scales/definition.js +1 -1
  164. package/src/grammar/scales/discretized.js +42 -12
  165. package/src/grammar/scales/fields.js +21 -4
  166. package/src/grammar/scales/ordinal.js +139 -60
  167. package/src/grammar/scales/temporal.js +40 -11
  168. package/src/grammar/scales/transformed.js +199 -52
  169. package/src/grammar/schemas/graphicBounds.js +270 -79
  170. package/src/grammar/seriesLayout.js +50 -18
  171. package/src/grammar/ticks.js +123 -53
  172. package/src/grammar/timeUnit.js +18 -8
  173. package/src/grammar/transforms.js +4 -1
  174. package/src/grammar/window.js +70 -57
  175. package/src/index.js +1 -1
  176. package/src/layout/canvas.js +4 -8
  177. package/src/layout/composition.js +39 -40
  178. package/src/layout/facets.js +55 -30
  179. package/src/layout/labels.js +91 -15
  180. package/src/layout/legend.js +22 -20
  181. package/src/layout/legendLane.js +54 -40
  182. package/src/layout/title.js +7 -7
  183. package/src/materialization/bars/aggregate.js +14 -40
  184. package/src/materialization/bars/grouped.js +7 -18
  185. package/src/materialization/bars/histogram.js +2 -19
  186. package/src/materialization/bars/ranged.js +20 -15
  187. package/src/materialization/bars/resolve.js +21 -0
  188. package/src/materialization/facetGuides/legacyCategorical.js +9 -2
  189. package/src/materialization/facetGuides/placement.js +49 -19
  190. package/src/materialization/facetGuides/preparation.js +35 -6
  191. package/src/materialization/facets.js +42 -28
  192. package/src/materialization/guides/resources.js +2 -1
  193. package/src/materialization/marks/capabilities.js +15 -4
  194. package/src/materialization/marks/index.js +29 -6
  195. package/src/materialization/rowEncoding.js +57 -1
  196. package/src/materialization/scales/policies/bar.js +61 -102
  197. package/src/materialization/scales/policies/binnedPosition.js +34 -0
  198. package/src/materialization/scales/policies/offset.js +54 -0
  199. package/src/materialization/scales/policies/series.js +13 -7
  200. package/src/materialization/scales/resolve.js +6 -9
  201. package/src/materialization/selection/items/arc.js +16 -2
  202. package/src/materialization/selection/items/bar.js +4 -2
  203. package/src/materialization/selection/items/common.js +21 -1
  204. package/src/materialization/selection/items/path.js +19 -2
  205. package/src/materialization/selection/items/point.js +4 -16
  206. package/src/materialization/selection/items/tick.js +13 -21
  207. package/src/materialization/selection/point.js +11 -4
  208. package/src/materialization/selection/styles.js +19 -27
  209. package/src/materialization/text.js +4 -1
  210. package/src/mcp/adapter.js +8 -3
  211. package/src/renderers/canvas/fill.js +2 -2
  212. package/src/renderers/canvas/index.js +35 -2
  213. package/src/renderers/canvas/native.js +160 -0
  214. package/src/renderers/pdf.js +14 -0
  215. package/src/renderers/svg.js +37 -4
  216. package/types/index.d.ts +3 -0
  217. package/types/program.d.ts +523 -131
  218. package/types/svg.d.ts +1 -0
  219. package/src/actions/errorBands/components.js +0 -81
  220. package/src/actions/errorBands/options.js +0 -39
  221. package/src/actions/errorBands/resolve.js +0 -28
  222. package/src/actions/errorBars/components.js +0 -62
  223. package/src/actions/errorBars/options.js +0 -58
  224. package/src/actions/errorBars/resolve.js +0 -25
@@ -11,11 +11,13 @@ import {
11
11
  import { normalizeRuleDatum } from "../../grammar/rules.js";
12
12
  import {
13
13
  canMaterializeArea,
14
- canMaterializeLine
14
+ canMaterializeLine,
15
+ getPositionEncodingMaterializationSteps
15
16
  } from "../../materialization/marks/index.js";
16
17
  import { findLayer } from "../../selectors/layers.js";
17
18
  import {
18
19
  resolveTarget,
20
+ setEncodingProperties,
19
21
  validateLineSeriesCompatibility,
20
22
  validateOptions
21
23
  } from "./shared.js";
@@ -138,28 +140,21 @@ function encodeSecondaryPosition(program, channel, args, operation, types) {
138
140
  });
139
141
  }
140
142
  }
141
- next = next
142
- .editSemantic({
143
- property: `layer[${target}].encoding.${channel}.${hasField ? "field" : "datum"}`,
144
- value: hasField ? args.field : args.datum
145
- })
146
- .editSemantic({
147
- property: `layer[${target}].encoding.${channel}.fieldType`,
148
- value: fieldType
149
- })
150
- .editSemantic({
151
- property: `layer[${target}].encoding.${channel}.scale`,
152
- value: primary.scale
153
- })
154
- .rematerializeScale({ id: primary.scale });
155
- if (rule) return next.rematerializeRuleMark({ id: target });
156
- return layer.mark.type === "bar"
157
- ? next.rematerializeBarMark({ id: target })
158
- : layer.mark.type === "rect"
159
- ? next.rematerializeRectMark({ id: target })
160
- : canMaterializeArea(next, findLayer(next, target))
161
- ? next.rematerializeAreaMark({ id: target })
162
- : next;
143
+ next = setEncodingProperties(next, target, channel, {
144
+ [hasField ? "field" : "datum"]: hasField ? args.field : args.datum,
145
+ fieldType,
146
+ scale: primary.scale
147
+ });
148
+
149
+ const updated = findLayer(next, target);
150
+ for (const step of getPositionEncodingMaterializationSteps(
151
+ next,
152
+ updated,
153
+ primary.scale
154
+ )) {
155
+ next = next[step.op](step.args);
156
+ }
157
+ return next;
163
158
  }
164
159
 
165
160
  const encodeX2 = action(
@@ -16,6 +16,7 @@ import {
16
16
  } from "../../materialization/marks/index.js";
17
17
  import { findLayer } from "../../selectors/layers.js";
18
18
  import { removeLegendKinds } from "../guides/legends/remove.js";
19
+ import { clearMarkGraphic } from "./shared.js";
19
20
 
20
21
  const OPTIONS = Object.freeze(["target", "channel"]);
21
22
  const REMOVABLE_CHANNELS = Object.freeze([
@@ -35,6 +36,10 @@ const SPECIALIZED_LEGEND_KIND = Object.freeze({
35
36
  opacity: Object.freeze(["opacity"]),
36
37
  strokeWidth: Object.freeze(["strokeWidth"])
37
38
  });
39
+ const LEGEND_OPTION_KEYS = Object.freeze([
40
+ "position", "align", "direction", "columns", "offset", "titlePosition",
41
+ "title", "symbol", "labels", "titleStyle", "itemGap", "border"
42
+ ]);
38
43
 
39
44
  function resolveTarget(program, requested, channel) {
40
45
  const candidates = program.semanticSpec.layers.filter(
@@ -178,22 +183,15 @@ function reconcileCategoricalLegend(program, target, channels) {
178
183
  return removeLegendKinds(program, [kind]);
179
184
  }
180
185
  const next = removeLegendKinds(program, [kind]);
181
- return next.createLegend({
182
- target,
183
- channels: remaining,
184
- position: config.position,
185
- align: config.align,
186
- direction: config.direction,
187
- ...(config.columns === undefined ? {} : { columns: config.columns }),
188
- offset: config.offset,
189
- titlePosition: config.titlePosition,
190
- ...(config.inferredTitle ? {} : { title: config.title }),
191
- symbol: config.symbol,
192
- labels: config.labels,
193
- titleStyle: config.titleStyle,
194
- itemGap: config.itemGap,
195
- border: config.border
196
- });
186
+ const options = { target, channels: remaining };
187
+ for (const property of LEGEND_OPTION_KEYS) {
188
+ if (
189
+ (property === "columns" && config[property] === undefined) ||
190
+ (property === "title" && config.inferredTitle)
191
+ ) continue;
192
+ options[property] = config[property];
193
+ }
194
+ return next.createLegend(options);
197
195
  }
198
196
 
199
197
  function cleanupLegends(program, target, channels) {
@@ -204,18 +202,10 @@ function cleanupLegends(program, target, channels) {
204
202
  return kinds.length === 0 ? next : removeLegendKinds(next, kinds);
205
203
  }
206
204
 
207
- function clearGraphic(program, target) {
208
- const graphic = program.graphicSpec.objects[target];
209
- if (graphic === undefined) return program;
210
- return graphic.type === "collection"
211
- ? program.editGraphics({ target, property: "items", value: [] })
212
- : program.editGraphics({ target, property: "length", value: 0 });
213
- }
214
-
215
205
  function rematerializeTarget(program, target) {
216
206
  const layer = findLayer(program, target);
217
207
  const step = getMarkMaterializationStep(program, layer);
218
- const baseline = clearGraphic(program, target);
208
+ const baseline = clearMarkGraphic(program, target);
219
209
  let next = step === undefined
220
210
  ? baseline
221
211
  : baseline[step.op](step.args);
@@ -7,12 +7,12 @@ import { readQuantitativeField } from "../../grammar/scales/index.js";
7
7
  import { resolveStrokeWidthScaleDefinition } from "../scales/definitions.js";
8
8
  import {
9
9
  applyEncodingScale,
10
+ rematerializeEncoding,
10
11
  resolveReassignmentScaleOptions,
11
12
  resolveTarget,
13
+ setEncodingProperties,
12
14
  validateOptions
13
15
  } from "./shared.js";
14
- import { applyMaterializationPlan } from "../../materialization/dependencies.js";
15
- import { planEncodingRematerialization } from "../../materialization/encodings.js";
16
16
  import { findLayer } from "../../selectors/layers.js";
17
17
 
18
18
  const STROKE_OPTIONS = Object.freeze(["target", "value"]);
@@ -91,31 +91,16 @@ const encodeStrokeWidth = action(
91
91
  const scale = resolveStrokeWidthScaleDefinition(this, requestedScale);
92
92
  const { strokeWidth, ...config } = this.markConfigs[id] ?? {};
93
93
  void strokeWidth;
94
- let next = this
95
- ._withMarkConfig(id, config)
96
- .editSemantic({
97
- property: `layer[${id}].encoding.strokeWidth.field`,
98
- value: args.field
99
- })
100
- .editSemantic({
101
- property: `layer[${id}].encoding.strokeWidth.fieldType`,
102
- value: fieldType
103
- })
104
- .editSemantic({
105
- property: `layer[${id}].encoding.strokeWidth.scale`,
106
- value: scale.id
107
- });
94
+ let next = setEncodingProperties(
95
+ this._withMarkConfig(id, config),
96
+ id,
97
+ "strokeWidth",
98
+ { field: args.field, fieldType, scale: scale.id }
99
+ );
108
100
  next = applyEncodingScale(next, scale, requestedScale, {
109
101
  reassignment: previous?.scale === scale.id
110
102
  });
111
- return applyMaterializationPlan(
112
- next,
113
- planEncodingRematerialization(next, {
114
- target: id,
115
- channel: "strokeWidth",
116
- scale: scale.id
117
- })
118
- );
103
+ return rematerializeEncoding(next, id, "strokeWidth", scale.id);
119
104
  }
120
105
  );
121
106
 
@@ -2,6 +2,8 @@ import { validateUserId } from "../../core/identifiers.js";
2
2
  import { findDataset } from "../../selectors/datasets.js";
3
3
  import { resolveEligibleLayer } from "../../selectors/layers.js";
4
4
  import { hasMaterializedLegend } from "../../materialization/legends.js";
5
+ import { applyMaterializationPlan } from "../../materialization/dependencies.js";
6
+ import { planEncodingRematerialization } from "../../materialization/encodings.js";
5
7
  import { findSemanticScale } from "../../selectors/scales.js";
6
8
  import { validateOptionObject } from "../../core/validation.js";
7
9
  import {
@@ -13,6 +15,41 @@ export function validateOptions(args, supported, operation) {
13
15
  validateOptionObject(args, supported, operation);
14
16
  }
15
17
 
18
+ export function setEncodingProperties(
19
+ program,
20
+ target,
21
+ channel,
22
+ properties
23
+ ) {
24
+ let next = program;
25
+ for (const [property, value] of Object.entries(properties)) {
26
+ next = next.editSemantic({
27
+ property: `layer[${target}].encoding.${channel}.${property}`,
28
+ value
29
+ });
30
+ }
31
+ return next;
32
+ }
33
+
34
+ export function rematerializeEncoding(program, target, channel, scale) {
35
+ return applyMaterializationPlan(
36
+ program,
37
+ planEncodingRematerialization(program, { target, channel, scale })
38
+ );
39
+ }
40
+
41
+ export function clearMarkGraphic(program, target) {
42
+ const graphic = program.graphicSpec.objects[target];
43
+ if (graphic === undefined) return program;
44
+ const collection = graphic.type === "collection";
45
+ return program.editGraphics({
46
+ target,
47
+ property: collection ? "items" : "length",
48
+ value: collection ? [] : 0
49
+ });
50
+ }
51
+
52
+
16
53
  export function validateLineSeriesCompatibility(layer, channel, field) {
17
54
  if (layer.mark?.type !== "line") return;
18
55
  for (const companion of ["group", "color", "strokeDash"]) {
@@ -5,12 +5,12 @@ import {
5
5
  validateNominalFieldType
6
6
  } from "../../grammar/scales/index.js";
7
7
  import { resolveStrokeDashScaleDefinition } from "../scales/definitions.js";
8
- import { applyMaterializationPlan } from "../../materialization/dependencies.js";
9
- import { planEncodingRematerialization } from "../../materialization/encodings.js";
10
8
  import {
11
9
  applyEncodingScale,
10
+ rematerializeEncoding,
12
11
  resolveReassignmentScaleOptions,
13
12
  resolveTarget,
13
+ setEncodingProperties,
14
14
  validateLineSeriesCompatibility,
15
15
  validateOptions
16
16
  } from "./shared.js";
@@ -111,31 +111,16 @@ const encodeStrokeDash = action(
111
111
  let next = previous === undefined
112
112
  ? this
113
113
  : this.clearStrokeDashEncoding({ target });
114
- next = next
115
- .editSemantic({
116
- property: `layer[${target}].encoding.strokeDash.field`,
117
- value: args.field
118
- })
119
- .editSemantic({
120
- property: `layer[${target}].encoding.strokeDash.fieldType`,
121
- value: fieldType
122
- })
123
- .editSemantic({
124
- property: `layer[${target}].encoding.strokeDash.scale`,
125
- value: scale.id
126
- });
114
+ next = setEncodingProperties(next, target, "strokeDash", {
115
+ field: args.field,
116
+ fieldType,
117
+ scale: scale.id
118
+ });
127
119
  next = applyEncodingScale(next, scale, requestedScale, {
128
120
  reassignment: previous?.scale === scale.id
129
121
  });
130
122
 
131
- return applyMaterializationPlan(
132
- next,
133
- planEncodingRematerialization(next, {
134
- target,
135
- channel: "strokeDash",
136
- scale: scale.id
137
- })
138
- );
123
+ return rematerializeEncoding(next, target, "strokeDash", scale.id);
139
124
  }
140
125
  );
141
126
 
@@ -2,7 +2,11 @@ import { action } from "../../core/action.js";
2
2
  import { formatTextValue, validateTextFormat } from "../../grammar/text.js";
3
3
  import { canMaterializeText } from "../../materialization/marks/index.js";
4
4
  import { findLayer } from "../../selectors/layers.js";
5
- import { resolveTarget, validateOptions } from "./shared.js";
5
+ import {
6
+ resolveTarget,
7
+ setEncodingProperties,
8
+ validateOptions
9
+ } from "./shared.js";
6
10
 
7
11
  const OPTIONS = Object.freeze(["target", "field", "value", "format"]);
8
12
 
@@ -52,15 +56,10 @@ export const encodeText = action(
52
56
  remove: true
53
57
  });
54
58
  }
55
- next = next
56
- .editSemantic({
57
- property: `layer[${target}].encoding.text.${hasField ? "field" : "datum"}`,
58
- value: hasField ? args.field : args.value
59
- })
60
- .editSemantic({
61
- property: `layer[${target}].encoding.text.format`,
62
- value: format
63
- });
59
+ next = setEncodingProperties(next, target, "text", {
60
+ [hasField ? "field" : "datum"]: hasField ? args.field : args.value,
61
+ format
62
+ });
64
63
  const updated = findLayer(next, target);
65
64
  return canMaterializeText(next, updated)
66
65
  ? next.rematerializeTextMark({ id: target })
@@ -5,8 +5,9 @@ import {
5
5
  } from "../../core/validation.js";
6
6
  import { DEFAULT_COLORS } from "../../theme/defaults.js";
7
7
  import { validateCurveInterpolation } from "../../grammar/curveCommands.js";
8
- import { resolveBoundaryAppearance } from "./options.js";
9
- import { resolveErrorBand } from "./resolve.js";
8
+ import { createResolvedIntervalData } from "../data/intervalEdit.js";
9
+ import { resolveIntervalComposite } from "../intervals/resolve.js";
10
+ import { errorBandBoundaries, resolveBoundaryAppearance } from "./edit.js";
10
11
 
11
12
  const OPTIONS = Object.freeze([
12
13
  "id",
@@ -21,6 +22,38 @@ const OPTIONS = Object.freeze([
21
22
  "curve",
22
23
  "boundaries"
23
24
  ]);
25
+
26
+ const ERROR_BAND_POLICY = Object.freeze({
27
+ operation: "createErrorBand",
28
+ resourceLabel: "error-band",
29
+ defaultId: "errorBand",
30
+ ownerLabel: "Error-band id",
31
+ positionTypes: Object.freeze(["quantitative", "temporal"]),
32
+ defaultPositionType: "quantitative",
33
+ defaultIntervalChannel: "y",
34
+ scaleDefaults: fieldType => fieldType === "temporal"
35
+ ? { nice: true }
36
+ : { nice: true, zero: false },
37
+ intervalScaleDefaults: Object.freeze({ nice: true, zero: false }),
38
+ allowExplicitGrouping: true,
39
+ ambiguousMessage:
40
+ "createErrorBand cannot infer the interval axis when both positions are quantitative; provide an interval option."
41
+ });
42
+
43
+ function resolveErrorBand(program, args) {
44
+ const resolved = resolveIntervalComposite(program, args, ERROR_BAND_POLICY);
45
+ if (resolved.groupField === resolved.position.field) {
46
+ throw new Error(
47
+ "createErrorBand groupBy must differ from the independent position field."
48
+ );
49
+ }
50
+ return {
51
+ ...resolved,
52
+ lowerBoundaryId: `${resolved.id}LowerBoundary`,
53
+ upperBoundaryId: `${resolved.id}UpperBoundary`
54
+ };
55
+ }
56
+
24
57
  function resolveBoundaries(value, areaCurve) {
25
58
  if (value === undefined || value === false) return undefined;
26
59
  if (!isPlainObject(value)) {
@@ -38,6 +71,62 @@ function resolveBoundaries(value, areaCurve) {
38
71
  });
39
72
  }
40
73
 
74
+ function positionOptions({ target, field, fieldType, coordinate, scale }) {
75
+ return { target, field, fieldType, coordinate, scale: { id: scale } };
76
+ }
77
+
78
+ export const createErrorBandBoundary = action(
79
+ {
80
+ op: "createErrorBandBoundary",
81
+ description: "Create one lower or upper error-band boundary line."
82
+ },
83
+ function ({
84
+ id,
85
+ data,
86
+ orientation,
87
+ bound,
88
+ position,
89
+ coordinate,
90
+ intervalScale,
91
+ positionScale,
92
+ groupBy,
93
+ stroke,
94
+ strokeWidth,
95
+ strokeDash,
96
+ opacity,
97
+ curve
98
+ } = {}) {
99
+ const vertical = orientation === "vertical";
100
+ let next = this
101
+ .createLineMark({ id, data, strokeWidth, curve })
102
+ .encodeY(positionOptions({
103
+ target: id,
104
+ field: vertical ? bound : position.field,
105
+ fieldType: vertical ? "quantitative" : position.fieldType,
106
+ coordinate,
107
+ scale: vertical ? intervalScale : positionScale
108
+ }))
109
+ .encodeX(positionOptions({
110
+ target: id,
111
+ field: vertical ? position.field : bound,
112
+ fieldType: vertical ? position.fieldType : "quantitative",
113
+ coordinate,
114
+ scale: vertical ? positionScale : intervalScale
115
+ }));
116
+ if (groupBy !== undefined) {
117
+ next = next.encodeGroup({ target: id, field: groupBy });
118
+ }
119
+ return next
120
+ .editGraphics({ target: id, property: "stroke", value: stroke })
121
+ .editGraphics({
122
+ target: id,
123
+ property: "strokeDash",
124
+ value: next.graphicSpec.objects[id].items.map(() => strokeDash)
125
+ })
126
+ .editGraphics({ target: id, property: "opacity", value: opacity });
127
+ }
128
+ );
129
+
41
130
  function positionArgs(resolved) {
42
131
  return {
43
132
  target: resolved.id,
@@ -69,21 +158,7 @@ export const createErrorBand = action(
69
158
  const resolved = resolveErrorBand(this, args);
70
159
  const curve = validateCurveInterpolation(args.curve ?? "linear");
71
160
  const boundaries = resolveBoundaries(args.boundaries, curve);
72
- let next = this;
73
-
74
- if (resolved.interval.mode === "statistical") {
75
- next = next.createIntervalData({
76
- id: resolved.dataId,
77
- source: resolved.source,
78
- field: resolved.interval.field,
79
- groupBy: resolved.groupBy,
80
- center: resolved.interval.center,
81
- extent: resolved.interval.extent,
82
- level: resolved.interval.level,
83
- as: resolved.fields
84
- });
85
- }
86
-
161
+ let next = createResolvedIntervalData(this, resolved);
87
162
  next = next.createAreaMark({
88
163
  id: resolved.id,
89
164
  data: resolved.dataId,
@@ -91,13 +166,11 @@ export const createErrorBand = action(
91
166
  ...(args.opacity === undefined ? {} : { opacity: args.opacity }),
92
167
  ...(Object.hasOwn(args, "curve") ? { curve } : {})
93
168
  });
94
- next = resolved.orientation === "vertical"
95
- ? next
96
- .encodeX(positionArgs(resolved))
97
- .encodeYRange(rangeArgs(resolved))
98
- : next
99
- .encodeY(positionArgs(resolved))
100
- .encodeXRange(rangeArgs(resolved));
169
+ const vertical = resolved.orientation === "vertical";
170
+ next = next[vertical ? "encodeX" : "encodeY"](positionArgs(resolved));
171
+ next = next[vertical ? "encodeYRange" : "encodeXRange"](
172
+ rangeArgs(resolved)
173
+ );
101
174
 
102
175
  if (resolved.interval.mode === "explicit") {
103
176
  next = next.editSemantic({
@@ -125,21 +198,14 @@ export const createErrorBand = action(
125
198
  groupBy: resolved.groupField,
126
199
  ...boundaries
127
200
  };
128
- next = next
129
- .createErrorBandBoundary({
201
+ for (const [id, field] of errorBandBoundaries(resolved)) {
202
+ next = next.createErrorBandBoundary({
130
203
  ...shared,
131
- id: `${resolved.id}LowerBoundary`,
132
- bound: resolved.fields.lower
133
- })
134
- .createErrorBandBoundary({
135
- ...shared,
136
- id: `${resolved.id}UpperBoundary`,
137
- bound: resolved.fields.upper
204
+ id,
205
+ bound: field
138
206
  });
139
- for (const [id, bound] of [
140
- [`${resolved.id}LowerBoundary`, "lower"],
141
- [`${resolved.id}UpperBoundary`, "upper"]
142
- ]) {
207
+ }
208
+ for (const [id, , bound] of errorBandBoundaries(resolved)) {
143
209
  next = next._withMarkConfig(id, {
144
210
  ...next.markConfigs[id],
145
211
  ...boundaries,
@@ -159,8 +225,8 @@ export const createErrorBand = action(
159
225
  groupBy: resolved.groupField,
160
226
  lowerField: resolved.fields.lower,
161
227
  upperField: resolved.fields.upper,
162
- lowerBoundaryId: `${resolved.id}LowerBoundary`,
163
- upperBoundaryId: `${resolved.id}UpperBoundary`
228
+ lowerBoundaryId: resolved.lowerBoundaryId,
229
+ upperBoundaryId: resolved.upperBoundaryId
164
230
  }
165
231
  });
166
232
  }