ggaction 0.0.3 → 0.0.5

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 (223) hide show
  1. package/CHANGELOG.md +58 -3
  2. package/README.md +117 -83
  3. package/package.json +27 -11
  4. package/src/actions/boxPlots/create.js +13 -7
  5. package/src/actions/boxPlots/edit.js +31 -35
  6. package/src/actions/boxPlots/materialize.js +3 -0
  7. package/src/actions/boxPlots/options.js +8 -1
  8. package/src/actions/boxPlots/resolve.js +10 -2
  9. package/src/actions/charts/bar.js +50 -0
  10. package/src/actions/charts/heatmap.js +205 -0
  11. package/src/actions/charts/histogram.js +58 -0
  12. package/src/actions/charts/index.js +15 -0
  13. package/src/actions/charts/line.js +69 -0
  14. package/src/actions/charts/parallel.js +76 -0
  15. package/src/actions/charts/scatter.js +56 -0
  16. package/src/actions/charts/shared.js +121 -0
  17. package/src/actions/coordinates/parallel.js +50 -0
  18. package/src/actions/data/bin2d.js +137 -0
  19. package/src/actions/data/density.js +54 -13
  20. package/src/actions/data/derived.js +22 -0
  21. package/src/actions/data/filter.js +5 -0
  22. package/src/actions/data/gradientProfile.js +65 -0
  23. package/src/actions/data/horizon.js +71 -0
  24. package/src/actions/data/index.js +29 -2
  25. package/src/actions/data/regression.js +1 -1
  26. package/src/actions/data/window.js +50 -0
  27. package/src/actions/encodings/appearance.js +1 -1
  28. package/src/actions/encodings/color/continuous.js +137 -0
  29. package/src/actions/encodings/color/index.js +167 -0
  30. package/src/actions/encodings/color/layout.js +99 -0
  31. package/src/actions/encodings/color/policy.js +88 -0
  32. package/src/actions/encodings/density/resolve.js +109 -0
  33. package/src/actions/encodings/density.js +193 -88
  34. package/src/actions/encodings/horizon/resolve.js +283 -0
  35. package/src/actions/encodings/horizon.js +364 -0
  36. package/src/actions/encodings/index.js +8 -2
  37. package/src/actions/encodings/offset.js +1 -1
  38. package/src/actions/encodings/parallel.js +115 -0
  39. package/src/actions/encodings/pathOrder.js +131 -0
  40. package/src/actions/encodings/position/apply.js +73 -4
  41. package/src/actions/encodings/{position.js → position/index.js} +21 -8
  42. package/src/actions/encodings/position/policies/arc.js +27 -4
  43. package/src/actions/encodings/position/policies/area.js +33 -2
  44. package/src/actions/encodings/position/policies/index.js +7 -2
  45. package/src/actions/encodings/position/policies/line.js +18 -2
  46. package/src/actions/encodings/position/resolve.js +25 -3
  47. package/src/actions/encodings/ranged.js +5 -3
  48. package/src/actions/encodings/ruleAppearance.js +88 -10
  49. package/src/actions/encodings/strokeDash.js +1 -1
  50. package/src/actions/encodings/text.js +1 -1
  51. package/src/actions/errorBands/options.js +1 -1
  52. package/src/actions/errorBars/options.js +1 -1
  53. package/src/actions/facets/actions.js +2 -3
  54. package/src/actions/facets/derive.js +35 -5
  55. package/src/actions/facets/guides.js +1 -1
  56. package/src/actions/facets/replay.js +7 -36
  57. package/src/actions/gradientPlots/components.js +177 -0
  58. package/src/actions/gradientPlots/create.js +96 -0
  59. package/src/actions/gradientPlots/edit.js +153 -0
  60. package/src/actions/gradientPlots/index.js +24 -0
  61. package/src/actions/gradientPlots/materialize.js +287 -0
  62. package/src/actions/gradientPlots/options.js +120 -0
  63. package/src/actions/gradientPlots/paint.js +107 -0
  64. package/src/actions/gradientPlots/rebind.js +51 -0
  65. package/src/actions/gradientPlots/resolve.js +81 -0
  66. package/src/actions/guides/applicability.js +44 -17
  67. package/src/actions/guides/axes/axes.js +37 -3
  68. package/src/actions/guides/axes/index.js +2 -0
  69. package/src/actions/guides/axes/labels.js +12 -7
  70. package/src/actions/guides/axes/parallel/resolve.js +86 -0
  71. package/src/actions/guides/axes/parallel.js +228 -0
  72. package/src/actions/guides/axes/ticks.js +1 -1
  73. package/src/actions/guides/axes/titles.js +12 -9
  74. package/src/actions/guides/grids/resolve.js +1 -1
  75. package/src/actions/guides/guides.js +60 -5
  76. package/src/actions/guides/legends/categorical/actions.js +48 -11
  77. package/src/actions/guides/legends/categorical/layout.js +4 -2
  78. package/src/actions/guides/legends/continuous/interval.js +1 -1
  79. package/src/actions/guides/legends/continuous/opacity.js +1 -1
  80. package/src/actions/guides/legends/edit.js +7 -20
  81. package/src/actions/guides/legends/index.js +2 -0
  82. package/src/actions/guides/legends/remove.js +6 -31
  83. package/src/actions/guides/legends/size.js +1 -1
  84. package/src/actions/guides/legends/strokeWidth.js +170 -0
  85. package/src/actions/guides/legends/target.js +25 -0
  86. package/src/actions/guides/polar/{axes.js → axes/index.js} +5 -5
  87. package/src/actions/guides/polar/index.js +1 -1
  88. package/src/actions/guides/polar/resolve.js +1 -1
  89. package/src/actions/guides/tickValues.js +1 -1
  90. package/src/actions/index.js +6 -0
  91. package/src/actions/intervals/resolve.js +1 -1
  92. package/src/actions/marks/arc/actions.js +2 -2
  93. package/src/actions/marks/area/actions.js +61 -137
  94. package/src/actions/marks/area/materialize.js +163 -0
  95. package/src/actions/marks/bar/edit.js +1 -1
  96. package/src/actions/marks/index.js +5 -1
  97. package/src/actions/marks/line/actions.js +100 -81
  98. package/src/actions/marks/line/materialize.js +146 -0
  99. package/src/actions/marks/point/create.js +76 -0
  100. package/src/actions/marks/point/edit.js +85 -0
  101. package/src/actions/marks/point/index.js +11 -1
  102. package/src/actions/marks/point/jitter.js +72 -0
  103. package/src/actions/marks/point/{actions.js → materialize.js} +175 -170
  104. package/src/actions/marks/rect/actions.js +4 -1
  105. package/src/actions/marks/remove.js +13 -2
  106. package/src/actions/marks/rule/actions.js +26 -4
  107. package/src/actions/marks/shared.js +1 -1
  108. package/src/actions/marks/text/actions.js +20 -5
  109. package/src/actions/marks/text/index.js +7 -1
  110. package/src/actions/marks/text/layout.js +321 -0
  111. package/src/actions/primitives/semanticValidation/dataset.js +22 -0
  112. package/src/actions/primitives/semanticValidation/guide.js +43 -0
  113. package/src/actions/primitives/semanticValidation/index.js +23 -0
  114. package/src/actions/primitives/semanticValidation/layer.js +74 -0
  115. package/src/actions/primitives/semanticValidation/scale.js +74 -0
  116. package/src/actions/primitives/semanticValidation/shared.js +5 -0
  117. package/src/actions/primitives/semanticValue.js +1 -204
  118. package/src/actions/regression/components.js +1 -1
  119. package/src/actions/regression/create.js +1 -1
  120. package/src/actions/regression/edit.js +20 -22
  121. package/src/actions/scales/consumers/common.js +109 -0
  122. package/src/actions/scales/consumers/families.js +58 -0
  123. package/src/actions/scales/consumers/index.js +41 -0
  124. package/src/actions/scales/{consumers.js → consumers/seriesLayout.js} +10 -132
  125. package/src/actions/scales/create.js +1 -1
  126. package/src/actions/scales/definitions.js +32 -2
  127. package/src/actions/scales/edit.js +8 -206
  128. package/src/actions/scales/editPolicy.js +214 -0
  129. package/src/actions/scales/materialize.js +3 -3
  130. package/src/actions/scales/patch.js +26 -0
  131. package/src/actions/selection/actions.js +28 -3
  132. package/src/actions/violinPlots/create.js +198 -0
  133. package/src/actions/violinPlots/index.js +5 -0
  134. package/src/core/ChartProgram.js +22 -236
  135. package/src/core/compositionState.js +169 -0
  136. package/src/core/materializationState.js +65 -0
  137. package/src/core/programState.js +28 -0
  138. package/src/core/textMetrics.js +47 -0
  139. package/src/core/vocabulary.js +7 -2
  140. package/src/grammar/arcs.js +50 -13
  141. package/src/grammar/areaSeries.js +42 -6
  142. package/src/grammar/bars/aggregate.js +1 -1
  143. package/src/grammar/bin2d.js +333 -0
  144. package/src/grammar/categoricalDensity.js +192 -0
  145. package/src/grammar/coordinates.js +1 -1
  146. package/src/grammar/curveCommands.js +3 -1
  147. package/src/grammar/density.js +253 -27
  148. package/src/grammar/facets/dependencies.js +3 -11
  149. package/src/grammar/{facets.js → facets/index.js} +8 -6
  150. package/src/grammar/gradientProfile.js +213 -0
  151. package/src/grammar/horizon.js +455 -0
  152. package/src/grammar/interval.js +2 -96
  153. package/src/grammar/jitter.js +197 -0
  154. package/src/grammar/lineSeries.js +70 -1
  155. package/src/grammar/paint.js +88 -0
  156. package/src/grammar/palettes.js +5 -7
  157. package/src/grammar/parallelCoordinates.js +213 -0
  158. package/src/grammar/pathOrder.js +35 -0
  159. package/src/grammar/pointShapes.js +24 -0
  160. package/src/grammar/polarLineCommands.js +1 -1
  161. package/src/grammar/regression/derive.js +97 -0
  162. package/src/grammar/regression/index.js +22 -0
  163. package/src/grammar/regression/models.js +282 -0
  164. package/src/grammar/regression/parameters.js +106 -0
  165. package/src/grammar/rules.js +2 -1
  166. package/src/grammar/scales/appearance.js +21 -0
  167. package/src/grammar/scales/color.js +4 -2
  168. package/src/grammar/schemas/concreteGraphic.js +11 -1
  169. package/src/grammar/schemas/graphicBounds.js +19 -37
  170. package/src/grammar/schemas/semanticPath.js +17 -2
  171. package/src/grammar/statistics/studentT.js +98 -0
  172. package/src/grammar/transforms.js +101 -12
  173. package/src/grammar/window.js +339 -0
  174. package/src/layout/labels.js +270 -0
  175. package/src/layout/text.js +4 -1
  176. package/src/layout/title.js +10 -20
  177. package/src/materialization/bars/aggregate.js +1 -1
  178. package/src/materialization/bars/grouped.js +1 -1
  179. package/src/materialization/bars/histogram.js +1 -1
  180. package/src/materialization/bars/ranged.js +1 -1
  181. package/src/materialization/dataProvenance.js +56 -2
  182. package/src/materialization/density.js +1 -1
  183. package/src/materialization/dependencies.js +15 -101
  184. package/src/materialization/encodings.js +2 -2
  185. package/src/materialization/facetGuides/index.js +2 -0
  186. package/src/materialization/facetGuides/legacyCategorical.js +125 -0
  187. package/src/materialization/facetGuides/placement.js +126 -0
  188. package/src/materialization/facetGuides/preparation.js +188 -0
  189. package/src/materialization/facets.js +1 -1
  190. package/src/materialization/guides/resources.js +74 -24
  191. package/src/materialization/horizon.js +29 -0
  192. package/src/materialization/layout.js +15 -0
  193. package/src/materialization/marks/capabilities.js +161 -0
  194. package/src/materialization/marks/index.js +108 -0
  195. package/src/materialization/marks/pathOrder.js +71 -0
  196. package/src/materialization/marks/policies.js +91 -0
  197. package/src/materialization/rect.js +1 -1
  198. package/src/materialization/rowEncoding.js +1 -1
  199. package/src/materialization/scaleGuideDependencies.js +102 -0
  200. package/src/materialization/scales/map.js +1 -1
  201. package/src/materialization/scales/policies/series.js +1 -1
  202. package/src/materialization/scales/resolve.js +3 -1
  203. package/src/materialization/selection/items/bar.js +1 -1
  204. package/src/materialization/selection/items/path.js +27 -1
  205. package/src/materialization/selection/items/point.js +1 -5
  206. package/src/materialization/selection/items/rect.js +21 -0
  207. package/src/materialization/selection/styles.js +21 -10
  208. package/src/materialization/text.js +12 -4
  209. package/src/renderers/canvas/fill.js +33 -0
  210. package/src/renderers/canvas/index.js +7 -0
  211. package/src/renderers/canvas/path.js +7 -4
  212. package/src/renderers/canvas/rect.js +9 -3
  213. package/src/renderers/canvas/text.js +14 -1
  214. package/src/selectors/datasets.js +4 -0
  215. package/src/theme/defaults.js +4 -0
  216. package/types/extension.d.ts +10 -1
  217. package/types/index.d.ts +76 -0
  218. package/types/program.d.ts +632 -10
  219. package/src/actions/encodings/color.js +0 -424
  220. package/src/grammar/regression.js +0 -578
  221. package/src/grammar/scales.js +0 -1
  222. package/src/materialization/facetGuides.js +0 -426
  223. package/src/materialization/marks.js +0 -316
@@ -1,204 +1 @@
1
- import { validateUserId } from "../../core/identifiers.js";
2
- import { isPlainObject } from "../../core/immutable.js";
3
- import {
4
- COLOR_LAYOUTS,
5
- STACK_MODES,
6
- CATEGORICAL_LEGEND_CHANNELS,
7
- MARK_TYPES
8
- } from "../../core/vocabulary.js";
9
- import { validateAggregate } from "../../grammar/aggregate.js";
10
- import { findLayer, findSemanticScale, hasDataset } from "../../selectors/index.js";
11
- import { validateCoordinateType } from "../../grammar/coordinates.js";
12
- import {
13
- normalizeHistogramBin,
14
- validateHistogramBinBoundaries,
15
- validateHistogramBinStep
16
- } from "../../grammar/histogram.js";
17
- import { validateDatasetTransforms } from "../../grammar/transforms.js";
18
- import {
19
- validateSemanticFieldType,
20
- validateContinuousColorInterpolation,
21
- validateScalePropertyForType,
22
- validateSemanticScaleDomain,
23
- validateSemanticScaleRange,
24
- validateSemanticScaleType
25
- } from "../../grammar/scales.js";
26
-
27
- function nonEmptyString(value, label) {
28
- if (typeof value !== "string" || value.length === 0) {
29
- throw new TypeError(`${label} must be a non-empty string.`);
30
- }
31
- }
32
-
33
- function validateLegend(property, value) {
34
- if (property === "title") {
35
- nonEmptyString(value, "Legend title");
36
- return;
37
- }
38
- if (!Array.isArray(value) || value.length === 0) {
39
- throw new TypeError(`Legend ${property} must be a non-empty array.`);
40
- }
41
- if (new Set(value).size !== value.length) {
42
- throw new Error(`Legend ${property} must not contain duplicates.`);
43
- }
44
- if (property === "channels") {
45
- if (!value.every(channel => CATEGORICAL_LEGEND_CHANNELS.includes(channel))) {
46
- throw new Error("Legend channels support only color, strokeDash, and shape.");
47
- }
48
- return;
49
- }
50
- for (const id of value) validateUserId(id, "Legend scale id");
51
- }
52
-
53
- export function validateSemanticValue(program, parsed, value) {
54
- if (parsed.kind === "dataset" && parsed.path[0] === "values") {
55
- if (!Array.isArray(value) || !value.every(isPlainObject)) {
56
- throw new TypeError("Dataset values must be an array of plain row objects.");
57
- }
58
- }
59
- if (parsed.kind === "dataset" && parsed.path[0] === "source") {
60
- validateUserId(value, "Dataset source id");
61
- if (!hasDataset(program, value)) {
62
- throw new Error(`Unknown source dataset "${value}".`);
63
- }
64
- }
65
- if (parsed.kind === "dataset" && parsed.path[0] === "transform") {
66
- validateDatasetTransforms(value);
67
- }
68
- if (
69
- parsed.kind === "layer" &&
70
- parsed.path.join(".") === "mark.type" &&
71
- !MARK_TYPES.includes(value)
72
- ) {
73
- throw new Error(`Unknown mark type "${value}".`);
74
- }
75
- if (parsed.kind === "layer") {
76
- const property = parsed.path.join(".");
77
- if (property === "source") {
78
- validateUserId(value, "Layer source id");
79
- if (value === parsed.id) {
80
- throw new Error("A layer cannot use itself as its source.");
81
- }
82
- const source = findLayer(program, value);
83
- if (source === undefined) {
84
- throw new Error(`Unknown source layer "${value}".`);
85
- }
86
- if (!["point", "bar", "rule", "rect"].includes(source.mark?.type)) {
87
- throw new Error(
88
- `Layer source "${value}" must be a point, bar, rule, or rect mark.`
89
- );
90
- }
91
- }
92
- if (property.endsWith(".title")) {
93
- nonEmptyString(value, "Encoding title");
94
- }
95
- if (property.endsWith(".fieldType")) validateSemanticFieldType(value);
96
- if (property.endsWith(".aggregate")) validateAggregate(value);
97
- if (property.endsWith(".bin.maxBins")) normalizeHistogramBin({ maxBins: value });
98
- if (property.endsWith(".bin.step")) validateHistogramBinStep(value);
99
- if (property.endsWith(".bin.boundaries")) {
100
- validateHistogramBinBoundaries(value);
101
- }
102
- if (
103
- property.endsWith(".stack") &&
104
- value !== null &&
105
- !STACK_MODES.includes(value)
106
- ) {
107
- throw new Error(`Unsupported stack "${value}".`);
108
- }
109
- if (
110
- property === "encoding.color.layout" &&
111
- !COLOR_LAYOUTS.includes(value)
112
- ) {
113
- throw new Error(`Unsupported color layout "${value}".`);
114
- }
115
- }
116
- if (parsed.kind === "scale") {
117
- const property = parsed.path.join(".");
118
- const existing = findSemanticScale(program, parsed.id);
119
- if (property === "type") {
120
- validateSemanticScaleType(value);
121
- for (const owned of [
122
- "nice", "zero", "clamp", "base", "exponent", "constant",
123
- "paddingInner", "paddingOuter", "padding", "align"
124
- ]) {
125
- if (existing?.[owned] !== undefined) {
126
- validateScalePropertyForType(value, owned);
127
- }
128
- }
129
- } else if (property === "domain") validateSemanticScaleDomain(value);
130
- else if (property === "range") validateSemanticScaleRange(value);
131
- else if (property === "nice") {
132
- if (typeof value !== "boolean") throw new TypeError("Scale nice must be a boolean.");
133
- if (existing?.type !== undefined) {
134
- validateScalePropertyForType(existing.type, "nice");
135
- }
136
- } else if (property === "zero") {
137
- if (typeof value !== "boolean") throw new TypeError("Scale zero must be a boolean.");
138
- if (existing?.type !== undefined) {
139
- validateScalePropertyForType(existing.type, "zero");
140
- }
141
- } else if (property === "clamp") {
142
- if (typeof value !== "boolean") throw new TypeError("Scale clamp must be a boolean.");
143
- if (existing?.type !== undefined) {
144
- validateScalePropertyForType(existing.type, "clamp");
145
- }
146
- } else if (property === "reverse") {
147
- if (typeof value !== "boolean") throw new TypeError("Scale reverse must be a boolean.");
148
- } else if (["base", "exponent", "constant"].includes(property)) {
149
- if (!Number.isFinite(value) || value <= 0) {
150
- throw new RangeError(`Scale ${property} must be a positive finite number.`);
151
- }
152
- if (property === "base" && value === 1) {
153
- throw new RangeError("Scale base must not equal 1.");
154
- }
155
- if (existing?.type !== undefined) {
156
- validateScalePropertyForType(existing.type, property);
157
- }
158
- } else if (property === "interpolate") {
159
- validateContinuousColorInterpolation(value);
160
- } else if (property === "paddingInner") {
161
- if (!Number.isFinite(value) || value < 0 || value >= 1) {
162
- throw new RangeError(
163
- "Scale paddingInner must be from 0 (inclusive) to 1 (exclusive)."
164
- );
165
- }
166
- if (existing?.type !== undefined) {
167
- validateScalePropertyForType(existing.type, property);
168
- }
169
- } else if (property === "paddingOuter" || property === "padding") {
170
- if (!Number.isFinite(value) || value < 0) {
171
- throw new RangeError(`Scale ${property} must be a non-negative finite number.`);
172
- }
173
- if (existing?.type !== undefined) {
174
- validateScalePropertyForType(existing.type, property);
175
- }
176
- } else if (property === "align") {
177
- if (!Number.isFinite(value) || value < 0 || value > 1) {
178
- throw new RangeError("Scale align must be between 0 and 1.");
179
- }
180
- if (existing?.type !== undefined) {
181
- validateScalePropertyForType(existing.type, property);
182
- }
183
- }
184
- }
185
- if (parsed.kind === "coordinate" && parsed.path[0] === "type") {
186
- validateCoordinateType(value);
187
- }
188
- if (parsed.kind === "guide" && parsed.id === "legend.series") {
189
- validateLegend(parsed.path.at(-1), value);
190
- }
191
- if (parsed.kind === "guide" && (
192
- parsed.id.startsWith("grid.") || parsed.id.startsWith("axis.")
193
- )) {
194
- const property = parsed.path.at(-1);
195
- if (property === "title") nonEmptyString(value, "Axis title");
196
- else {
197
- const guideKind = parsed.id.startsWith("grid.") ? "Grid" : "Axis";
198
- validateUserId(value, `${guideKind} ${property} id`);
199
- }
200
- }
201
- if (parsed.kind === "title") {
202
- nonEmptyString(value, `Chart title ${parsed.path[0]}`);
203
- }
204
- }
1
+ export { validateSemanticValue } from "./semanticValidation/index.js";
@@ -7,7 +7,7 @@ import { resolveEligibleLayer } from "../../selectors/layers.js";
7
7
  import {
8
8
  REGRESSION_LOWER_FIELD,
9
9
  REGRESSION_UPPER_FIELD
10
- } from "../../grammar/regression.js";
10
+ } from "../../grammar/regression/index.js";
11
11
  import { validateAreaCreateOutline } from "../marks/area/index.js";
12
12
 
13
13
  const BAND_OPTIONS = Object.freeze([
@@ -1,6 +1,6 @@
1
1
  import { action } from "../../core/action.js";
2
2
  import { validateKeys } from "../../core/validation.js";
3
- import { normalizeRegressionParameters } from "../../grammar/regression.js";
3
+ import { normalizeRegressionParameters } from "../../grammar/regression/index.js";
4
4
  import {
5
5
  findRegressionPoint,
6
6
  inferRegressionGroup,
@@ -7,9 +7,10 @@ import {
7
7
  validateNonNegativeFinite,
8
8
  validateUnitInterval
9
9
  } from "../../core/validation.js";
10
- import { normalizeRegressionParameters } from "../../grammar/regression.js";
10
+ import { normalizeRegressionParameters } from "../../grammar/regression/index.js";
11
11
  import { validateCurveInterpolation } from "../../grammar/curveCommands.js";
12
- import { hasDataset } from "../../selectors/index.js";
12
+ import { planDerivedDataRevision } from
13
+ "../../materialization/dataProvenance.js";
13
14
  import { findLayer } from "../../selectors/layers.js";
14
15
 
15
16
  const OPTIONS = Object.freeze([
@@ -117,14 +118,6 @@ function resolveParameters(previous, args) {
117
118
  return normalizeRegressionParameters(raw);
118
119
  }
119
120
 
120
- function nextRevisionId(program, ownerId) {
121
- let revision = 1;
122
- while (hasDataset(program, `${ownerId}RegressionDataRevision${revision}`)) {
123
- revision += 1;
124
- }
125
- return `${ownerId}RegressionDataRevision${revision}`;
126
- }
127
-
128
121
  function removeBand(program, id) {
129
122
  return program
130
123
  .editSemantic({ property: `layer[${id}]`, remove: true })
@@ -165,9 +158,20 @@ export const editRegression = action(
165
158
  : hadBand || bandPatch !== undefined || current.parameters.method === "loess");
166
159
  let dataId = current.dataId;
167
160
  let next = this;
161
+ const revision = changesStatistics
162
+ ? planDerivedDataRevision(this, {
163
+ owner: owner.id,
164
+ role: "RegressionData",
165
+ previous: current.dataId,
166
+ consumers: [
167
+ current.lineId,
168
+ ...(hadBand ? [current.bandId] : [])
169
+ ]
170
+ })
171
+ : undefined;
168
172
 
169
173
  if (changesStatistics) {
170
- dataId = nextRevisionId(this, owner.id);
174
+ dataId = revision.id;
171
175
  next = next.createRegressionData({
172
176
  id: dataId,
173
177
  source: current.source,
@@ -176,6 +180,9 @@ export const editRegression = action(
176
180
  ...(current.groupBy === undefined ? {} : { groupBy: current.groupBy }),
177
181
  ...parameters
178
182
  });
183
+ for (const rebind of revision.rebinds) {
184
+ next = next.rebindLayerData(rebind);
185
+ }
179
186
  }
180
187
 
181
188
  let bandId = current.bandId;
@@ -198,21 +205,12 @@ export const editRegression = action(
198
205
  });
199
206
  } else if (hadBand && changesStatistics) {
200
207
  const bandConfig = next.markConfigs[bandId];
201
- next = next
202
- .editSemantic({ property: `layer[${bandId}].data`, value: dataId })
203
- ._withMarkConfig(bandId, {
208
+ next = next._withMarkConfig(bandId, {
204
209
  ...bandConfig,
205
210
  errorBand: { ...bandConfig.errorBand, data: dataId }
206
211
  });
207
212
  }
208
213
 
209
- if (changesStatistics) {
210
- next = next.editSemantic({
211
- property: `layer[${current.lineId}].data`,
212
- value: dataId
213
- });
214
- }
215
-
216
214
  if (bandId !== undefined) {
217
215
  if (
218
216
  bandPatch !== undefined &&
@@ -232,7 +230,7 @@ export const editRegression = action(
232
230
  }
233
231
 
234
232
  if (changesStatistics) {
235
- next = next.releaseDerivedData({ id: current.dataId });
233
+ next = next.releaseDerivedData(revision.release);
236
234
  }
237
235
  return next._withMarkConfig(owner.id, {
238
236
  ...next.markConfigs[owner.id],
@@ -0,0 +1,109 @@
1
+ import {
2
+ readNominalField,
3
+ readQuantitativeField,
4
+ readScaleField,
5
+ readTemporalField
6
+ } from "../../../grammar/scales/index.js";
7
+ import { findDataset } from "../../../selectors/datasets.js";
8
+ import { requireSemanticScale } from "../../../selectors/scales.js";
9
+ import { SCALED_ENCODING_CHANNELS } from "../../../core/vocabulary.js";
10
+
11
+ export function findScale(program, id) {
12
+ return requireSemanticScale(program, id);
13
+ }
14
+
15
+ export function findScaleConsumers(program, id) {
16
+ const consumers = [];
17
+ for (const layer of program.semanticSpec.layers) {
18
+ for (const channel of SCALED_ENCODING_CHANNELS) {
19
+ const encoding = layer.encoding?.[channel];
20
+ if (encoding?.scale === id) consumers.push({ layer, channel, encoding });
21
+ }
22
+ for (const dimension of layer.encoding?.parallel?.dimensions ?? []) {
23
+ if (dimension.scale === id) {
24
+ consumers.push({
25
+ layer,
26
+ channel: "y",
27
+ encoding: dimension,
28
+ role: "parallelDimension"
29
+ });
30
+ }
31
+ }
32
+ }
33
+ return consumers;
34
+ }
35
+
36
+ export function requireConsumerDataset(program, consumer) {
37
+ const dataset = findDataset(program, consumer.layer.data);
38
+ if (dataset === undefined) {
39
+ throw new Error(
40
+ `Mark "${consumer.layer.id}" references unknown dataset "${consumer.layer.data}".`
41
+ );
42
+ }
43
+ return dataset;
44
+ }
45
+
46
+ export function isDirectCategoricalConsumer(consumer) {
47
+ return ["color", "strokeDash", "xOffset", "yOffset", "shape"].includes(
48
+ consumer.channel
49
+ ) && consumer.encoding.fieldType === "nominal";
50
+ }
51
+
52
+ export function readConsumerFieldValues(
53
+ program,
54
+ consumer,
55
+ dataset,
56
+ scale = findScale(program, consumer.encoding.scale)
57
+ ) {
58
+ const allowUnknown = Object.hasOwn(scale, "unknown");
59
+ if (consumer.role === "parallelDimension") {
60
+ return readScaleField(
61
+ dataset.values,
62
+ consumer.encoding.field,
63
+ consumer.encoding.fieldType,
64
+ { allowUnknown: true }
65
+ );
66
+ }
67
+ if (isDirectCategoricalConsumer(consumer)) {
68
+ return allowUnknown
69
+ ? readScaleField(
70
+ dataset.values,
71
+ consumer.encoding.field,
72
+ consumer.encoding.fieldType,
73
+ { allowUnknown: true }
74
+ )
75
+ : readNominalField(dataset.values, consumer.encoding.field);
76
+ }
77
+ if (consumer.encoding.fieldType === "temporal") {
78
+ return allowUnknown
79
+ ? readScaleField(dataset.values, consumer.encoding.field, "temporal", {
80
+ allowUnknown: true
81
+ })
82
+ : readTemporalField(dataset.values, consumer.encoding.field);
83
+ }
84
+ if (["nominal", "ordinal"].includes(consumer.encoding.fieldType)) {
85
+ if (!["ordinal", "band", "point"].includes(scale.type)) {
86
+ throw new Error(
87
+ `Scale materialization requires a quantitative encoding on mark "${consumer.layer.id}".`
88
+ );
89
+ }
90
+ return allowUnknown
91
+ ? readScaleField(
92
+ dataset.values,
93
+ consumer.encoding.field,
94
+ consumer.encoding.fieldType,
95
+ { allowUnknown: true }
96
+ )
97
+ : readNominalField(dataset.values, consumer.encoding.field);
98
+ }
99
+ if (consumer.encoding.fieldType !== "quantitative") {
100
+ throw new Error(
101
+ `Scale materialization requires a quantitative encoding on mark "${consumer.layer.id}".`
102
+ );
103
+ }
104
+ return allowUnknown
105
+ ? readScaleField(dataset.values, consumer.encoding.field, "quantitative", {
106
+ allowUnknown: true
107
+ })
108
+ : readQuantitativeField(dataset.values, consumer.encoding.field);
109
+ }
@@ -0,0 +1,58 @@
1
+ import { deriveBarAggregates } from "../../../grammar/bars/aggregate.js";
2
+ import {
3
+ BAR_GRAINS,
4
+ resolveBarGrain
5
+ } from "../../../grammar/bars/policy.js";
6
+ import {
7
+ deriveLineSeries,
8
+ deriveLineSeriesFieldValues
9
+ } from "../../../grammar/lineSeries.js";
10
+ import { isAggregate } from "../../../grammar/aggregate.js";
11
+ import {
12
+ resolveRectConsumerValues
13
+ } from "../../../materialization/rect.js";
14
+
15
+ export function resolveMarkFamilyConsumerValues(consumer, dataset) {
16
+ if (consumer.layer.mark?.type === "rect") {
17
+ return {
18
+ matched: true,
19
+ values: resolveRectConsumerValues(
20
+ consumer.layer,
21
+ dataset,
22
+ consumer.channel
23
+ )
24
+ };
25
+ }
26
+ if (
27
+ consumer.layer.mark?.type === "line" &&
28
+ (isAggregate(consumer.layer.encoding?.y?.aggregate) ||
29
+ consumer.channel === "strokeWidth")
30
+ ) {
31
+ const derived = deriveLineSeries(dataset.values, consumer.layer);
32
+ return {
33
+ matched: true,
34
+ values: consumer.channel === "strokeWidth"
35
+ ? deriveLineSeriesFieldValues(
36
+ dataset.values,
37
+ consumer.layer,
38
+ derived,
39
+ consumer.encoding.field
40
+ )
41
+ : consumer.channel === "x" ? derived.xValues : derived.yValues
42
+ };
43
+ }
44
+ if (resolveBarGrain(consumer.layer) === BAR_GRAINS.aggregate) {
45
+ const derived = deriveBarAggregates(dataset.values, consumer.layer);
46
+ if (consumer.channel === "color") {
47
+ return {
48
+ matched: true,
49
+ values: derived.values.map(value => value.color)
50
+ };
51
+ }
52
+ return {
53
+ matched: true,
54
+ values: consumer.channel === "x" ? derived.xValues : derived.yValues
55
+ };
56
+ }
57
+ return { matched: false };
58
+ }
@@ -0,0 +1,41 @@
1
+ import { normalizeRuleDatum } from "../../../grammar/rules.js";
2
+ import {
3
+ findScale,
4
+ isDirectCategoricalConsumer,
5
+ readConsumerFieldValues,
6
+ requireConsumerDataset
7
+ } from "./common.js";
8
+ import { resolveMarkFamilyConsumerValues } from "./families.js";
9
+
10
+ export { findScale, findScaleConsumers } from "./common.js";
11
+ export {
12
+ resolveHistogramCountValues,
13
+ resolveSeriesLayoutScaleValues
14
+ } from "./seriesLayout.js";
15
+
16
+ export function resolveConsumerValues(program, consumer) {
17
+ const dataset = requireConsumerDataset(program, consumer);
18
+ if (Object.hasOwn(consumer.encoding, "datum")) {
19
+ return [normalizeRuleDatum(
20
+ consumer.encoding.datum,
21
+ consumer.encoding.fieldType,
22
+ consumer.channel
23
+ )];
24
+ }
25
+ if (
26
+ consumer.channel === "strokeDash" &&
27
+ !["line", "rule"].includes(consumer.layer.mark?.type)
28
+ ) {
29
+ throw new Error(
30
+ "strokeDash scale materialization requires a line mark or rule mark."
31
+ );
32
+ }
33
+ const scale = findScale(program, consumer.encoding.scale);
34
+ if (isDirectCategoricalConsumer(consumer)) {
35
+ return readConsumerFieldValues(program, consumer, dataset, scale);
36
+ }
37
+ const family = resolveMarkFamilyConsumerValues(consumer, dataset);
38
+ return family.matched
39
+ ? family.values
40
+ : readConsumerFieldValues(program, consumer, dataset, scale);
41
+ }