ggaction 0.0.4 → 0.0.6

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 (169) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/README.md +35 -13
  3. package/package.json +18 -7
  4. package/src/actions/boxPlots/create.js +13 -7
  5. package/src/actions/boxPlots/edit.js +484 -36
  6. package/src/actions/boxPlots/materialize.js +3 -0
  7. package/src/actions/boxPlots/options.js +11 -4
  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/composition/actions.js +13 -3
  18. package/src/actions/coordinates/parallel.js +50 -0
  19. package/src/actions/data/bin2d.js +260 -0
  20. package/src/actions/data/density.js +54 -13
  21. package/src/actions/data/derived.js +22 -0
  22. package/src/actions/data/filter.js +5 -0
  23. package/src/actions/data/gradientProfile.js +65 -0
  24. package/src/actions/data/horizon.js +71 -0
  25. package/src/actions/data/index.js +34 -2
  26. package/src/actions/data/intervalEdit.js +76 -0
  27. package/src/actions/data/window.js +50 -0
  28. package/src/actions/distributions/revision.js +326 -0
  29. package/src/actions/encodings/appearance.js +35 -1
  30. package/src/actions/encodings/color/index.js +17 -1
  31. package/src/actions/encodings/color/layout.js +8 -0
  32. package/src/actions/encodings/density/resolve.js +109 -0
  33. package/src/actions/encodings/density.js +330 -90
  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 -0
  37. package/src/actions/encodings/parallel.js +115 -0
  38. package/src/actions/encodings/pathOrder.js +131 -0
  39. package/src/actions/encodings/position/apply.js +73 -4
  40. package/src/actions/encodings/position/index.js +15 -2
  41. package/src/actions/encodings/position/policies/arc.js +27 -4
  42. package/src/actions/encodings/position/policies/area.js +33 -2
  43. package/src/actions/encodings/position/policies/index.js +7 -2
  44. package/src/actions/encodings/position/policies/line.js +18 -2
  45. package/src/actions/encodings/position/resolve.js +24 -2
  46. package/src/actions/encodings/ranged.js +3 -1
  47. package/src/actions/encodings/remove.js +268 -0
  48. package/src/actions/encodings/ruleAppearance.js +88 -10
  49. package/src/actions/errorBands/edit.js +121 -13
  50. package/src/actions/errorBars/edit.js +45 -8
  51. package/src/actions/errorBars/options.js +7 -1
  52. package/src/actions/facets/actions.js +137 -3
  53. package/src/actions/facets/derive.js +33 -3
  54. package/src/actions/facets/replay.js +7 -36
  55. package/src/actions/gradientPlots/components.js +177 -0
  56. package/src/actions/gradientPlots/create.js +96 -0
  57. package/src/actions/gradientPlots/edit.js +501 -0
  58. package/src/actions/gradientPlots/index.js +24 -0
  59. package/src/actions/gradientPlots/materialize.js +287 -0
  60. package/src/actions/gradientPlots/options.js +120 -0
  61. package/src/actions/gradientPlots/paint.js +107 -0
  62. package/src/actions/gradientPlots/rebind.js +51 -0
  63. package/src/actions/gradientPlots/resolve.js +81 -0
  64. package/src/actions/guides/applicability.js +44 -17
  65. package/src/actions/guides/axes/axes.js +37 -3
  66. package/src/actions/guides/axes/edit.js +129 -47
  67. package/src/actions/guides/axes/index.js +2 -0
  68. package/src/actions/guides/axes/labels.js +11 -6
  69. package/src/actions/guides/axes/parallel/resolve.js +86 -0
  70. package/src/actions/guides/axes/parallel.js +228 -0
  71. package/src/actions/guides/axes/titles.js +11 -8
  72. package/src/actions/guides/guides.js +60 -5
  73. package/src/actions/guides/legends/categorical/actions.js +67 -19
  74. package/src/actions/guides/legends/continuous/common.js +3 -3
  75. package/src/actions/guides/legends/edit.js +63 -0
  76. package/src/actions/guides/legends/index.js +2 -0
  77. package/src/actions/guides/legends/remove.js +124 -30
  78. package/src/actions/guides/legends/strokeWidth.js +198 -0
  79. package/src/actions/index.js +6 -0
  80. package/src/actions/marks/arc/actions.js +46 -14
  81. package/src/actions/marks/area/actions.js +60 -136
  82. package/src/actions/marks/area/materialize.js +163 -0
  83. package/src/actions/marks/index.js +5 -1
  84. package/src/actions/marks/line/actions.js +99 -80
  85. package/src/actions/marks/line/materialize.js +146 -0
  86. package/src/actions/marks/point/create.js +76 -0
  87. package/src/actions/marks/point/edit.js +97 -0
  88. package/src/actions/marks/point/index.js +11 -1
  89. package/src/actions/marks/point/jitter.js +72 -0
  90. package/src/actions/marks/point/{actions.js → materialize.js} +183 -172
  91. package/src/actions/marks/rect/actions.js +3 -0
  92. package/src/actions/marks/remove.js +13 -2
  93. package/src/actions/marks/rule/actions.js +29 -4
  94. package/src/actions/marks/text/actions.js +19 -4
  95. package/src/actions/marks/text/index.js +7 -1
  96. package/src/actions/marks/text/layout.js +321 -0
  97. package/src/actions/primitives/semanticValidation/dataset.js +22 -0
  98. package/src/actions/primitives/semanticValidation/guide.js +43 -0
  99. package/src/actions/primitives/semanticValidation/index.js +23 -0
  100. package/src/actions/primitives/semanticValidation/layer.js +74 -0
  101. package/src/actions/primitives/semanticValidation/scale.js +74 -0
  102. package/src/actions/primitives/semanticValidation/shared.js +5 -0
  103. package/src/actions/primitives/semanticValue.js +1 -204
  104. package/src/actions/regression/edit.js +247 -88
  105. package/src/actions/scales/consumers/common.js +18 -0
  106. package/src/actions/scales/consumers/families.js +14 -3
  107. package/src/actions/scales/definitions.js +30 -0
  108. package/src/actions/scales/edit.js +6 -204
  109. package/src/actions/scales/editPolicy.js +214 -0
  110. package/src/actions/scales/materialize.js +1 -1
  111. package/src/actions/scales/patch.js +26 -0
  112. package/src/actions/selection/actions.js +150 -5
  113. package/src/actions/selection/index.js +6 -0
  114. package/src/actions/violinPlots/create.js +198 -0
  115. package/src/actions/violinPlots/index.js +5 -0
  116. package/src/core/textMetrics.js +47 -0
  117. package/src/core/vocabulary.js +14 -2
  118. package/src/grammar/arcs.js +49 -12
  119. package/src/grammar/areaSeries.js +41 -5
  120. package/src/grammar/bin2d.js +333 -0
  121. package/src/grammar/categoricalDensity.js +192 -0
  122. package/src/grammar/coordinates.js +1 -1
  123. package/src/grammar/curveCommands.js +3 -1
  124. package/src/grammar/density.js +252 -26
  125. package/src/grammar/facets/dependencies.js +3 -11
  126. package/src/grammar/facets/index.js +3 -1
  127. package/src/grammar/gradientProfile.js +213 -0
  128. package/src/grammar/horizon.js +455 -0
  129. package/src/grammar/jitter.js +197 -0
  130. package/src/grammar/lineSeries.js +69 -0
  131. package/src/grammar/paint.js +88 -0
  132. package/src/grammar/parallelCoordinates.js +213 -0
  133. package/src/grammar/pathOrder.js +35 -0
  134. package/src/grammar/pointShapes.js +24 -0
  135. package/src/grammar/rules.js +1 -0
  136. package/src/grammar/scales/appearance.js +21 -0
  137. package/src/grammar/schemas/concreteGraphic.js +11 -1
  138. package/src/grammar/schemas/graphicBounds.js +19 -37
  139. package/src/grammar/schemas/semanticPath.js +17 -2
  140. package/src/grammar/transforms.js +100 -11
  141. package/src/grammar/window.js +339 -0
  142. package/src/layout/labels.js +270 -0
  143. package/src/layout/text.js +4 -1
  144. package/src/layout/title.js +10 -20
  145. package/src/materialization/dataProvenance.js +56 -2
  146. package/src/materialization/dependencies.js +9 -14
  147. package/src/materialization/guides/resources.js +74 -24
  148. package/src/materialization/horizon.js +29 -0
  149. package/src/materialization/layout.js +15 -0
  150. package/src/materialization/marks/capabilities.js +21 -1
  151. package/src/materialization/marks/index.js +3 -0
  152. package/src/materialization/marks/pathOrder.js +71 -0
  153. package/src/materialization/marks/policies.js +6 -2
  154. package/src/materialization/scaleGuideDependencies.js +8 -0
  155. package/src/materialization/scales/resolve.js +2 -0
  156. package/src/materialization/selection/items/path.js +27 -1
  157. package/src/materialization/selection/items/point.js +1 -5
  158. package/src/materialization/selection/items/rect.js +21 -0
  159. package/src/materialization/selection/styles.js +20 -9
  160. package/src/materialization/text.js +12 -4
  161. package/src/renderers/canvas/fill.js +33 -0
  162. package/src/renderers/canvas/index.js +7 -0
  163. package/src/renderers/canvas/path.js +7 -4
  164. package/src/renderers/canvas/rect.js +9 -3
  165. package/src/selectors/datasets.js +4 -0
  166. package/src/theme/defaults.js +4 -0
  167. package/types/extension.d.ts +6 -0
  168. package/types/index.d.ts +77 -0
  169. package/types/program.d.ts +703 -17
@@ -0,0 +1,501 @@
1
+ import { action } from "../../core/action.js";
2
+ import { isPlainObject } from "../../core/immutable.js";
3
+ import { validateUserId } from "../../core/identifiers.js";
4
+ import { validateKeys } from "../../core/validation.js";
5
+ import { planDerivedDataRevision } from "../../materialization/dataProvenance.js";
6
+ import { findDataset } from "../../selectors/datasets.js";
7
+ import { findLayer } from "../../selectors/layers.js";
8
+ import { GRADIENT_PROFILE_FIELDS } from "../../grammar/gradientProfile.js";
9
+ import { removeGradientPlotLegend } from "./components.js";
10
+ import {
11
+ assertDistributionScaleHandoff,
12
+ clearCartesianPositions,
13
+ rebindDistributionGuides,
14
+ resolveDistributionScalePlan,
15
+ setCartesianPosition,
16
+ setCartesianRange
17
+ } from "../distributions/revision.js";
18
+ import {
19
+ resolveGradientAppearance,
20
+ resolveGradientCenter,
21
+ resolveGradientDensity,
22
+ resolveGradientPosition,
23
+ resolveGradientWidth
24
+ } from "./options.js";
25
+ import {
26
+ normalizeGradientPositionTypes,
27
+ resolveGradientOwner
28
+ } from "./resolve.js";
29
+
30
+ const OPTIONS = Object.freeze([
31
+ "target", "data", "x", "y", "density", "width", "gradient", "center"
32
+ ]);
33
+
34
+ function patch(value, label) {
35
+ if (!isPlainObject(value)) {
36
+ throw new TypeError(`editGradientPlot ${label} must be a plain object.`);
37
+ }
38
+ return value;
39
+ }
40
+
41
+ function removeCenter(program, id) {
42
+ let next = program;
43
+ const selections = Object.entries(
44
+ program.materializationConfigs.selections ?? {}
45
+ ).filter(([, config]) => config.target === id).map(([selection]) => selection);
46
+ for (const [highlight, config] of Object.entries(
47
+ program.materializationConfigs.highlights ?? {}
48
+ )) {
49
+ if (config.target === id || selections.includes(config.selection)) {
50
+ next = next._withoutMaterializationConfig(["highlights", highlight]);
51
+ }
52
+ }
53
+ for (const selection of selections) {
54
+ next = next._withoutMaterializationConfig(["selections", selection]);
55
+ }
56
+ if (findLayer(next, id) !== undefined) {
57
+ next = next.editSemantic({ property: `layer[${id}]`, remove: true });
58
+ }
59
+ if (next.graphicSpec.objects[id] !== undefined) {
60
+ next = next.editGraphics({ target: id, remove: true });
61
+ }
62
+ return next
63
+ ._withoutMaterializationConfig(["marks", id])
64
+ ._withContext({
65
+ ...(program.context.currentMark === id ? { currentMark: undefined } : {}),
66
+ ...(selections.includes(program.context.currentSelection)
67
+ ? { currentSelection: undefined }
68
+ : {})
69
+ });
70
+ }
71
+
72
+ function currentGradientPositions(owner, current) {
73
+ const categoryChannel = current.orientation === "vertical" ? "x" : "y";
74
+ const measureChannel = current.orientation === "vertical" ? "y" : "x";
75
+ const categoryEncoding = owner.encoding[categoryChannel];
76
+ const measureEncoding = owner.encoding[measureChannel];
77
+ return {
78
+ x: current.orientation === "vertical"
79
+ ? {
80
+ field: current.category,
81
+ fieldType: current.categoryType,
82
+ scale: categoryEncoding.scale
83
+ }
84
+ : {
85
+ field: current.measure,
86
+ fieldType: "quantitative",
87
+ scale: measureEncoding.scale
88
+ },
89
+ y: current.orientation === "vertical"
90
+ ? {
91
+ field: current.measure,
92
+ fieldType: "quantitative",
93
+ scale: measureEncoding.scale
94
+ }
95
+ : {
96
+ field: current.category,
97
+ fieldType: current.categoryType,
98
+ scale: categoryEncoding.scale
99
+ },
100
+ categoryScale: categoryEncoding.scale,
101
+ measureScale: measureEncoding.scale
102
+ };
103
+ }
104
+
105
+ function requirePositionField(position, label) {
106
+ if (typeof position?.field !== "string" || position.field.length === 0) {
107
+ throw new TypeError(`${label} field must be a non-empty string.`);
108
+ }
109
+ return position;
110
+ }
111
+
112
+ function roleCandidate(program, owner, current, args) {
113
+ const previous = currentGradientPositions(owner, current);
114
+ const positions = normalizeGradientPositionTypes(
115
+ requirePositionField(
116
+ Object.hasOwn(args, "x")
117
+ ? resolveGradientPosition(args.x, "x", "editGradientPlot")
118
+ : previous.x,
119
+ "editGradientPlot x"
120
+ ),
121
+ requirePositionField(
122
+ Object.hasOwn(args, "y")
123
+ ? resolveGradientPosition(args.y, "y", "editGradientPlot")
124
+ : previous.y,
125
+ "editGradientPlot y"
126
+ )
127
+ );
128
+ if (positions.orientation === undefined) {
129
+ throw new Error(
130
+ "editGradientPlot requires one categorical axis and one quantitative axis."
131
+ );
132
+ }
133
+ const xRoleScale = positions.orientation === "vertical"
134
+ ? previous.categoryScale
135
+ : previous.measureScale;
136
+ const yRoleScale = positions.orientation === "vertical"
137
+ ? previous.measureScale
138
+ : previous.categoryScale;
139
+ const xScale = resolveDistributionScalePlan(program, {
140
+ channel: "x",
141
+ fieldType: positions.x.fieldType,
142
+ requested: positions.x.scale,
143
+ fallback: xRoleScale,
144
+ defaults: ["ordinal", "nominal"].includes(positions.x.fieldType)
145
+ ? { discreteType: "band" }
146
+ : {}
147
+ });
148
+ const yScale = resolveDistributionScalePlan(program, {
149
+ channel: "y",
150
+ fieldType: positions.y.fieldType,
151
+ requested: positions.y.scale,
152
+ fallback: yRoleScale,
153
+ defaults: ["ordinal", "nominal"].includes(positions.y.fieldType)
154
+ ? { discreteType: "band" }
155
+ : {}
156
+ });
157
+ return {
158
+ source: Object.hasOwn(args, "data")
159
+ ? validateUserId(args.data, "Gradient-plot data id")
160
+ : current.source,
161
+ orientation: positions.orientation,
162
+ x: { ...positions.x, scale: xScale.id },
163
+ y: { ...positions.y, scale: yScale.id },
164
+ xScale,
165
+ yScale,
166
+ category: positions.orientation === "vertical"
167
+ ? positions.x.field
168
+ : positions.y.field,
169
+ categoryType: positions.orientation === "vertical"
170
+ ? positions.x.fieldType
171
+ : positions.y.fieldType,
172
+ measure: positions.orientation === "vertical"
173
+ ? positions.y.field
174
+ : positions.x.field,
175
+ previous
176
+ };
177
+ }
178
+
179
+ function sameRoleCandidate(current, candidate) {
180
+ return candidate.source === current.source &&
181
+ candidate.orientation === current.orientation &&
182
+ candidate.category === current.category &&
183
+ candidate.categoryType === current.categoryType &&
184
+ candidate.measure === current.measure &&
185
+ candidate.xScale.id === candidate.previous.x.scale &&
186
+ candidate.yScale.id === candidate.previous.y.scale &&
187
+ candidate.xScale.edit === undefined &&
188
+ candidate.yScale.edit === undefined;
189
+ }
190
+
191
+ function updateGradientPositions(program, owner, current, candidate, hasCenter) {
192
+ const owned = [owner.id, ...(hasCenter ? [current.centerId] : [])];
193
+ assertDistributionScaleHandoff(program, {
194
+ owned,
195
+ oldXScale: candidate.previous.x.scale,
196
+ oldYScale: candidate.previous.y.scale,
197
+ newXScale: candidate.xScale.id,
198
+ newYScale: candidate.yScale.id
199
+ });
200
+ let next = program;
201
+ for (const id of owned) next = clearCartesianPositions(next, id);
202
+ for (const scale of [candidate.xScale, candidate.yScale]) {
203
+ if (scale.create) next = next.createScale(scale.definition);
204
+ }
205
+ const category = candidate.orientation === "vertical" ? candidate.x : candidate.y;
206
+ const measure = candidate.orientation === "vertical" ? candidate.y : candidate.x;
207
+ const categoryChannel = candidate.orientation === "vertical" ? "x" : "y";
208
+ const measureChannel = candidate.orientation === "vertical" ? "y" : "x";
209
+ next = setCartesianPosition(next, owner.id, categoryChannel, {
210
+ field: candidate.category,
211
+ fieldType: candidate.categoryType,
212
+ scale: category.scale
213
+ });
214
+ next = setCartesianRange(
215
+ next,
216
+ owner.id,
217
+ measureChannel,
218
+ GRADIENT_PROFILE_FIELDS.lower,
219
+ GRADIENT_PROFILE_FIELDS.upper,
220
+ measure.scale
221
+ );
222
+ if (hasCenter) {
223
+ next = setCartesianPosition(next, current.centerId, categoryChannel, {
224
+ field: candidate.category,
225
+ fieldType: candidate.categoryType,
226
+ scale: category.scale
227
+ });
228
+ next = setCartesianPosition(next, current.centerId, measureChannel, {
229
+ field: GRADIENT_PROFILE_FIELDS.center,
230
+ fieldType: "quantitative",
231
+ scale: measure.scale
232
+ })._withMarkConfig(current.centerId, {
233
+ ...next.markConfigs[current.centerId],
234
+ fixedSpan: {
235
+ ...next.markConfigs[current.centerId].fixedSpan,
236
+ orientation: candidate.orientation === "vertical"
237
+ ? "horizontal"
238
+ : "vertical"
239
+ }
240
+ });
241
+ }
242
+ for (const id of new Set([candidate.xScale.id, candidate.yScale.id])) {
243
+ next = next.rematerializeScale({ id, marks: false, guides: false });
244
+ }
245
+ next = rebindDistributionGuides(next, {
246
+ oldXScale: candidate.previous.x.scale,
247
+ oldYScale: candidate.previous.y.scale,
248
+ newXScale: candidate.xScale.id,
249
+ newYScale: candidate.yScale.id,
250
+ oldXTitle: candidate.previous.x.field,
251
+ oldYTitle: candidate.previous.y.field,
252
+ newXTitle: candidate.x.field,
253
+ newYTitle: candidate.y.field,
254
+ oldMeasureChannel: current.orientation === "vertical" ? "y" : "x",
255
+ newMeasureChannel: candidate.orientation === "vertical" ? "y" : "x"
256
+ });
257
+ for (const scale of [candidate.xScale, candidate.yScale]) {
258
+ if (scale.edit !== undefined) next = next.editScale(scale.edit);
259
+ }
260
+ return next;
261
+ }
262
+
263
+ export const editGradientPlot = action(
264
+ {
265
+ op: "editGradientPlot",
266
+ description: "Edit one stable categorical gradient plot."
267
+ },
268
+ function (args = {}) {
269
+ validateKeys(args, OPTIONS, "editGradientPlot");
270
+ if (!OPTIONS.slice(1).some(key => Object.hasOwn(args, key))) {
271
+ throw new Error("editGradientPlot requires at least one gradient-plot option.");
272
+ }
273
+ const owner = resolveGradientOwner(this, args.target, "editGradientPlot");
274
+ const current = this.markConfigs[owner.id].gradientPlot;
275
+ const density = Object.hasOwn(args, "density")
276
+ ? resolveGradientDensity(patch(args.density, "density"), current.density, "editGradientPlot")
277
+ : current.density;
278
+ const width = Object.hasOwn(args, "width")
279
+ ? resolveGradientWidth(patch(args.width, "width"), current.width, "editGradientPlot")
280
+ : current.width;
281
+ const gradient = Object.hasOwn(args, "gradient")
282
+ ? resolveGradientAppearance(
283
+ patch(args.gradient, "gradient"),
284
+ current.gradient,
285
+ "editGradientPlot"
286
+ )
287
+ : current.gradient;
288
+ const center = Object.hasOwn(args, "center")
289
+ ? resolveGradientCenter(
290
+ args.center === false ? false : patch(args.center, "center"),
291
+ current.center === false ? undefined : current.center,
292
+ "editGradientPlot"
293
+ )
294
+ : current.center;
295
+ const candidate = roleCandidate(this, owner, current, args);
296
+ const sourceDataset = findDataset(this, candidate.source);
297
+ if (sourceDataset === undefined) {
298
+ throw new Error(`Unknown gradient-plot data "${candidate.source}".`);
299
+ }
300
+ const roleRequested = ["data", "x", "y"].some(
301
+ key => Object.hasOwn(args, key)
302
+ );
303
+ const changesRoles = !sameRoleCandidate(current, candidate);
304
+ if (changesRoles) {
305
+ const hadCenter = findLayer(this, current.centerId) !== undefined;
306
+ const revision = planDerivedDataRevision(this, {
307
+ owner: owner.id,
308
+ role: "ProfileData",
309
+ previous: current.profileId,
310
+ consumers: [owner.id, ...(hadCenter ? [current.centerId] : [])]
311
+ });
312
+ const applyEdit = program => {
313
+ let next = program.createGradientProfileData({
314
+ id: revision.id,
315
+ source: candidate.source,
316
+ category: candidate.category,
317
+ field: candidate.measure,
318
+ ...density,
319
+ center: center === false ? "median" : center.type
320
+ });
321
+ for (const rebind of revision.rebinds) {
322
+ next = next.rebindLayerData(rebind);
323
+ }
324
+ const profile = findDataset(next, revision.id);
325
+ next = next._withMarkConfig(owner.id, {
326
+ ...next.markConfigs[owner.id],
327
+ gradientPlot: {
328
+ ...current,
329
+ source: candidate.source,
330
+ orientation: candidate.orientation,
331
+ category: candidate.category,
332
+ categoryType: candidate.categoryType,
333
+ measure: candidate.measure,
334
+ density,
335
+ width,
336
+ gradient,
337
+ center,
338
+ profileId: revision.id,
339
+ intensityDomain: profile.transform[0].resolved.intensityDomain
340
+ }
341
+ });
342
+ if (center === false && hadCenter) {
343
+ next = removeCenter(next, current.centerId);
344
+ }
345
+ const retainedCenter = center !== false && hadCenter;
346
+ next = updateGradientPositions(
347
+ next,
348
+ owner,
349
+ current,
350
+ candidate,
351
+ retainedCenter
352
+ ).rematerializeRectMark({ id: owner.id });
353
+ const category = candidate.orientation === "vertical"
354
+ ? candidate.x
355
+ : candidate.y;
356
+ const measure = candidate.orientation === "vertical"
357
+ ? candidate.y
358
+ : candidate.x;
359
+ const categoryScale = next.resolvedScales[category.scale];
360
+ const spanSize = Math.max(
361
+ 1,
362
+ categoryScale.bandwidth * width.band - 16
363
+ );
364
+ if (center !== false && !hadCenter) {
365
+ next = next.createGradientPlotCenter({
366
+ id: current.centerId,
367
+ owner: owner.id,
368
+ data: revision.id,
369
+ category: candidate.category,
370
+ categoryType: candidate.categoryType,
371
+ coordinate: owner.coordinate,
372
+ categoryScale: category.scale,
373
+ measureScale: measure.scale,
374
+ orientation: candidate.orientation,
375
+ size: spanSize,
376
+ stroke: center.stroke,
377
+ strokeWidth: center.strokeWidth
378
+ });
379
+ } else if (retainedCenter) {
380
+ next = next
381
+ .encodeStroke({ target: current.centerId, value: center.stroke })
382
+ .encodeStrokeWidth({
383
+ target: current.centerId,
384
+ value: center.strokeWidth
385
+ })
386
+ .materializeRuleSpan({
387
+ id: current.centerId,
388
+ orientation: candidate.orientation === "vertical"
389
+ ? "horizontal"
390
+ : "vertical",
391
+ size: spanSize
392
+ });
393
+ }
394
+ if (current.guides?.legend !== false && current.guides?.legend !== undefined) {
395
+ next = removeGradientPlotLegend(next, owner.id)
396
+ .createGradientPlotLegend({
397
+ owner: owner.id,
398
+ ...current.guides.legend
399
+ });
400
+ }
401
+ next = next.releaseDerivedData(revision.release);
402
+ return next._withContext({
403
+ currentMark: owner.id,
404
+ currentData: candidate.source
405
+ });
406
+ };
407
+ applyEdit(this);
408
+ return applyEdit(this);
409
+ }
410
+ if (roleRequested && !OPTIONS.slice(4).some(
411
+ key => Object.hasOwn(args, key)
412
+ )) {
413
+ return this._withContext({
414
+ currentMark: owner.id,
415
+ currentData: current.source
416
+ });
417
+ }
418
+ const densityChanged = JSON.stringify(density) !== JSON.stringify(current.density);
419
+ const centerTypeChanged = center !== false && current.center !== false &&
420
+ center.type !== current.center.type;
421
+ const statistical = densityChanged || centerTypeChanged;
422
+ let next = this;
423
+ let profileId = current.profileId;
424
+ if (statistical) {
425
+ const consumers = [owner.id, ...(findLayer(next, current.centerId) ? [current.centerId] : [])];
426
+ const revision = planDerivedDataRevision(next, {
427
+ owner: owner.id,
428
+ role: "ProfileData",
429
+ previous: current.profileId,
430
+ consumers
431
+ });
432
+ profileId = revision.id;
433
+ next = next.createGradientProfileData({
434
+ id: profileId,
435
+ source: current.source,
436
+ category: current.category,
437
+ field: current.measure,
438
+ ...density,
439
+ center: center === false ? "median" : center.type
440
+ });
441
+ for (const rebind of revision.rebinds) next = next.rebindLayerData(rebind);
442
+ next = next.releaseDerivedData(revision.release);
443
+ }
444
+ const profile = findDataset(next, profileId);
445
+ next = next._withMarkConfig(owner.id, {
446
+ ...next.markConfigs[owner.id],
447
+ gradientPlot: {
448
+ ...current,
449
+ density,
450
+ width,
451
+ gradient,
452
+ center,
453
+ profileId,
454
+ intensityDomain: profile.transform[0].resolved.intensityDomain
455
+ }
456
+ });
457
+ const hadCenter = findLayer(next, current.centerId) !== undefined;
458
+ if (center === false && hadCenter) {
459
+ next = removeCenter(next, current.centerId);
460
+ } else if (center !== false && !hadCenter) {
461
+ const categoryEncoding = owner.encoding[current.orientation === "vertical" ? "x" : "y"];
462
+ const measureEncoding = owner.encoding[current.orientation === "vertical" ? "y" : "x"];
463
+ next = next.createGradientPlotCenter({
464
+ id: current.centerId,
465
+ owner: owner.id,
466
+ data: profileId,
467
+ category: current.category,
468
+ categoryType: current.categoryType,
469
+ coordinate: owner.coordinate,
470
+ categoryScale: categoryEncoding.scale,
471
+ measureScale: measureEncoding.scale,
472
+ orientation: current.orientation,
473
+ size: 1,
474
+ stroke: center.stroke,
475
+ strokeWidth: center.strokeWidth
476
+ });
477
+ } else if (center !== false) {
478
+ next = next
479
+ .encodeStroke({ target: current.centerId, value: center.stroke })
480
+ .encodeStrokeWidth({ target: current.centerId, value: center.strokeWidth });
481
+ }
482
+ next = next.rematerializeRectMark({ id: owner.id });
483
+ if (center !== false && findLayer(next, current.centerId) !== undefined) {
484
+ const categoryChannel = current.orientation === "vertical" ? "x" : "y";
485
+ const categoryScale = next.resolvedScales[owner.encoding[categoryChannel].scale];
486
+ next = next.materializeRuleSpan({
487
+ id: current.centerId,
488
+ orientation: current.orientation === "vertical" ? "horizontal" : "vertical",
489
+ size: Math.max(1, categoryScale.bandwidth * width.band - 16)
490
+ });
491
+ }
492
+ if (current.guides?.legend !== false && current.guides?.legend !== undefined) {
493
+ next = removeGradientPlotLegend(next, owner.id)
494
+ .createGradientPlotLegend({
495
+ owner: owner.id,
496
+ ...current.guides.legend
497
+ });
498
+ }
499
+ return next._withContext({ currentMark: owner.id, currentData: current.source });
500
+ }
501
+ );
@@ -0,0 +1,24 @@
1
+ import {
2
+ createGradientPlotCenter,
3
+ createGradientPlotLegend,
4
+ rematerializeGradientPlotLegend
5
+ } from "./components.js";
6
+ import { createGradientPlot } from "./create.js";
7
+ import { editGradientPlot } from "./edit.js";
8
+ import {
9
+ materializeGradientPlot,
10
+ materializeGradientPlotFill
11
+ } from "./materialize.js";
12
+ import { rebindGradientPlotProfile } from "./rebind.js";
13
+
14
+ export function registerGradientPlotActions(ProgramClass) {
15
+ ProgramClass.prototype.createGradientPlot = createGradientPlot;
16
+ ProgramClass.prototype.editGradientPlot = editGradientPlot;
17
+ ProgramClass.prototype.materializeGradientPlot = materializeGradientPlot;
18
+ ProgramClass.prototype.materializeGradientPlotFill = materializeGradientPlotFill;
19
+ ProgramClass.prototype.createGradientPlotCenter = createGradientPlotCenter;
20
+ ProgramClass.prototype.createGradientPlotLegend = createGradientPlotLegend;
21
+ ProgramClass.prototype.rematerializeGradientPlotLegend =
22
+ rematerializeGradientPlotLegend;
23
+ ProgramClass.prototype.rebindGradientPlotProfile = rebindGradientPlotProfile;
24
+ }