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
@@ -1,3 +1,5 @@
1
+ import { validateFillPaint } from "../../../grammar/paint.js";
2
+ import { validateAreaPaintConfig } from "../../../grammar/areaPaint.js";
1
3
  import { validateAreaMissing } from "../../../grammar/areaEndpoints.js";
2
4
  import { action } from "../../../core/action.js";
3
5
  import { validateUserId } from "../../../core/identifiers.js";
@@ -81,7 +83,7 @@ const createAreaMark = /* @__PURE__ */ action(
81
83
  if (missing === "break" && ["density", "horizon"].some(type => findUpstreamTransform(this, findDataset(this, data), type))) {
82
84
  throw new Error("Area missing:break does not reinterpret density or horizon data.");
83
85
  }
84
- const fill = validateNonEmptyString(args.fill ?? DEFAULT_COLORS.mark, "Area fill");
86
+ const fill = validateFillPaint(args.fill ?? DEFAULT_COLORS.mark, "Area fill");
85
87
  const opacity = validateUnitInterval(args.opacity ?? 0.2, "Area opacity");
86
88
  const curve = validateCurveInterpolation(args.curve ?? "linear");
87
89
  const { stroke, strokeWidth } = validateAreaCreateOutline(args);
@@ -127,14 +129,14 @@ const configureAreaStrokeFromFill = /* @__PURE__ */ action(
127
129
  }
128
130
  const { stroke: _stroke, ...config } = this.markConfigs[id] ?? {};
129
131
  void _stroke;
130
- return this._withMarkConfig(id, {
132
+ return this._withMarkConfig(id, validateAreaPaintConfig({
131
133
  ...config,
132
134
  strokeFromFill: true,
133
135
  strokeWidth: validateNonNegativeFinite(
134
136
  args.strokeWidth ?? 1,
135
137
  "Area strokeWidth"
136
138
  )
137
- });
139
+ }));
138
140
  }
139
141
  );
140
142
 
@@ -199,7 +201,7 @@ const rematerializeAreaMark = /* @__PURE__ */ action(
199
201
  if (args.scales !== false && strokeEncoding?.scale !== undefined) {
200
202
  resolved = resolved.rematerializeScale({ id: strokeEncoding.scale });
201
203
  }
202
- const config = this.markConfigs[id];
204
+ const config = validateAreaPaintConfig(this.markConfigs[id]);
203
205
  const { paths, fills, strokes } = resolveAreaMaterialization({
204
206
  rows: dataset.values,
205
207
  layer,
@@ -273,7 +275,7 @@ const editAreaMark = /* @__PURE__ */ action(
273
275
  let config = { ...this.markConfigs[layer.id] };
274
276
  Object.assign(config, strokeDetails);
275
277
  if (Object.hasOwn(args, "fill")) {
276
- config.fill = validateNonEmptyString(args.fill, "Area fill");
278
+ config.fill = validateFillPaint(args.fill, "Area fill");
277
279
  if (config.errorBand !== undefined) config.errorBand = { ...config.errorBand, fill: config.fill };
278
280
  }
279
281
  if (Object.hasOwn(args, "opacity")) {
@@ -314,6 +316,7 @@ const editAreaMark = /* @__PURE__ */ action(
314
316
  );
315
317
  }
316
318
 
319
+ validateAreaPaintConfig(config);
317
320
  let next = this._withMarkConfig(layer.id, config);
318
321
  if (Object.hasOwn(args, "missing")) {
319
322
  const missing = validateAreaMissing(args.missing);
@@ -6,6 +6,7 @@ import {
6
6
  } from "../../../grammar/areaSeries.js";
7
7
  import {
8
8
  mapContinuousScaleValues,
9
+ mapOrdinalPositionValues,
9
10
  mapOrdinalValues
10
11
  } from "../../../grammar/scales/index.js";
11
12
  import { mapScaleConsumerValues } from
@@ -37,9 +38,8 @@ function resolveAreaPaths({
37
38
  }
38
39
  return derived.series.map(series => {
39
40
  if (densityTransform === undefined && derived.orientation === "horizontal") {
40
- const y = mapContinuousScaleValues(
41
- series.values.map(value => value.y),
42
- yScale
41
+ const y = (["point", "band"].includes(yScale.type) ? mapOrdinalPositionValues : mapContinuousScaleValues)(
42
+ series.values.map(value => value.y), yScale
43
43
  );
44
44
  const lower = mapContinuousScaleValues(
45
45
  series.values.map(value => value.x),
@@ -57,9 +57,8 @@ function resolveAreaPaths({
57
57
  );
58
58
  }
59
59
  if (densityTransform !== undefined && derived.mode === "x-density") {
60
- const y = mapContinuousScaleValues(
61
- series.values.map(value => value.y),
62
- yScale
60
+ const y = (["point", "band"].includes(yScale.type) ? mapOrdinalPositionValues : mapContinuousScaleValues)(
61
+ series.values.map(value => value.y), yScale
63
62
  );
64
63
  const upper = mapContinuousScaleValues(
65
64
  series.values.map(value => value.x),
@@ -80,7 +79,7 @@ function resolveAreaPaths({
80
79
  { independentAxis: "y" }
81
80
  );
82
81
  }
83
- const x = mapContinuousScaleValues(
82
+ const x = (["point", "band"].includes(xScale.type) ? mapOrdinalPositionValues : mapContinuousScaleValues)(
84
83
  series.values.map(value => value.x),
85
84
  xScale
86
85
  );
@@ -134,8 +133,8 @@ export function resolveAreaMaterialization({
134
133
  layer.encoding?.y?.stack === "center";
135
134
  const rawDerived = densityTransform === undefined
136
135
  ? centered
137
- ? deriveCenteredAreaSeries(rows, layer)
138
- : deriveAreaSeries(rows, layer)
136
+ ? deriveCenteredAreaSeries(rows, layer, { xDomain: xScale.domain })
137
+ : deriveAreaSeries(rows, layer, { xDomain: xScale.domain, yDomain: yScale.domain })
139
138
  : deriveDensityAreaSeries(rows, layer, densityTransform);
140
139
  const colorEncoding = layer.encoding?.color;
141
140
  const layout = layer.layout?.mode ?? colorEncoding?.layout ?? "overlay";
@@ -10,14 +10,15 @@ import {
10
10
  } from "../shared.js";
11
11
  import { resolveMarkGraphicPlacement } from
12
12
  "../../../materialization/graphicHierarchy.js";
13
- import { requestedRectStyleDetails } from
13
+ import { requestedRectStyleDetails, RECT_RADIUS_PROPERTIES } from
14
14
  "../../../grammar/roundedRect.js";
15
15
  import { STROKE_STYLE_PROPERTIES } from
16
16
  "../../../grammar/strokeStyle.js";
17
+ import { validateItemMissing } from "../../../grammar/itemMissing.js";
17
18
 
18
19
  const CREATE_OPTIONS = Object.freeze([
19
- "id", "data", "fill", "opacity", "stroke", "strokeWidth",
20
- "cornerRadius", ...STROKE_STYLE_PROPERTIES
20
+ "id", "data", "orientation", "missing", "fill", "opacity", "stroke", "strokeWidth",
21
+ ...RECT_RADIUS_PROPERTIES, ...STROKE_STYLE_PROPERTIES
21
22
  ]);
22
23
 
23
24
  export const createBarMark = /* @__PURE__ */ action(
@@ -28,6 +29,9 @@ export const createBarMark = /* @__PURE__ */ action(
28
29
  function (args = {}) {
29
30
  validateMarkOptions(args, CREATE_OPTIONS, "createBarMark");
30
31
  requestedRectStyleDetails(args, "createBarMark");
32
+ if (args.orientation !== undefined && !["vertical", "horizontal"].includes(args.orientation)) {
33
+ throw new Error("Bar orientation must be vertical or horizontal.");
34
+ }
31
35
  const id = resolveMarkId(this, args.id, {
32
36
  defaultId: "bar",
33
37
  label: "Bar mark id",
@@ -52,6 +56,15 @@ export const createBarMark = /* @__PURE__ */ action(
52
56
  property: `layer[${id}].data`,
53
57
  value: data
54
58
  });
59
+ if (Object.hasOwn(args, "missing")) {
60
+ created = created.editSemantic({
61
+ property: `layer[${id}].mark.missing`,
62
+ value: validateItemMissing(args.missing, "Bar missing")
63
+ });
64
+ }
65
+ if (args.orientation !== undefined) {
66
+ created = created.editSemantic({ property: `layer[${id}].mark.orientation`, value: args.orientation });
67
+ }
55
68
  created = applyLayeredMarkInheritance(created, id, inherited);
56
69
  created = created
57
70
  .createGraphics({
@@ -63,7 +76,7 @@ export const createBarMark = /* @__PURE__ */ action(
63
76
  created = materializeInheritedMark(created, id);
64
77
  const appearance = Object.fromEntries(
65
78
  [
66
- "fill", "opacity", "stroke", "strokeWidth", "cornerRadius",
79
+ "fill", "opacity", "stroke", "strokeWidth", ...RECT_RADIUS_PROPERTIES,
67
80
  ...STROKE_STYLE_PROPERTIES
68
81
  ]
69
82
  .filter(property => Object.hasOwn(args, property))
@@ -9,14 +9,16 @@ import { canMaterializeBar } from "../../../materialization/marks/index.js";
9
9
  import { DEFAULT_BAR_STROKE_WIDTH } from "../../../materialization/bars/resolve.js";
10
10
  import { resolveEligibleLayer } from "../../../selectors/layers.js";
11
11
  import { validateMarkOptions } from "../shared.js";
12
- import { requestedRectStyleDetails } from
12
+ import { requestedRectStyleDetails, RECT_RADIUS_PROPERTIES } from
13
13
  "../../../grammar/roundedRect.js";
14
14
  import { STROKE_STYLE_PROPERTIES } from
15
15
  "../../../grammar/strokeStyle.js";
16
16
  import { rematerializeExistingLegend } from "../../encodings/shared.js";
17
+ import { validateFillPaint } from "../../../grammar/paint.js";
18
+ import { validateItemMissing } from "../../../grammar/itemMissing.js";
17
19
 
18
20
  const EDIT_OPTIONS = Object.freeze([
19
- "target", "fill", "opacity", "stroke", "strokeWidth", "cornerRadius",
21
+ "target", "missing", "fill", "opacity", "stroke", "strokeWidth", ...RECT_RADIUS_PROPERTIES,
20
22
  ...STROKE_STYLE_PROPERTIES
21
23
  ]);
22
24
 
@@ -29,12 +31,12 @@ export const editBarMark = /* @__PURE__ */ action(
29
31
  validateMarkOptions(args, EDIT_OPTIONS, "editBarMark");
30
32
  const styleDetails = requestedRectStyleDetails(args, "editBarMark");
31
33
  const changes = [
32
- "fill", "opacity", "stroke", "strokeWidth", "cornerRadius",
34
+ "missing", "fill", "opacity", "stroke", "strokeWidth", ...RECT_RADIUS_PROPERTIES,
33
35
  ...STROKE_STYLE_PROPERTIES
34
36
  ];
35
37
  if (!changes.some(key => Object.hasOwn(args, key))) {
36
38
  throw new Error(
37
- "editBarMark requires fill, opacity, stroke, or strokeWidth."
39
+ "editBarMark requires fill, opacity, stroke, or strokeWidth; missing is also editable."
38
40
  );
39
41
  }
40
42
  const requested = Object.hasOwn(args, "target")
@@ -55,17 +57,26 @@ export const editBarMark = /* @__PURE__ */ action(
55
57
  "editBarMark stroke conflicts with a field encoding; use encodeStroke with value to replace it."
56
58
  );
57
59
  }
60
+ if (Object.hasOwn(args, "opacity") && layer.encoding?.opacity !== undefined) {
61
+ throw new Error("editBarMark opacity conflicts with a field encoding; use encodeOpacity with value to replace it.");
62
+ }
58
63
  if (args.stroke === false && Object.hasOwn(args, "strokeWidth")) {
59
64
  throw new Error(
60
65
  "editBarMark cannot set strokeWidth while removing stroke."
61
66
  );
62
67
  }
63
68
 
64
- const config = { ...this.markConfigs[layer.id] };
69
+ const semantic = Object.hasOwn(args, "missing")
70
+ ? this.editSemantic({
71
+ property: `layer[${layer.id}].mark.missing`,
72
+ value: validateItemMissing(args.missing, "Bar missing")
73
+ })
74
+ : this;
75
+ const config = { ...semantic.markConfigs[layer.id] };
65
76
  const appearance = { ...config.barAppearance };
66
77
  Object.assign(appearance, styleDetails);
67
78
  if (Object.hasOwn(args, "fill")) {
68
- appearance.fill = validateNonEmptyString(args.fill, "Bar fill");
79
+ appearance.fill = validateFillPaint(args.fill, "Bar fill");
69
80
  }
70
81
  if (Object.hasOwn(args, "opacity")) {
71
82
  appearance.opacity = validateUnitInterval(args.opacity, "Bar opacity");
@@ -95,7 +106,7 @@ export const editBarMark = /* @__PURE__ */ action(
95
106
  );
96
107
  }
97
108
 
98
- const next = this._withMarkConfig(layer.id, {
109
+ const next = semantic._withMarkConfig(layer.id, {
99
110
  ...config,
100
111
  barAppearance: appearance
101
112
  });
@@ -13,19 +13,17 @@ import {
13
13
  import { rematerializeHighlightBaseline } from "../lifecycle.js";
14
14
  import { offsetCategoryRectangles } from
15
15
  "../../../materialization/categorySlotOffset.js";
16
- import { materializeRectItem } from "../../../grammar/roundedRect.js";
17
- import { STROKE_STYLE_PROPERTIES } from
18
- "../../../grammar/strokeStyle.js";
16
+ import { materializeRectItem, RECT_STYLE_PROPERTIES } from "../../../grammar/roundedRect.js";
19
17
  import { replaceGraphicItems } from "../../primitives/editGraphics.js";
20
18
 
21
19
  const REMATERIALIZE_OPTIONS = Object.freeze(["id", "scales"]);
22
20
 
23
21
  function editRectangles(program, id, rectangles) {
24
22
  const appearance = program.markConfigs[id]?.barAppearance ?? {};
25
- const hasShapeStyle = ["cornerRadius", ...STROKE_STYLE_PROPERTIES]
23
+ const hasShapeStyle = RECT_STYLE_PROPERTIES
26
24
  .some(property => Object.hasOwn(appearance, property));
27
25
  if (hasShapeStyle) {
28
- const radius = appearance.cornerRadius ?? 0;
26
+ const radius = appearance;
29
27
  return replaceGraphicItems(
30
28
  program,
31
29
  id,
@@ -92,6 +90,11 @@ export const rematerializeBarMark = /* @__PURE__ */ action(
92
90
  resolved = resolved.rematerializeScale({ id: strokeScaleId });
93
91
  }
94
92
 
93
+ const opacityScaleId = required.layer.encoding?.opacity?.scale;
94
+ if (opacityScaleId !== undefined && args.scales !== false) {
95
+ resolved = resolved.rematerializeScale({ id: opacityScaleId });
96
+ }
97
+
95
98
  if (required.materialization === "aggregate") {
96
99
  const offsetChannel = resolveBarOffsetChannel(required.layer);
97
100
  const offsetScaleId = required.layer.encoding?.[offsetChannel]?.scale;
@@ -104,7 +107,7 @@ export const rematerializeBarMark = /* @__PURE__ */ action(
104
107
  ));
105
108
  }
106
109
 
107
- if (required.materialization === BAR_GRAINS.ranged) {
110
+ if ([BAR_GRAINS.ranged, BAR_GRAINS.centered].includes(required.materialization)) {
108
111
  const width = resolved.markConfigs[id]?.barWidth;
109
112
  return editRectangles(resolved, id, offsetCategoryRectangles(
110
113
  resolved, required.layer, deriveRangedRectangles(required, resolved, width)
@@ -18,7 +18,7 @@ import {
18
18
  import { DEFAULT_COLORS } from "../../../theme/defaults.js";
19
19
  import { findDataset } from "../../../selectors/datasets.js";
20
20
  import { findLayer, resolveEligibleLayer } from "../../../selectors/layers.js";
21
- import { validateCurveInterpolation } from "../../../grammar/curveCommands.js";
21
+ import { validateCurveInterpolation, validateCardinalTension } from "../../../grammar/curveCommands.js";
22
22
  import {
23
23
  resolveCoordinateBounds,
24
24
  resolveCoordinatePolarFrame
@@ -43,11 +43,11 @@ import {
43
43
  const DEFAULT_LINE_STROKE = DEFAULT_COLORS.mark;
44
44
  const DEFAULT_LINE_WIDTH = 2;
45
45
  const CREATE_OPTIONS = Object.freeze([
46
- "id", "data", "stroke", "strokeWidth", "opacity", "curve", "closed",
46
+ "id", "data", "stroke", "strokeWidth", "opacity", "curve", "tension", "closed",
47
47
  ...STROKE_STYLE_PROPERTIES
48
48
  ]);
49
49
  const EDIT_OPTIONS = Object.freeze([
50
- "target", "stroke", "strokeWidth", "opacity", "curve", "closed",
50
+ "target", "stroke", "strokeWidth", "opacity", "curve", "tension", "closed",
51
51
  ...STROKE_STYLE_PROPERTIES
52
52
  ]);
53
53
  const REMATERIALIZE_OPTIONS = Object.freeze(["id", "scales"]);
@@ -118,6 +118,7 @@ const createLineMark = /* @__PURE__ */ action(
118
118
  "Line strokeWidth"
119
119
  );
120
120
  const curve = validateCurveInterpolation(args.curve ?? "linear");
121
+ validateCardinalTension(args.tension, curve);
121
122
  const closed = Object.hasOwn(args, "closed")
122
123
  ? validateClosed(args.closed)
123
124
  : false;
@@ -151,6 +152,7 @@ const createLineMark = /* @__PURE__ */ action(
151
152
  {
152
153
  ...(Object.hasOwn(args, "strokeWidth") ? { strokeWidth } : {}),
153
154
  ...(Object.hasOwn(args, "curve") ? { curve } : {}),
155
+ ...(args.tension === undefined ? {} : { tension: args.tension }),
154
156
  ...(Object.hasOwn(args, "closed") ? { closed } : {}),
155
157
  ...strokeDetails
156
158
  }
@@ -277,7 +279,7 @@ const editLineMark = /* @__PURE__ */ action(
277
279
  validateMarkOptions(args, EDIT_OPTIONS, "editLineMark");
278
280
  const strokeDetails = requestedStrokeDetails(args, "editLineMark");
279
281
  if (!EDIT_OPTIONS.slice(1).some(key => Object.hasOwn(args, key))) {
280
- throw new Error("editLineMark requires stroke, strokeWidth, opacity, curve, or closed.");
282
+ throw new Error("editLineMark requires stroke, strokeWidth, opacity, curve, tension, or closed.");
281
283
  }
282
284
  const target = Object.hasOwn(args, "target")
283
285
  ? validateUserId(args.target, "Line mark id")
@@ -315,6 +317,11 @@ const editLineMark = /* @__PURE__ */ action(
315
317
  })) {
316
318
  if (Object.hasOwn(args, key)) config[key] = validate(args[key], `Line ${key}`);
317
319
  }
320
+ if (args.curve !== undefined && args.curve !== "cardinal" && args.tension === undefined) {
321
+ delete config.tension;
322
+ }
323
+ if (Object.hasOwn(args, "tension")) config.tension = args.tension;
324
+ validateCardinalTension(config.tension, config.curve ?? "linear");
318
325
  validatePolarLineConfig(layer, config);
319
326
  const next = this._withMarkConfig(layer.id, config);
320
327
  const materialized = canMaterializeLine(next, layer)
@@ -3,6 +3,7 @@ import {
3
3
  } from "../../../grammar/lineSeries.js";
4
4
  import {
5
5
  mapContinuousScaleValues,
6
+ mapOrdinalPositionValues,
6
7
  normalizeStrokeDashPattern
7
8
  } from "../../../grammar/scales/index.js";
8
9
  import { buildCurvePathCommands } from
@@ -97,9 +98,7 @@ export function resolvePositionedLineMaterialization({
97
98
  const radiusScaleId = layer.encoding?.radius?.scale;
98
99
  const lineOptions = polar
99
100
  ? { thetaDomain: resolvedScales[thetaScaleId].domain }
100
- : layer.encoding?.x?.bin === undefined
101
- ? undefined
102
- : { xBinBoundaries };
101
+ : { xBinBoundaries, xDomain: resolvedScales[xScaleId].domain };
103
102
  const derived = deriveLineSeries(rows, layer, lineOptions);
104
103
  const commands = polar
105
104
  ? derived.series.map(series => buildPolarLinePathCommands({
@@ -111,7 +110,9 @@ export function resolvePositionedLineMaterialization({
111
110
  closed: config.closed ?? false
112
111
  }))
113
112
  : derived.series.map(series => {
114
- const x = mapContinuousScaleValues(
113
+ const mapX = ["nominal", "ordinal"].includes(layer.encoding.x.fieldType)
114
+ ? mapOrdinalPositionValues : mapContinuousScaleValues;
115
+ const x = mapX(
115
116
  series.values.map(value => value.x),
116
117
  resolvedScales[xScaleId]
117
118
  );
@@ -119,9 +120,12 @@ export function resolvePositionedLineMaterialization({
119
120
  series.values.map(value => value.y),
120
121
  resolvedScales[yScaleId]
121
122
  );
123
+ // A raw singleton retains its data position and series identity, but
124
+ // contributes no segment. Do not fabricate another observation.
125
+ if (series.values.length === 1) return [{ op: "M", x: x[0], y: y[0] }];
122
126
  return buildCurvePathCommands(
123
127
  series.values.map((_, index) => ({ x: x[index], y: y[index] })),
124
- config.curve ?? "linear"
128
+ config.curve ?? "linear", config.tension
125
129
  );
126
130
  });
127
131
  const appearance = appearanceMapper(layer, resolvedScales,
@@ -16,9 +16,10 @@ import {
16
16
  resolveMarkId,
17
17
  validateMarkOptions
18
18
  } from "../shared.js";
19
+ import { validateItemMissing } from "../../../grammar/itemMissing.js";
19
20
 
20
21
  const OPTIONS = Object.freeze([
21
- "id", "data", "shape", "fill", "opacity", "stroke", "strokeWidth",
22
+ "id", "data", "missing", "shape", "fill", "opacity", "stroke", "strokeWidth",
22
23
  ...STROKE_STYLE_PROPERTIES
23
24
  ]);
24
25
 
@@ -61,6 +62,12 @@ export const createPointMark = /* @__PURE__ */ action(
61
62
  property: `layer[${id}].data`,
62
63
  value: data
63
64
  });
65
+ if (Object.hasOwn(args, "missing")) {
66
+ next = next.editSemantic({
67
+ property: `layer[${id}].mark.missing`,
68
+ value: validateItemMissing(args.missing, "Point missing")
69
+ });
70
+ }
64
71
  next = applyLayeredMarkInheritance(next, id, inherited)
65
72
  .createGraphics({
66
73
  id,
@@ -13,9 +13,10 @@ import {
13
13
  import { resolveEligibleLayer } from "../../../selectors/layers.js";
14
14
  import { rematerializeExistingLegend } from "../../encodings/shared.js";
15
15
  import { validateMarkOptions } from "../shared.js";
16
+ import { validateItemMissing } from "../../../grammar/itemMissing.js";
16
17
 
17
18
  const OPTIONS = Object.freeze([
18
- "target", "shape", "fill", "opacity", "stroke", "strokeWidth",
19
+ "target", "missing", "shape", "fill", "opacity", "stroke", "strokeWidth",
19
20
  ...STROKE_STYLE_PROPERTIES
20
21
  ]);
21
22
 
@@ -28,12 +29,12 @@ export const editPointMark = /* @__PURE__ */ action(
28
29
  validateMarkOptions(args, OPTIONS, "editPointMark");
29
30
  const strokeDetails = requestedStrokeDetails(args, "editPointMark");
30
31
  const editable = [
31
- "shape", "fill", "opacity", "stroke", "strokeWidth",
32
+ "missing", "shape", "fill", "opacity", "stroke", "strokeWidth",
32
33
  ...STROKE_STYLE_PROPERTIES
33
34
  ];
34
35
  if (!editable.some(property => Object.hasOwn(args, property))) {
35
36
  throw new Error(
36
- "editPointMark requires shape, fill, opacity, stroke, or strokeWidth."
37
+ "editPointMark requires shape, fill, opacity, stroke, strokeWidth, or missing."
37
38
  );
38
39
  }
39
40
  if (args.target !== undefined) validateUserId(args.target, "Point mark id");
@@ -61,7 +62,13 @@ export const editPointMark = /* @__PURE__ */ action(
61
62
  if (Object.hasOwn(args, "opacity") && layer.encoding?.opacity?.field !== undefined) {
62
63
  throw new Error("editPointMark opacity conflicts with a field encoding; use encodeOpacity with value to replace it.");
63
64
  }
64
- const config = { ...this.markConfigs[id] };
65
+ const semantic = Object.hasOwn(args, "missing")
66
+ ? this.editSemantic({
67
+ property: `layer[${id}].mark.missing`,
68
+ value: validateItemMissing(args.missing, "Point missing")
69
+ })
70
+ : this;
71
+ const config = { ...semantic.markConfigs[id] };
65
72
  Object.assign(config, strokeDetails);
66
73
  if (Object.hasOwn(args, "shape")) {
67
74
  config.shape = validatePointShape(args.shape);
@@ -89,7 +96,7 @@ export const editPointMark = /* @__PURE__ */ action(
89
96
  }
90
97
  }
91
98
  if (Object.hasOwn(args, "strokeWidth")) {
92
- if (typeof config.stroke !== "string") {
99
+ if (typeof config.stroke !== "string" && layer.encoding?.stroke?.field === undefined) {
93
100
  throw new Error("Point strokeWidth requires an active stroke.");
94
101
  }
95
102
  config.strokeWidth = validateNonNegativeFinite(
@@ -97,7 +104,7 @@ export const editPointMark = /* @__PURE__ */ action(
97
104
  "Point strokeWidth"
98
105
  );
99
106
  }
100
- const next = this
107
+ const next = semantic
101
108
  ._withMarkConfig(id, config)
102
109
  .rematerializePointMark({ id });
103
110
  return rematerializeExistingLegend(next);
@@ -40,6 +40,7 @@ import { offsetCategoryPositions } from
40
40
  "../../../materialization/categorySlotOffset.js";
41
41
  import { requestedStrokeDetails } from
42
42
  "../../../grammar/strokeStyle.js";
43
+ import { applyItemMissingPolicy } from "../../../grammar/itemMissing.js";
43
44
 
44
45
  const REMATERIALIZE_OPTIONS = Object.freeze(["id"]);
45
46
  const DEFAULT_POINT_FILL = DEFAULT_COLORS.mark;
@@ -337,10 +338,11 @@ export const rematerializePointMark = /* @__PURE__ */ action(
337
338
  if (!["circle", "rect", "path", "collection"].includes(graphic?.type)) {
338
339
  throw new Error(`Point mark "${id}" requires point graphics.`);
339
340
  }
340
- const dataset = findDataset(resolved, layer.data);
341
- if (dataset === undefined) {
341
+ const sourceDataset = findDataset(resolved, layer.data);
342
+ if (sourceDataset === undefined) {
342
343
  throw new Error(`Point mark "${id}" requires an existing dataset.`);
343
344
  }
345
+ const dataset = applyItemMissingPolicy(layer, sourceDataset);
344
346
 
345
347
  if (graphic.items.length === 0 && !canMaterializePoint(resolved, layer)) {
346
348
  return resolved;
@@ -25,17 +25,19 @@ import { resolveMarkGraphicPlacement } from
25
25
  import { rematerializeExistingLegend } from "../../encodings/shared.js";
26
26
  import { rematerializeHighlightBaseline } from "../lifecycle.js";
27
27
  import {
28
- requestedRectStyleDetails
28
+ requestedRectStyleDetails, hasRectRadius, RECT_RADIUS_PROPERTIES
29
29
  } from "../../../grammar/roundedRect.js";
30
30
  import { STROKE_STYLE_PROPERTIES } from
31
31
  "../../../grammar/strokeStyle.js";
32
+ import { applyItemMissingPolicy, validateItemMissing } from
33
+ "../../../grammar/itemMissing.js";
32
34
 
33
35
  const STYLE_OPTIONS = Object.freeze([
34
- "fill", "opacity", "stroke", "strokeWidth", "cornerRadius",
36
+ "fill", "opacity", "stroke", "strokeWidth", ...RECT_RADIUS_PROPERTIES,
35
37
  ...STROKE_STYLE_PROPERTIES
36
38
  ]);
37
- const CREATE_OPTIONS = Object.freeze(["id", "data", ...STYLE_OPTIONS]);
38
- const EDIT_OPTIONS = Object.freeze(["target", ...STYLE_OPTIONS]);
39
+ const CREATE_OPTIONS = Object.freeze(["id", "data", "missing", ...STYLE_OPTIONS]);
40
+ const EDIT_OPTIONS = Object.freeze(["target", "missing", ...STYLE_OPTIONS]);
39
41
  const REMATERIALIZE_OPTIONS = Object.freeze(["id", "scales"]);
40
42
 
41
43
  function requireRectLayer(program, requested, operation) {
@@ -71,6 +73,12 @@ const createRectMark = /* @__PURE__ */ action(
71
73
  let next = this
72
74
  .editSemantic({ property: `layer[${id}].mark.type`, value: "rect" })
73
75
  .editSemantic({ property: `layer[${id}].data`, value: data });
76
+ if (Object.hasOwn(args, "missing")) {
77
+ next = next.editSemantic({
78
+ property: `layer[${id}].mark.missing`,
79
+ value: validateItemMissing(args.missing, "Rect missing")
80
+ });
81
+ }
74
82
  next = applyLayeredMarkInheritance(next, id, inherited)
75
83
  .createGraphics({
76
84
  id,
@@ -109,7 +117,7 @@ const rematerializeRectMark = /* @__PURE__ */ action(
109
117
  const graphic = this.graphicSpec.objects[id];
110
118
  if (layer?.mark?.type !== "rect") throw new Error(`Unknown rect mark "${id}".`);
111
119
  const roundedCollection = graphic?.type === "collection" &&
112
- Object.hasOwn(this.markConfigs[id] ?? {}, "cornerRadius") &&
120
+ hasRectRadius(this.markConfigs[id]) &&
113
121
  graphic.items?.every(item => ["rect", "path"].includes(item.type));
114
122
  if (
115
123
  (graphic?.type !== "rect" && !roundedCollection) ||
@@ -120,10 +128,11 @@ const rematerializeRectMark = /* @__PURE__ */ action(
120
128
  if (this.markConfigs[id]?.gradientPlot?.materialized === true) {
121
129
  return this.materializeGradientPlotFill({ id });
122
130
  }
123
- const dataset = findDataset(this, layer.data);
124
- if (dataset === undefined) {
131
+ const sourceDataset = findDataset(this, layer.data);
132
+ if (sourceDataset === undefined) {
125
133
  throw new Error(`Rect mark "${id}" requires an existing dataset.`);
126
134
  }
135
+ const dataset = applyItemMissingPolicy(layer, sourceDataset);
127
136
  if (!canMaterializeRect(this, layer)) {
128
137
  return replaceGraphicItems(this, id, "rect", []);
129
138
  }
@@ -151,7 +160,7 @@ const editRectMark = /* @__PURE__ */ action(
151
160
  function (args = {}) {
152
161
  validateMarkOptions(args, EDIT_OPTIONS, "editRectMark");
153
162
  requestedRectStyleDetails(args, "editRectMark");
154
- if (!STYLE_OPTIONS.some(option => Object.hasOwn(args, option))) {
163
+ if (!["missing", ...STYLE_OPTIONS].some(option => Object.hasOwn(args, option))) {
155
164
  throw new Error("editRectMark requires at least one editable property.");
156
165
  }
157
166
  const layer = requireRectLayer(this, args.target, "editRectMark");
@@ -163,16 +172,22 @@ const editRectMark = /* @__PURE__ */ action(
163
172
  "editRectMark stroke conflicts with a field encoding; use encodeStroke with value to replace it."
164
173
  );
165
174
  }
166
- const next = this._withMarkConfig(
175
+ const semantic = Object.hasOwn(args, "missing")
176
+ ? this.editSemantic({
177
+ property: `layer[${layer.id}].mark.missing`,
178
+ value: validateItemMissing(args.missing, "Rect missing")
179
+ })
180
+ : this;
181
+ const next = semantic._withMarkConfig(
167
182
  layer.id,
168
183
  {
169
184
  ...normalizeRectMarkConfig(
170
185
  args,
171
- this.markConfigs[layer.id] ?? DEFAULT_RECT_MARK
186
+ semantic.markConfigs[layer.id] ?? DEFAULT_RECT_MARK
172
187
  ),
173
188
  fillExplicit: Object.hasOwn(args, "fill")
174
189
  ? true
175
- : this.markConfigs[layer.id]?.fillExplicit ?? false
190
+ : semantic.markConfigs[layer.id]?.fillExplicit ?? false
176
191
  }
177
192
  );
178
193
  const materialized = canMaterializeRect(next, layer)
@@ -163,6 +163,7 @@ export function removeOwnedMark(program, id, partial = false) {
163
163
  next = next.editGraphics({ target: id, remove: true });
164
164
  }
165
165
  return next
166
+ ._withoutMaterializationConfig(["guides", "legendGroups", id])
166
167
  ._withoutMaterializationConfig(["marks", id])
167
168
  ._withContext({
168
169
  ...(program.context.currentMark === id ? { currentMark: undefined } : {}),
@@ -262,6 +263,7 @@ export const removeMark = /* @__PURE__ */ closedAction(
262
263
  if (next.graphicSpec.objects[id] !== undefined) {
263
264
  next = next.editGraphics({ target: id, remove: true });
264
265
  }
266
+ next = next._withoutMaterializationConfig(["guides", "legendGroups", id]);
265
267
  next = next._withoutMaterializationConfig(["marks", id]);
266
268
  next = next._withoutMaterializationConfig(["jitters", id]);
267
269
  next = next._withoutMaterializationConfig(["pointPacking", id]);