ggaction 0.0.20 → 0.0.22

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 (254) hide show
  1. package/CHANGELOG.md +77 -0
  2. package/README.md +3 -2
  3. package/knowledge/action-cards.json +1 -1
  4. package/knowledge/intent-taxonomy.json +1 -1
  5. package/knowledge/mcp-resources.json +1 -1
  6. package/package.json +6 -2
  7. package/src/BasicChartProgram.js +1 -1
  8. package/src/accessibility.js +1 -0
  9. package/src/actions/boxPlots/components.js +1 -96
  10. package/src/actions/boxPlots/create.js +6 -1
  11. package/src/actions/boxPlots/edit.js +8 -0
  12. package/src/actions/boxPlots/materialize.js +3 -0
  13. package/src/actions/boxPlots/options.js +1 -1
  14. package/src/actions/canvas/actions.js +2 -1
  15. package/src/actions/canvas/index.js +3 -0
  16. package/src/actions/canvas/plotBorder.js +2 -1
  17. package/src/actions/canvas/plotOrder.js +14 -0
  18. package/src/actions/charts/area.js +1 -1
  19. package/src/actions/charts/bar.js +4 -4
  20. package/src/actions/charts/density.js +1 -1
  21. package/src/actions/charts/ecdf.js +1 -1
  22. package/src/actions/charts/endpoints.js +54 -5
  23. package/src/actions/charts/heatmap.js +1 -1
  24. package/src/actions/charts/histogram.js +1 -1
  25. package/src/actions/charts/horizon.js +1 -1
  26. package/src/actions/charts/index.js +2 -1
  27. package/src/actions/charts/interval-regression.js +1 -1
  28. package/src/actions/charts/line.js +1 -1
  29. package/src/actions/charts/radar.js +63 -3
  30. package/src/actions/charts/raincloud.js +37 -6
  31. package/src/actions/charts/rug-strip.js +2 -2
  32. package/src/actions/charts/scatter.js +3 -3
  33. package/src/actions/charts/text.js +1 -1
  34. package/src/actions/composition/actions.js +10 -3
  35. package/src/actions/coordinates/actions.js +2 -0
  36. package/src/actions/data/basic.js +6 -0
  37. package/src/actions/data/derived.js +6 -2
  38. package/src/actions/data/edit.js +21 -35
  39. package/src/actions/data/filter.js +1 -376
  40. package/src/actions/data/flatten.js +19 -0
  41. package/src/actions/data/index.js +6 -0
  42. package/src/actions/data/lookup.js +50 -0
  43. package/src/actions/data/raincloudFilter.js +56 -0
  44. package/src/actions/data/shared.js +10 -0
  45. package/src/actions/distributions/revision.js +3 -1
  46. package/src/actions/encodings/angle.js +2 -1
  47. package/src/actions/encodings/appearance.js +38 -13
  48. package/src/actions/encodings/channels.js +14 -3
  49. package/src/actions/encodings/color/continuous.js +26 -10
  50. package/src/actions/encodings/color/index.js +50 -20
  51. package/src/actions/encodings/color/policy.js +57 -4
  52. package/src/actions/encodings/parallel.js +4 -0
  53. package/src/actions/encodings/position/policies/bar.js +10 -6
  54. package/src/actions/encodings/position/policies/line.js +1 -152
  55. package/src/actions/encodings/position/resolve.js +4 -1
  56. package/src/actions/encodings/ranged.js +46 -0
  57. package/src/actions/encodings/remove.js +1 -1
  58. package/src/actions/encodings/ruleAppearance.js +7 -6
  59. package/src/actions/encodings/shared.js +14 -2
  60. package/src/actions/encodings/stroke.js +20 -14
  61. package/src/actions/encodings/strokeDash.js +9 -11
  62. package/src/actions/encodings/text.js +30 -2
  63. package/src/actions/errorBars/edit.js +24 -4
  64. package/src/actions/facets/actions.js +1 -793
  65. package/src/actions/facets/derive.js +34 -5
  66. package/src/actions/facets/replay.js +1 -38
  67. package/src/actions/gradientPlots/components.js +21 -9
  68. package/src/actions/gradientPlots/create.js +4 -2
  69. package/src/actions/gradientPlots/edit.js +18 -1
  70. package/src/actions/gradientPlots/materialize.js +35 -36
  71. package/src/actions/gradientPlots/options.js +1 -1
  72. package/src/actions/gradientPlots/paint.js +13 -7
  73. package/src/actions/guides/applicability.js +2 -0
  74. package/src/actions/guides/axes/axes.js +39 -0
  75. package/src/actions/guides/axes/axis.js +9 -5
  76. package/src/actions/guides/axes/edit.js +11 -8
  77. package/src/actions/guides/axes/labels.js +24 -8
  78. package/src/actions/guides/axes/parallel/policy.js +3 -5
  79. package/src/actions/guides/axes/parallel/resolve.js +4 -3
  80. package/src/actions/guides/axes/policy.js +22 -0
  81. package/src/actions/guides/axes/tickGroups.js +4 -2
  82. package/src/actions/guides/axes/ticks.js +22 -4
  83. package/src/actions/guides/axes/titles.js +19 -6
  84. package/src/actions/guides/facadeAxes.js +3 -0
  85. package/src/actions/guides/grids/grid.js +3 -1
  86. package/src/actions/guides/grids/resolve.js +7 -11
  87. package/src/actions/guides/guides.js +21 -1
  88. package/src/actions/guides/legends/categorical/actions.js +1 -515
  89. package/src/actions/guides/legends/categorical/layout.js +5 -2
  90. package/src/actions/guides/legends/categorical/options.js +4 -0
  91. package/src/actions/guides/legends/categorical/resolve.js +8 -3
  92. package/src/actions/guides/legends/categorical/symbols.js +3 -3
  93. package/src/actions/guides/legends/colorSize.js +2 -0
  94. package/src/actions/guides/legends/continuous/common.js +1 -1
  95. package/src/actions/guides/legends/continuous/gradient.js +1 -360
  96. package/src/actions/guides/legends/continuous/interval.js +1 -1
  97. package/src/actions/guides/legends/continuous/opacity.js +15 -1
  98. package/src/actions/guides/legends/edit.js +1 -1
  99. package/src/actions/guides/legends/focused.js +1 -1
  100. package/src/actions/guides/legends/lane.js +1 -1
  101. package/src/actions/guides/legends/sampling.js +1 -140
  102. package/src/actions/guides/legends/strokeWidth.js +3 -2
  103. package/src/actions/guides/legends/transition.js +19 -2
  104. package/src/actions/guides/polar/axes/facade.js +5 -4
  105. package/src/actions/guides/polar/axes/labels.js +5 -4
  106. package/src/actions/guides/polar/axes/titles.js +5 -4
  107. package/src/actions/guides/polar/resolve.js +2 -1
  108. package/src/actions/guides/tickValues.js +7 -7
  109. package/src/actions/marks/arc/actions.js +5 -7
  110. package/src/actions/marks/area/actions.js +9 -3
  111. package/src/actions/marks/bar/create.js +4 -2
  112. package/src/actions/marks/bar/edit.js +9 -6
  113. package/src/actions/marks/bar/materialize.js +5 -3
  114. package/src/actions/marks/dependentReferences.js +87 -0
  115. package/src/actions/marks/index.js +2 -0
  116. package/src/actions/marks/lifecycle.js +2 -1
  117. package/src/actions/marks/line/actions.js +24 -6
  118. package/src/actions/marks/line/materialize.js +7 -3
  119. package/src/actions/marks/point/create.js +5 -4
  120. package/src/actions/marks/point/edit.js +6 -3
  121. package/src/actions/marks/point/jitter.js +6 -6
  122. package/src/actions/marks/point/materialize.js +36 -39
  123. package/src/actions/marks/rect/actions.js +16 -4
  124. package/src/actions/marks/references.js +183 -35
  125. package/src/actions/marks/rule/actions.js +20 -9
  126. package/src/actions/marks/shared.js +2 -1
  127. package/src/actions/marks/text/actions.js +10 -3
  128. package/src/actions/marks/tick/actions.js +11 -2
  129. package/src/actions/primitives/editGraphics.js +28 -3
  130. package/src/actions/primitives/index.js +9 -0
  131. package/src/actions/primitives/semanticAction.js +13 -7
  132. package/src/actions/primitives/semanticValidation/layer.js +20 -0
  133. package/src/actions/primitives/semanticValidation/scale.js +8 -1
  134. package/src/actions/scales/channels.js +1 -1
  135. package/src/actions/scales/consumers/common.js +19 -1
  136. package/src/actions/scales/consumers/families.js +2 -2
  137. package/src/actions/scales/consumers/seriesLayout.js +4 -3
  138. package/src/actions/scales/edit.js +11 -0
  139. package/src/actions/scales/editPolicy.js +5 -3
  140. package/src/actions/scales/preview.js +2 -1
  141. package/src/actions/scales/quantitativeColor.js +2 -4
  142. package/src/actions/selection/actions.js +10 -1
  143. package/src/actions/theme/reconcile.js +37 -9
  144. package/src/core/compositionState.js +21 -4
  145. package/src/core/datasetReferences.js +17 -0
  146. package/src/core/immutable.js +1 -0
  147. package/src/core/resourceReferences.js +5 -3
  148. package/src/core/validation.js +4 -0
  149. package/src/core/vocabulary.js +24 -2
  150. package/src/grammar/aggregate.js +1 -0
  151. package/src/grammar/arcs.js +1 -1
  152. package/src/grammar/areaSeries.js +1 -1
  153. package/src/grammar/axisTextPlacement.js +1 -1
  154. package/src/grammar/bars/policy.js +2 -2
  155. package/src/grammar/datasetDependencies.js +97 -0
  156. package/src/grammar/datasetSchema.js +31 -1
  157. package/src/grammar/facetHeaders.js +1 -0
  158. package/src/grammar/facets/dependencies.js +6 -2
  159. package/src/grammar/facets/legendIntent.js +6 -2
  160. package/src/grammar/facets/scales.js +27 -7
  161. package/src/grammar/flatten.js +85 -0
  162. package/src/grammar/histogram.js +13 -1
  163. package/src/grammar/horizon.js +1 -494
  164. package/src/grammar/interval.js +4 -3
  165. package/src/grammar/jitter.js +2 -2
  166. package/src/grammar/legendSpacing.js +45 -0
  167. package/src/grammar/lineMissing.js +90 -0
  168. package/src/grammar/lineSeries.js +1 -376
  169. package/src/grammar/lookup.js +97 -0
  170. package/src/grammar/markFilter.js +4 -2
  171. package/src/grammar/markLabels.js +8 -3
  172. package/src/grammar/numeric.js +1 -1
  173. package/src/grammar/pathCommands.js +1 -1
  174. package/src/grammar/pathSeries.js +1 -109
  175. package/src/grammar/pointPolygon.js +81 -0
  176. package/src/grammar/pointShapes.js +15 -15
  177. package/src/grammar/rects.js +1 -1
  178. package/src/grammar/regression/derive.js +1 -195
  179. package/src/grammar/scales/appearance.js +1 -1
  180. package/src/grammar/scales/color.js +1 -1
  181. package/src/grammar/scales/colorConsumers.js +1 -63
  182. package/src/grammar/scales/continuous.js +58 -1
  183. package/src/grammar/scales/definition.js +1 -1
  184. package/src/grammar/scales/ordinal.js +5 -2
  185. package/src/grammar/scales/policies.js +3 -3
  186. package/src/grammar/schemas/graphicBounds.js +40 -12
  187. package/src/grammar/schemas/graphicClip.js +23 -0
  188. package/src/grammar/schemas/graphicTree.js +16 -0
  189. package/src/grammar/statisticalReference.js +22 -0
  190. package/src/grammar/text.js +65 -1
  191. package/src/grammar/ticks.js +92 -1
  192. package/src/grammar/transformTopology.js +3 -1
  193. package/src/grammar/transforms.js +24 -0
  194. package/src/inspection.js +1 -495
  195. package/src/inspectionDescriptors.js +1 -1
  196. package/src/layout/canvas.js +1 -1
  197. package/src/layout/facetContentFlow.js +101 -0
  198. package/src/layout/facetHeaderTitles.js +1 -0
  199. package/src/layout/facetTopLane.js +29 -0
  200. package/src/layout/facets.js +1 -1
  201. package/src/layout/legendGroup.js +1 -1
  202. package/src/layout/legendItems.js +26 -10
  203. package/src/layout/legendSpacing.js +66 -0
  204. package/src/layout/plotDrawOrder.js +17 -0
  205. package/src/materialization/bars/histogram.js +8 -2
  206. package/src/materialization/bars/resolve.js +1 -1
  207. package/src/materialization/composition.js +1 -0
  208. package/src/materialization/compositionSnapshot.js +22 -2
  209. package/src/materialization/dataProvenance.js +1 -1
  210. package/src/materialization/facetGuides/layout.js +1 -1
  211. package/src/materialization/facetGuides/placement.js +23 -11
  212. package/src/materialization/facetGuides/preparation.js +43 -24
  213. package/src/materialization/facetHeaders.js +1 -1
  214. package/src/materialization/facetTopLane.js +25 -0
  215. package/src/materialization/facets.js +1 -1
  216. package/src/materialization/graphicHierarchy.js +5 -0
  217. package/src/materialization/graphicTranslation.js +2 -0
  218. package/src/materialization/guides/layout.js +1 -1
  219. package/src/materialization/guides/resources.js +1 -1
  220. package/src/materialization/legends.js +1 -1
  221. package/src/materialization/markClip.js +26 -0
  222. package/src/materialization/marks/capabilities.js +3 -2
  223. package/src/materialization/marks/grouping.js +7 -5
  224. package/src/materialization/marks/index.js +4 -2
  225. package/src/materialization/marks/policies.js +4 -0
  226. package/src/materialization/plotDrawOrder.js +44 -0
  227. package/src/materialization/rect.js +11 -3
  228. package/src/materialization/scaleGuideDependencies.js +13 -5
  229. package/src/materialization/selection/items/common.js +1 -1
  230. package/src/materialization/selection/items/path.js +1 -0
  231. package/src/materialization/selection/policies/index.js +2 -1
  232. package/src/materialization/text.js +10 -5
  233. package/src/mcp/cli.js +0 -0
  234. package/src/persistence/validation.js +108 -18
  235. package/src/persistence.js +4 -2
  236. package/src/renderers/canvas/index.js +16 -0
  237. package/src/renderers/canvas/isolated.js +11 -1
  238. package/src/renderers/canvas/native.js +7 -0
  239. package/src/renderers/canvas/scene.js +66 -0
  240. package/src/renderers/canvas/text.js +3 -2
  241. package/src/renderers/svg.js +1 -617
  242. package/src/renderers/svgSerialization.js +643 -0
  243. package/src/renderers/transitionScene.js +67 -0
  244. package/src/scene.js +89 -0
  245. package/src/selectors/axes.js +1 -1
  246. package/src/selectors/datasets.js +1 -0
  247. package/src/selectors/grids.js +9 -0
  248. package/src/selectors/markClip.js +28 -0
  249. package/src/selectors/raincloud.js +7 -0
  250. package/src/version.js +1 -1
  251. package/types/basic.d.ts +7 -0
  252. package/types/index.d.ts +8 -0
  253. package/types/program.d.ts +270 -60
  254. package/types/scene.d.ts +35 -0
@@ -2,6 +2,7 @@ import { action } from "../../core/action.js";
2
2
  import { noOptions } from "../../core/validation.js";
3
3
  import { resolveGraphicBounds } from "../../layout/canvas.js";
4
4
  import { resolvePlotGraphicPlacement } from "../../materialization/graphicHierarchy.js";
5
+ import { hasPlotUnderlays } from "../../materialization/plotDrawOrder.js";
5
6
 
6
7
  export const rematerializePlotBorder = /* @__PURE__ */ action(
7
8
  { op: "rematerializePlotBorder", description: "Materialize the optional inset plot border." },
@@ -23,6 +24,6 @@ export const rematerializePlotBorder = /* @__PURE__ */ action(
23
24
  for (const [property, value] of Object.entries(properties)) {
24
25
  next = next.editGraphics({ target: "plotBorder", property, value });
25
26
  }
26
- return next;
27
+ return hasPlotUnderlays(next) ? next.rematerializePlotDrawOrder() : next;
27
28
  }
28
29
  );
@@ -0,0 +1,14 @@
1
+ import { action } from "../../core/action.js";
2
+ import { noOptions } from "../../core/validation.js";
3
+ import { resolveProgramPlotDrawOrder } from "../../materialization/plotDrawOrder.js";
4
+
5
+ export const rematerializePlotDrawOrder = action({
6
+ op: "rematerializePlotDrawOrder", description: "Reconcile explicit guide underlays without changing graphic content."
7
+ }, function (args = {}) {
8
+ noOptions(args, "rematerializePlotDrawOrder");
9
+ const plot = this.graphicSpec.objects["plot-main"];
10
+ if (this.compositionSpec !== undefined || plot === undefined) return this;
11
+ const children = resolveProgramPlotDrawOrder(this);
12
+ if (children.every((id, index) => id === plot.children[index])) return this;
13
+ return this.editGraphics({ target: "plot-main", property: "children", value: children });
14
+ });
@@ -33,7 +33,7 @@ export const createAreaPlot = /* @__PURE__ */ action({ op: OPERATION, descriptio
33
33
  validateAreaEndpointPair(normalizeAreaBound(lower), normalizeAreaBound(upper));
34
34
  const groupBy = args.groupBy === undefined ? undefined : normalizeGroupFields(args.groupBy);
35
35
  const area = omitUndefinedOptions(normalizeAppearance(args.area, [
36
- "fill", "opacity", "stroke", "strokeWidth", "curve",
36
+ "clip", "fill", "opacity", "stroke", "strokeWidth", "curve",
37
37
  ...STROKE_STYLE_PROPERTIES
38
38
  ], `${OPERATION} area`));
39
39
  const color = args.color === undefined ? undefined : normalizeCategoricalColor(args.color, `${OPERATION} color`);
@@ -17,10 +17,10 @@ import {
17
17
  } from "./shared.js";
18
18
 
19
19
  const OPTIONS = Object.freeze([
20
- "id", "data", "coordinate", "orientation", "x", "y", "color", "width", "bar", "guides"
20
+ "id", "data", "coordinate", "orientation", "x", "y", "color", "width", "bar", "missing", "guides"
21
21
  ]);
22
22
  const BAR_OPTIONS = Object.freeze([
23
- "fill", "opacity", "stroke", "strokeWidth", ...RECT_STYLE_PROPERTIES
23
+ "clip", "fill", "opacity", "stroke", "strokeWidth", ...RECT_STYLE_PROPERTIES
24
24
  ]);
25
25
 
26
26
  function normalizeBarPosition(value, label) {
@@ -40,7 +40,7 @@ function normalizeBarPosition(value, label) {
40
40
  export const createBarPlot = /* @__PURE__ */ action(
41
41
  {
42
42
  op: "createBarPlot",
43
- description: "Create a Cartesian bar plot from existing chart data."
43
+ description: "Create a Cartesian bar plot, including interval-width X bins with Y aggregates, from existing chart data."
44
44
  },
45
45
  function (args = {}) {
46
46
  validateFacadeOptions(args, OPTIONS, "createBarPlot");
@@ -74,7 +74,7 @@ export const createBarPlot = /* @__PURE__ */ action(
74
74
  const positions = isBarCategoryEncoding(y) || orientation === "horizontal"
75
75
  ? [["encodeY", y], [xRange ? "encodeXRange" : "encodeX", x]]
76
76
  : [["encodeX", x], [yRange ? "encodeYRange" : "encodeY", y]];
77
- let next = this.createBarMark({ id, data, ...bar, ...(orientation === undefined ? {} : { orientation }) });
77
+ let next = this.createBarMark({ id, data, ...bar, ...(args.missing === undefined ? {} : { missing: args.missing }), ...(orientation === undefined ? {} : { orientation }) });
78
78
  for (const [operation, encoding] of positions) {
79
79
  next = next[operation](positionArgs(encoding, {
80
80
  target: id, coordinate: args.coordinate
@@ -10,7 +10,7 @@ const OPERATION = "createDensityPlot";
10
10
  const OPTIONS = ["id", "data", "coordinate", "field", "groupBy", "bandwidth", "extent", "steps",
11
11
  "kernel", "normalization", "weight", "as", "densityChannel", "valueScale", "densityScale", "color", "area", "guides"];
12
12
  const AREA_OPTIONS = [
13
- "fill", "opacity", "stroke", "strokeWidth", "curve",
13
+ "clip", "fill", "opacity", "stroke", "strokeWidth", "curve",
14
14
  ...STROKE_STYLE_PROPERTIES
15
15
  ];
16
16
 
@@ -23,7 +23,7 @@ const EDIT_OPTIONS = Object.freeze([
23
23
  "target", "data", "coordinate", "field", "groupBy", "weight", "missing", "as", "color"
24
24
  ]);
25
25
  const LINE_OPTIONS = Object.freeze([
26
- "strokeWidth", "stroke", "opacity", ...STROKE_STYLE_PROPERTIES
26
+ "clip", "strokeWidth", "stroke", "opacity", ...STROKE_STYLE_PROPERTIES
27
27
  ]);
28
28
  const LABEL_OPTIONS = Object.freeze([
29
29
  "field", "value", "content", "normalizeBy", "format", "layout", "fill",
@@ -9,6 +9,7 @@ import {
9
9
  validateNonNegativeFinite,
10
10
  validateOptionObject
11
11
  } from "../../core/validation.js";
12
+ import { retainStatisticalDependents, restoreStatisticalDependents } from "../marks/dependentReferences.js";
12
13
  import { findDataset } from "../../selectors/datasets.js";
13
14
  import { findSemanticScale } from "../../selectors/scales.js";
14
15
  import {
@@ -33,14 +34,14 @@ const DUMBBELL_OPTIONS = Object.freeze([
33
34
  const CATEGORY_OPTIONS = Object.freeze(["field", "fieldType", "scale"]);
34
35
  const VALUE_OPTIONS = Object.freeze(["field", "fieldType", "scale"]);
35
36
  const POINT_OPTIONS = Object.freeze([
36
- "shape", "fill", "opacity", "stroke", "strokeWidth", "radius",
37
+ "clip", "shape", "fill", "opacity", "stroke", "strokeWidth", "radius",
37
38
  ...STROKE_STYLE_PROPERTIES
38
39
  ]);
39
40
  const RULE_OPTIONS = Object.freeze([
40
- "stroke", "strokeWidth", "strokeDash", "opacity", ...STROKE_STYLE_PROPERTIES
41
+ "clip", "stroke", "strokeWidth", "strokeDash", "opacity", ...STROKE_STYLE_PROPERTIES
41
42
  ]);
42
43
  const LABEL_OPTIONS = Object.freeze([
43
- "endpoint", "field", "value", "content", "normalizeBy", "format", "layout",
44
+ "clip", "endpoint", "field", "value", "content", "normalizeBy", "format", "layout",
44
45
  "fill", "opacity", "fontSize", "fontFamily", "fontWeight", "align", "baseline",
45
46
  "rotation", "dx", "dy"
46
47
  ]);
@@ -435,6 +436,49 @@ function reviseRequestedScales(program, ownerId, revised, changes) {
435
436
  return next;
436
437
  }
437
438
 
439
+ function reviseRoleGuideSamples(program, owner, current, revised, changes) {
440
+ const categoryAxis = current.orientation === "vertical" ? "x" : "y";
441
+ const valueAxis = categoryAxis === "x" ? "y" : "x";
442
+ const field = value => typeof value === "string" ? value : value?.field;
443
+ const axes = new Set();
444
+ for (const key of changes) {
445
+ if (["data", "orientation"].includes(key) && revised[key] !== current.options[key]) {
446
+ axes.add("x"); axes.add("y");
447
+ } else if (key === "category" &&
448
+ (field(revised.category) !== current.roles.category.field || revised.category?.scale)) {
449
+ axes.add(categoryAxis);
450
+ } else if (["value", "start", "end"].includes(key) &&
451
+ (field(revised[key]) !== current.roles[key]?.field || revised[key]?.scale) ||
452
+ ["summary", "baseline"].includes(key) && revised[key] !== current.options[key]) {
453
+ axes.add(valueAxis);
454
+ }
455
+ }
456
+ let next = program;
457
+ for (const axis of axes) {
458
+ const saved = revised.guides?.axes?.[axis]?.ticksAndLabels;
459
+ if (saved?.values) {
460
+ const { values, ...rest } = saved;
461
+ revised.guides = { ...revised.guides, axes: { ...revised.guides.axes,
462
+ [axis]: { ...revised.guides.axes[axis], ticksAndLabels: {
463
+ ...rest, ...(axis === categoryAxis ? {} : { count: Math.max(2, values.length) })
464
+ } }
465
+ } };
466
+ }
467
+ const scale = owner.encoding[axis]?.scale;
468
+ for (const [id, config] of Object.entries(next.guideConfigs.axis ?? {})) {
469
+ if (config.ticks?.scale !== scale || config.ticks.mode !== "values") continue;
470
+ const refresh = part => {
471
+ if (axis === categoryAxis) return { ...part, inferredValues: true };
472
+ const { values, ...rest } = part;
473
+ return { ...rest, mode: "count", count: Math.max(2, values?.length ?? 5), inferredValues: false };
474
+ };
475
+ next = next._withGuideConfig(id, "ticks", refresh(config.ticks));
476
+ if (config.labels) next = next._withGuideConfig(id, "labels", refresh(config.labels));
477
+ }
478
+ }
479
+ return next;
480
+ }
481
+
438
482
  export const editEndpointPlot = /* @__PURE__ */ action(
439
483
  { op: "editEndpointPlot", description: "Atomically revise an endpoint plot's semantic roles." },
440
484
  function (args = {}) {
@@ -452,8 +496,13 @@ export const editEndpointPlot = /* @__PURE__ */ action(
452
496
  }
453
497
  const revised = { ...current.options };
454
498
  for (const key of changes) revised[key] = args[key];
455
- let next = this.removeMark({ target: owner.id });
499
+ const axes = { fromAxis: current.orientation === "vertical" ? "y" : "x",
500
+ toAxis: revised.orientation === "vertical" ? "y" : "x" };
501
+ const retained = retainStatisticalDependents(this, owner.id);
502
+ let next = reviseRoleGuideSamples(retained.program, owner, current, revised, changes);
503
+ next = next.removeMark({ target: owner.id });
456
504
  next = reviseRequestedScales(next, owner.id, revised, changes);
457
- return next[KIND_METHOD[current.kind]](revised);
505
+ const rebuilt = next[KIND_METHOD[current.kind]](revised);
506
+ return restoreStatisticalDependents(rebuilt, retained.references, { ...axes, fallbackSource: owner.id });
458
507
  }
459
508
  );
@@ -23,7 +23,7 @@ const OPTIONS = [
23
23
  "id", "data", "coordinate", "x", "y", "bin", "color", "rect", "guides"
24
24
  ];
25
25
  const RECT_OPTIONS = [
26
- "opacity", "stroke", "strokeWidth", ...RECT_STYLE_PROPERTIES
26
+ "clip", "opacity", "stroke", "strokeWidth", ...RECT_STYLE_PROPERTIES
27
27
  ];
28
28
  const BIN_OPTIONS = [
29
29
  "bins", "extent", "includeEmpty"
@@ -17,7 +17,7 @@ const OPTIONS = Object.freeze([
17
17
  "binBoundaries", "stack", "xScale", "yScale", "weight", "color", "bar", "guides"
18
18
  ]);
19
19
  const BAR_OPTIONS = Object.freeze([
20
- "fill", "opacity", "stroke", "strokeWidth", ...RECT_STYLE_PROPERTIES
20
+ "clip", "fill", "opacity", "stroke", "strokeWidth", ...RECT_STYLE_PROPERTIES
21
21
  ]);
22
22
  const HISTOGRAM_OPTIONS = Object.freeze([
23
23
  "maxBins", "binStep", "binBoundaries", "stack", "xScale", "yScale", "weight"
@@ -52,7 +52,7 @@ export const createHorizonPlot = /* @__PURE__ */ action({
52
52
  const { id: requestedId, data: requestedData, coordinate, x: requestedX, y: requestedY,
53
53
  area: requestedArea, guides: requestedGuides, ...horizon } = omitUndefinedOptions(args);
54
54
  const { opacity, ...area } = omitUndefinedOptions(normalizeAppearance(requestedArea,
55
- ["opacity", "stroke", "strokeWidth", "curve", ...STROKE_STYLE_PROPERTIES],
55
+ ["clip", "opacity", "stroke", "strokeWidth", "curve", ...STROKE_STYLE_PROPERTIES],
56
56
  `${OPERATION} area`));
57
57
  for (const [key, value] of Object.entries({ ...horizon, ...area })) {
58
58
  if (value === null) throw new TypeError(`${OPERATION} ${key} cannot be null.`);
@@ -11,7 +11,7 @@ import { createPiePlot } from "./pie.js";
11
11
  import { createDensityPlot } from "./density.js";
12
12
  import { createHorizonPlot } from "./horizon.js";
13
13
  import { createPolarLinePlot, createPolarScatterPlot } from "./polar.js";
14
- import { createRadarPlot } from "./radar.js";
14
+ import { createRadarPlot, editRadarPlot } from "./radar.js";
15
15
  import { createRugPlot, createStripPlot } from "./rug-strip.js";
16
16
  import { createIntervalPlot, createRegressionPlot } from "./interval-regression.js";
17
17
  import {
@@ -38,6 +38,7 @@ export function registerChartActions(ProgramClass) {
38
38
  ProgramClass.prototype.createPolarScatterPlot = createPolarScatterPlot;
39
39
  ProgramClass.prototype.createPolarLinePlot = createPolarLinePlot;
40
40
  ProgramClass.prototype.createRadarPlot = createRadarPlot;
41
+ ProgramClass.prototype.editRadarPlot = editRadarPlot;
41
42
  ProgramClass.prototype.createRugPlot = createRugPlot;
42
43
  ProgramClass.prototype.createStripPlot = createStripPlot;
43
44
  ProgramClass.prototype.createIntervalPlot = createIntervalPlot;
@@ -19,7 +19,7 @@ const INTERVAL_OPTIONS = Object.freeze([
19
19
  "color", "point", "errorBar", "guides"
20
20
  ]);
21
21
  const POINT_OPTIONS = Object.freeze([
22
- "shape", "fill", "opacity", "stroke", "strokeWidth", "radius",
22
+ "clip", "shape", "fill", "opacity", "stroke", "strokeWidth", "radius",
23
23
  ...STROKE_STYLE_PROPERTIES
24
24
  ]);
25
25
  const ERROR_BAR_OPTIONS = Object.freeze([
@@ -20,7 +20,7 @@ const OPTIONS = Object.freeze([
20
20
  "strokeDash", "line", "guides"
21
21
  ]);
22
22
  const LINE_OPTIONS = Object.freeze([
23
- "strokeWidth", "curve", "tension", "stroke", "opacity", "closed",
23
+ "clip", "missing", "strokeWidth", "curve", "tension", "stroke", "opacity", "closed",
24
24
  ...STROKE_STYLE_PROPERTIES
25
25
  ]);
26
26
 
@@ -7,6 +7,10 @@ import { normalizeGroupFields } from "../../grammar/pathSeries.js";
7
7
  import { readNominalField, readQuantitativeField } from
8
8
  "../../grammar/scales/fields.js";
9
9
  import { findDataset } from "../../selectors/datasets.js";
10
+ import { resolveEligibleLayer } from "../../selectors/layers.js";
11
+ import { planDerivedDataRevision } from "../../materialization/dataProvenance.js";
12
+ import { applyLayerDataRematerialization } from "../../materialization/dependencies.js";
13
+ import { deriveFoldRows, normalizeFoldTransform } from "../../grammar/fold.js";
10
14
  import {
11
15
  normalizeAppearance,
12
16
  normalizeEncoding,
@@ -76,13 +80,13 @@ function normalizeValue(value, operation) {
76
80
  return { ...omitUndefinedOptions(result), fieldType: "quantitative" };
77
81
  }
78
82
 
79
- function normalizeWide(value, id, operation) {
83
+ function normalizeWide(value, id, operation, minimum = 3) {
80
84
  if (!isPlainObject(value)) {
81
85
  throw new TypeError(`${operation} wide must be a plain object.`);
82
86
  }
83
87
  validateOptionObject(value, WIDE_OPTIONS, `${operation} wide`);
84
- if (!Array.isArray(value.fields) || value.fields.length < 3) {
85
- throw new Error(`${operation} wide.fields must contain at least three fields.`);
88
+ if (!Array.isArray(value.fields) || value.fields.length < minimum) {
89
+ throw new Error(`${operation} wide.fields must contain at least ${minimum === 3 ? "three" : "two"} fields.`);
86
90
  }
87
91
  const fields = [...value.fields];
88
92
  for (const field of fields) validateNonEmptyString(field, `${operation} wide field`);
@@ -303,3 +307,59 @@ export const createRadarPlot = /* @__PURE__ */ action(
303
307
  });
304
308
  }
305
309
  );
310
+
311
+ export const editRadarPlot = /* @__PURE__ */ action(
312
+ {
313
+ op: "editRadarPlot",
314
+ description: "Revise a wide Radar plot's dimensions and order while preserving its series and appearance."
315
+ },
316
+ function (args = {}) {
317
+ const operation = "editRadarPlot";
318
+ validateFacadeOptions(args, ["target", "wide"], operation);
319
+ validateOptionObject(args.wide, ["fields"], `${operation} wide`);
320
+ const owner = resolveEligibleLayer(this, {
321
+ target: args.target,
322
+ label: "wide Radar plot",
323
+ predicate: layer => this.markConfigs[layer.id]?.compositionRole === "radar" &&
324
+ findDataset(this, layer.data)?.transform?.[0]?.type === "fold"
325
+ });
326
+ const previous = findDataset(this, owner.data);
327
+ const prior = previous.transform[0];
328
+ const source = findDataset(this, previous.source);
329
+ const wide = normalizeWide({ ...args.wide, as: prior.as }, owner.id, operation, 2);
330
+ for (const field of wide.fields) readQuantitativeField(source.values, field);
331
+ const transform = normalizeFoldTransform(wide, previous.id);
332
+ const rows = deriveFoldRows(source.values, transform);
333
+ const group = owner.encoding.group;
334
+ const groupBy = group?.fields ?? (group?.field ? [group.field] : []);
335
+ validateRadarRows(rows, { category: prior.as.key, value: prior.as.value,
336
+ groupBy, order: wide.fields }, operation);
337
+ const revision = planDerivedDataRevision(this, {
338
+ owner: owner.id, role: "RadarData", previous: previous.id, consumers: [owner.id]
339
+ });
340
+ let next = this.createFoldData({
341
+ id: revision.id, source: source.id, fields: wide.fields, as: prior.as
342
+ }).rebindLayerData(revision.rebinds[0]);
343
+ const theta = owner.encoding.theta.scale;
344
+ const revisedValues = config => {
345
+ const full = config.inferredValues || !Array.isArray(config.values) ||
346
+ config.values.length === prior.fields.length && prior.fields.every(field => config.values.includes(field));
347
+ const retained = full ? wide.fields : config.values.filter(field => wide.fields.includes(field));
348
+ return { ...config, mode: "values", values: retained.length ? retained : wide.fields };
349
+ };
350
+ for (const [id, axis] of Object.entries(next.semanticSpec.guides.axis ?? {})) {
351
+ if (axis.scale !== theta) continue;
352
+ for (const role of ["ticks", "labels"]) {
353
+ const config = next.guideConfigs.axis?.[id]?.[role];
354
+ if (config) next = next._withGuideConfig(id, role, revisedValues(config));
355
+ }
356
+ }
357
+ const thetaGrid = next.guideConfigs.grid?.theta;
358
+ if (thetaGrid?.scale === theta) next = next._withGridConfig("theta", revisedValues(thetaGrid));
359
+ next = next.editThetaScale({ target: owner.id, domain: wide.fields });
360
+ next = applyLayerDataRematerialization(next, owner.id);
361
+ if (next.markConfigs[owner.id]?.closed !== true) next = next.editLineMark({ target: owner.id, closed: true });
362
+ return next.releaseDerivedData(revision.release)
363
+ ._withContext({ currentMark: owner.id, currentData: source.id });
364
+ }
365
+ );
@@ -5,6 +5,7 @@ import { validateOptionObject } from "../../core/validation.js";
5
5
  import { findDataset } from "../../selectors/datasets.js";
6
6
  import { findLayer } from "../../selectors/layers.js";
7
7
  import { findSemanticScale } from "../../selectors/scales.js";
8
+ import { retainStatisticalDependents, restoreStatisticalDependents } from "../marks/dependentReferences.js";
8
9
  import { resolveViolinRoles } from "../violinPlots/create.js";
9
10
  import {
10
11
  applyFacadeGuides,
@@ -111,6 +112,13 @@ function roleScale(encoding, fallbackId, label, defaults = {}) {
111
112
 
112
113
  function normalizeColor(value, category, id) {
113
114
  if (value === undefined || value === false) return undefined;
115
+ if (isPlainObject(value) && Object.hasOwn(value, "value")) {
116
+ validateOptionObject(value, ["value"], "Raincloud fixed color");
117
+ if (typeof value.value !== "string" || value.value.length === 0) {
118
+ throw new TypeError("Raincloud fixed color requires a non-empty value.");
119
+ }
120
+ return { value: value.value };
121
+ }
114
122
  const color = normalizeEncoding(value, "Raincloud color");
115
123
  if (color.field !== category.field) {
116
124
  throw new Error("Raincloud color must encode its category field.");
@@ -194,6 +202,9 @@ function addSummary(program, config, positions) {
194
202
  y: positions.y,
195
203
  width: options.width ?? { band: 0.24 },
196
204
  ...options,
205
+ ...(config.color?.value === undefined ? {} : {
206
+ box: { ...(options.box ?? {}), fill: config.color.value }
207
+ }),
197
208
  guides: false
198
209
  });
199
210
  if (config.color !== undefined) next = next.encodeColor({ target: id, ...config.color });
@@ -238,7 +249,7 @@ function addPoints(program, config, positions) {
238
249
  coordinate: config.coordinate,
239
250
  x: positions.x,
240
251
  y: positions.y,
241
- ...(config.color === undefined ? {} : { color: config.color }),
252
+ ...(config.color?.field === undefined ? {} : { color: config.color }),
242
253
  ...(size === undefined ? {} : { size }),
243
254
  ...(shape === undefined ? {} : { shape }),
244
255
  ...(point === undefined ? {} : { point }),
@@ -274,7 +285,7 @@ function buildRaincloud(program, config) {
274
285
  coordinate: config.coordinate,
275
286
  x: positions.x,
276
287
  y: positions.y,
277
- ...(config.color === undefined ? {} : { color: config.color }),
288
+ ...(config.color?.field === undefined ? {} : { color: config.color }),
278
289
  density: { ...density, side: densitySide(config.orientation, config.side) },
279
290
  ...(area === undefined ? {} : { area }),
280
291
  guides: false
@@ -282,6 +293,11 @@ function buildRaincloud(program, config) {
282
293
  }
283
294
  next = addSummary(next, config, positions);
284
295
  next = addPoints(next, config, positions);
296
+ if (config.color?.value !== undefined) {
297
+ for (const id of [config.childIds.cloud, config.childIds.points]) {
298
+ if (findLayer(next, id) !== undefined) next = next.encodeColor({ target: id, value: config.color.value });
299
+ }
300
+ }
285
301
  const ownerId = [config.childIds.cloud, config.childIds.summary, config.childIds.points]
286
302
  .find(id => findLayer(next, id) !== undefined);
287
303
  if (ownerId === undefined) throw new Error("Raincloud requires at least one enabled component.");
@@ -395,11 +411,16 @@ export const editRaincloudPlot = /* @__PURE__ */ action(
395
411
  }
396
412
  const ownerId = raincloudOwner(this, args.target);
397
413
  const current = this.markConfigs[ownerId].raincloudPlot;
414
+ const category = Object.hasOwn(args, "category") ? args.category : current.category;
415
+ const color = Object.hasOwn(args, "color") ? args.color
416
+ : Object.hasOwn(args, "category") && current.color?.field === current.category.field
417
+ ? { ...current.color, field: normalizeEncoding(category, "Raincloud category").field }
418
+ : current.color;
398
419
  const candidateArgs = {
399
420
  id: current.id,
400
421
  data: Object.hasOwn(args, "data") ? args.data : current.source,
401
422
  ...(current.coordinate === undefined ? {} : { coordinate: current.coordinate }),
402
- category: Object.hasOwn(args, "category") ? args.category : current.category,
423
+ category,
403
424
  value: Object.hasOwn(args, "value") ? args.value : current.value,
404
425
  orientation: args.orientation ?? current.orientation,
405
426
  side: args.side ?? current.side,
@@ -412,11 +433,21 @@ export const editRaincloudPlot = /* @__PURE__ */ action(
412
433
  points: Object.hasOwn(args, "points")
413
434
  ? normalizePoints(args.points, current.points)
414
435
  : current.points,
415
- color: Object.hasOwn(args, "color") ? args.color : current.color,
436
+ color,
416
437
  guides: current.guides
417
438
  };
418
- const withoutCurrent = this.removeMark({ target: ownerId });
439
+ const axes = { fromAxis: current.orientation === "vertical" ? "y" : "x",
440
+ toAxis: candidateArgs.orientation === "vertical" ? "y" : "x" };
441
+ const retained = retainStatisticalDependents(this, ownerId);
442
+ const withoutCurrent = retained.program.removeMark({ target: ownerId });
419
443
  const revised = normalizeCreate(withoutCurrent, candidateArgs, operation);
420
- return buildRaincloud(alignExistingRoleScales(withoutCurrent, revised), revised);
444
+ const rebuilt = buildRaincloud(alignExistingRoleScales(withoutCurrent, revised), {
445
+ ...revised,
446
+ ...(!Object.hasOwn(args, "data") && current.sourceFilter !== undefined
447
+ ? { sourceFilter: current.sourceFilter } : {})
448
+ });
449
+ return restoreStatisticalDependents(rebuilt, retained.references, {
450
+ ...axes, fallbackSource: raincloudOwner(rebuilt, current.id)
451
+ });
421
452
  }
422
453
  );
@@ -21,13 +21,13 @@ import {
21
21
  const RUG_OPTIONS = Object.freeze(["id", "data", "coordinate", "x", "y", "edge", "tick", "guides"]);
22
22
  const MEASURE_OPTIONS = Object.freeze(["field", "fieldType", "temporalUnit", "scale"]);
23
23
  const TICK_OPTIONS = Object.freeze([
24
- "length", "stroke", "strokeWidth", "opacity", ...STROKE_STYLE_PROPERTIES
24
+ "clip", "length", "stroke", "strokeWidth", "opacity", ...STROKE_STYLE_PROPERTIES
25
25
  ]);
26
26
  const STRIP_OPTIONS = Object.freeze([
27
27
  "id", "data", "coordinate", "x", "y", "color", "size", "shape", "point", "jitter", "guides"
28
28
  ]);
29
29
  const POINT_OPTIONS = Object.freeze([
30
- "radius", "shape", "fill", "opacity", "stroke", "strokeWidth",
30
+ "clip", "radius", "shape", "fill", "opacity", "stroke", "strokeWidth",
31
31
  ...STROKE_STYLE_PROPERTIES
32
32
  ]);
33
33
  const JITTER_OPTIONS = Object.freeze(["maxOffset", "seed", "key"]);
@@ -17,10 +17,10 @@ import {
17
17
 
18
18
  const OPTIONS = Object.freeze([
19
19
  "id", "data", "coordinate", "x", "y", "color", "stroke", "size", "shape",
20
- "point", "guides"
20
+ "point", "missing", "guides"
21
21
  ]);
22
22
  const POINT_OPTIONS = Object.freeze([
23
- "shape", "fill", "opacity", "stroke", "strokeWidth", "radius",
23
+ "clip", "shape", "fill", "opacity", "stroke", "strokeWidth", "radius",
24
24
  ...STROKE_STYLE_PROPERTIES
25
25
  ]);
26
26
 
@@ -62,7 +62,7 @@ export const createScatterPlot = /* @__PURE__ */ action(
62
62
  const initialPoint = { ...point };
63
63
  if (stroke !== undefined) delete initialPoint.strokeWidth;
64
64
  let next = this
65
- .createPointMark({ id, data, ...initialPoint })
65
+ .createPointMark({ id, data, ...initialPoint, ...(args.missing === undefined ? {} : { missing: args.missing }) })
66
66
  .encodeX(positionArgs(x, { target: id, coordinate: args.coordinate }))
67
67
  .encodeY(positionArgs(y, { target: id, coordinate: args.coordinate }));
68
68
  if (radius !== undefined) next = next.encodePointRadius({ target: id, value: radius });
@@ -7,7 +7,7 @@ import {
7
7
  } from "./shared.js";
8
8
 
9
9
  const OPTIONS = Object.freeze(["id", "data", "coordinate", "x", "y", "text", "color", "style", "guides"]);
10
- const STYLE_OPTIONS = Object.freeze(["missing", ...TEXT_MARK_STYLE_OPTIONS.filter(key => key !== "inheritColor")]);
10
+ const STYLE_OPTIONS = Object.freeze(["clip", "missing", ...TEXT_MARK_STYLE_OPTIONS.filter(key => key !== "inheritColor")]);
11
11
 
12
12
  export const createTextPlot = /* @__PURE__ */ action({
13
13
  op: "createTextPlot", description: "Create a Cartesian text plot from existing chart data."
@@ -20,7 +20,7 @@ const CONCAT_OPTIONS = Object.freeze([
20
20
  "id", "programs", "gap", "align", "padding"
21
21
  ]);
22
22
  const LAYOUT_EDIT_OPTIONS = Object.freeze([
23
- "columns", "gap", "spacing", "align", "padding"
23
+ "columns", "gap", "spacing", "topLane", "align", "padding"
24
24
  ]);
25
25
  const REPLACEMENT_OPTIONS = Object.freeze(["target", "program"]);
26
26
  const INSERTION_OPTIONS = Object.freeze(["id", "program", "before", "after"]);
@@ -181,6 +181,9 @@ const editCompositionLayout = /* @__PURE__ */ closedAction(
181
181
  );
182
182
  }
183
183
  if (current.type !== "facet" && Object.hasOwn(args, "spacing")) throw new Error("Plot spacing requires a facet composition.");
184
+ if (current.type !== "facet" && Object.hasOwn(args, "topLane")) {
185
+ throw new Error("topLane requires a facet composition.");
186
+ }
184
187
  const padding = Object.hasOwn(args, "padding")
185
188
  ? normalizeCompositionPadding(args.padding, current.padding)
186
189
  : current.padding;
@@ -191,6 +194,7 @@ const editCompositionLayout = /* @__PURE__ */ closedAction(
191
194
  );
192
195
  layout = resolveFacetLayout({
193
196
  spacing: option(args, "spacing", current.spacing),
197
+ topLane: args.topLane === undefined ? current.topLane : args.topLane,
194
198
  plots: current.children.map(id => ({ id, ...resolveGraphicBounds(this.children[id]) })),
195
199
  children: current.children.map((id, index) => ({
196
200
  ...childDescriptor({ id, program: this.children[id] }),
@@ -218,11 +222,14 @@ const editCompositionLayout = /* @__PURE__ */ closedAction(
218
222
  padding
219
223
  });
220
224
  }
225
+ const { topLane: _previousTopLane, ...currentLayoutSpec } = current;
221
226
  return applyCompositionState(this, {
222
227
  children: this.children,
223
228
  compositionSpec: {
224
- ...current,
225
- ...(current.type === "facet" ? { columns: layout.columns, ...(layout.spacing !== undefined ? { spacing: layout.spacing } : current.spacing !== undefined ? { spacing: "canvas" } : {}) } : {}),
229
+ ...currentLayoutSpec,
230
+ ...(current.type === "facet" ? { columns: layout.columns,
231
+ ...(layout.spacing !== undefined ? { spacing: layout.spacing } : current.spacing !== undefined ? { spacing: "canvas" } : {}),
232
+ ...(layout.topLane === undefined ? {} : { topLane: layout.topLane }) } : {}),
226
233
  gap: layout.gap,
227
234
  align: layout.align,
228
235
  padding: layout.padding
@@ -1,4 +1,5 @@
1
1
  import { action } from "../../core/action.js";
2
+ import { validateMarkClipCoordinate } from "../../selectors/markClip.js";
2
3
  import { validateCoordinateType } from "../../grammar/coordinates.js";
3
4
  import { validateUserId } from "../../core/identifiers.js";
4
5
  import { findCoordinate } from "../../selectors/coordinates.js";
@@ -51,6 +52,7 @@ export const createCoordinate = /* @__PURE__ */ action(
51
52
 
52
53
  for (const layerId of layers) {
53
54
  const layer = findLayer(this, layerId);
55
+ validateMarkClipCoordinate(this, layer, this.markConfigs[layerId]?.clip, type);
54
56
 
55
57
  if (layer.coordinate !== undefined && layer.coordinate !== id) {
56
58
  throw new Error(
@@ -1,3 +1,5 @@
1
+ import { createFlattenData, materializeFlattenData } from "./flatten.js";
2
+ import {createLookupData,materializeLookupData} from "./lookup.js";
1
3
  import { createData } from "./create.js";
2
4
  import { createDerivedData } from "./derived.js";
3
5
  import { createBasicBin2DData } from "./basicBin2d.js";
@@ -5,6 +7,10 @@ import { materializeBin2DData } from "./bin2dMaterialize.js";
5
7
 
6
8
  export function registerBasicDataActions(ProgramClass) {
7
9
  ProgramClass.prototype.createData = createData;
10
+ ProgramClass.prototype.createFlattenData = createFlattenData;
11
+ ProgramClass.prototype.materializeFlattenData = materializeFlattenData;
12
+ ProgramClass.prototype.createLookupData=createLookupData;
13
+ ProgramClass.prototype.materializeLookupData=materializeLookupData;
8
14
  ProgramClass.prototype.createDerivedData = createDerivedData;
9
15
  ProgramClass.prototype.createBin2DData = createBasicBin2DData;
10
16
  ProgramClass.prototype.materializeBin2DData = materializeBin2DData;
@@ -1,3 +1,4 @@
1
+ import {resolveDatasetTransformInputs} from "./shared.js";
1
2
  import { closedAction } from "../../core/action.js";
2
3
  import { validateGraphicItemLimit } from "../../core/validation.js";
3
4
  import { resolveGraphicItemCapacity } from "../../selectors/graphicCapacity.js";
@@ -49,11 +50,14 @@ export const createDerivedData = /* @__PURE__ */ closedAction(
49
50
  if (!hasDataset(this, source)) {
50
51
  throw new Error(`Unknown source dataset "${source}".`);
51
52
  }
53
+ const transforms=args.transform?.[0]?.type==="lookup"
54
+ ? [resolveDatasetTransformInputs(this,args.transform[0])]
55
+ : args.transform;
52
56
  const created = this
53
57
  .editSemantic({ property: `dataset[${id}].source`, value: source })
54
- .editSemantic({ property: `dataset[${id}].transform`, value: args.transform });
58
+ .editSemantic({ property: `dataset[${id}].transform`, value: transforms });
55
59
  const input = resolveDatasetReference(created, source, "Source dataset");
56
- const transform = args.transform[0];
60
+ const transform = transforms[0];
57
61
  assertFieldsAvailable(input.schema, transformInputFields(transform), {
58
62
  data: input.id,
59
63
  operation: "createDerivedData"