ggaction 0.0.17 → 0.0.19

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 (315) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/README.md +6 -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 +3 -3
  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 +20 -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 +8 -7
  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/box.js +2 -1
  34. package/src/actions/data/derived.js +11 -1
  35. package/src/actions/data/edit.js +3 -1
  36. package/src/actions/data/index.js +4 -0
  37. package/src/actions/data/regression.js +25 -16
  38. package/src/actions/data/regressionStatistics.js +73 -0
  39. package/src/actions/data/shared.js +8 -4
  40. package/src/actions/data/stack.js +3 -1
  41. package/src/actions/distributions/revision.js +24 -18
  42. package/src/actions/encodings/appearance.js +37 -29
  43. package/src/actions/encodings/barWidth.js +36 -11
  44. package/src/actions/encodings/channels.js +0 -1
  45. package/src/actions/encodings/color/continuous.js +1 -1
  46. package/src/actions/encodings/color/index.js +1 -1
  47. package/src/actions/encodings/color/policy.js +10 -3
  48. package/src/actions/encodings/density/resolve.js +1 -6
  49. package/src/actions/encodings/position/policies/arc.js +12 -10
  50. package/src/actions/encodings/position/policies/area.js +3 -3
  51. package/src/actions/encodings/position/policies/bar.js +15 -6
  52. package/src/actions/encodings/position/policies/line.js +17 -6
  53. package/src/actions/encodings/position/resolve.js +3 -2
  54. package/src/actions/encodings/ranged.js +17 -0
  55. package/src/actions/encodings/remove.js +14 -8
  56. package/src/actions/encodings/seriesLayout.js +1 -0
  57. package/src/actions/encodings/shared.js +26 -24
  58. package/src/actions/encodings/text.js +6 -3
  59. package/src/actions/errorBands/create.js +2 -2
  60. package/src/actions/errorBands/edit.js +2 -1
  61. package/src/actions/facets/actions.js +176 -247
  62. package/src/actions/facets/guides.js +2 -1
  63. package/src/actions/gradientPlots/resolve.js +2 -17
  64. package/src/actions/guides/applicability.js +1 -1
  65. package/src/actions/guides/axes/axes.js +30 -11
  66. package/src/actions/guides/axes/axis.js +72 -10
  67. package/src/actions/guides/axes/components.js +13 -13
  68. package/src/actions/guides/axes/edit.js +36 -24
  69. package/src/actions/guides/axes/labels.js +155 -71
  70. package/src/actions/guides/axes/lines.js +27 -24
  71. package/src/actions/guides/axes/parallel/lifecycle.js +1 -1
  72. package/src/actions/guides/axes/parallel/policy.js +8 -7
  73. package/src/actions/guides/axes/remove.js +13 -8
  74. package/src/actions/guides/axes/tickGroups.js +11 -7
  75. package/src/actions/guides/axes/ticks.js +36 -24
  76. package/src/actions/guides/axes/titles.js +107 -69
  77. package/src/actions/guides/basic.js +2 -0
  78. package/src/actions/guides/facadeAxes.js +33 -24
  79. package/src/actions/guides/facadeLegend.js +12 -4
  80. package/src/actions/guides/grids/grid.js +8 -2
  81. package/src/actions/guides/grids/resolve.js +102 -58
  82. package/src/actions/guides/legends/blocks.js +46 -20
  83. package/src/actions/guides/legends/categorical/actions.js +95 -19
  84. package/src/actions/guides/legends/categorical/components.js +15 -18
  85. package/src/actions/guides/legends/categorical/index.js +8 -0
  86. package/src/actions/guides/legends/categorical/layout.js +45 -20
  87. package/src/actions/guides/legends/categorical/options.js +39 -22
  88. package/src/actions/guides/legends/categorical/recipes.js +7 -3
  89. package/src/actions/guides/legends/categorical/resolve.js +11 -4
  90. package/src/actions/guides/legends/categorical/symbols.js +38 -18
  91. package/src/actions/guides/legends/categorical/validation.js +1 -6
  92. package/src/actions/guides/legends/colorSize.js +110 -0
  93. package/src/actions/guides/legends/combine.js +37 -0
  94. package/src/actions/guides/legends/continuous/colorSymbols.js +1 -0
  95. package/src/actions/guides/legends/continuous/common.js +1 -447
  96. package/src/actions/guides/legends/continuous/gradient.js +73 -46
  97. package/src/actions/guides/legends/continuous/interval.js +4 -4
  98. package/src/actions/guides/legends/continuous/opacity.js +33 -21
  99. package/src/actions/guides/legends/creation.js +2 -0
  100. package/src/actions/guides/legends/edit.js +1 -604
  101. package/src/actions/guides/legends/focused.js +8 -7
  102. package/src/actions/guides/legends/group.js +1 -0
  103. package/src/actions/guides/legends/lane.js +1 -516
  104. package/src/actions/guides/legends/lifecycle.js +2 -1
  105. package/src/actions/guides/legends/remove.js +9 -3
  106. package/src/actions/guides/legends/sampling.js +1 -1
  107. package/src/actions/guides/legends/size.js +65 -47
  108. package/src/actions/guides/legends/strokeWidth.js +13 -29
  109. package/src/actions/guides/legends/target.js +2 -79
  110. package/src/actions/guides/legends/transition.js +44 -11
  111. package/src/actions/guides/polar/axes/labels.js +4 -2
  112. package/src/actions/guides/polar/axes/titles.js +7 -5
  113. package/src/actions/guides/polar/resolve.js +6 -14
  114. package/src/actions/guides/tickValues.js +8 -5
  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 +10 -4
  120. package/src/actions/marks/bar/edit.js +29 -14
  121. package/src/actions/marks/bar/materialize.js +15 -7
  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/edit.js +1 -1
  125. package/src/actions/marks/point/materialize.js +5 -1
  126. package/src/actions/marks/rect/actions.js +3 -3
  127. package/src/actions/marks/remove.js +12 -10
  128. package/src/actions/marks/rule/actions.js +7 -4
  129. package/src/actions/marks/text/actions.js +17 -5
  130. package/src/actions/marks/text/layout.js +3 -0
  131. package/src/actions/primitives/createGraphics.js +3 -2
  132. package/src/actions/primitives/editGraphics.js +15 -11
  133. package/src/actions/primitives/graphicProperties.js +1 -6
  134. package/src/actions/primitives/semanticAction.js +27 -1
  135. package/src/actions/primitives/semanticValidation/guide.js +11 -1
  136. package/src/actions/primitives/semanticValidation/layer.js +8 -0
  137. package/src/actions/regression/create.js +1 -1
  138. package/src/actions/regression/edit.js +18 -6
  139. package/src/actions/regression/resolve.js +2 -6
  140. package/src/actions/scales/channels.js +9 -4
  141. package/src/actions/scales/consumers/families.js +16 -5
  142. package/src/actions/scales/create.js +10 -5
  143. package/src/actions/scales/definitions.js +1 -425
  144. package/src/actions/scales/edit.js +3 -3
  145. package/src/actions/scales/editPolicy.js +12 -3
  146. package/src/actions/scales/materialize.js +1 -1
  147. package/src/actions/scales/patch.js +1 -1
  148. package/src/actions/scales/preview.js +21 -1
  149. package/src/actions/scales/quantitativeColor.js +8 -3
  150. package/src/actions/selection/actions.js +1 -13
  151. package/src/actions/theme/reconcile.js +126 -45
  152. package/src/actions/theme/state.js +12 -0
  153. package/src/actions/titles/actions.js +5 -18
  154. package/src/actions/titles/resolve.js +41 -19
  155. package/src/actions/violinPlots/edit.js +3 -20
  156. package/src/core/compositionState.js +6 -3
  157. package/src/core/font.js +9 -2
  158. package/src/core/graphicProperties.js +1 -0
  159. package/src/core/optionRequirements.js +1 -0
  160. package/src/core/programState.js +4 -0
  161. package/src/core/resourceReferences.js +5 -2
  162. package/src/core/textMetricProfile.js +3 -2
  163. package/src/core/textMetrics.js +41 -4
  164. package/src/core/validation.js +35 -0
  165. package/src/core/vocabulary.js +7 -0
  166. package/src/grammar/aggregate.js +4 -18
  167. package/src/grammar/arcs.js +1 -386
  168. package/src/grammar/areaPaint.js +10 -0
  169. package/src/grammar/areaSeries.js +18 -10
  170. package/src/grammar/axisTextPlacement.js +1 -0
  171. package/src/grammar/bars/geometry.js +30 -3
  172. package/src/grammar/bars/policy.js +12 -1
  173. package/src/grammar/bin.js +2 -12
  174. package/src/grammar/bin2d.js +1 -6
  175. package/src/grammar/boxPlot.js +56 -9
  176. package/src/grammar/categoryOrder.js +1 -6
  177. package/src/grammar/colorSizeLegend.js +39 -0
  178. package/src/grammar/complete.js +15 -19
  179. package/src/grammar/computed.js +115 -26
  180. package/src/grammar/curveCommands.js +29 -16
  181. package/src/grammar/datasetSchema.js +13 -9
  182. package/src/grammar/displayLabels.js +36 -0
  183. package/src/grammar/ecdf.js +2 -9
  184. package/src/grammar/facets/dependencies.js +6 -2
  185. package/src/grammar/facets/guides.js +58 -30
  186. package/src/grammar/facets/index.js +2 -5
  187. package/src/grammar/facets/legendIntent.js +65 -0
  188. package/src/grammar/fold.js +2 -12
  189. package/src/grammar/gradientProfile.js +1 -6
  190. package/src/grammar/guideDash.js +1 -0
  191. package/src/grammar/impute.js +21 -48
  192. package/src/grammar/interval.js +2 -9
  193. package/src/grammar/legendBlocks.js +1 -0
  194. package/src/grammar/legendBorder.js +9 -0
  195. package/src/grammar/legendClip.js +19 -0
  196. package/src/grammar/legendCombinations.js +45 -0
  197. package/src/grammar/legendGradient.js +27 -0
  198. package/src/grammar/legendGroups.js +1 -0
  199. package/src/grammar/legendOpacity.js +6 -0
  200. package/src/grammar/legendTitleStyle.js +22 -0
  201. package/src/grammar/lineSeries.js +32 -17
  202. package/src/grammar/markLabels.js +5 -2
  203. package/src/grammar/normalize.js +4 -54
  204. package/src/grammar/numeric.js +1 -290
  205. package/src/grammar/parallelCoordinates.js +1 -6
  206. package/src/grammar/parseTime.js +41 -0
  207. package/src/grammar/pathCommands.js +11 -4
  208. package/src/grammar/pointShapes.js +16 -2
  209. package/src/grammar/polar.js +1 -184
  210. package/src/grammar/positionCompatibility.js +3 -3
  211. package/src/grammar/regression/derive.js +16 -2
  212. package/src/grammar/regression/models.js +142 -54
  213. package/src/grammar/regression/parameters.js +23 -4
  214. package/src/grammar/regression/statistics.js +64 -0
  215. package/src/grammar/roundedRect.js +42 -54
  216. package/src/grammar/scales/appearance.js +1 -206
  217. package/src/grammar/scales/color.js +1 -382
  218. package/src/grammar/scales/colorConsumers.js +5 -2
  219. package/src/grammar/scales/definition.js +1 -197
  220. package/src/grammar/scales/discretized.js +1 -195
  221. package/src/grammar/scales/size.js +1 -456
  222. package/src/grammar/scales/temporal.js +2 -13
  223. package/src/grammar/scales/transformed.js +1 -383
  224. package/src/grammar/scales/types.js +1 -1
  225. package/src/grammar/scales/validation.js +1 -66
  226. package/src/grammar/schemas/concreteGraphic.js +14 -0
  227. package/src/grammar/schemas/graphic.js +6 -0
  228. package/src/grammar/schemas/graphicBounds.js +31 -7
  229. package/src/grammar/schemas/semanticPath.js +19 -3
  230. package/src/grammar/stack.js +2 -10
  231. package/src/grammar/statisticalReference.js +1 -6
  232. package/src/grammar/summary.js +4 -27
  233. package/src/grammar/text.js +19 -0
  234. package/src/grammar/timeUnit.js +22 -11
  235. package/src/grammar/transformKeys.js +47 -0
  236. package/src/grammar/transformTopology.js +1 -0
  237. package/src/grammar/transforms.js +15 -2
  238. package/src/grammar/valueFormat.js +17 -5
  239. package/src/grammar/weightedStatistics.js +1 -6
  240. package/src/grammar/window.js +4 -58
  241. package/src/inspectionDescriptors.js +1 -1
  242. package/src/layout/canvas.js +1 -110
  243. package/src/layout/facets.js +1 -244
  244. package/src/layout/guideCollisions.js +9 -1
  245. package/src/layout/labels.js +1 -0
  246. package/src/layout/legend.js +13 -0
  247. package/src/layout/legendGroup.js +1 -0
  248. package/src/layout/legendItems.js +59 -15
  249. package/src/layout/legendLane.js +1 -441
  250. package/src/layout/legendText.js +1 -0
  251. package/src/layout/title.js +35 -13
  252. package/src/materialization/bars/aggregate.js +11 -5
  253. package/src/materialization/bars/histogram.js +4 -1
  254. package/src/materialization/bars/ranged.js +19 -6
  255. package/src/materialization/bars/resolve.js +22 -1
  256. package/src/materialization/compositionSnapshot.js +2 -7
  257. package/src/materialization/coordinateBounds.js +21 -0
  258. package/src/materialization/dependencies.js +4 -44
  259. package/src/materialization/facetGuides/edit.js +30 -0
  260. package/src/materialization/facetGuides/layout.js +1 -0
  261. package/src/materialization/facetGuides/owner.js +125 -0
  262. package/src/materialization/facetGuides/ownerLayout.js +71 -0
  263. package/src/materialization/facetGuides/placement.js +71 -14
  264. package/src/materialization/facetGuides/preparation.js +22 -7
  265. package/src/materialization/facetHeaders.js +1 -347
  266. package/src/materialization/facets.js +1 -185
  267. package/src/materialization/graphicHierarchy.js +5 -1
  268. package/src/materialization/graphicTranslation.js +73 -0
  269. package/src/materialization/guides/layout.js +1 -57
  270. package/src/materialization/guides/resources.js +1 -123
  271. package/src/materialization/layout.js +53 -0
  272. package/src/materialization/legendBorder.js +28 -0
  273. package/src/materialization/legendClip.js +76 -0
  274. package/src/materialization/legendGroups.js +1 -0
  275. package/src/materialization/legendText.js +1 -0
  276. package/src/materialization/legends.js +2 -2
  277. package/src/materialization/marks/capabilities.js +5 -6
  278. package/src/materialization/rect.js +1 -1
  279. package/src/materialization/scaleGuideDependencies.js +14 -15
  280. package/src/materialization/scales/map.js +6 -0
  281. package/src/materialization/scales/policies/arc.js +8 -7
  282. package/src/materialization/scales/policies/bar.js +19 -7
  283. package/src/materialization/scales/policies/series.js +2 -1
  284. package/src/materialization/scales/resolve.js +7 -3
  285. package/src/materialization/selection/items/arc.js +1 -1
  286. package/src/materialization/selection/items/bar.js +7 -6
  287. package/src/materialization/selection/items/path.js +7 -6
  288. package/src/materialization/selection/items/rect.js +2 -2
  289. package/src/materialization/text.js +13 -4
  290. package/src/materialization/textLines.js +15 -0
  291. package/src/materialization/typography.js +10 -2
  292. package/src/persistence/codec.js +37 -6
  293. package/src/persistence/validation.js +220 -0
  294. package/src/persistence.js +19 -6
  295. package/src/renderers/canvas/circle.js +6 -3
  296. package/src/renderers/canvas/fill.js +7 -1
  297. package/src/renderers/canvas/index.js +22 -11
  298. package/src/renderers/canvas/isolated.js +166 -0
  299. package/src/renderers/canvas/limits.js +3 -0
  300. package/src/renderers/canvas/line.js +1 -0
  301. package/src/renderers/canvas/path.js +1 -1
  302. package/src/renderers/canvas/rect.js +11 -8
  303. package/src/renderers/canvas/stroke.js +2 -1
  304. package/src/renderers/canvas/text.js +5 -3
  305. package/src/renderers/pdf.js +2 -2
  306. package/src/renderers/png.js +2 -2
  307. package/src/renderers/svg.js +14 -4
  308. package/src/selectors/axes.js +1 -0
  309. package/src/selectors/graphicCapacity.js +38 -0
  310. package/src/selectors/layers.js +19 -0
  311. package/src/theme/defaults.js +8 -2
  312. package/src/version.js +1 -1
  313. package/types/basic.d.ts +3 -0
  314. package/types/index.d.ts +12 -0
  315. package/types/program.d.ts +536 -129
@@ -135,7 +135,7 @@ export const createIntervalPlot = /* @__PURE__ */ action(
135
135
 
136
136
  const REGRESSION_OPTIONS = Object.freeze([
137
137
  "id", "data", "coordinate", "x", "y", "color", "size", "shape", "point",
138
- "groupBy", "method", "degree", "span", "confidenceMethod", "level", "confidence",
138
+ "groupBy", "method", "degree", "span", "robustIterations", "confidenceMethod", "level", "confidence",
139
139
  "interval", "band", "line", "guides"
140
140
  ]);
141
141
 
@@ -163,7 +163,7 @@ export const createRegressionPlot = /* @__PURE__ */ action(
163
163
  guides: false
164
164
  });
165
165
  const regression = Object.fromEntries([
166
- "groupBy", "method", "degree", "span", "confidenceMethod", "level",
166
+ "groupBy", "method", "degree", "span", "robustIterations", "confidenceMethod", "level",
167
167
  "confidence", "interval", "band", "line"
168
168
  ].filter(key => Object.hasOwn(args, key)).map(key => [key, args[key]]));
169
169
  next = next.createRegression({ target: id, ...regression });
@@ -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", "stroke", "opacity", "closed",
23
+ "strokeWidth", "curve", "tension", "stroke", "opacity", "closed",
24
24
  ...STROKE_STYLE_PROPERTIES
25
25
  ]);
26
26
 
@@ -1,52 +1,8 @@
1
1
  import { action } from "../../core/action.js";
2
- import { STROKE_STYLE_PROPERTIES } from "../../grammar/strokeStyle.js";
3
- import {
4
- applyFacadeGuides, normalizeAppearance, normalizeCategoricalColor, normalizeCategoricalGuides,
5
- normalizeArcCategory, normalizeCategoryAggregate, omitUndefinedOptions,
6
- resolveFacadeData, resolveFacadeId, validateFacadeOptions
7
- } from "./shared.js";
8
-
9
- const OPERATION = "createPiePlot";
10
- const OPTIONS = ["id", "data", "coordinate", "category", "value", "aggregate", "color", "arc", "guides"];
11
- const ARC_OPTIONS = [
12
- "innerRadius", "padAngle", "fill", "opacity", "stroke", "strokeWidth",
13
- ...STROKE_STYLE_PROPERTIES
14
- ];
15
- function guideOptions(value, color) {
16
- const guides = normalizeCategoricalGuides(value, OPERATION, color);
17
- if (guides === false) return false;
18
- for (const key of ["axes", "grid"]) {
19
- if (guides[key] !== undefined && guides[key] !== false) {
20
- throw new TypeError(`${OPERATION} guides.${key} only accepts false.`);
21
- }
22
- }
23
- return { ...guides, axes: false, grid: false };
24
- }
2
+ import { createArcPlot } from "./radial.js";
25
3
 
26
4
  export const createPiePlot = /* @__PURE__ */ action({
27
- op: OPERATION, description: "Create a categorical count or weighted-sum pie or donut plot."
5
+ op: "createPiePlot", description: "Create a categorical count or weighted-sum pie or donut plot."
28
6
  }, function (args = {}) {
29
- validateFacadeOptions(args, OPTIONS, OPERATION);
30
- const id = resolveFacadeId(this, args.id, { defaultId: "piePlot", operation: OPERATION });
31
- const data = resolveFacadeData(this, args.data, OPERATION);
32
- const category = normalizeArcCategory(args.category, OPERATION);
33
- const aggregate = normalizeCategoryAggregate(args, OPERATION);
34
- const arc = omitUndefinedOptions(normalizeAppearance(args.arc, ARC_OPTIONS, `${OPERATION} arc`));
35
- const color = args.color === false ? undefined
36
- : normalizeCategoricalColor(args.color === undefined ? category.field : args.color, `${OPERATION} color`);
37
- if (color !== undefined && arc.fill !== undefined) {
38
- throw new Error(`${OPERATION} arc.fill cannot be combined with color; use color:false.`);
39
- }
40
- const guides = guideOptions(args.guides, color);
41
- let next = this.createArcMark({ id, data, ...arc }).encodeTheta({
42
- ...category, target: id, aggregate,
43
- ...(args.coordinate === undefined ? {} : { coordinate: args.coordinate }),
44
- ...(aggregate === "sum" ? { weight: args.value } : {})
45
- });
46
- if (color !== undefined) next = next.encodeColor({ ...color, target: id });
47
- next = applyFacadeGuides(next, guides, id, args.guides ?? {});
48
- return next._withMarkConfig(id, {
49
- ...next.markConfigs[id],
50
- compositionRole: "pie"
51
- });
7
+ return createArcPlot(this, args, { operation: "createPiePlot", defaultId: "piePlot" });
52
8
  });
@@ -1,3 +1,4 @@
1
+ import { findSemanticScale } from "../../selectors/scales.js";
1
2
  import { action } from "../../core/action.js";
2
3
  import { STROKE_STYLE_PROPERTIES } from "../../grammar/strokeStyle.js";
3
4
  import {
@@ -6,21 +7,22 @@ import {
6
7
  resolveFacadeData, resolveFacadeId, validateFacadeOptions
7
8
  } from "./shared.js";
8
9
 
9
- const OPTIONS = ["id", "data", "coordinate", "category", "value", "aggregate", "radiusScale", "color", "arc", "guides"];
10
+ const OPTIONS = ["id", "data", "coordinate", "category", "value", "aggregate", "color", "arc", "guides"];
10
11
  const ARC_OPTIONS = [
11
12
  "innerRadius", "padAngle", "fill", "opacity", "stroke", "strokeWidth",
12
13
  ...STROKE_STYLE_PROPERTIES
13
14
  ];
14
15
  const RADIUS_OPTIONS = ["id", "type", "domain", "range", "zero", "nice", "reverse", "clamp"];
15
16
 
16
- function createMeasuredPlot(program, args, { operation, defaultId, mapping }) {
17
- validateFacadeOptions(args, OPTIONS, operation);
17
+ export function createArcPlot(program, args, { operation, defaultId, mapping }) {
18
+ const measured = mapping !== undefined;
19
+ validateFacadeOptions(args, [...OPTIONS, measured ? "radiusScale" : "radius"], operation);
18
20
  const id = resolveFacadeId(program, args.id, { defaultId, operation });
19
21
  const data = resolveFacadeData(program, args.data, operation);
20
22
  const category = normalizeArcCategory(args.category, operation);
21
23
  const aggregate = normalizeCategoryAggregate(args, operation);
22
24
  const arc = omitUndefinedOptions(normalizeAppearance(args.arc, ARC_OPTIONS, `${operation} arc`));
23
- if (arc.padAngle !== undefined && arc.padAngle !== 0) throw new Error(`${operation} requires padAngle 0.`);
25
+ if (measured && arc.padAngle !== undefined && arc.padAngle !== 0) throw new Error(`${operation} requires padAngle 0.`);
24
26
  const radiusScale = args.radiusScale === undefined ? undefined
25
27
  : omitUndefinedOptions(normalizeAppearance(args.radiusScale, RADIUS_OPTIONS, `${operation} radiusScale`));
26
28
  const color = args.color === false ? undefined
@@ -28,23 +30,43 @@ function createMeasuredPlot(program, args, { operation, defaultId, mapping }) {
28
30
  if (color !== undefined && arc.fill !== undefined) {
29
31
  throw new Error(`${operation} arc.fill cannot be combined with color; use color:false.`);
30
32
  }
31
- const guides = normalizeCategoricalGuides(args.guides, operation, color);
33
+ let guides = normalizeCategoricalGuides(args.guides, operation, color);
34
+ if (!measured && guides !== false) {
35
+ for (const key of ["axes", "grid"]) {
36
+ if (guides[key] !== undefined && guides[key] !== false) {
37
+ throw new TypeError(`${operation} guides.${key} only accepts false.`);
38
+ }
39
+ }
40
+ guides = { ...guides, axes: false, grid: false };
41
+ }
32
42
  let next = program.createArcMark({ id, data, ...arc }).encodeTheta({
33
- ...category, target: id, ...(args.coordinate === undefined ? {} : { coordinate: args.coordinate })
34
- }).encodeR({ target: id, aggregate, mapping,
35
- ...(aggregate === "sum" ? { field: args.value } : {}),
36
- ...(radiusScale === undefined ? {} : { scale: radiusScale }) });
43
+ ...category, target: id, ...(args.coordinate === undefined ? {} : { coordinate: args.coordinate }),
44
+ ...(!measured ? { aggregate, ...(aggregate === "sum" ? { weight: args.value } : {}) } : {})
45
+ });
46
+ if (measured) {
47
+ next = next.encodeR({ target: id, aggregate, mapping,
48
+ ...(aggregate === "sum" ? { field: args.value } : {}),
49
+ ...(radiusScale === undefined ? {} : { scale: radiusScale }) });
50
+ } else if (args.radius !== undefined) {
51
+ const radius = typeof args.radius === "string" ? { field: args.radius }
52
+ : normalizeAppearance(args.radius, ["field", "aggregate", "scale"], `${operation} radius`);
53
+ const scale = radius.scale ?? {};
54
+ next = next.encodeR({ target: id, ...omitUndefinedOptions(radius), scale: {
55
+ ...(scale.type !== "log" && findSemanticScale(next, scale.id ?? "radius") === undefined ? { zero: true } : {}),
56
+ ...scale
57
+ } });
58
+ }
37
59
  if (color !== undefined) next = next.encodeColor({ ...color, target: id });
38
60
  next = applyFacadeGuides(next, guides, id, args.guides ?? {});
39
61
  return next._withMarkConfig(id, {
40
62
  ...next.markConfigs[id],
41
- compositionRole: "rose"
63
+ compositionRole: measured ? "rose" : "pie"
42
64
  });
43
65
  }
44
66
 
45
67
  export const createRosePlot = /* @__PURE__ */ action({ op: "createRosePlot", description: "Create equal-angle sectors with area proportional to category count or sum." }, function (args = {}) {
46
- return createMeasuredPlot(this, args, { operation: "createRosePlot", defaultId: "rosePlot", mapping: "area" });
68
+ return createArcPlot(this, args, { operation: "createRosePlot", defaultId: "rosePlot", mapping: "area" });
47
69
  });
48
70
  export const createRadialBarPlot = /* @__PURE__ */ action({ op: "createRadialBarPlot", description: "Create equal-angle sectors with radial length proportional to category count or sum." }, function (args = {}) {
49
- return createMeasuredPlot(this, args, { operation: "createRadialBarPlot", defaultId: "radialBarPlot", mapping: "radius-length" });
71
+ return createArcPlot(this, args, { operation: "createRadialBarPlot", defaultId: "radialBarPlot", mapping: "radius-length" });
50
72
  });
@@ -1,3 +1,4 @@
1
+ import { encodeStroke } from "../encodings/stroke.js";
1
2
  import { action } from "../../core/action.js";
2
3
  import { validateNonNegativeFinite } from "../../core/validation.js";
3
4
  import { STROKE_STYLE_PROPERTIES } from "../../grammar/strokeStyle.js";
@@ -15,7 +16,7 @@ import {
15
16
  } from "./shared.js";
16
17
 
17
18
  const OPTIONS = Object.freeze([
18
- "id", "data", "coordinate", "x", "y", "color", "size", "shape",
19
+ "id", "data", "coordinate", "x", "y", "color", "stroke", "size", "shape",
19
20
  "point", "guides"
20
21
  ]);
21
22
  const POINT_OPTIONS = Object.freeze([
@@ -43,6 +44,13 @@ export const createScatterPlot = /* @__PURE__ */ action(
43
44
  const x = normalizeFieldEncoding(args.x, "createScatterPlot x");
44
45
  const y = normalizeFieldEncoding(args.y, "createScatterPlot y");
45
46
  const color = normalizeEncoding(args.color, "createScatterPlot color");
47
+ const stroke = normalizeEncoding(args.stroke, "createScatterPlot stroke");
48
+ if (stroke !== undefined) {
49
+ if (typeof stroke.field !== "string" || stroke.field.length === 0 || Object.hasOwn(stroke, "value")) {
50
+ throw new TypeError("createScatterPlot stroke requires a field; constant stroke belongs in point.");
51
+ }
52
+ if (Object.hasOwn(point, "stroke")) throw new Error("createScatterPlot stroke conflicts with point.stroke.");
53
+ }
46
54
  const size = normalizeEncoding(args.size, "createScatterPlot size");
47
55
  const shape = normalizeEncoding(args.shape, "createScatterPlot shape");
48
56
  const guides = normalizeGuides(args.guides, "createScatterPlot");
@@ -51,12 +59,18 @@ export const createScatterPlot = /* @__PURE__ */ action(
51
59
  if (size !== undefined) throw new Error("createScatterPlot point radius conflicts with size.");
52
60
  }
53
61
 
62
+ const initialPoint = { ...point };
63
+ if (stroke !== undefined) delete initialPoint.strokeWidth;
54
64
  let next = this
55
- .createPointMark({ id, data, ...point })
65
+ .createPointMark({ id, data, ...initialPoint })
56
66
  .encodeX(positionArgs(x, { target: id, coordinate: args.coordinate }))
57
67
  .encodeY(positionArgs(y, { target: id, coordinate: args.coordinate }));
58
68
  if (radius !== undefined) next = next.encodePointRadius({ target: id, value: radius });
59
69
  if (color !== undefined) next = next.encodeColor(targetArgs(color, id));
70
+ if (stroke !== undefined) {
71
+ next = encodeStroke.call(next, targetArgs(stroke, id));
72
+ if (Object.hasOwn(point, "strokeWidth")) next = next.editPointMark({ target: id, strokeWidth: point.strokeWidth });
73
+ }
60
74
  if (size !== undefined) next = next.encodeSize(targetArgs(size, id));
61
75
  if (shape !== undefined) next = next.encodeShape(targetArgs(shape, id));
62
76
  return applyFacadeGuides(next, guides, id);
@@ -191,9 +191,9 @@ export function normalizeArcCategory(value, operation) {
191
191
  }
192
192
 
193
193
  export function normalizeCategoryAggregate(args, operation) {
194
- const aggregate = args.aggregate === undefined ? "count" : args.aggregate;
194
+ const aggregate = args.aggregate === undefined ? args.value === undefined ? "count" : "sum" : args.aggregate;
195
195
  if (!["count", "sum"].includes(aggregate) || (aggregate === "sum") !== (args.value !== undefined)) {
196
- throw new Error(`${operation} requires count without value or explicit sum with value.`);
196
+ throw new Error(`${operation} requires count without value or sum with value.`);
197
197
  }
198
198
  if (args.value !== undefined) validateNonEmptyString(args.value, `${operation} value`);
199
199
  return aggregate;
@@ -0,0 +1,35 @@
1
+ import { action } from "../../core/action.js";
2
+ import { TEXT_MARK_STYLE_OPTIONS } from "../../grammar/text.js";
3
+ import {
4
+ applyFacadeGuides, normalizeAppearance, normalizeEncoding, normalizeFieldEncoding,
5
+ normalizeGuides, positionArgs, resolveFacadeData, resolveFacadeId, targetArgs,
6
+ validateFacadeOptions
7
+ } from "./shared.js";
8
+
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")]);
11
+
12
+ export const createTextPlot = /* @__PURE__ */ action({
13
+ op: "createTextPlot", description: "Create a Cartesian text plot from existing chart data."
14
+ }, function (args = {}) {
15
+ validateFacadeOptions(args, OPTIONS, "createTextPlot");
16
+ const id = resolveFacadeId(this, args.id, { defaultId: "textPlot", operation: "createTextPlot" });
17
+ const data = resolveFacadeData(this, args.data, "createTextPlot");
18
+ const x = normalizeFieldEncoding(args.x, "createTextPlot x");
19
+ const y = normalizeFieldEncoding(args.y, "createTextPlot y");
20
+ const text = normalizeEncoding(args.text, "createTextPlot text");
21
+ if (text === undefined) throw new Error("createTextPlot requires text.");
22
+ const color = normalizeEncoding(args.color, "createTextPlot color");
23
+ const style = normalizeAppearance(args.style, STYLE_OPTIONS, "createTextPlot style");
24
+ const guides = normalizeGuides(args.guides, "createTextPlot");
25
+ const apply = program => {
26
+ let next = program.createTextMark({ id, data, ...style })
27
+ .encodeX(positionArgs(x, { target: id, coordinate: args.coordinate }))
28
+ .encodeY(positionArgs(y, { target: id, coordinate: args.coordinate }))
29
+ .encodeText(targetArgs(text, id));
30
+ if (color !== undefined) next = next.encodeColor(targetArgs(color, id));
31
+ return applyFacadeGuides(next, guides, id);
32
+ };
33
+ apply(this);
34
+ return apply(this);
35
+ });
@@ -1,3 +1,4 @@
1
+ import { resolveGraphicBounds } from "../../layout/canvas.js";
1
2
  import { inheritTextMetrics } from "../textMetrics/index.js";
2
3
  import { action, closedAction } from "../../core/action.js";
3
4
  import { ChartProgram as CoreChartProgram } from "../../core/ChartProgram.js";
@@ -19,7 +20,7 @@ const CONCAT_OPTIONS = Object.freeze([
19
20
  "id", "programs", "gap", "align", "padding"
20
21
  ]);
21
22
  const LAYOUT_EDIT_OPTIONS = Object.freeze([
22
- "columns", "gap", "align", "padding"
23
+ "columns", "gap", "spacing", "align", "padding"
23
24
  ]);
24
25
  const REPLACEMENT_OPTIONS = Object.freeze(["target", "program"]);
25
26
  const INSERTION_OPTIONS = Object.freeze(["id", "program", "before", "after"]);
@@ -179,6 +180,7 @@ const editCompositionLayout = /* @__PURE__ */ closedAction(
179
180
  "editCompositionLayout columns is available only on a facet composition."
180
181
  );
181
182
  }
183
+ if (current.type !== "facet" && Object.hasOwn(args, "spacing")) throw new Error("Plot spacing requires a facet composition.");
182
184
  const padding = Object.hasOwn(args, "padding")
183
185
  ? normalizeCompositionPadding(args.padding, current.padding)
184
186
  : current.padding;
@@ -188,6 +190,8 @@ const editCompositionLayout = /* @__PURE__ */ closedAction(
188
190
  (current.facet.grid?.cells ?? []).map(cell => [cell.id, cell])
189
191
  );
190
192
  layout = resolveFacetLayout({
193
+ spacing: option(args, "spacing", current.spacing),
194
+ plots: current.children.map(id => ({ id, ...resolveGraphicBounds(this.children[id]) })),
191
195
  children: current.children.map((id, index) => ({
192
196
  ...childDescriptor({ id, program: this.children[id] }),
193
197
  value: current.facet.values[index],
@@ -218,7 +222,7 @@ const editCompositionLayout = /* @__PURE__ */ closedAction(
218
222
  children: this.children,
219
223
  compositionSpec: {
220
224
  ...current,
221
- ...(current.type === "facet" ? { columns: layout.columns } : {}),
225
+ ...(current.type === "facet" ? { columns: layout.columns, ...(layout.spacing !== undefined ? { spacing: layout.spacing } : current.spacing !== undefined ? { spacing: "canvas" } : {}) } : {}),
222
226
  gap: layout.gap,
223
227
  align: layout.align,
224
228
  padding: layout.padding
@@ -2,7 +2,7 @@ import { BOX_FIELDS, deriveBoxData, normalizeBoxTransform } from "../../grammar/
2
2
  import { derivedCreator, derivedMaterializer } from "./shared.js";
3
3
 
4
4
  const OPTIONS = Object.freeze([
5
- "id", "source", "category", "field", "whisker", "factor", "as"
5
+ "id", "source", "category", "field", "whisker", "factor", "as", "summary"
6
6
  ]);
7
7
 
8
8
  function materializer(type, op, select) {
@@ -28,6 +28,7 @@ function creator(type, op, materialize) {
28
28
  type,
29
29
  category: args.category,
30
30
  field: args.field,
31
+ ...(args.summary === undefined ? {} : { summary: args.summary }),
31
32
  ...(args.whisker === undefined ? {} : { whisker: args.whisker }),
32
33
  ...(args.factor === undefined ? {} : { factor: args.factor }),
33
34
  as: args.as ?? BOX_FIELDS
@@ -1,4 +1,6 @@
1
1
  import { closedAction } from "../../core/action.js";
2
+ import { validateGraphicItemLimit } from "../../core/validation.js";
3
+ import { resolveGraphicItemCapacity } from "../../selectors/graphicCapacity.js";
2
4
  import { validateUserId } from "../../core/identifiers.js";
3
5
 
4
6
  import {
@@ -89,6 +91,7 @@ export const releaseDerivedData = /* @__PURE__ */ closedAction(
89
91
  next = next._withoutMaterializationConfig([
90
92
  "calculations", "datasets", validatedId
91
93
  ]);
94
+ next = next._withoutMaterializationConfig(["regressionFits", validatedId]);
92
95
  for (const owner of selfOwners) {
93
96
  const [, family, ownerId] = owner.path;
94
97
  next = next._withoutMaterializationConfig(["data", family, ownerId]);
@@ -110,10 +113,17 @@ export const rebindLayerData = /* @__PURE__ */ closedAction(
110
113
  requestedData,
111
114
  "Layer dataset"
112
115
  ).id;
113
- requireLayer(this, id);
116
+ const layer = requireLayer(this, id);
114
117
  if (!hasDataset(this, data)) {
115
118
  throw new Error(`Layer dataset "${data}" does not exist.`);
116
119
  }
120
+ const dataset = findDataset(this, data);
121
+ if (layer.mark?.type === "point" && Array.isArray(dataset.values)) {
122
+ validateGraphicItemLimit(dataset.values.length, {
123
+ ...resolveGraphicItemCapacity(this, { id, label: "Point mark item count" }),
124
+ rawCount: dataset.values.length
125
+ });
126
+ }
117
127
  return this.editSemantic({
118
128
  property: `layer[${id}].data`,
119
129
  value: data
@@ -38,6 +38,7 @@ const CREATOR_BY_TYPE = Object.freeze({
38
38
  interval: "createIntervalData",
39
39
  normalize: "createNormalizedData",
40
40
  regression: "createRegressionData",
41
+ regressionStatistics: "createRegressionStatisticsData",
41
42
  sort: "createSortedData",
42
43
  stack: "createStackData",
43
44
  summary: "createSummaryData",
@@ -57,6 +58,7 @@ const REVISION_ROLE = Object.freeze({
57
58
  interval: "IntervalData",
58
59
  normalize: "NormalizedData",
59
60
  regression: "RegressionData",
61
+ regressionStatistics: "RegressionStatisticsData",
60
62
  sort: "SortedData",
61
63
  stack: "StackData",
62
64
  summary: "SummaryData",
@@ -509,7 +511,7 @@ export const editStackData = /* @__PURE__ */ focusedEditor(
509
511
  );
510
512
  export const editRegressionData = /* @__PURE__ */ focusedEditor(
511
513
  "editRegressionData", "regression", [
512
- "x", "y", "groupBy", "method", "degree", "span", "confidenceMethod",
514
+ "x", "y", "groupBy", "method", "degree", "span", "robustIterations", "confidenceMethod",
513
515
  "level", "confidence", "interval", "predict"
514
516
  ]
515
517
  );
@@ -21,6 +21,7 @@ import {
21
21
  materializeGradientProfileData
22
22
  } from "./gradientProfile.js";
23
23
  import { createRegressionData, materializeRegressionData } from "./regression.js";
24
+ import { createRegressionStatisticsData, editRegressionStatisticsData, materializeRegressionStatisticsData } from "./regressionStatistics.js";
24
25
  import {
25
26
  createCategoricalDensityData,
26
27
  createDensityData,
@@ -69,6 +70,9 @@ export function registerDataActions(ProgramClass) {
69
70
  materializeEmptyMark,
70
71
  removeMarkFilter,
71
72
  materializeRegressionData,
73
+ materializeRegressionStatisticsData,
74
+ createRegressionStatisticsData,
75
+ editRegressionStatisticsData,
72
76
  materializeDensityData,
73
77
  createGradientProfileData,
74
78
  materializeGradientProfileData,
@@ -5,33 +5,42 @@ import {
5
5
  deriveRegression,
6
6
  normalizeRegressionTransform
7
7
  } from "../../grammar/regression/index.js";
8
- import { resolveDatasetReference } from "../../selectors/datasets.js";
8
+ import { resolveDatasetReference, requireDataset, findDataset } from "../../selectors/datasets.js";
9
9
  import { derivedMaterializer } from "./shared.js";
10
10
 
11
11
  const OPTIONS = Object.freeze([
12
- "id", "source", "x", "y", "groupBy", "method", "degree", "span",
12
+ "id", "source", "x", "y", "groupBy", "method", "degree", "span", "robustIterations",
13
13
  "confidenceMethod", "level", "confidence", "interval"
14
14
  , "predict", "missing"
15
15
  ]);
16
16
 
17
+ function calculateRegression(values, transform) {
18
+ return deriveRegression(values, transform);
19
+ }
20
+
21
+ function retainFitStatistics(program, { id, result }) {
22
+ return result.fitStatistics === undefined ? program : program._withMaterializationConfig(
23
+ ["regressionFits", id], result.fitStatistics
24
+ );
25
+ }
26
+
27
+ function materializeMissingFitStatistics(program, args) {
28
+ const dataset = findDataset(program, args.id);
29
+ if (dataset?.values === undefined || dataset.transform?.length !== 1 ||
30
+ dataset.transform[0].type !== "regression" || dataset.transform[0].method !== "linear" ||
31
+ program.materializationConfigs.regressionFits?.[dataset.id] !== undefined) return undefined;
32
+ const source = requireDataset(program, dataset.source);
33
+ const result = calculateRegression(source.values, dataset.transform[0]);
34
+ return retainFitStatistics(program, { id: dataset.id, result });
35
+ }
36
+
17
37
  export const materializeRegressionData = /* @__PURE__ */ derivedMaterializer(
18
38
  "materializeRegressionData",
19
39
  "Materialize one regression derived dataset.",
20
40
  "regression",
21
- (values, transform) => deriveRegression(values, {
22
- x: transform.x,
23
- y: transform.y,
24
- groupBy: transform.groupBy,
25
- method: transform.method,
26
- degree: transform.degree,
27
- span: transform.span,
28
- confidenceMethod: transform.confidenceMethod,
29
- level: transform.level,
30
- confidence: transform.confidence,
31
- interval: transform.interval,
32
- predict: transform.predict
33
- , missing: transform.missing
34
- })
41
+ calculateRegression,
42
+ undefined,
43
+ { before: materializeMissingFitStatistics, after: retainFitStatistics }
35
44
  );
36
45
 
37
46
  export const createRegressionData = /* @__PURE__ */ closedAction(
@@ -0,0 +1,73 @@
1
+ import { closedAction } from "../../core/action.js";
2
+ import { validateUserId } from "../../core/identifiers.js";
3
+ import { requireDataset, resolveDatasetReference, hasDataset, hasDatasetOwner } from "../../selectors/datasets.js";
4
+ import {
5
+ requireRegressionStatisticsSource,
6
+ validateRegressionFitStatistics,
7
+ regressionStatisticsRows
8
+ } from "../../grammar/regression/statistics.js";
9
+ import { requireDerivedDataset } from "./shared.js";
10
+ import { resolveDerivedDataOwner, reviseDerivedData } from "./edit.js";
11
+
12
+ function ensureFitStatistics(program, source) {
13
+ requireRegressionStatisticsSource(source);
14
+ if (!Array.isArray(source.values)) throw new Error("Regression statistics requires a materialized linear fit source.");
15
+ const next = program.materializationConfigs.regressionFits?.[source.id] === undefined
16
+ ? program.materializeRegressionData({ id: source.id }) : program;
17
+ validateRegressionFitStatistics(next.materializationConfigs.regressionFits[source.id], source,
18
+ requireDataset(next, source.source));
19
+ return next;
20
+ }
21
+
22
+ function statisticsOwner(program, target) {
23
+ if (target !== undefined) return resolveDerivedDataOwner(program, target, "regressionStatistics");
24
+ const records = Object.entries(program.materializationConfigs.data?.regressionStatistics ?? {})
25
+ .map(([owner, config]) => ({ owner, current: config.current }));
26
+ const current = records.filter(record => record.current === program.context.currentData);
27
+ const candidate = current.length === 1 ? current[0] : records.length === 1 ? records[0] : undefined;
28
+ if (candidate === undefined) throw new Error(records.length === 0
29
+ ? "No standalone regression statistics dataset is available."
30
+ : "Regression statistics target is ambiguous; provide target.");
31
+ return resolveDerivedDataOwner(program, candidate.owner, "regressionStatistics");
32
+ }
33
+
34
+ export const materializeRegressionStatisticsData = /* @__PURE__ */ closedAction(
35
+ { op: "materializeRegressionStatisticsData", description: "Materialize statistics from an existing linear fit cache." }, ["id"],
36
+ function (args = {}) {
37
+ const { id, source } = requireDerivedDataset(this, args.id, "regressionStatistics");
38
+ const next = ensureFitStatistics(this, source);
39
+ const values = regressionStatisticsRows(next.materializationConfigs.regressionFits[source.id], source);
40
+ return next.editSemantic({ property: `dataset[${id}].values`, value: values });
41
+ }
42
+ );
43
+
44
+ export const createRegressionStatisticsData = /* @__PURE__ */ closedAction(
45
+ { op: "createRegressionStatisticsData", description: "Create counts, coefficients and R-squared from a linear regression dataset." }, ["id", "source"],
46
+ function (args = {}) {
47
+ const id = validateUserId(args.id, "Regression statistics dataset id");
48
+ if (hasDataset(this, id) || hasDatasetOwner(this, id)) throw new Error(`Dataset "${id}" already exists.`);
49
+ const source = resolveDatasetReference(this,
50
+ validateUserId(args.source, "Regression statistics source id"), "Regression statistics source");
51
+ const next = ensureFitStatistics(this, source)
52
+ .createDerivedData({ id, source: source.id, transform: [{ type: "regressionStatistics" }] })
53
+ .materializeRegressionStatisticsData({ id });
54
+ return this.actionStack.length === 1 ? next._withMaterializationConfig(
55
+ ["data", "regressionStatistics", id], { current: id }
56
+ ) : next;
57
+ }
58
+ );
59
+
60
+ export const editRegressionStatisticsData = /* @__PURE__ */ closedAction(
61
+ { op: "editRegressionStatisticsData", description: "Revise the source of a standalone regression statistics dataset." }, ["target", "source"],
62
+ function (args = {}) {
63
+ const resolved = statisticsOwner(this, args.target);
64
+ if (!Object.hasOwn(args, "source")) return this;
65
+ const source = resolveDatasetReference(this,
66
+ validateUserId(args.source, "Regression statistics source id"), "Regression statistics source");
67
+ if (source.id === resolved.dataset.source) return this;
68
+ const next = ensureFitStatistics(this, source);
69
+ return reviseDerivedData(next, {
70
+ resolved, source: source.id, definition: { type: "regressionStatistics" }, dependents: "recompute"
71
+ });
72
+ }
73
+ );
@@ -35,9 +35,11 @@ export function requireDerivedDataset(program, id, type) {
35
35
  return { id: validatedId, dataset, source, transform: dataset.transform[0] };
36
36
  }
37
37
 
38
- export function derivedMaterializer(op, description, type, derive, resolve) {
38
+ export function derivedMaterializer(op, description, type, derive, resolve, { before, after } = {}) {
39
39
  return action({ op, description }, function (args = {}) {
40
40
  validateKeys(args, MATERIALIZE_OPTIONS, op);
41
+ const existing = before?.(this, args);
42
+ if (existing !== undefined) return existing;
41
43
  const { id, source, transform } = requireDerivedDataset(
42
44
  this,
43
45
  args.id,
@@ -51,6 +53,7 @@ export function derivedMaterializer(op, description, type, derive, resolve) {
51
53
  const values = Array.isArray(result) ? result : result.values;
52
54
  const schema = deriveTransformSchema(source.schema, transform, values, {
53
55
  sourceId: source.id,
56
+ sourceTransform: source.transform?.[0],
54
57
  ownerId: id
55
58
  });
56
59
  validateRowsAgainstSchema(values, schema, `Derived dataset "${id}"`);
@@ -75,7 +78,7 @@ export function derivedMaterializer(op, description, type, derive, resolve) {
75
78
  report
76
79
  );
77
80
  }
78
- return next;
81
+ return after === undefined ? next : after(next, { id, source, transform, result });
79
82
  });
80
83
  }
81
84
 
@@ -87,11 +90,12 @@ export function derivedCreator(
87
90
  sourceLabel,
88
91
  transform,
89
92
  materialize,
90
- requireSource = false
93
+ requireSource = false,
94
+ defaultId
91
95
  ) {
92
96
  return action({ op, description }, function (args = {}) {
93
97
  validateKeys(args, options, op);
94
- const id = validateUserId(args.id, idLabel);
98
+ const id = validateUserId(args.id === undefined ? defaultId : args.id, idLabel);
95
99
  const requestedSource = validateUserId(
96
100
  requireSource ? args.source : args.source ?? this.context.currentData,
97
101
  sourceLabel
@@ -22,5 +22,7 @@ export const createStackData = /* @__PURE__ */ derivedCreator(
22
22
  "Stack dataset id",
23
23
  "Stack source dataset id",
24
24
  normalizeStackTransform,
25
- "materializeStackData"
25
+ "materializeStackData",
26
+ false,
27
+ "stacked"
26
28
  );