ggaction 0.0.1
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.
- package/CHANGELOG.md +27 -0
- package/LICENSE +21 -0
- package/README.md +108 -0
- package/package.json +84 -0
- package/src/AGENTS.md +119 -0
- package/src/ChartProgram.js +10 -0
- package/src/actions/boxPlots/components.js +88 -0
- package/src/actions/boxPlots/create.js +84 -0
- package/src/actions/boxPlots/index.js +10 -0
- package/src/actions/boxPlots/materialize.js +183 -0
- package/src/actions/boxPlots/options.js +147 -0
- package/src/actions/boxPlots/resolve.js +38 -0
- package/src/actions/canvas/actions.js +144 -0
- package/src/actions/canvas/index.js +6 -0
- package/src/actions/coordinates/actions.js +85 -0
- package/src/actions/coordinates/index.js +5 -0
- package/src/actions/data/box.js +40 -0
- package/src/actions/data/create.js +33 -0
- package/src/actions/data/density.js +62 -0
- package/src/actions/data/derived.js +52 -0
- package/src/actions/data/filter.js +168 -0
- package/src/actions/data/index.js +32 -0
- package/src/actions/data/interval.js +69 -0
- package/src/actions/data/regression.js +71 -0
- package/src/actions/data/shared.js +25 -0
- package/src/actions/encodings/appearance.js +243 -0
- package/src/actions/encodings/barWidth.js +60 -0
- package/src/actions/encodings/color.js +329 -0
- package/src/actions/encodings/density.js +225 -0
- package/src/actions/encodings/histogram.js +50 -0
- package/src/actions/encodings/index.js +23 -0
- package/src/actions/encodings/offset.js +104 -0
- package/src/actions/encodings/position/policies/area.js +24 -0
- package/src/actions/encodings/position/policies/bar.js +110 -0
- package/src/actions/encodings/position/policies/common.js +17 -0
- package/src/actions/encodings/position/policies/index.js +25 -0
- package/src/actions/encodings/position/policies/line.js +74 -0
- package/src/actions/encodings/position/policies/point.js +19 -0
- package/src/actions/encodings/position/policies/rule.js +17 -0
- package/src/actions/encodings/position/resolve.js +170 -0
- package/src/actions/encodings/position.js +176 -0
- package/src/actions/encodings/ranged.js +276 -0
- package/src/actions/encodings/ruleAppearance.js +47 -0
- package/src/actions/encodings/shared.js +164 -0
- package/src/actions/encodings/strokeDash.js +145 -0
- package/src/actions/errorBands/components.js +81 -0
- package/src/actions/errorBands/create.js +159 -0
- package/src/actions/errorBands/index.js +7 -0
- package/src/actions/errorBands/resolve.js +28 -0
- package/src/actions/errorBars/components.js +62 -0
- package/src/actions/errorBars/create.js +144 -0
- package/src/actions/errorBars/index.js +7 -0
- package/src/actions/errorBars/resolve.js +25 -0
- package/src/actions/guides/axes/axes.js +204 -0
- package/src/actions/guides/axes/axis.js +131 -0
- package/src/actions/guides/axes/index.js +17 -0
- package/src/actions/guides/axes/labels.js +231 -0
- package/src/actions/guides/axes/lines.js +149 -0
- package/src/actions/guides/axes/policy.js +155 -0
- package/src/actions/guides/axes/tickGroups.js +149 -0
- package/src/actions/guides/axes/ticks.js +179 -0
- package/src/actions/guides/axes/titles.js +264 -0
- package/src/actions/guides/grids/grid.js +228 -0
- package/src/actions/guides/grids/index.js +1 -0
- package/src/actions/guides/grids/resolve.js +258 -0
- package/src/actions/guides/guides.js +138 -0
- package/src/actions/guides/index.js +11 -0
- package/src/actions/guides/legends/categorical/actions.js +329 -0
- package/src/actions/guides/legends/categorical/components.js +140 -0
- package/src/actions/guides/legends/categorical/index.js +49 -0
- package/src/actions/guides/legends/categorical/layout.js +445 -0
- package/src/actions/guides/legends/categorical/options.js +188 -0
- package/src/actions/guides/legends/categorical/recipes.js +120 -0
- package/src/actions/guides/legends/categorical/resolve.js +131 -0
- package/src/actions/guides/legends/categorical/symbols.js +280 -0
- package/src/actions/guides/legends/categorical/validation.js +27 -0
- package/src/actions/guides/legends/continuous/common.js +273 -0
- package/src/actions/guides/legends/continuous/gradient.js +363 -0
- package/src/actions/guides/legends/continuous/index.js +25 -0
- package/src/actions/guides/legends/continuous/interval.js +234 -0
- package/src/actions/guides/legends/continuous/opacity.js +382 -0
- package/src/actions/guides/legends/edit.js +314 -0
- package/src/actions/guides/legends/index.js +9 -0
- package/src/actions/guides/legends/size.js +220 -0
- package/src/actions/guides/tickValues.js +84 -0
- package/src/actions/index.js +31 -0
- package/src/actions/intervals/resolve.js +352 -0
- package/src/actions/marks/area.js +369 -0
- package/src/actions/marks/bar/create.js +46 -0
- package/src/actions/marks/bar/edit.js +90 -0
- package/src/actions/marks/bar/index.js +9 -0
- package/src/actions/marks/bar/materialize.js +130 -0
- package/src/actions/marks/index.js +13 -0
- package/src/actions/marks/line.js +248 -0
- package/src/actions/marks/point.js +420 -0
- package/src/actions/marks/rule.js +287 -0
- package/src/actions/marks/shared.js +112 -0
- package/src/actions/primitives/createGraphics.js +163 -0
- package/src/actions/primitives/editGraphics.js +208 -0
- package/src/actions/primitives/index.js +9 -0
- package/src/actions/primitives/semantic.js +168 -0
- package/src/actions/primitives/semanticValue.js +182 -0
- package/src/actions/regression/components.js +216 -0
- package/src/actions/regression/create.js +102 -0
- package/src/actions/regression/index.js +23 -0
- package/src/actions/regression/resolve.js +57 -0
- package/src/actions/scales/consumers.js +283 -0
- package/src/actions/scales/create.js +148 -0
- package/src/actions/scales/definitions.js +319 -0
- package/src/actions/scales/edit.js +290 -0
- package/src/actions/scales/index.js +11 -0
- package/src/actions/scales/materialize.js +466 -0
- package/src/actions/scales/quantitativeColor.js +45 -0
- package/src/actions/selection/actions.js +384 -0
- package/src/actions/selection/index.js +23 -0
- package/src/actions/titles/actions.js +235 -0
- package/src/actions/titles/index.js +19 -0
- package/src/actions/titles/resolve.js +321 -0
- package/src/core/ChartProgram.js +316 -0
- package/src/core/action.js +120 -0
- package/src/core/identifiers.js +28 -0
- package/src/core/immutable.js +94 -0
- package/src/core/specs.js +29 -0
- package/src/core/textMetrics.js +23 -0
- package/src/core/validation.js +54 -0
- package/src/core/vocabulary.js +58 -0
- package/src/extension.js +2 -0
- package/src/grammar/aggregate.js +259 -0
- package/src/grammar/areaSeries.js +217 -0
- package/src/grammar/bars/aggregate.js +98 -0
- package/src/grammar/bars/geometry.js +54 -0
- package/src/grammar/bars/policy.js +90 -0
- package/src/grammar/boxPlot.js +140 -0
- package/src/grammar/coordinates.js +25 -0
- package/src/grammar/curveCommands.js +356 -0
- package/src/grammar/density.js +296 -0
- package/src/grammar/filter.js +169 -0
- package/src/grammar/histogram.js +332 -0
- package/src/grammar/interval.js +343 -0
- package/src/grammar/lineSeries.js +197 -0
- package/src/grammar/markFilter.js +46 -0
- package/src/grammar/markSelection.js +326 -0
- package/src/grammar/palettes.js +208 -0
- package/src/grammar/pathCommands.js +86 -0
- package/src/grammar/pointShapes.js +164 -0
- package/src/grammar/positionCompatibility.js +32 -0
- package/src/grammar/regression.js +578 -0
- package/src/grammar/ruleAppearance.js +12 -0
- package/src/grammar/rules.js +90 -0
- package/src/grammar/scales/appearance.js +185 -0
- package/src/grammar/scales/color.js +349 -0
- package/src/grammar/scales/continuous.js +111 -0
- package/src/grammar/scales/definition.js +153 -0
- package/src/grammar/scales/discretized.js +159 -0
- package/src/grammar/scales/fields.js +133 -0
- package/src/grammar/scales/index.js +12 -0
- package/src/grammar/scales/mapping.js +27 -0
- package/src/grammar/scales/ordinal.js +221 -0
- package/src/grammar/scales/policies.js +50 -0
- package/src/grammar/scales/temporal.js +53 -0
- package/src/grammar/scales/transformed.js +236 -0
- package/src/grammar/scales/types.js +102 -0
- package/src/grammar/scales/validation.js +66 -0
- package/src/grammar/scales.js +1 -0
- package/src/grammar/schemas/concreteGraphic.js +73 -0
- package/src/grammar/schemas/graphic.js +131 -0
- package/src/grammar/schemas/graphicBounds.js +146 -0
- package/src/grammar/schemas/graphicTree.js +133 -0
- package/src/grammar/schemas/mark.js +4 -0
- package/src/grammar/schemas/semanticPath.js +174 -0
- package/src/grammar/seriesLayout.js +76 -0
- package/src/grammar/ticks.js +149 -0
- package/src/grammar/transforms.js +37 -0
- package/src/index.js +2 -0
- package/src/layout/canvas.js +114 -0
- package/src/layout/grid.js +17 -0
- package/src/layout/legend.js +51 -0
- package/src/layout/text.js +62 -0
- package/src/layout/title.js +107 -0
- package/src/materialization/bars/aggregate.js +184 -0
- package/src/materialization/bars/grouped.js +117 -0
- package/src/materialization/bars/histogram.js +163 -0
- package/src/materialization/bars/ranged.js +53 -0
- package/src/materialization/bars/resolve.js +56 -0
- package/src/materialization/dataProvenance.js +15 -0
- package/src/materialization/density.js +29 -0
- package/src/materialization/dependencies.js +134 -0
- package/src/materialization/encodings.js +50 -0
- package/src/materialization/graphicHierarchy.js +104 -0
- package/src/materialization/legends.js +20 -0
- package/src/materialization/marks.js +110 -0
- package/src/materialization/planner.js +38 -0
- package/src/materialization/selection/filter.js +40 -0
- package/src/materialization/selection/items.js +503 -0
- package/src/materialization/selection/path.js +37 -0
- package/src/materialization/selection/point.js +118 -0
- package/src/materialization/selection/policies/area.js +10 -0
- package/src/materialization/selection/policies/bar.js +10 -0
- package/src/materialization/selection/policies/index.js +40 -0
- package/src/materialization/selection/policies/line.js +10 -0
- package/src/materialization/selection/policies/point.js +10 -0
- package/src/materialization/selection/policies/rule.js +10 -0
- package/src/materialization/selection/state.js +67 -0
- package/src/materialization/selection/styles.js +183 -0
- package/src/renderers/canvas/circle.js +62 -0
- package/src/renderers/canvas/index.js +132 -0
- package/src/renderers/canvas/line.js +54 -0
- package/src/renderers/canvas/path.js +98 -0
- package/src/renderers/canvas/rect.js +57 -0
- package/src/renderers/canvas/text.js +96 -0
- package/src/renderers/canvas/validation.js +23 -0
- package/src/renderers/png.js +29 -0
- package/src/selectors/coordinates.js +13 -0
- package/src/selectors/datasets.js +13 -0
- package/src/selectors/index.js +4 -0
- package/src/selectors/layers.js +32 -0
- package/src/selectors/scales.js +21 -0
- package/src/theme/defaults.js +13 -0
- package/types/extension.d.ts +14 -0
- package/types/index.d.ts +81 -0
- package/types/png.d.ts +14 -0
- package/types/program.d.ts +1096 -0
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import { action } from "../../../core/action.js";
|
|
2
|
+
import { validateUserId } from "../../../core/identifiers.js";
|
|
3
|
+
import { isPlainObject } from "../../../core/immutable.js";
|
|
4
|
+
import { validateKeys } from "../../../core/validation.js";
|
|
5
|
+
import { normalizeOptions } from "./categorical/options.js";
|
|
6
|
+
import { symbolGraphic } from "./categorical/layout.js";
|
|
7
|
+
import {
|
|
8
|
+
normalizeContinuousLegend,
|
|
9
|
+
validatePositive
|
|
10
|
+
} from "./continuous/common.js";
|
|
11
|
+
import { normalizeOpacitySymbol } from "./continuous/opacity.js";
|
|
12
|
+
import { normalizeIntervalLegend } from "./continuous/interval.js";
|
|
13
|
+
import { findLayer } from "../../../selectors/layers.js";
|
|
14
|
+
import { resolveLegendGraphicPlacement } from
|
|
15
|
+
"../../../materialization/graphicHierarchy.js";
|
|
16
|
+
|
|
17
|
+
const OPTIONS = Object.freeze([
|
|
18
|
+
"target", "position", "align", "direction", "columns", "offset",
|
|
19
|
+
"titlePosition", "title", "symbol", "labels", "titleStyle", "itemGap",
|
|
20
|
+
"border", "count", "gradient"
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
function mergeObject(previous, patch) {
|
|
24
|
+
return patch === undefined
|
|
25
|
+
? previous
|
|
26
|
+
: { ...previous, ...patch };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function mergeBorder(previous, patch) {
|
|
30
|
+
if (patch === undefined) return previous;
|
|
31
|
+
if (patch === false || patch === true) return patch;
|
|
32
|
+
return previous === false ? patch : { ...previous, ...patch };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function resolveTarget(program, requested) {
|
|
36
|
+
const configs = program.guideConfigs.legend ?? {};
|
|
37
|
+
const targets = [...new Set(Object.values(configs)
|
|
38
|
+
.map(config => config?.target)
|
|
39
|
+
.filter(Boolean))];
|
|
40
|
+
if (requested !== undefined) {
|
|
41
|
+
const target = validateUserId(requested, "Legend target id");
|
|
42
|
+
if (!targets.includes(target)) {
|
|
43
|
+
throw new Error(`Unknown legend target "${target}".`);
|
|
44
|
+
}
|
|
45
|
+
return target;
|
|
46
|
+
}
|
|
47
|
+
if (targets.length !== 1) {
|
|
48
|
+
throw new Error("editLegend requires target when the legend is ambiguous.");
|
|
49
|
+
}
|
|
50
|
+
return targets[0];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function reconcileGraphic(program, id, shouldExist, definition) {
|
|
54
|
+
const exists = program.graphicSpec.objects[id] !== undefined;
|
|
55
|
+
if (exists && !shouldExist) return program.editGraphics({ target: id, remove: true });
|
|
56
|
+
if (!exists && shouldExist) {
|
|
57
|
+
return program.createGraphics({
|
|
58
|
+
id,
|
|
59
|
+
...definition,
|
|
60
|
+
...resolveLegendGraphicPlacement(program)
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return program;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function editContinuous(program, kind, previous, args) {
|
|
67
|
+
const allowed = kind === "gradient"
|
|
68
|
+
? ["target", "position", "align", "offset", "title", "labels",
|
|
69
|
+
"titleStyle", "border", "count", "gradient"]
|
|
70
|
+
: ["target", "position", "align", "offset", "title", "symbol", "labels",
|
|
71
|
+
"titleStyle", "itemGap", "border", "count"];
|
|
72
|
+
for (const key of Object.keys(args)) {
|
|
73
|
+
if (!allowed.includes(key)) throw new Error(`${kind} legend does not accept ${key}.`);
|
|
74
|
+
}
|
|
75
|
+
const titleMode = args.title;
|
|
76
|
+
const inferredTitle = titleMode === "auto"
|
|
77
|
+
? true
|
|
78
|
+
: typeof titleMode === "string" ? false : previous.inferredTitle;
|
|
79
|
+
const titleVisible = titleMode === false ? false
|
|
80
|
+
: titleMode === undefined ? previous.titleVisible !== false : true;
|
|
81
|
+
const layer = findLayer(program, previous.target);
|
|
82
|
+
const inferred = layer?.encoding?.[kind === "gradient" ? "color" : "opacity"]?.field;
|
|
83
|
+
const title = titleMode === "auto"
|
|
84
|
+
? inferred
|
|
85
|
+
: typeof titleMode === "string" ? titleMode : previous.title;
|
|
86
|
+
const normalized = normalizeContinuousLegend({
|
|
87
|
+
target: previous.target,
|
|
88
|
+
position: args.position ?? previous.position,
|
|
89
|
+
align: args.align ?? previous.align,
|
|
90
|
+
offset: args.offset ?? previous.offset,
|
|
91
|
+
count: args.count ?? previous.count,
|
|
92
|
+
title,
|
|
93
|
+
labels: mergeObject(previous.labels, args.labels),
|
|
94
|
+
titleStyle: mergeObject(previous.titleStyle, args.titleStyle),
|
|
95
|
+
border: mergeBorder(previous.border, args.border),
|
|
96
|
+
...(kind === "opacity" ? {
|
|
97
|
+
itemGap: args.itemGap ?? previous.itemGap,
|
|
98
|
+
symbol: args.symbol ?? previous.symbol
|
|
99
|
+
} : {})
|
|
100
|
+
}, kind);
|
|
101
|
+
const config = {
|
|
102
|
+
...previous,
|
|
103
|
+
...normalized,
|
|
104
|
+
inferredTitle,
|
|
105
|
+
titleVisible
|
|
106
|
+
};
|
|
107
|
+
if (kind === "gradient") {
|
|
108
|
+
const gradient = mergeObject(previous.gradient, args.gradient);
|
|
109
|
+
validatePositive(gradient.length, "Gradient length");
|
|
110
|
+
validatePositive(gradient.thickness, "Gradient thickness");
|
|
111
|
+
config.gradient = gradient;
|
|
112
|
+
} else {
|
|
113
|
+
config.symbol = normalizeOpacitySymbol(args.symbol ?? previous.symbol);
|
|
114
|
+
}
|
|
115
|
+
const prefix = kind === "gradient" ? "colorGradient" : "opacityLegend";
|
|
116
|
+
let next = program;
|
|
117
|
+
if (titleMode === "auto" || typeof titleMode === "string") {
|
|
118
|
+
next = next.editSemantic({
|
|
119
|
+
property: `guide.legend.${kind === "gradient" ? "color" : "opacity"}.title`,
|
|
120
|
+
value: title
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
next = next._withLegendConfig(kind, config);
|
|
124
|
+
next = reconcileGraphic(next, `${prefix}Background`, config.border !== false, {
|
|
125
|
+
type: "rect",
|
|
126
|
+
before: kind === "gradient" ? "colorGradientStrips" : "opacityLegendSymbols"
|
|
127
|
+
});
|
|
128
|
+
next = reconcileGraphic(next, `${prefix}Title`, titleVisible, { type: "text" });
|
|
129
|
+
return kind === "gradient"
|
|
130
|
+
? next.rematerializeGradientLegend()
|
|
131
|
+
: next.rematerializeOpacityLegend();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function editInterval(program, previous, args) {
|
|
135
|
+
for (const key of Object.keys(args)) {
|
|
136
|
+
if (![
|
|
137
|
+
"target", "position", "align", "direction", "offset", "title",
|
|
138
|
+
"symbol", "labels", "titleStyle", "itemGap", "border"
|
|
139
|
+
].includes(key)) {
|
|
140
|
+
throw new Error(`interval legend does not accept ${key}.`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
const titleMode = args.title;
|
|
144
|
+
const layer = findLayer(program, previous.target);
|
|
145
|
+
const inferredTitle = titleMode === "auto"
|
|
146
|
+
? true
|
|
147
|
+
: typeof titleMode === "string" ? false : previous.inferredTitle;
|
|
148
|
+
const titleVisible = titleMode === false
|
|
149
|
+
? false
|
|
150
|
+
: titleMode === undefined ? previous.titleVisible !== false : true;
|
|
151
|
+
const title = titleMode === "auto"
|
|
152
|
+
? layer?.encoding?.color?.field
|
|
153
|
+
: typeof titleMode === "string" ? titleMode : previous.title;
|
|
154
|
+
const normalized = normalizeIntervalLegend({
|
|
155
|
+
target: previous.target,
|
|
156
|
+
position: args.position ?? previous.position,
|
|
157
|
+
align: args.align ?? previous.align,
|
|
158
|
+
direction: args.direction ?? previous.direction,
|
|
159
|
+
offset: args.offset ?? previous.offset,
|
|
160
|
+
title,
|
|
161
|
+
symbol: mergeObject(previous.symbol, args.symbol),
|
|
162
|
+
labels: mergeObject(previous.labels, args.labels),
|
|
163
|
+
titleStyle: mergeObject(previous.titleStyle, args.titleStyle),
|
|
164
|
+
itemGap: args.itemGap ?? previous.itemGap,
|
|
165
|
+
border: mergeBorder(previous.border, args.border)
|
|
166
|
+
});
|
|
167
|
+
let next = program._withLegendConfig("interval", {
|
|
168
|
+
...previous,
|
|
169
|
+
...normalized,
|
|
170
|
+
inferredTitle,
|
|
171
|
+
titleVisible
|
|
172
|
+
});
|
|
173
|
+
if (titleMode === "auto" || typeof titleMode === "string") {
|
|
174
|
+
next = next.editSemantic({
|
|
175
|
+
property: "guide.legend.color.title",
|
|
176
|
+
value: title
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
next = reconcileGraphic(next, "colorLegendTitle", titleVisible, {
|
|
180
|
+
type: "text"
|
|
181
|
+
});
|
|
182
|
+
return next.rematerializeIntervalLegend();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function categoricalSymbolIds(config) {
|
|
186
|
+
return new Set(config.symbol.layers.map(layer => symbolGraphic(config, layer.type)));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function editCategorical(program, kind, previous, size, args) {
|
|
190
|
+
if (args.gradient !== undefined) {
|
|
191
|
+
throw new Error("Categorical legends do not accept gradient.");
|
|
192
|
+
}
|
|
193
|
+
if (args.count !== undefined && size === undefined) {
|
|
194
|
+
throw new Error("Legend count requires an existing size legend.");
|
|
195
|
+
}
|
|
196
|
+
const titleMode = args.title;
|
|
197
|
+
const inferredTitle = titleMode === "auto"
|
|
198
|
+
? true
|
|
199
|
+
: typeof titleMode === "string" ? false : previous.inferredTitle;
|
|
200
|
+
const titleVisible = titleMode === false ? false
|
|
201
|
+
: titleMode === undefined ? previous.titleVisible !== false : true;
|
|
202
|
+
const title = titleMode === "auto"
|
|
203
|
+
? previous.field
|
|
204
|
+
: typeof titleMode === "string" ? titleMode : previous.title;
|
|
205
|
+
const normalized = normalizeOptions({
|
|
206
|
+
target: previous.target,
|
|
207
|
+
channels: previous.channels,
|
|
208
|
+
position: args.position ?? previous.position,
|
|
209
|
+
align: args.align ?? previous.align,
|
|
210
|
+
direction: args.direction ?? (args.position === "left"
|
|
211
|
+
? "vertical"
|
|
212
|
+
: previous.direction),
|
|
213
|
+
...(args.columns === undefined && previous.columns === undefined
|
|
214
|
+
? {} : { columns: args.columns ?? previous.columns }),
|
|
215
|
+
offset: args.offset ?? previous.offset,
|
|
216
|
+
titlePosition: args.titlePosition ?? previous.titlePosition,
|
|
217
|
+
title,
|
|
218
|
+
symbol: args.symbol ?? previous.symbol,
|
|
219
|
+
labels: mergeObject(previous.labels, args.labels),
|
|
220
|
+
titleStyle: mergeObject(previous.titleStyle, args.titleStyle),
|
|
221
|
+
itemGap: args.itemGap ?? previous.itemGap,
|
|
222
|
+
border: mergeBorder(previous.border, args.border)
|
|
223
|
+
}, kind);
|
|
224
|
+
const config = {
|
|
225
|
+
...previous,
|
|
226
|
+
...normalized,
|
|
227
|
+
inferredTitle,
|
|
228
|
+
titleVisible
|
|
229
|
+
};
|
|
230
|
+
const oldSymbols = categoricalSymbolIds(previous);
|
|
231
|
+
const newSymbols = categoricalSymbolIds(config);
|
|
232
|
+
let next = program;
|
|
233
|
+
if (titleMode === "auto" || typeof titleMode === "string") {
|
|
234
|
+
next = next.editSemantic({
|
|
235
|
+
property: `guide.legend.${kind}.title`,
|
|
236
|
+
value: title
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
next = next._withLegendConfig(kind, config);
|
|
240
|
+
for (const id of oldSymbols) {
|
|
241
|
+
if (!newSymbols.has(id) && next.graphicSpec.objects[id] !== undefined) {
|
|
242
|
+
next = next.editGraphics({ target: id, remove: true });
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
next = reconcileGraphic(next, `${kind === "series" ? "series" : "color"}LegendBackground`,
|
|
246
|
+
config.border !== false, {
|
|
247
|
+
type: "rect",
|
|
248
|
+
before: [...newSymbols][0]
|
|
249
|
+
});
|
|
250
|
+
for (const layer of config.symbol.layers) {
|
|
251
|
+
const id = symbolGraphic(config, layer.type);
|
|
252
|
+
if (next.graphicSpec.objects[id] === undefined) {
|
|
253
|
+
const suffix = { line: "Lines", point: "Points", swatch: "Swatches" }[layer.type];
|
|
254
|
+
next = next[`createLegendSymbol${suffix}`]();
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
const titleId = `${kind === "series" ? "series" : "color"}LegendTitle`;
|
|
258
|
+
next = reconcileGraphic(next, titleId, titleVisible, {
|
|
259
|
+
type: "text",
|
|
260
|
+
...(next.graphicSpec.objects.sizeLegendSymbols === undefined
|
|
261
|
+
? {} : { before: "sizeLegendSymbols" })
|
|
262
|
+
});
|
|
263
|
+
if (size !== undefined) {
|
|
264
|
+
if (args.count !== undefined && (!Number.isInteger(args.count) || args.count < 2)) {
|
|
265
|
+
throw new RangeError("Size legend count must be an integer of at least 2.");
|
|
266
|
+
}
|
|
267
|
+
next = next._withLegendConfig("size", {
|
|
268
|
+
...size,
|
|
269
|
+
count: args.count ?? size.count,
|
|
270
|
+
inheritAppearance: true
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
return next.rematerializeLegend();
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export const editLegend = action(
|
|
277
|
+
{ op: "editLegend", description: "Edit one stable legend layout or appearance." },
|
|
278
|
+
function (args = {}) {
|
|
279
|
+
if (!isPlainObject(args)) {
|
|
280
|
+
throw new TypeError("editLegend options must be a plain object.");
|
|
281
|
+
}
|
|
282
|
+
validateKeys(args, OPTIONS, "editLegend");
|
|
283
|
+
const changes = Object.keys(args).filter(key => key !== "target");
|
|
284
|
+
if (changes.length === 0) {
|
|
285
|
+
throw new Error("editLegend requires at least one change.");
|
|
286
|
+
}
|
|
287
|
+
if (args.title !== undefined && args.title !== false && args.title !== "auto" && (
|
|
288
|
+
typeof args.title !== "string" || args.title.length === 0
|
|
289
|
+
)) {
|
|
290
|
+
throw new TypeError('editLegend title must be a non-empty string, "auto", or false.');
|
|
291
|
+
}
|
|
292
|
+
const target = resolveTarget(this, args.target);
|
|
293
|
+
const configs = this.guideConfigs.legend ?? {};
|
|
294
|
+
const categoricalKind = ["series", "color"].find(
|
|
295
|
+
kind => configs[kind]?.target === target
|
|
296
|
+
);
|
|
297
|
+
if (categoricalKind !== undefined) {
|
|
298
|
+
return editCategorical(
|
|
299
|
+
this,
|
|
300
|
+
categoricalKind,
|
|
301
|
+
configs[categoricalKind],
|
|
302
|
+
configs.size?.target === target ? configs.size : undefined,
|
|
303
|
+
args
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
if (configs.interval?.target === target) {
|
|
307
|
+
return editInterval(this, configs.interval, args);
|
|
308
|
+
}
|
|
309
|
+
const continuousKind = ["gradient", "opacity"].find(
|
|
310
|
+
kind => configs[kind]?.target === target
|
|
311
|
+
);
|
|
312
|
+
return editContinuous(this, continuousKind, configs[continuousKind], args);
|
|
313
|
+
}
|
|
314
|
+
);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { registerCategoricalLegendActions } from "./categorical/index.js";
|
|
2
|
+
import { registerContinuousLegendActions } from "./continuous/index.js";
|
|
3
|
+
import { registerSizeLegendActions } from "./size.js";
|
|
4
|
+
|
|
5
|
+
export function registerLegendActions(ProgramClass) {
|
|
6
|
+
registerCategoricalLegendActions(ProgramClass);
|
|
7
|
+
registerContinuousLegendActions(ProgramClass);
|
|
8
|
+
registerSizeLegendActions(ProgramClass);
|
|
9
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { action } from "../../../core/action.js";
|
|
2
|
+
import { validateKeys } from "../../../core/validation.js";
|
|
3
|
+
import { mapLinearValues } from "../../../grammar/scales.js";
|
|
4
|
+
import { resolveGraphicBounds } from "../../../layout/canvas.js";
|
|
5
|
+
import { DEFAULT_COLORS, DEFAULT_FONT_FAMILY } from
|
|
6
|
+
"../../../theme/defaults.js";
|
|
7
|
+
import { resolveLayout as resolveCategoricalLayout } from
|
|
8
|
+
"./categorical/layout.js";
|
|
9
|
+
import { findLayer } from "../../../selectors/layers.js";
|
|
10
|
+
import { resolveLegendGraphicPlacement } from
|
|
11
|
+
"../../../materialization/graphicHierarchy.js";
|
|
12
|
+
|
|
13
|
+
const SIZE_OPTIONS = Object.freeze(["target", "count"]);
|
|
14
|
+
|
|
15
|
+
function resolvePoint(program, requested) {
|
|
16
|
+
const candidates = program.semanticSpec.layers.filter(layer =>
|
|
17
|
+
layer.mark?.type === "point" &&
|
|
18
|
+
layer.encoding?.color?.scale !== undefined &&
|
|
19
|
+
layer.encoding?.shape?.scale !== undefined
|
|
20
|
+
);
|
|
21
|
+
const layer = requested === undefined
|
|
22
|
+
? candidates.length === 1 ? candidates[0] : undefined
|
|
23
|
+
: (() => {
|
|
24
|
+
const candidate = findLayer(program, requested);
|
|
25
|
+
return candidates.includes(candidate) ? candidate : undefined;
|
|
26
|
+
})();
|
|
27
|
+
if (layer === undefined) {
|
|
28
|
+
throw new Error(
|
|
29
|
+
requested === undefined
|
|
30
|
+
? "Point series legend requires one eligible point mark."
|
|
31
|
+
: `Unknown point series legend target "${requested}".`
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
if (layer.encoding.color.field !== layer.encoding.shape.field) {
|
|
35
|
+
throw new Error("Point color and shape legends require the same field.");
|
|
36
|
+
}
|
|
37
|
+
return layer;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function requireScale(program, id, type) {
|
|
41
|
+
const scale = program.resolvedScales[id];
|
|
42
|
+
if (scale?.type !== type) {
|
|
43
|
+
throw new Error(`Legend requires resolved ${type} scale "${id}".`);
|
|
44
|
+
}
|
|
45
|
+
return scale;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function bounds(program) {
|
|
49
|
+
const value = resolveGraphicBounds(program);
|
|
50
|
+
const canvas = program.graphicSpec.objects.canvas;
|
|
51
|
+
if (
|
|
52
|
+
value === undefined ||
|
|
53
|
+
![value.x, value.y, value.width, value.height].every(Number.isFinite) ||
|
|
54
|
+
!Number.isFinite(canvas?.properties.width)
|
|
55
|
+
) {
|
|
56
|
+
throw new Error("Legend layout requires Canvas bounds and width.");
|
|
57
|
+
}
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function styleText(program, id, { title = false, style } = {}) {
|
|
62
|
+
const resolved = style ?? {
|
|
63
|
+
color: title ? DEFAULT_COLORS.strongText : DEFAULT_COLORS.text,
|
|
64
|
+
fontSize: title ? 13 : 12,
|
|
65
|
+
fontFamily: DEFAULT_FONT_FAMILY,
|
|
66
|
+
fontWeight: title ? 600 : "normal"
|
|
67
|
+
};
|
|
68
|
+
return program
|
|
69
|
+
.editGraphics({
|
|
70
|
+
target: id,
|
|
71
|
+
property: "fill",
|
|
72
|
+
value: resolved.color
|
|
73
|
+
})
|
|
74
|
+
.editGraphics({ target: id, property: "fontSize", value: resolved.fontSize })
|
|
75
|
+
.editGraphics({ target: id, property: "fontFamily", value: resolved.fontFamily })
|
|
76
|
+
.editGraphics({ target: id, property: "fontWeight", value: resolved.fontWeight })
|
|
77
|
+
.editGraphics({ target: id, property: "textAlign", value: "left" })
|
|
78
|
+
.editGraphics({ target: id, property: "textBaseline", value: "middle" });
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export const rematerializeSizeLegend = action(
|
|
82
|
+
{
|
|
83
|
+
op: "rematerializeSizeLegend",
|
|
84
|
+
description: "Rematerialize a quantitative point-size legend."
|
|
85
|
+
},
|
|
86
|
+
function (args = {}) {
|
|
87
|
+
validateKeys(args, [], "rematerializeSizeLegend");
|
|
88
|
+
const config = this.guideConfigs.legend?.size;
|
|
89
|
+
if (config === undefined) throw new Error("Size legend requires stored configuration.");
|
|
90
|
+
const layer = findLayer(this, config.target);
|
|
91
|
+
const encoding = layer?.encoding?.size;
|
|
92
|
+
if (encoding?.scale === undefined) {
|
|
93
|
+
throw new Error("Size legend target requires a size encoding.");
|
|
94
|
+
}
|
|
95
|
+
const scale = requireScale(this, encoding.scale, "linear");
|
|
96
|
+
const title = config.inferredTitle === true ? encoding.field : config.title;
|
|
97
|
+
const currentConfig = {
|
|
98
|
+
...config,
|
|
99
|
+
scale: encoding.scale,
|
|
100
|
+
title,
|
|
101
|
+
domain: scale.domain
|
|
102
|
+
};
|
|
103
|
+
const plot = bounds(this);
|
|
104
|
+
const categorical = this.guideConfigs.legend?.series ??
|
|
105
|
+
this.guideConfigs.legend?.color;
|
|
106
|
+
const leftLayout = categorical?.position === "left"
|
|
107
|
+
? resolveCategoricalLayout(this, categorical).size
|
|
108
|
+
: undefined;
|
|
109
|
+
const originX = leftLayout?.titleX ?? plot.x + plot.width + 30;
|
|
110
|
+
const seriesCount = categorical?.domain.length ?? 0;
|
|
111
|
+
const titleY = leftLayout?.titleY ?? plot.y + 56 + seriesCount * 34 + 22;
|
|
112
|
+
const values = Array.from(
|
|
113
|
+
{ length: currentConfig.count },
|
|
114
|
+
(_, index) => scale.domain[0] +
|
|
115
|
+
index / (currentConfig.count - 1) * (scale.domain[1] - scale.domain[0])
|
|
116
|
+
);
|
|
117
|
+
const areas = mapLinearValues(values, scale.domain, scale.range, {
|
|
118
|
+
clamp: scale.clamp ?? false
|
|
119
|
+
});
|
|
120
|
+
const itemY = leftLayout?.itemY ??
|
|
121
|
+
values.map((_, index) => titleY + 34 + index * 40);
|
|
122
|
+
const symbolX = leftLayout?.symbolX ?? values.map(() => originX + 16);
|
|
123
|
+
const labelX = leftLayout?.labelX ?? values.map(() => originX + 44);
|
|
124
|
+
let next = this
|
|
125
|
+
.editSemantic({ property: "guide.legend.size.scale", value: encoding.scale })
|
|
126
|
+
.editSemantic({ property: "guide.legend.size.title", value: title })
|
|
127
|
+
._withLegendConfig("size", currentConfig)
|
|
128
|
+
.editGraphics({ target: "sizeLegendSymbols", property: "length", value: values.length })
|
|
129
|
+
.editGraphics({ target: "sizeLegendSymbols", property: "x", value: symbolX })
|
|
130
|
+
.editGraphics({ target: "sizeLegendSymbols", property: "y", value: itemY })
|
|
131
|
+
.editGraphics({
|
|
132
|
+
target: "sizeLegendSymbols",
|
|
133
|
+
property: "radius",
|
|
134
|
+
value: areas.map(area => Math.sqrt(area / Math.PI))
|
|
135
|
+
})
|
|
136
|
+
.editGraphics({
|
|
137
|
+
target: "sizeLegendSymbols",
|
|
138
|
+
property: "fill",
|
|
139
|
+
value: DEFAULT_COLORS.sizeSymbol
|
|
140
|
+
})
|
|
141
|
+
.editGraphics({ target: "sizeLegendSymbols", property: "opacity", value: 0.7 })
|
|
142
|
+
.editGraphics({ target: "sizeLegendLabels", property: "length", value: values.length })
|
|
143
|
+
.editGraphics({ target: "sizeLegendLabels", property: "x", value: labelX })
|
|
144
|
+
.editGraphics({ target: "sizeLegendLabels", property: "y", value: itemY })
|
|
145
|
+
.editGraphics({
|
|
146
|
+
target: "sizeLegendLabels",
|
|
147
|
+
property: "text",
|
|
148
|
+
value: values.map(value => String(+value.toPrecision(3)))
|
|
149
|
+
})
|
|
150
|
+
.editGraphics({ target: "sizeLegendTitle", property: "x", value: originX })
|
|
151
|
+
.editGraphics({ target: "sizeLegendTitle", property: "y", value: titleY })
|
|
152
|
+
.editGraphics({ target: "sizeLegendTitle", property: "text", value: title });
|
|
153
|
+
next = styleText(next, "sizeLegendLabels", {
|
|
154
|
+
style: currentConfig.inheritAppearance === true
|
|
155
|
+
? categorical.labels
|
|
156
|
+
: undefined
|
|
157
|
+
});
|
|
158
|
+
return styleText(next, "sizeLegendTitle", {
|
|
159
|
+
title: true,
|
|
160
|
+
style: currentConfig.inheritAppearance === true
|
|
161
|
+
? categorical.titleStyle
|
|
162
|
+
: undefined
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
export const createSizeLegend = action(
|
|
168
|
+
{
|
|
169
|
+
op: "createSizeLegend",
|
|
170
|
+
description: "Create a quantitative equal-area point-size legend."
|
|
171
|
+
},
|
|
172
|
+
function (args = {}) {
|
|
173
|
+
validateKeys(args, [...SIZE_OPTIONS, "inheritAppearance"], "createSizeLegend");
|
|
174
|
+
const layer = resolvePoint(this, args.target);
|
|
175
|
+
const encoding = layer.encoding?.size;
|
|
176
|
+
if (encoding?.scale === undefined) {
|
|
177
|
+
throw new Error(`Point mark "${layer.id}" requires a size encoding.`);
|
|
178
|
+
}
|
|
179
|
+
const scale = requireScale(this, encoding.scale, "linear");
|
|
180
|
+
const count = args.count ?? 5;
|
|
181
|
+
if (!Number.isInteger(count) || count < 2) {
|
|
182
|
+
throw new RangeError("Size legend count must be an integer of at least 2.");
|
|
183
|
+
}
|
|
184
|
+
return this
|
|
185
|
+
.editSemantic({ property: "guide.legend.size.scale", value: encoding.scale })
|
|
186
|
+
.editSemantic({ property: "guide.legend.size.title", value: encoding.field })
|
|
187
|
+
._withLegendConfig("size", {
|
|
188
|
+
target: layer.id,
|
|
189
|
+
scale: encoding.scale,
|
|
190
|
+
title: encoding.field,
|
|
191
|
+
inferredTitle: true,
|
|
192
|
+
domain: scale.domain,
|
|
193
|
+
count,
|
|
194
|
+
inheritAppearance: args.inheritAppearance === true
|
|
195
|
+
})
|
|
196
|
+
.createGraphics({
|
|
197
|
+
id: "sizeLegendSymbols",
|
|
198
|
+
type: "circle",
|
|
199
|
+
length: count,
|
|
200
|
+
...resolveLegendGraphicPlacement(this)
|
|
201
|
+
})
|
|
202
|
+
.createGraphics({
|
|
203
|
+
id: "sizeLegendLabels",
|
|
204
|
+
type: "text",
|
|
205
|
+
length: count,
|
|
206
|
+
...resolveLegendGraphicPlacement(this)
|
|
207
|
+
})
|
|
208
|
+
.createGraphics({
|
|
209
|
+
id: "sizeLegendTitle",
|
|
210
|
+
type: "text",
|
|
211
|
+
...resolveLegendGraphicPlacement(this)
|
|
212
|
+
})
|
|
213
|
+
.rematerializeSizeLegend();
|
|
214
|
+
}
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
export function registerSizeLegendActions(ProgramClass) {
|
|
218
|
+
ProgramClass.prototype.createSizeLegend = createSizeLegend;
|
|
219
|
+
ProgramClass.prototype.rematerializeSizeLegend = rematerializeSizeLegend;
|
|
220
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { resolveHistogramBins } from "../../grammar/histogram.js";
|
|
2
|
+
import {
|
|
3
|
+
isTransformedScaleType,
|
|
4
|
+
readQuantitativeField,
|
|
5
|
+
transformedTicks
|
|
6
|
+
} from "../../grammar/scales.js";
|
|
7
|
+
import { niceTicks, timeTicks } from "../../grammar/ticks.js";
|
|
8
|
+
import { findDataset } from "../../selectors/datasets.js";
|
|
9
|
+
import { findSemanticScale } from "../../selectors/scales.js";
|
|
10
|
+
|
|
11
|
+
export const DEFAULT_TICK_COUNT = 5;
|
|
12
|
+
|
|
13
|
+
export function inferHistogramBoundaries(program, channel, scaleId) {
|
|
14
|
+
if (channel !== "x") return undefined;
|
|
15
|
+
|
|
16
|
+
const consumers = program.semanticSpec.layers.filter(
|
|
17
|
+
layer =>
|
|
18
|
+
layer.encoding?.x?.scale === scaleId &&
|
|
19
|
+
layer.encoding.x.bin !== undefined
|
|
20
|
+
);
|
|
21
|
+
if (consumers.length === 0) return undefined;
|
|
22
|
+
if (consumers.length > 1) {
|
|
23
|
+
throw new Error(
|
|
24
|
+
`Guide values cannot infer shared histogram bins for scale "${scaleId}".`
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const [layer] = consumers;
|
|
29
|
+
const encoding = layer.encoding.x;
|
|
30
|
+
const dataset = findDataset(program, layer.data);
|
|
31
|
+
const scale = findSemanticScale(program, scaleId);
|
|
32
|
+
if (dataset === undefined || scale === undefined) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
`Guide values require histogram data and scale "${scaleId}".`
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return resolveHistogramBins({
|
|
39
|
+
values: readQuantitativeField(dataset.values, encoding.field),
|
|
40
|
+
bin: encoding.bin,
|
|
41
|
+
domain: scale.domain,
|
|
42
|
+
nice: scale.nice ?? true,
|
|
43
|
+
zero: scale.zero ?? false
|
|
44
|
+
}).boundaries;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function valuesFromTickConfig(program, config) {
|
|
48
|
+
if (config.mode === "values") return config.values;
|
|
49
|
+
|
|
50
|
+
const scale = program.resolvedScales[config.scale];
|
|
51
|
+
if (scale?.type === "time") return timeTicks(scale.domain, config.count);
|
|
52
|
+
if (scale?.type === "linear") return niceTicks(scale.domain, config.count);
|
|
53
|
+
if (isTransformedScaleType(scale?.type)) {
|
|
54
|
+
return transformedTicks(scale.type, scale.domain, config.count, {
|
|
55
|
+
...(scale.base === undefined ? {} : { base: scale.base }),
|
|
56
|
+
...(scale.exponent === undefined ? {} : { exponent: scale.exponent }),
|
|
57
|
+
...(scale.constant === undefined ? {} : { constant: scale.constant })
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
throw new Error(
|
|
61
|
+
`Guide values require resolved continuous scale "${config.scale}".`
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function inferGridTickConfig(program, channel, scaleId) {
|
|
66
|
+
const axis = program.guideConfigs.axis?.[channel]?.ticks;
|
|
67
|
+
if (axis?.scale === scaleId) {
|
|
68
|
+
return axis.mode === "values"
|
|
69
|
+
? { mode: "values", values: axis.values }
|
|
70
|
+
: { mode: "count", count: axis.count };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const boundaries = inferHistogramBoundaries(program, channel, scaleId);
|
|
74
|
+
const scale = program.resolvedScales[scaleId];
|
|
75
|
+
if (
|
|
76
|
+
boundaries === undefined &&
|
|
77
|
+
["ordinal", "band", "point"].includes(scale?.type)
|
|
78
|
+
) {
|
|
79
|
+
return { mode: "values", values: scale.domain };
|
|
80
|
+
}
|
|
81
|
+
return boundaries === undefined
|
|
82
|
+
? { mode: "count", count: DEFAULT_TICK_COUNT }
|
|
83
|
+
: { mode: "values", values: boundaries };
|
|
84
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { registerCanvasActions } from "./canvas/index.js";
|
|
2
|
+
import { registerCoordinateActions } from "./coordinates/index.js";
|
|
3
|
+
import { registerDataActions } from "./data/index.js";
|
|
4
|
+
import { registerEncodingActions } from "./encodings/index.js";
|
|
5
|
+
import { registerErrorBandActions } from "./errorBands/index.js";
|
|
6
|
+
import { registerErrorBarActions } from "./errorBars/index.js";
|
|
7
|
+
import { registerBoxPlotActions } from "./boxPlots/index.js";
|
|
8
|
+
import { registerGuideActions } from "./guides/index.js";
|
|
9
|
+
import { registerMarkActions } from "./marks/index.js";
|
|
10
|
+
import { registerPrimitiveActions } from "./primitives/index.js";
|
|
11
|
+
import { registerRegressionActions } from "./regression/index.js";
|
|
12
|
+
import { registerScaleActions } from "./scales/index.js";
|
|
13
|
+
import { registerTitleActions } from "./titles/index.js";
|
|
14
|
+
import { registerSelectionActions } from "./selection/index.js";
|
|
15
|
+
|
|
16
|
+
export function registerActions(ProgramClass) {
|
|
17
|
+
registerPrimitiveActions(ProgramClass);
|
|
18
|
+
registerCanvasActions(ProgramClass);
|
|
19
|
+
registerDataActions(ProgramClass);
|
|
20
|
+
registerMarkActions(ProgramClass);
|
|
21
|
+
registerScaleActions(ProgramClass);
|
|
22
|
+
registerEncodingActions(ProgramClass);
|
|
23
|
+
registerCoordinateActions(ProgramClass);
|
|
24
|
+
registerGuideActions(ProgramClass);
|
|
25
|
+
registerTitleActions(ProgramClass);
|
|
26
|
+
registerRegressionActions(ProgramClass);
|
|
27
|
+
registerErrorBarActions(ProgramClass);
|
|
28
|
+
registerErrorBandActions(ProgramClass);
|
|
29
|
+
registerBoxPlotActions(ProgramClass);
|
|
30
|
+
registerSelectionActions(ProgramClass);
|
|
31
|
+
}
|