ggaction 0.0.16 → 0.0.18

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 (299) hide show
  1. package/CHANGELOG.md +66 -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/intent-taxonomy.json +1 -1
  6. package/knowledge/mcp-resources.json +1 -1
  7. package/knowledge/task-resolver.js +1 -1
  8. package/package.json +10 -4
  9. package/src/BasicChartProgram.js +1 -1
  10. package/src/accessibility.js +3 -2
  11. package/src/actions/boxPlots/create.js +14 -4
  12. package/src/actions/boxPlots/edit.js +39 -33
  13. package/src/actions/boxPlots/materialize.js +7 -3
  14. package/src/actions/boxPlots/options.js +37 -11
  15. package/src/actions/canvas/actions.js +70 -95
  16. package/src/actions/canvas/fitting.js +16 -1
  17. package/src/actions/canvas/index.js +3 -0
  18. package/src/actions/canvas/plotBorder.js +28 -0
  19. package/src/actions/charts/area.js +2 -2
  20. package/src/actions/charts/bar.js +34 -7
  21. package/src/actions/charts/ecdf.js +4 -18
  22. package/src/actions/charts/endpoints.js +4 -18
  23. package/src/actions/charts/heatmap.js +4 -4
  24. package/src/actions/charts/index.js +2 -0
  25. package/src/actions/charts/interval-regression.js +2 -2
  26. package/src/actions/charts/line.js +1 -1
  27. package/src/actions/charts/pie.js +3 -47
  28. package/src/actions/charts/radial.js +34 -12
  29. package/src/actions/charts/scatter.js +16 -2
  30. package/src/actions/charts/shared.js +2 -2
  31. package/src/actions/charts/text.js +35 -0
  32. package/src/actions/composition/actions.js +6 -2
  33. package/src/actions/data/bin.js +1 -1
  34. package/src/actions/data/box.js +2 -1
  35. package/src/actions/data/create.js +18 -5
  36. package/src/actions/data/density.js +1 -1
  37. package/src/actions/data/derived.js +15 -1
  38. package/src/actions/data/edit.js +20 -7
  39. package/src/actions/data/filter.js +15 -4
  40. package/src/actions/data/index.js +3 -0
  41. package/src/actions/data/interval.js +2 -0
  42. package/src/actions/data/regression.js +6 -2
  43. package/src/actions/data/revise.js +37 -5
  44. package/src/actions/data/shared.js +33 -4
  45. package/src/actions/data/sort.js +21 -0
  46. package/src/actions/data/stack.js +3 -1
  47. package/src/actions/data/summary.js +1 -1
  48. package/src/actions/encodings/angle.js +4 -1
  49. package/src/actions/encodings/appearance.js +43 -31
  50. package/src/actions/encodings/barWidth.js +36 -11
  51. package/src/actions/encodings/channels.js +0 -1
  52. package/src/actions/encodings/color/continuous.js +5 -2
  53. package/src/actions/encodings/color/index.js +5 -2
  54. package/src/actions/encodings/color/policy.js +10 -3
  55. package/src/actions/encodings/density/resolve.js +1 -6
  56. package/src/actions/encodings/offset.js +4 -1
  57. package/src/actions/encodings/position/policies/arc.js +12 -10
  58. package/src/actions/encodings/position/policies/area.js +3 -3
  59. package/src/actions/encodings/position/policies/bar.js +15 -6
  60. package/src/actions/encodings/position/policies/line.js +17 -6
  61. package/src/actions/encodings/position/resolve.js +9 -3
  62. package/src/actions/encodings/ranged.js +23 -1
  63. package/src/actions/encodings/remove.js +3 -2
  64. package/src/actions/encodings/ruleAppearance.js +4 -1
  65. package/src/actions/encodings/seriesLayout.js +1 -0
  66. package/src/actions/encodings/stroke.js +4 -1
  67. package/src/actions/encodings/strokeDash.js +6 -1
  68. package/src/actions/encodings/text.js +12 -4
  69. package/src/actions/errorBands/create.js +2 -2
  70. package/src/actions/errorBands/edit.js +2 -1
  71. package/src/actions/facets/actions.js +158 -240
  72. package/src/actions/gradientPlots/resolve.js +2 -17
  73. package/src/actions/guides/applicability.js +1 -1
  74. package/src/actions/guides/axes/axis.js +16 -4
  75. package/src/actions/guides/axes/edit.js +4 -4
  76. package/src/actions/guides/axes/labels.js +120 -46
  77. package/src/actions/guides/axes/lines.js +6 -5
  78. package/src/actions/guides/axes/parallel/lifecycle.js +1 -1
  79. package/src/actions/guides/axes/parallel/policy.js +8 -7
  80. package/src/actions/guides/axes/remove.js +3 -2
  81. package/src/actions/guides/axes/tickGroups.js +5 -2
  82. package/src/actions/guides/axes/ticks.js +6 -3
  83. package/src/actions/guides/axes/titles.js +71 -26
  84. package/src/actions/guides/basic.js +2 -0
  85. package/src/actions/guides/facadeLegend.js +10 -2
  86. package/src/actions/guides/grids/grid.js +7 -1
  87. package/src/actions/guides/grids/resolve.js +59 -12
  88. package/src/actions/guides/legends/blocks.js +3 -1
  89. package/src/actions/guides/legends/categorical/actions.js +70 -17
  90. package/src/actions/guides/legends/categorical/components.js +12 -8
  91. package/src/actions/guides/legends/categorical/index.js +6 -0
  92. package/src/actions/guides/legends/categorical/layout.js +37 -8
  93. package/src/actions/guides/legends/categorical/options.js +31 -20
  94. package/src/actions/guides/legends/categorical/resolve.js +2 -2
  95. package/src/actions/guides/legends/categorical/symbols.js +18 -12
  96. package/src/actions/guides/legends/categorical/validation.js +1 -6
  97. package/src/actions/guides/legends/continuous/colorSymbols.js +1 -0
  98. package/src/actions/guides/legends/continuous/common.js +1 -447
  99. package/src/actions/guides/legends/continuous/gradient.js +47 -34
  100. package/src/actions/guides/legends/continuous/interval.js +4 -4
  101. package/src/actions/guides/legends/continuous/opacity.js +19 -12
  102. package/src/actions/guides/legends/edit.js +1 -604
  103. package/src/actions/guides/legends/focused.js +2 -2
  104. package/src/actions/guides/legends/group.js +1 -0
  105. package/src/actions/guides/legends/lane.js +1 -516
  106. package/src/actions/guides/legends/remove.js +3 -0
  107. package/src/actions/guides/legends/sampling.js +1 -1
  108. package/src/actions/guides/legends/size.js +51 -43
  109. package/src/actions/guides/legends/strokeWidth.js +11 -29
  110. package/src/actions/guides/legends/target.js +3 -3
  111. package/src/actions/guides/legends/transition.js +21 -7
  112. package/src/actions/guides/polar/axes/labels.js +4 -2
  113. package/src/actions/guides/polar/axes/titles.js +7 -5
  114. package/src/actions/guides/polar/resolve.js +6 -14
  115. package/src/actions/index.js +16 -16
  116. package/src/actions/marks/arc/actions.js +3 -10
  117. package/src/actions/marks/area/actions.js +8 -5
  118. package/src/actions/marks/area/materialize.js +8 -9
  119. package/src/actions/marks/bar/create.js +17 -4
  120. package/src/actions/marks/bar/edit.js +18 -7
  121. package/src/actions/marks/bar/materialize.js +9 -6
  122. package/src/actions/marks/line/actions.js +11 -4
  123. package/src/actions/marks/line/materialize.js +9 -5
  124. package/src/actions/marks/point/create.js +8 -1
  125. package/src/actions/marks/point/edit.js +13 -6
  126. package/src/actions/marks/point/materialize.js +4 -2
  127. package/src/actions/marks/rect/actions.js +26 -11
  128. package/src/actions/marks/remove.js +2 -0
  129. package/src/actions/marks/rule/actions.js +31 -12
  130. package/src/actions/marks/text/actions.js +42 -11
  131. package/src/actions/marks/text/layout.js +3 -0
  132. package/src/actions/marks/tick/actions.js +23 -8
  133. package/src/actions/primitives/semanticAction.js +37 -3
  134. package/src/actions/primitives/semanticValidation/dataset.js +13 -1
  135. package/src/actions/primitives/semanticValidation/layer.js +26 -2
  136. package/src/actions/primitives/semanticValidation/scale.js +6 -0
  137. package/src/actions/regression/create.js +26 -5
  138. package/src/actions/regression/edit.js +50 -15
  139. package/src/actions/regression/follow.js +22 -0
  140. package/src/actions/regression/resolve.js +2 -6
  141. package/src/actions/scales/channels.js +20 -15
  142. package/src/actions/scales/consumers/common.js +2 -1
  143. package/src/actions/scales/consumers/families.js +16 -5
  144. package/src/actions/scales/create.js +10 -4
  145. package/src/actions/scales/definitions.js +1 -425
  146. package/src/actions/scales/edit.js +3 -3
  147. package/src/actions/scales/editPolicy.js +9 -3
  148. package/src/actions/scales/materialize.js +1 -1
  149. package/src/actions/scales/parallel.js +1 -1
  150. package/src/actions/scales/patch.js +1 -1
  151. package/src/actions/scales/preview.js +49 -2
  152. package/src/actions/scales/quantitativeColor.js +3 -3
  153. package/src/actions/selection/actions.js +1 -13
  154. package/src/actions/theme/reconcile.js +83 -27
  155. package/src/actions/theme/state.js +12 -0
  156. package/src/actions/titles/actions.js +5 -18
  157. package/src/actions/titles/resolve.js +41 -19
  158. package/src/actions/violinPlots/edit.js +3 -20
  159. package/src/core/action.js +3 -2
  160. package/src/core/compositionState.js +4 -1
  161. package/src/core/font.js +9 -2
  162. package/src/core/optionRequirements.js +1 -0
  163. package/src/core/programState.js +5 -1
  164. package/src/core/resourceReferences.js +3 -0
  165. package/src/core/textMetricProfile.js +3 -2
  166. package/src/core/textMetrics.js +41 -4
  167. package/src/core/validation.js +26 -0
  168. package/src/core/vocabulary.js +4 -0
  169. package/src/grammar/aggregate.js +4 -18
  170. package/src/grammar/arcs.js +1 -386
  171. package/src/grammar/areaPaint.js +10 -0
  172. package/src/grammar/areaSeries.js +18 -10
  173. package/src/grammar/bars/geometry.js +30 -3
  174. package/src/grammar/bars/policy.js +12 -1
  175. package/src/grammar/bin.js +41 -22
  176. package/src/grammar/bin2d.js +1 -6
  177. package/src/grammar/boxPlot.js +56 -9
  178. package/src/grammar/categoryOrder.js +1 -6
  179. package/src/grammar/complete.js +15 -19
  180. package/src/grammar/computed.js +92 -25
  181. package/src/grammar/curveCommands.js +29 -16
  182. package/src/grammar/datasetSchema.js +382 -0
  183. package/src/grammar/density.js +53 -5
  184. package/src/grammar/displayLabels.js +28 -0
  185. package/src/grammar/ecdf.js +2 -9
  186. package/src/grammar/facets/dependencies.js +6 -2
  187. package/src/grammar/facets/guides.js +5 -0
  188. package/src/grammar/facets/index.js +2 -5
  189. package/src/grammar/filter.js +133 -39
  190. package/src/grammar/fold.js +2 -12
  191. package/src/grammar/gradientProfile.js +1 -6
  192. package/src/grammar/impute.js +21 -48
  193. package/src/grammar/interval.js +34 -10
  194. package/src/grammar/itemMissing.js +59 -0
  195. package/src/grammar/legendGradient.js +18 -0
  196. package/src/grammar/legendGroups.js +1 -0
  197. package/src/grammar/legendTitleStyle.js +22 -0
  198. package/src/grammar/lineSeries.js +32 -17
  199. package/src/grammar/markLabels.js +5 -2
  200. package/src/grammar/normalize.js +4 -54
  201. package/src/grammar/numeric.js +1 -290
  202. package/src/grammar/parallelCoordinates.js +1 -6
  203. package/src/grammar/parseTime.js +41 -0
  204. package/src/grammar/pathCommands.js +11 -4
  205. package/src/grammar/polar.js +1 -184
  206. package/src/grammar/positionCompatibility.js +3 -3
  207. package/src/grammar/regression/derive.js +63 -16
  208. package/src/grammar/regression/index.js +1 -0
  209. package/src/grammar/regression/models.js +116 -59
  210. package/src/grammar/regression/parameters.js +69 -6
  211. package/src/grammar/roundedRect.js +42 -54
  212. package/src/grammar/scales/appearance.js +1 -206
  213. package/src/grammar/scales/color.js +1 -382
  214. package/src/grammar/scales/colorConsumers.js +5 -2
  215. package/src/grammar/scales/definition.js +1 -190
  216. package/src/grammar/scales/discretized.js +1 -195
  217. package/src/grammar/scales/size.js +1 -456
  218. package/src/grammar/scales/temporal.js +2 -13
  219. package/src/grammar/scales/transformed.js +1 -383
  220. package/src/grammar/scales/types.js +1 -1
  221. package/src/grammar/scales/validation.js +1 -66
  222. package/src/grammar/schemas/concreteGraphic.js +11 -0
  223. package/src/grammar/schemas/graphic.js +3 -0
  224. package/src/grammar/schemas/semanticPath.js +5 -3
  225. package/src/grammar/sort.js +88 -0
  226. package/src/grammar/stack.js +2 -10
  227. package/src/grammar/statisticalReference.js +1 -6
  228. package/src/grammar/summary.js +120 -45
  229. package/src/grammar/text.js +19 -0
  230. package/src/grammar/timeUnit.js +16 -7
  231. package/src/grammar/transformKeys.js +47 -0
  232. package/src/grammar/transformTopology.js +1 -0
  233. package/src/grammar/transforms.js +25 -12
  234. package/src/grammar/valueFormat.js +17 -5
  235. package/src/grammar/weightedStatistics.js +1 -6
  236. package/src/grammar/window.js +4 -58
  237. package/src/inspection.js +495 -0
  238. package/src/inspectionDescriptors.js +3 -0
  239. package/src/layout/canvas.js +1 -110
  240. package/src/layout/facets.js +1 -244
  241. package/src/layout/labels.js +1 -0
  242. package/src/layout/legend.js +13 -0
  243. package/src/layout/legendGroup.js +1 -0
  244. package/src/layout/legendItems.js +59 -15
  245. package/src/layout/legendLane.js +1 -441
  246. package/src/layout/legendText.js +1 -0
  247. package/src/layout/title.js +35 -13
  248. package/src/materialization/bars/aggregate.js +11 -5
  249. package/src/materialization/bars/histogram.js +4 -1
  250. package/src/materialization/bars/ranged.js +19 -6
  251. package/src/materialization/bars/resolve.js +26 -3
  252. package/src/materialization/coordinateBounds.js +8 -0
  253. package/src/materialization/dependencies.js +4 -44
  254. package/src/materialization/facetGuides/layout.js +1 -0
  255. package/src/materialization/facetGuides/placement.js +19 -8
  256. package/src/materialization/facetGuides/preparation.js +15 -7
  257. package/src/materialization/facetHeaders.js +1 -347
  258. package/src/materialization/facets.js +1 -185
  259. package/src/materialization/graphicHierarchy.js +1 -1
  260. package/src/materialization/guides/layout.js +1 -57
  261. package/src/materialization/guides/resources.js +1 -123
  262. package/src/materialization/layout.js +53 -0
  263. package/src/materialization/legendGroups.js +1 -0
  264. package/src/materialization/legendText.js +1 -0
  265. package/src/materialization/legends.js +1 -1
  266. package/src/materialization/marks/capabilities.js +5 -6
  267. package/src/materialization/rect.js +1 -1
  268. package/src/materialization/revisionIdentity.js +19 -0
  269. package/src/materialization/scaleGuideDependencies.js +1 -1
  270. package/src/materialization/scales/map.js +3 -0
  271. package/src/materialization/scales/policies/arc.js +8 -7
  272. package/src/materialization/scales/policies/bar.js +19 -7
  273. package/src/materialization/scales/policies/series.js +2 -1
  274. package/src/materialization/scales/resolve.js +72 -7
  275. package/src/materialization/selection/items/arc.js +1 -1
  276. package/src/materialization/selection/items/bar.js +7 -6
  277. package/src/materialization/selection/items/path.js +7 -6
  278. package/src/materialization/selection/items/rect.js +2 -2
  279. package/src/materialization/text.js +17 -6
  280. package/src/materialization/textLines.js +15 -0
  281. package/src/persistence/codec.js +37 -6
  282. package/src/persistence/validation.js +47 -0
  283. package/src/persistence.js +94 -8
  284. package/src/renderers/canvas/circle.js +6 -3
  285. package/src/renderers/canvas/fill.js +7 -1
  286. package/src/renderers/canvas/index.js +2 -1
  287. package/src/renderers/canvas/line.js +1 -0
  288. package/src/renderers/canvas/path.js +1 -1
  289. package/src/renderers/canvas/rect.js +11 -8
  290. package/src/renderers/canvas/stroke.js +2 -1
  291. package/src/renderers/canvas/text.js +5 -3
  292. package/src/renderers/svg.js +4 -1
  293. package/src/selectors/layers.js +19 -0
  294. package/src/theme/defaults.js +8 -2
  295. package/src/version.js +1 -1
  296. package/types/basic.d.ts +1 -0
  297. package/types/index.d.ts +7 -0
  298. package/types/inspection.d.ts +17 -0
  299. package/types/program.d.ts +494 -112
@@ -26,7 +26,15 @@ function resolveBarChannelPolicy({
26
26
  let stack;
27
27
  const xEncoding = layer.encoding?.x;
28
28
  const opposite = layer.encoding?.[channel === "x" ? "y" : "x"];
29
- const pendingBoxRange = program.markConfigs[layer.id]?.boxPlot !== undefined;
29
+ const pendingRange = program.markConfigs[layer.id]?.boxPlot !== undefined ||
30
+ layer.encoding?.[`${channel}2`] !== undefined;
31
+
32
+ if (layer.mark?.orientation !== undefined) {
33
+ if (fieldType !== "quantitative" || ["bin", "aggregate", "stack", "weight"].some(key => args[key] !== undefined)) {
34
+ throw new Error("Oriented numeric bars require raw quantitative positions without bin, aggregate, stack, or weight.");
35
+ }
36
+ return { bin, aggregate, stack };
37
+ }
30
38
 
31
39
  if (["nominal", "ordinal", "temporal"].includes(fieldType)) {
32
40
  if (args.aggregate !== undefined || args.bin !== undefined || args.stack !== undefined) {
@@ -73,11 +81,11 @@ function resolveBarChannelPolicy({
73
81
  }
74
82
  aggregate = args.aggregate ?? (
75
83
  ["nominal", "ordinal", "temporal"].includes(opposite?.fieldType) &&
76
- !pendingBoxRange
84
+ !pendingRange
77
85
  ? "mean"
78
86
  : undefined
79
87
  );
80
- if (pendingBoxRange && args.aggregate === undefined) {
88
+ if (pendingRange && args.aggregate === undefined) {
81
89
  return { bin, aggregate, stack };
82
90
  }
83
91
  stack = Object.hasOwn(args, "stack") ? args.stack : opposite === undefined ? undefined : null;
@@ -106,7 +114,8 @@ export function resolveBarPositionPolicy(context) {
106
114
  const policy = resolveBarChannelPolicy(context);
107
115
  const oppositeChannel = channel === "x" ? "y" : "x";
108
116
  const opposite = layer.encoding?.[oppositeChannel];
109
- const pendingBoxRange = program.markConfigs[layer.id]?.boxPlot !== undefined;
117
+ const pendingRange = program.markConfigs[layer.id]?.boxPlot !== undefined ||
118
+ layer.encoding?.[`${channel}2`] !== undefined;
110
119
  const candidate = {
111
120
  ...layer,
112
121
  encoding: {
@@ -118,7 +127,7 @@ export function resolveBarPositionPolicy(context) {
118
127
  // Absence of an aggregate/stack is sufficient to represent an unresolved role.
119
128
  let companion;
120
129
  if (
121
- !pendingBoxRange && resolveBarGrain(layer) === undefined &&
130
+ !pendingRange && resolveBarGrain(layer) === undefined &&
122
131
  opposite?.fieldType === "quantitative" && opposite.bin === undefined
123
132
  ) {
124
133
  const resolved = resolveBarChannelPolicy({
@@ -138,7 +147,7 @@ export function resolveBarPositionPolicy(context) {
138
147
  }
139
148
  }
140
149
  const orientation = resolveBarOrientation(candidate);
141
- if (opposite !== undefined && orientation === undefined && !pendingBoxRange) {
150
+ if (opposite !== undefined && orientation === undefined && !pendingRange) {
142
151
  throw new Error(
143
152
  `Bar ${channel} encoding requires a quantitative field opposite a categorical position.`
144
153
  );
@@ -68,6 +68,13 @@ export function resolveLinePositionPolicy({
68
68
  stack: undefined
69
69
  };
70
70
  }
71
+ if (["nominal", "ordinal"].includes(fieldType)) {
72
+ if (layer.encoding?.y !== undefined && layer.encoding.y.aggregate === undefined &&
73
+ layer.encoding.y.fieldType !== "quantitative") {
74
+ throw new Error("Categorical line x requires a quantitative value or aggregate y encoding.");
75
+ }
76
+ return emptyPositionPolicy();
77
+ }
71
78
  const directPair =
72
79
  layer.encoding?.y?.aggregate === undefined &&
73
80
  ["quantitative", "temporal"].includes(layer.encoding?.y?.fieldType) &&
@@ -82,7 +89,7 @@ export function resolveLinePositionPolicy({
82
89
  )
83
90
  ) {
84
91
  throw new Error(
85
- "Line x encoding requires a temporal field or a compatible derived quantitative field."
92
+ "Line x encoding requires a categorical or temporal field, or a compatible quantitative field."
86
93
  );
87
94
  }
88
95
  return emptyPositionPolicy();
@@ -93,15 +100,19 @@ export function resolveLinePositionPolicy({
93
100
  if (layer.encoding?.x?.bin !== undefined && args.aggregate === undefined) {
94
101
  throw new Error("Binned line x encoding requires an aggregate y encoding.");
95
102
  }
96
- const quantitativePair =
103
+ if (["nominal", "ordinal"].includes(layer.encoding?.x?.fieldType) &&
104
+ args.aggregate === undefined && fieldType !== "quantitative") {
105
+ throw new Error("Categorical line x requires a quantitative value or aggregate y encoding.");
106
+ }
107
+ const continuousPair =
97
108
  fieldType === "quantitative" &&
98
- layer.encoding?.x?.fieldType === "quantitative" &&
109
+ ["quantitative", "temporal", "nominal", "ordinal"].includes(layer.encoding?.x?.fieldType) &&
99
110
  layer.encoding.x.bin === undefined;
100
111
  const prospectiveDirect =
101
112
  args.aggregate === undefined &&
102
113
  (fieldType === "temporal" ||
103
114
  (fieldType === "quantitative" &&
104
- (layer.encoding?.x === undefined || quantitativePair)));
115
+ (layer.encoding?.x === undefined || continuousPair)));
105
116
  if (interval || (windowOutput && args.aggregate === undefined) || prospectiveDirect) {
106
117
  if (!["quantitative", "temporal"].includes(fieldType)) {
107
118
  throw new Error(
@@ -125,12 +136,12 @@ export function resolveLinePositionPolicy({
125
136
 
126
137
  if (layer.encoding?.x !== undefined) {
127
138
  const compatibleAggregateX =
128
- layer.encoding.x.fieldType === "temporal" ||
139
+ ["temporal", "nominal", "ordinal"].includes(layer.encoding.x.fieldType) ||
129
140
  (layer.encoding.x.fieldType === "quantitative" &&
130
141
  layer.encoding.x.bin !== undefined);
131
142
  if (!compatibleAggregateX) {
132
143
  throw new Error(
133
- "Aggregate line y encoding requires a temporal or binned quantitative x encoding."
144
+ "Aggregate line y encoding requires a temporal, categorical, or binned quantitative x encoding."
134
145
  );
135
146
  }
136
147
  }
@@ -13,6 +13,8 @@ import {
13
13
  validateFieldType,
14
14
  validatePositionChannel
15
15
  } from "../../../grammar/scales/index.js";
16
+ import { applyRequestedItemMissingPolicy } from
17
+ "../../../grammar/itemMissing.js";
16
18
  import { resolvePositionScaleDefinition } from "../../scales/definitions.js";
17
19
  import { findCoordinate } from "../../../selectors/coordinates.js";
18
20
  import {
@@ -92,7 +94,7 @@ function resolveCoordinate(program, channel, layer, requestedId) {
92
94
  export function resolvePositionEncoding(program, channel, args, operation) {
93
95
  validateOptions(args, POSITION_ENCODING_OPTIONS, operation);
94
96
  validatePositionChannel(channel);
95
- const { id: target, dataset, layer } = resolveTarget(
97
+ const { id: target, dataset: sourceDataset, layer } = resolveTarget(
96
98
  program,
97
99
  args.target,
98
100
  getPositionChannelDefinition(channel).markTypes
@@ -140,7 +142,8 @@ export function resolvePositionEncoding(program, channel, args, operation) {
140
142
  ? undefined
141
143
  : args.mapping ?? findSemanticScale(program, previous?.scale)?.radialMapping
142
144
  : undefined;
143
- const countRadius = mapping !== undefined && (args.aggregate ?? previous?.aggregate) === "count";
145
+ const countRadius = channel === "radius" && layer.mark.type === "arc" &&
146
+ !clearRadialMapping && (args.aggregate ?? previous?.aggregate) === "count";
144
147
  const requestedFieldType = args.fieldType ?? previous?.fieldType ?? (
145
148
  ["rule", "rect", "text", "point", "tick"].includes(layer.mark.type) && hasDatum
146
149
  ? inferDatumFieldType(args.datum, operation)
@@ -157,6 +160,9 @@ export function resolvePositionEncoding(program, channel, args, operation) {
157
160
  xEncoding?.bin !== undefined && args.field === undefined
158
161
  ? xEncoding.field
159
162
  : args.field;
163
+ const dataset = hasField
164
+ ? applyRequestedItemMissingPolicy(layer, sourceDataset, field)
165
+ : applyRequestedItemMissingPolicy(layer, sourceDataset);
160
166
  const datum = args.datum;
161
167
  const temporalUnit = resolveTemporalUnit({ ...args, ...(hasDatum ? {} : { field }) }, fieldType, previous);
162
168
  const usesField = !countRadius && (!["rule", "area", "rect", "text", "point", "tick"].includes(layer.mark.type) || hasField);
@@ -174,7 +180,7 @@ export function resolvePositionEncoding(program, channel, args, operation) {
174
180
  } else readScaleField(dataset.values, field, fieldType, { temporalUnit });
175
181
  }
176
182
  const effectiveArgs = { ...args };
177
- if (clearRadialMapping) delete effectiveArgs.mapping;
183
+ if (clearRadialMapping && args.aggregate === undefined) delete effectiveArgs.mapping;
178
184
  else if (mapping !== undefined) effectiveArgs.mapping = mapping;
179
185
  const directQuantitativeArcTheta =
180
186
  layer.mark.type === "arc" &&
@@ -14,6 +14,8 @@ import { deriveAreaSeries } from "../../grammar/areaSeries.js";
14
14
  import { resolvePositionEncoding } from "./position/resolve.js";
15
15
  import { resolveScalePreview } from "../scales/preview.js";
16
16
  import { findUpstreamTransform } from "../../materialization/dataProvenance.js";
17
+ import { applyRequestedItemMissingPolicy } from
18
+ "../../grammar/itemMissing.js";
17
19
 
18
20
  const SECONDARY_OPTIONS = Object.freeze([
19
21
  "field", "datum", "target", "fieldType", "scale", "coordinate", "temporalUnit"
@@ -46,7 +48,7 @@ function validateSecondaryField(dataset, field, fieldType, temporalUnit) {
46
48
 
47
49
  function encodeSecondaryPosition(program, channel, args, operation, types) {
48
50
  validateOptions(args, SECONDARY_OPTIONS, operation);
49
- const { id: target, dataset, layer } = resolveTarget(
51
+ const { id: target, dataset: sourceDataset, layer } = resolveTarget(
50
52
  program,
51
53
  args.target,
52
54
  types,
@@ -72,6 +74,9 @@ function encodeSecondaryPosition(program, channel, args, operation, types) {
72
74
  const rect = layer.mark.type === "rect";
73
75
  const hasField = Object.hasOwn(args, "field");
74
76
  const hasDatum = Object.hasOwn(args, "datum");
77
+ const dataset = hasField
78
+ ? applyRequestedItemMissingPolicy(layer, sourceDataset, args.field)
79
+ : applyRequestedItemMissingPolicy(layer, sourceDataset);
75
80
  if ((rule || area || rect) && hasField === hasDatum) {
76
81
  throw new Error(`${operation} requires exactly one of field or datum for a ${layer.mark.type} mark.`);
77
82
  }
@@ -122,6 +127,9 @@ function encodeSecondaryPosition(program, channel, args, operation, types) {
122
127
 
123
128
  let next = program;
124
129
  if (layer.mark.type === "bar") {
130
+ if (layer.mark.orientation !== undefined && primaryChannel !== (layer.mark.orientation === "vertical" ? "y" : "x")) {
131
+ throw new Error("Numeric bar endpoints must use the measure channel.");
132
+ }
125
133
  if (layer.layout?.mode !== undefined && layer.layout.mode !== "overlay") throw new Error("Ranged bars support only overlay layout; clear series layout before assigning endpoints.");
126
134
  for (const property of ["aggregate", "stack", "bin"]) {
127
135
  if (layer.encoding?.[primaryChannel]?.[property] !== undefined) {
@@ -205,6 +213,20 @@ function rangeAction(channel) {
205
213
  const { layer, dataset } = resolveTarget(this, args.target, ["area", "bar", "rect", "rule"], "ranged mark");
206
214
  let next = this;
207
215
  let lower = { field: args.lower }, upper = { field: args.upper };
216
+ if (layer.mark.type === "bar") {
217
+ if (layer.mark.orientation !== undefined && channel !== (layer.mark.orientation === "vertical" ? "y" : "x")) {
218
+ throw new Error("Numeric bar endpoints must use the measure channel.");
219
+ }
220
+ common.target = layer.id;
221
+ const resolved = resolvePositionEncoding(this, channel, {
222
+ ...common, ...lower,
223
+ ...(args.coordinate === undefined ? {} : { coordinate: args.coordinate }),
224
+ ...(args.scale === undefined ? {} : { scale: args.scale })
225
+ }, op);
226
+ next = setEncodingProperties(next, layer.id, `${channel}2`, {
227
+ ...upper, fieldType: common.fieldType, scale: resolved.scale.id
228
+ });
229
+ }
208
230
  if (layer.mark.type === "area") {
209
231
  common.target = layer.id;
210
232
  const prepared = prepareAreaRange(this, layer, dataset, channel, args, common);
@@ -26,6 +26,7 @@ import { findDataset } from "../../selectors/datasets.js";
26
26
  import { assertPathGroupCompatible, validatePathGroupAppearance } from "../../materialization/marks/grouping.js";
27
27
  import { assertEncodingSelectionCompatibility } from "../../materialization/selection/compatibility.js";
28
28
  import { planLegendBlockTransitions } from "../guides/legends/transition.js";
29
+ import { withGuideLayoutValidation } from "../../materialization/guides/layout.js";
29
30
 
30
31
  const OPTIONS = Object.freeze(["target", "channel"]);
31
32
  const REMOVABLE_CHANNELS = Object.freeze([
@@ -208,7 +209,7 @@ export const removeEncoding = /* @__PURE__ */ closedAction(
208
209
  op: "removeEncoding",
209
210
  description: "Remove one semantic encoding and its owned materialized state."
210
211
  }, OPTIONS,
211
- function (args = {}) {
212
+ withGuideLayoutValidation(function (args = {}) {
212
213
  if (!REMOVABLE_CHANNELS.includes(args.channel)) {
213
214
  throw new Error(
214
215
  `Unsupported removable encoding channel "${args.channel}".`
@@ -259,7 +260,7 @@ export const removeEncoding = /* @__PURE__ */ closedAction(
259
260
  next = cleanupLegends(next, layer.id, channels);
260
261
  next = cleanupPositionGuides(next, removedPositions);
261
262
  return applyDetachedScaleRematerialization(rematerializeTarget(next, layer.id), [layer]);
262
- }
263
+ })
263
264
  );
264
265
 
265
266
  export function registerEncodingRemovalAction(ProgramClass) {
@@ -14,6 +14,8 @@ import {
14
14
  import { findLayer } from "../../selectors/layers.js";
15
15
  import { validatePathSeriesAppearance } from "../../grammar/pathSeries.js";
16
16
  import { assertEncodingSelectionCompatibility } from "../../materialization/selection/compatibility.js";
17
+ import { applyRequestedItemMissingPolicy } from
18
+ "../../grammar/itemMissing.js";
17
19
 
18
20
  const WIDTH_OPTIONS = Object.freeze([
19
21
  "target", "value", "field", "fieldType", "scale"
@@ -57,12 +59,13 @@ const encodeStrokeWidth = /* @__PURE__ */ action(
57
59
  next = next.rematerializeRuleMark({ id });
58
60
  return applyDetachedScaleRematerialization(next, [layer]);
59
61
  }
60
- const { id, dataset, layer } = resolveTarget(
62
+ const { id, dataset: sourceDataset, layer } = resolveTarget(
61
63
  this,
62
64
  args.target,
63
65
  ["rule", "line"],
64
66
  "rule or line mark"
65
67
  );
68
+ const dataset = applyRequestedItemMissingPolicy(layer, sourceDataset, args.field);
66
69
  const fieldType = args.fieldType ?? "quantitative";
67
70
  if (fieldType !== "quantitative") {
68
71
  throw new Error("encodeStrokeWidth requires a quantitative field.");
@@ -48,6 +48,7 @@ export const layoutSeries = /* @__PURE__ */ action({ op: "layoutSeries", descrip
48
48
  if (grain === undefined || encoding.x?.scale === undefined || encoding.y?.scale === undefined) throw new Error("Series layout requires a complete bar.");
49
49
  if (mode === "center") throw new Error("Centered bars are not supported.");
50
50
  if (grain === BAR_GRAINS.ranged && mode !== "overlay") throw new Error("Ranged bars support only overlay layout.");
51
+ if (grain === BAR_GRAINS.centered && mode !== "overlay") throw new Error("Numeric-center bars support only overlay layout.");
51
52
  if (grain === BAR_GRAINS.aggregate) deriveBarAggregates(dataset.values, updated);
52
53
  if (mode === "group" && grain === BAR_GRAINS.aggregate) {
53
54
  if (encoding.group === undefined) throw new Error("Grouped bars require a group encoding.");
@@ -26,6 +26,8 @@ import {
26
26
  setEncodingProperties,
27
27
  validateOptions
28
28
  } from "./shared.js";
29
+ import { applyRequestedItemMissingPolicy } from
30
+ "../../grammar/itemMissing.js";
29
31
 
30
32
  const OPTIONS = Object.freeze([
31
33
  "target", "value", "field", "fieldType", "temporalUnit", "scale"
@@ -122,7 +124,7 @@ export const encodeStroke = /* @__PURE__ */ action(
122
124
  "Constant stroke does not accept fieldType, temporalUnit, or scale."
123
125
  );
124
126
  }
125
- const { id: target, dataset, layer } = resolveTarget(
127
+ const { id: target, dataset: sourceDataset, layer } = resolveTarget(
126
128
  this,
127
129
  args.target,
128
130
  MARK_TYPES,
@@ -155,6 +157,7 @@ export const encodeStroke = /* @__PURE__ */ action(
155
157
  }
156
158
 
157
159
  const fieldType = args.fieldType ?? "nominal";
160
+ const dataset = applyRequestedItemMissingPolicy(layer, sourceDataset, args.field);
158
161
  const previous = layer.encoding?.stroke;
159
162
  const temporalUnit = resolveTemporalUnit(args, fieldType, previous);
160
163
  const requestedScale = resolveReassignmentScaleOptions(
@@ -15,6 +15,8 @@ import {
15
15
  } from "./shared.js";
16
16
  import { findLayer } from "../../selectors/layers.js";
17
17
  import { validatePathSeriesAppearance } from "../../grammar/pathSeries.js";
18
+ import { applyRequestedItemMissingPolicy } from
19
+ "../../grammar/itemMissing.js";
18
20
 
19
21
  const STROKE_DASH_ENCODING_OPTIONS = Object.freeze([
20
22
  "field", "value", "target", "fieldType", "scale"
@@ -73,12 +75,15 @@ const encodeStrokeDash = /* @__PURE__ */ action(
73
75
  if (hasValue && (args.fieldType !== undefined || args.scale !== undefined)) {
74
76
  throw new Error("Constant stroke dash does not accept fieldType or scale.");
75
77
  }
76
- const { id: target, dataset, layer } = resolveTarget(
78
+ const { id: target, dataset: sourceDataset, layer } = resolveTarget(
77
79
  this,
78
80
  args.target,
79
81
  ["line", "rule"],
80
82
  "line or rule mark"
81
83
  );
84
+ const dataset = hasField
85
+ ? applyRequestedItemMissingPolicy(layer, sourceDataset, args.field)
86
+ : sourceDataset;
82
87
  if (hasValue) {
83
88
  normalizeStrokeDashPattern(args.value);
84
89
  validatePathSeriesAppearance(dataset.values, {
@@ -1,4 +1,5 @@
1
1
  import { action } from "../../core/action.js";
2
+ import { schemaField } from "../../grammar/datasetSchema.js";
2
3
  import { formatTextValue, validateTextFormat } from "../../grammar/text.js";
3
4
  import { normalizeMarkLabelContent, resolveMarkLabelValues } from "../../grammar/markLabels.js";
4
5
  import { canMaterializeText } from "../../materialization/marks/index.js";
@@ -9,14 +10,18 @@ import {
9
10
  setEncodingProperties,
10
11
  validateOptions
11
12
  } from "./shared.js";
13
+ import { applyRequestedItemMissingPolicy } from
14
+ "../../grammar/itemMissing.js";
12
15
 
13
16
  const OPTIONS = Object.freeze(["target", "field", "value", "content", "normalizeBy", "format"]);
14
17
 
15
- function validateTextField(rows, field, format) {
18
+ function validateTextField(dataset, field, format) {
19
+ const rows = dataset.values;
16
20
  if (typeof field !== "string" || field.length === 0) {
17
21
  throw new TypeError("encodeText field must be a non-empty string.");
18
22
  }
19
- let found = false;
23
+ let found = rows.length === 0 && dataset.schema !== undefined &&
24
+ schemaField(dataset.schema, field) !== undefined;
20
25
  for (const row of rows) {
21
26
  if (!Object.hasOwn(row, field)) continue;
22
27
  found = true;
@@ -41,18 +46,21 @@ export const encodeText = /* @__PURE__ */ action(
41
46
  if (Object.hasOwn(args, "normalizeBy") && (!hasContent || args.content !== "share")) {
42
47
  throw new Error("Text normalizeBy is only supported with share content.");
43
48
  }
44
- const { id: target, dataset, layer } = resolveTarget(
49
+ const { id: target, dataset: sourceDataset, layer } = resolveTarget(
45
50
  this,
46
51
  args.target,
47
52
  ["text"],
48
53
  "text mark"
49
54
  );
55
+ const dataset = hasField
56
+ ? applyRequestedItemMissingPolicy(layer, sourceDataset, args.field)
57
+ : applyRequestedItemMissingPolicy(layer, sourceDataset);
50
58
  const previous = layer.encoding?.text;
51
59
  const format = validateTextFormat(args.format ?? previous?.format ?? "auto");
52
60
  const content = hasContent
53
61
  ? normalizeMarkLabelContent(findLayer(this, layer.source), args)
54
62
  : undefined;
55
- if (hasField) validateTextField(dataset.values, args.field, format);
63
+ if (hasField) validateTextField(dataset, args.field, format);
56
64
  else if (hasValue && formatTextValue(args.value, format) === undefined) {
57
65
  throw new Error("encodeText value must produce non-empty text.");
58
66
  }
@@ -1,7 +1,7 @@
1
+ import { validateFillPaint } from "../../grammar/paint.js";
1
2
  import { editGraphicProperties } from "../primitives/graphicProperties.js";
2
3
  import { action, closedAction } from "../../core/action.js";
3
4
  import { isPlainObject } from "../../core/immutable.js";
4
- import { validateNonEmptyString } from "../../core/validation.js";
5
5
  import { DEFAULT_COLORS } from "../../theme/defaults.js";
6
6
  import { validateCurveInterpolation } from "../../grammar/curveCommands.js";
7
7
  import {
@@ -176,7 +176,7 @@ export const createErrorBand = /* @__PURE__ */ closedAction(
176
176
  const resolved = resolveErrorBand(this, args);
177
177
  const curve = validateCurveInterpolation(args.curve ?? "linear");
178
178
  const boundaries = resolveBoundaries(args.boundaries, curve);
179
- if (args.fill !== undefined) validateNonEmptyString(args.fill, "Error-band fill");
179
+ if (args.fill !== undefined) validateFillPaint(args.fill, "Error-band fill");
180
180
  let next = createResolvedIntervalData(this, resolved);
181
181
  next = next.createAreaMark({
182
182
  id: resolved.id,
@@ -1,3 +1,4 @@
1
+ import { validateFillPaint } from "../../grammar/paint.js";
1
2
  import { currentIntervalRoleArgs, resolveIntervalRoleScales, applyIntervalStatistics } from "../intervals/revision.js";
2
3
  import { closedAction } from "../../core/action.js";
3
4
  import { isPlainObject } from "../../core/immutable.js";
@@ -333,7 +334,7 @@ export const editErrorBand = /* @__PURE__ */ closedAction(
333
334
  if (owner.encoding?.color !== undefined) {
334
335
  throw new Error("editErrorBand fill conflicts with a color encoding; remove the color encoding first.");
335
336
  }
336
- errorBand.fill = validateNonEmptyString(args.fill, "Error-band fill");
337
+ errorBand.fill = validateFillPaint(args.fill, "Error-band fill");
337
338
  config.fill = errorBand.fill;
338
339
  }
339
340
  }