ggaction 0.0.2 → 0.0.4
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 +55 -2
- package/README.md +95 -72
- package/package.json +15 -5
- package/src/actions/boxPlots/components.js +3 -3
- package/src/actions/boxPlots/edit.js +277 -0
- package/src/actions/boxPlots/index.js +2 -0
- package/src/actions/boxPlots/materialize.js +12 -1
- package/src/actions/boxPlots/options.js +25 -14
- package/src/actions/canvas/actions.js +21 -17
- package/src/actions/composition/actions.js +247 -0
- package/src/actions/composition/index.js +1 -0
- package/src/actions/coordinates/actions.js +2 -5
- package/src/actions/data/density.js +7 -1
- package/src/actions/data/filter.js +3 -1
- package/src/actions/data/regression.js +1 -1
- package/src/actions/encodings/appearance.js +12 -1
- package/src/actions/encodings/barWidth.js +5 -3
- package/src/actions/encodings/color/continuous.js +137 -0
- package/src/actions/encodings/color/index.js +151 -0
- package/src/actions/encodings/color/layout.js +91 -0
- package/src/actions/encodings/color/policy.js +88 -0
- package/src/actions/encodings/index.js +4 -2
- package/src/actions/encodings/offset.js +91 -73
- package/src/actions/encodings/position/apply.js +66 -0
- package/src/actions/encodings/position/index.js +130 -0
- package/src/actions/encodings/position/policies/arc.js +26 -0
- package/src/actions/encodings/position/policies/bar.js +9 -5
- package/src/actions/encodings/position/policies/index.js +5 -1
- package/src/actions/encodings/position/policies/line.js +20 -3
- package/src/actions/encodings/position/resolve.js +58 -9
- package/src/actions/encodings/ranged.js +30 -8
- package/src/actions/encodings/shared.js +17 -21
- package/src/actions/encodings/strokeDash.js +1 -1
- package/src/actions/encodings/text.js +73 -0
- package/src/actions/errorBands/create.js +37 -29
- package/src/actions/errorBands/edit.js +206 -0
- package/src/actions/errorBands/index.js +9 -0
- package/src/actions/errorBands/options.js +39 -0
- package/src/actions/errorBars/create.js +50 -43
- package/src/actions/errorBars/edit.js +130 -0
- package/src/actions/errorBars/index.js +3 -0
- package/src/actions/errorBars/options.js +52 -0
- package/src/actions/facets/actions.js +162 -0
- package/src/actions/facets/derive.js +200 -0
- package/src/actions/facets/guides.js +16 -0
- package/src/actions/facets/index.js +1 -0
- package/src/actions/facets/replay.js +69 -0
- package/src/actions/guides/applicability.js +113 -0
- package/src/actions/guides/axes/axes.js +70 -16
- package/src/actions/guides/axes/edit.js +126 -0
- package/src/actions/guides/axes/index.js +12 -0
- package/src/actions/guides/axes/labels.js +22 -19
- package/src/actions/guides/axes/remove.js +60 -0
- package/src/actions/guides/axes/tickGroups.js +7 -5
- package/src/actions/guides/axes/ticks.js +10 -5
- package/src/actions/guides/axes/titles.js +8 -6
- package/src/actions/guides/grids/grid.js +144 -15
- package/src/actions/guides/grids/resolve.js +6 -14
- package/src/actions/guides/guides.js +17 -75
- package/src/actions/guides/index.js +6 -0
- package/src/actions/guides/legends/categorical/actions.js +48 -9
- package/src/actions/guides/legends/categorical/components.js +16 -16
- package/src/actions/guides/legends/categorical/index.js +14 -14
- package/src/actions/guides/legends/categorical/layout.js +7 -3
- package/src/actions/guides/legends/categorical/options.js +2 -5
- package/src/actions/guides/legends/categorical/resolve.js +2 -2
- package/src/actions/guides/legends/categorical/symbols.js +14 -14
- package/src/actions/guides/legends/continuous/common.js +7 -7
- package/src/actions/guides/legends/continuous/interval.js +6 -6
- package/src/actions/guides/legends/continuous/opacity.js +1 -1
- package/src/actions/guides/legends/edit.js +4 -26
- package/src/actions/guides/legends/focused.js +63 -0
- package/src/actions/guides/legends/index.js +4 -0
- package/src/actions/guides/legends/remove.js +46 -0
- package/src/actions/guides/legends/size.js +13 -15
- package/src/actions/guides/legends/target.js +25 -0
- package/src/actions/guides/polar/axes/facade.js +219 -0
- package/src/actions/guides/polar/axes/index.js +55 -0
- package/src/actions/guides/polar/axes/labels.js +188 -0
- package/src/actions/guides/polar/axes/lines.js +113 -0
- package/src/actions/guides/polar/axes/shared.js +112 -0
- package/src/actions/guides/polar/axes/ticks.js +164 -0
- package/src/actions/guides/polar/axes/titles.js +165 -0
- package/src/actions/guides/polar/grids.js +252 -0
- package/src/actions/guides/polar/index.js +2 -0
- package/src/actions/guides/polar/resolve.js +269 -0
- package/src/actions/guides/tickValues.js +1 -1
- package/src/actions/index.js +4 -0
- package/src/actions/intervals/resolve.js +1 -1
- package/src/actions/marks/arc/actions.js +239 -0
- package/src/actions/marks/arc/index.js +1 -0
- package/src/actions/marks/{area.js → area/actions.js} +51 -38
- package/src/actions/marks/area/index.js +4 -0
- package/src/actions/marks/bar/create.js +26 -5
- package/src/actions/marks/bar/edit.js +1 -1
- package/src/actions/marks/bar/materialize.js +25 -31
- package/src/actions/marks/index.js +12 -4
- package/src/actions/marks/lifecycle.js +27 -0
- package/src/actions/marks/line/actions.js +373 -0
- package/src/actions/marks/line/index.js +1 -0
- package/src/actions/marks/{point.js → point/actions.js} +67 -108
- package/src/actions/marks/point/index.js +1 -0
- package/src/actions/marks/rect/actions.js +165 -0
- package/src/actions/marks/rect/index.js +1 -0
- package/src/actions/marks/remove.js +214 -0
- package/src/actions/marks/{rule.js → rule/actions.js} +34 -28
- package/src/actions/marks/rule/index.js +1 -0
- package/src/actions/marks/shared.js +156 -25
- package/src/actions/marks/text/actions.js +192 -0
- package/src/actions/marks/text/index.js +1 -0
- package/src/actions/primitives/createGraphics.js +13 -6
- package/src/actions/primitives/editGraphics.js +10 -3
- package/src/actions/primitives/semantic.js +26 -3
- package/src/actions/primitives/semanticValue.js +26 -4
- package/src/actions/regression/components.js +2 -2
- package/src/actions/regression/create.js +19 -2
- package/src/actions/regression/edit.js +247 -0
- package/src/actions/regression/index.js +3 -0
- package/src/actions/regression/resolve.js +3 -1
- package/src/actions/scales/consumers/common.js +91 -0
- package/src/actions/scales/consumers/families.js +47 -0
- package/src/actions/scales/consumers/index.js +41 -0
- package/src/actions/scales/{consumers.js → consumers/seriesLayout.js} +14 -127
- package/src/actions/scales/create.js +1 -1
- package/src/actions/scales/definitions.js +15 -6
- package/src/actions/scales/edit.js +45 -22
- package/src/actions/scales/materialize.js +58 -409
- package/src/actions/selection/actions.js +38 -24
- package/src/actions/selection/index.js +2 -0
- package/src/actions/titles/actions.js +63 -2
- package/src/actions/titles/index.js +2 -0
- package/src/actions/titles/resolve.js +12 -17
- package/src/composition.js +10 -0
- package/src/core/ChartProgram.js +49 -65
- package/src/core/action.js +10 -0
- package/src/core/compositionState.js +169 -0
- package/src/core/materializationState.js +65 -0
- package/src/core/programState.js +28 -0
- package/src/core/validation.js +16 -0
- package/src/core/vocabulary.js +154 -4
- package/src/grammar/arcs.js +154 -0
- package/src/grammar/areaSeries.js +1 -1
- package/src/grammar/bars/aggregate.js +3 -3
- package/src/grammar/bars/geometry.js +3 -3
- package/src/grammar/bars/policy.js +7 -1
- package/src/grammar/coordinates.js +3 -7
- package/src/grammar/density.js +21 -2
- package/src/grammar/facets/dependencies.js +185 -0
- package/src/grammar/facets/guides.js +257 -0
- package/src/grammar/facets/index.js +122 -0
- package/src/grammar/facets/scales.js +254 -0
- package/src/grammar/interval.js +2 -96
- package/src/grammar/lineSeries.js +112 -4
- package/src/grammar/palettes.js +5 -7
- package/src/grammar/polar.js +102 -0
- package/src/grammar/polarGuides.js +243 -0
- package/src/grammar/polarLineCommands.js +43 -0
- package/src/grammar/polarPaths.js +161 -0
- package/src/grammar/positionCompatibility.js +18 -2
- package/src/grammar/rects.js +26 -0
- package/src/grammar/regression/derive.js +97 -0
- package/src/grammar/regression/index.js +22 -0
- package/src/grammar/regression/models.js +282 -0
- package/src/grammar/regression/parameters.js +106 -0
- package/src/grammar/rules.js +1 -1
- package/src/grammar/scales/color.js +4 -2
- package/src/grammar/scales/continuous.js +6 -1
- package/src/grammar/scales/fields.js +7 -0
- package/src/grammar/scales/ordinal.js +5 -2
- package/src/grammar/scales/policies.js +1 -1
- package/src/grammar/schemas/graphic.js +1 -1
- package/src/grammar/schemas/graphicBounds.js +64 -9
- package/src/grammar/schemas/graphicTree.js +44 -10
- package/src/grammar/schemas/semanticPath.js +31 -9
- package/src/grammar/seriesLayout.js +13 -2
- package/src/grammar/statistics/studentT.js +98 -0
- package/src/grammar/text.js +71 -0
- package/src/grammar/ticks.js +50 -13
- package/src/grammar/transforms.js +1 -1
- package/src/index.js +1 -0
- package/src/layout/composition.js +310 -0
- package/src/layout/facets.js +131 -0
- package/src/layout/title.js +6 -0
- package/src/materialization/bars/aggregate.js +6 -3
- package/src/materialization/bars/grouped.js +75 -37
- package/src/materialization/bars/histogram.js +1 -1
- package/src/materialization/bars/ranged.js +1 -1
- package/src/materialization/composition.js +152 -0
- package/src/materialization/compositionSnapshot.js +114 -0
- package/src/materialization/density.js +1 -1
- package/src/materialization/dependencies.js +47 -73
- package/src/materialization/encodings.js +14 -28
- package/src/materialization/facetGuides/index.js +2 -0
- package/src/materialization/facetGuides/legacyCategorical.js +125 -0
- package/src/materialization/facetGuides/placement.js +126 -0
- package/src/materialization/facetGuides/preparation.js +188 -0
- package/src/materialization/facets.js +214 -0
- package/src/materialization/graphicHierarchy.js +15 -5
- package/src/materialization/guides/resources.js +46 -0
- package/src/materialization/marks/capabilities.js +141 -0
- package/src/materialization/marks/index.js +105 -0
- package/src/materialization/marks/policies.js +87 -0
- package/src/materialization/planner.js +105 -12
- package/src/materialization/rect.js +138 -0
- package/src/materialization/rectConfig.js +38 -0
- package/src/materialization/rowEncoding.js +33 -0
- package/src/materialization/scaleGuideDependencies.js +94 -0
- package/src/materialization/scales/map.js +40 -0
- package/src/materialization/scales/policies/arc.js +37 -0
- package/src/materialization/scales/policies/bar.js +132 -0
- package/src/materialization/scales/policies/series.js +76 -0
- package/src/materialization/scales/resolve.js +326 -0
- package/src/materialization/selection/items/arc.js +29 -0
- package/src/materialization/selection/items/bar.js +255 -0
- package/src/materialization/selection/items/common.js +131 -0
- package/src/materialization/selection/items/index.js +6 -0
- package/src/materialization/selection/items/path.js +59 -0
- package/src/materialization/selection/items/point.js +55 -0
- package/src/materialization/selection/items/rect.js +23 -0
- package/src/materialization/selection/items/rule.js +25 -0
- package/src/materialization/selection/policies/arc.js +10 -0
- package/src/materialization/selection/policies/area.js +1 -1
- package/src/materialization/selection/policies/bar.js +1 -1
- package/src/materialization/selection/policies/index.js +5 -1
- package/src/materialization/selection/policies/line.js +1 -1
- package/src/materialization/selection/policies/point.js +1 -1
- package/src/materialization/selection/policies/rect.js +10 -0
- package/src/materialization/selection/policies/rule.js +1 -1
- package/src/materialization/selection/styles.js +25 -17
- package/src/materialization/text.js +156 -0
- package/src/renderers/canvas/index.js +68 -22
- package/src/renderers/canvas/text.js +14 -1
- package/types/extension.d.ts +5 -1
- package/types/index.d.ts +56 -1
- package/types/program.d.ts +664 -27
- package/src/actions/encodings/color.js +0 -329
- package/src/actions/encodings/position.js +0 -176
- package/src/actions/marks/line.js +0 -248
- package/src/grammar/regression.js +0 -578
- package/src/grammar/scales.js +0 -1
- package/src/materialization/marks.js +0 -110
- package/src/materialization/selection/items.js +0 -503
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { action } from "../../../core/action.js";
|
|
2
2
|
import { isPlainObject } from "../../../core/immutable.js";
|
|
3
3
|
import { validateUserId } from "../../../core/identifiers.js";
|
|
4
|
-
import { validateKeys } from "../../../core/validation.js";
|
|
4
|
+
import { validateKeys, validateOptionObject } from "../../../core/validation.js";
|
|
5
5
|
import { findCoordinate } from "../../../selectors/coordinates.js";
|
|
6
|
+
import { resolvePolarGuideResources } from "../polar/resolve.js";
|
|
6
7
|
|
|
7
|
-
const TOP_OPTIONS = Object.freeze([
|
|
8
|
+
const TOP_OPTIONS = Object.freeze([
|
|
9
|
+
"coordinate", "x", "y", "theta", "radius"
|
|
10
|
+
]);
|
|
8
11
|
const COORDINATE_OPTIONS = Object.freeze(["id", "type"]);
|
|
9
12
|
const COORDINATE_API_TYPES = new Set(["auto", "cartesian", "polar"]);
|
|
10
13
|
|
|
@@ -15,13 +18,11 @@ function validateAxisOption(value, channel) {
|
|
|
15
18
|
}
|
|
16
19
|
|
|
17
20
|
function validateArgs(args) {
|
|
18
|
-
|
|
19
|
-
throw new TypeError("createAxes options must be a plain object.");
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
validateKeys(args, TOP_OPTIONS, "createAxes");
|
|
21
|
+
validateOptionObject(args, TOP_OPTIONS, "createAxes");
|
|
23
22
|
validateAxisOption(args.x, "x");
|
|
24
23
|
validateAxisOption(args.y, "y");
|
|
24
|
+
validateAxisOption(args.theta, "theta");
|
|
25
|
+
validateAxisOption(args.radius, "radius");
|
|
25
26
|
|
|
26
27
|
const coordinate = args.coordinate ?? {};
|
|
27
28
|
if (!isPlainObject(coordinate)) {
|
|
@@ -60,14 +61,10 @@ function inspectChannels(layers) {
|
|
|
60
61
|
);
|
|
61
62
|
}
|
|
62
63
|
|
|
63
|
-
return { cartesianLayers,
|
|
64
|
+
return { cartesianLayers, polarLayers };
|
|
64
65
|
}
|
|
65
66
|
|
|
66
|
-
function resolveCoordinate(program, descriptor, cartesianLayers
|
|
67
|
-
if (hasPolar && cartesianLayers.length === 0) {
|
|
68
|
-
throw new Error("createAxes does not yet support Polar axes.");
|
|
69
|
-
}
|
|
70
|
-
|
|
67
|
+
function resolveCoordinate(program, descriptor, cartesianLayers) {
|
|
71
68
|
if (cartesianLayers.length === 0) {
|
|
72
69
|
throw new Error("createAxes requires an x or y encoding.");
|
|
73
70
|
}
|
|
@@ -124,6 +121,24 @@ function resolveCoordinate(program, descriptor, cartesianLayers, hasPolar) {
|
|
|
124
121
|
return { id, layers };
|
|
125
122
|
}
|
|
126
123
|
|
|
124
|
+
function resolvePolarAxisArgs(program, layers, channel, option, descriptor) {
|
|
125
|
+
const selected = option === false
|
|
126
|
+
? false
|
|
127
|
+
: option !== undefined || hasChannel(layers, channel);
|
|
128
|
+
if (!selected) return undefined;
|
|
129
|
+
const args = option ?? {};
|
|
130
|
+
const kind = channel === "theta" ? "theta" : "radius";
|
|
131
|
+
const resource = resolvePolarGuideResources(program, kind, {
|
|
132
|
+
...args,
|
|
133
|
+
...(descriptor.id === undefined ? {} : { coordinate: descriptor.id })
|
|
134
|
+
}, "createAxes");
|
|
135
|
+
return {
|
|
136
|
+
...args,
|
|
137
|
+
scale: resource.scale,
|
|
138
|
+
coordinate: resource.coordinate
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
127
142
|
function hasChannel(layers, channel) {
|
|
128
143
|
return layers.some(layer => layer.encoding?.[channel] !== undefined);
|
|
129
144
|
}
|
|
@@ -170,14 +185,53 @@ const createAxes = action(
|
|
|
170
185
|
},
|
|
171
186
|
function (args = {}) {
|
|
172
187
|
const coordinateDescriptor = validateArgs(args);
|
|
173
|
-
const { cartesianLayers,
|
|
188
|
+
const { cartesianLayers, polarLayers } = inspectChannels(
|
|
174
189
|
this.semanticSpec.layers
|
|
175
190
|
);
|
|
191
|
+
const requestedCoordinateType = coordinateDescriptor.id === undefined
|
|
192
|
+
? undefined
|
|
193
|
+
: findCoordinate(this, coordinateDescriptor.id)?.type;
|
|
194
|
+
const explicitlyPolar = coordinateDescriptor.type === "polar" ||
|
|
195
|
+
requestedCoordinateType === "polar" ||
|
|
196
|
+
args.theta !== undefined || args.radius !== undefined;
|
|
197
|
+
if (polarLayers.length > 0 &&
|
|
198
|
+
(cartesianLayers.length === 0 || explicitlyPolar)) {
|
|
199
|
+
if (coordinateDescriptor.type === "cartesian") {
|
|
200
|
+
throw new Error("createAxes coordinate type conflicts with Polar encodings.");
|
|
201
|
+
}
|
|
202
|
+
if (args.x !== undefined || args.y !== undefined) {
|
|
203
|
+
throw new Error("createAxes x/y options require Cartesian encodings.");
|
|
204
|
+
}
|
|
205
|
+
const theta = resolvePolarAxisArgs(
|
|
206
|
+
this,
|
|
207
|
+
polarLayers,
|
|
208
|
+
"theta",
|
|
209
|
+
args.theta,
|
|
210
|
+
coordinateDescriptor
|
|
211
|
+
);
|
|
212
|
+
const radius = resolvePolarAxisArgs(
|
|
213
|
+
this,
|
|
214
|
+
polarLayers,
|
|
215
|
+
"radius",
|
|
216
|
+
args.radius,
|
|
217
|
+
coordinateDescriptor
|
|
218
|
+
);
|
|
219
|
+
if (theta === undefined && radius === undefined) {
|
|
220
|
+
throw new Error("createAxes requires at least one selected axis.");
|
|
221
|
+
}
|
|
222
|
+
let polarProgram = this;
|
|
223
|
+
if (theta !== undefined) {
|
|
224
|
+
polarProgram = polarProgram.createThetaAxis(theta);
|
|
225
|
+
}
|
|
226
|
+
if (radius !== undefined) {
|
|
227
|
+
polarProgram = polarProgram.createRadialAxis(radius);
|
|
228
|
+
}
|
|
229
|
+
return polarProgram;
|
|
230
|
+
}
|
|
176
231
|
const coordinate = resolveCoordinate(
|
|
177
232
|
this,
|
|
178
233
|
coordinateDescriptor,
|
|
179
|
-
cartesianLayers
|
|
180
|
-
hasPolar
|
|
234
|
+
cartesianLayers
|
|
181
235
|
);
|
|
182
236
|
const x = resolveAxisArgs(coordinate.layers, "x", args.x);
|
|
183
237
|
const y = resolveAxisArgs(coordinate.layers, "y", args.y);
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { action } from "../../../core/action.js";
|
|
2
|
+
import { isPlainObject } from "../../../core/immutable.js";
|
|
3
|
+
import { validateKeys, validateOptionObject } from "../../../core/validation.js";
|
|
4
|
+
|
|
5
|
+
const OPTIONS = Object.freeze([
|
|
6
|
+
"position", "line", "ticks", "labels", "ticksAndLabels", "title"
|
|
7
|
+
]);
|
|
8
|
+
const LINE_OPTIONS = Object.freeze(["color", "lineWidth"]);
|
|
9
|
+
const TICK_OPTIONS = Object.freeze([
|
|
10
|
+
"count", "values", "length", "color", "lineWidth"
|
|
11
|
+
]);
|
|
12
|
+
const LABEL_OPTIONS = Object.freeze([
|
|
13
|
+
"count", "values", "offset", "format", "color", "fontSize",
|
|
14
|
+
"fontFamily", "fontWeight"
|
|
15
|
+
]);
|
|
16
|
+
const GROUP_OPTIONS = Object.freeze(["count", "values", "ticks", "labels"]);
|
|
17
|
+
const TITLE_OPTIONS = Object.freeze([
|
|
18
|
+
"text", "at", "offset", "rotation", "color", "fontSize",
|
|
19
|
+
"fontFamily", "fontWeight"
|
|
20
|
+
]);
|
|
21
|
+
|
|
22
|
+
function validateNested(args, key, options, operation) {
|
|
23
|
+
if (!Object.hasOwn(args, key)) return;
|
|
24
|
+
if (!isPlainObject(args[key])) {
|
|
25
|
+
throw new TypeError(`${operation}.${key} must be a plain object.`);
|
|
26
|
+
}
|
|
27
|
+
validateKeys(args[key], options, `${operation}.${key}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function validateArgs(args, operation) {
|
|
31
|
+
validateOptionObject(args, OPTIONS, operation, {
|
|
32
|
+
allowEmpty: false,
|
|
33
|
+
emptyMessage: `${operation} requires at least one axis change.`,
|
|
34
|
+
emptyError: Error
|
|
35
|
+
});
|
|
36
|
+
validateNested(args, "line", LINE_OPTIONS, operation);
|
|
37
|
+
validateNested(args, "ticks", TICK_OPTIONS, operation);
|
|
38
|
+
validateNested(args, "labels", LABEL_OPTIONS, operation);
|
|
39
|
+
validateNested(args, "ticksAndLabels", GROUP_OPTIONS, operation);
|
|
40
|
+
validateNested(args, "title", TITLE_OPTIONS, operation);
|
|
41
|
+
if (args.ticksAndLabels !== undefined && (
|
|
42
|
+
args.ticks !== undefined || args.labels !== undefined
|
|
43
|
+
)) {
|
|
44
|
+
throw new Error(
|
|
45
|
+
`${operation} cannot combine ticksAndLabels with ticks or labels.`
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
if (args.ticksAndLabels?.ticks !== undefined) {
|
|
49
|
+
validateNested(args.ticksAndLabels, "ticks", ["length", "color", "lineWidth"], `${operation}.ticksAndLabels`);
|
|
50
|
+
}
|
|
51
|
+
if (args.ticksAndLabels?.labels !== undefined) {
|
|
52
|
+
validateNested(args.ticksAndLabels, "labels", [
|
|
53
|
+
"offset", "format", "color", "fontSize", "fontFamily", "fontWeight"
|
|
54
|
+
], `${operation}.ticksAndLabels`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function names(channel) {
|
|
59
|
+
const prefix = channel === "x" ? "X" : "Y";
|
|
60
|
+
return {
|
|
61
|
+
operation: `edit${prefix}Axis`,
|
|
62
|
+
line: `edit${prefix}AxisLine`,
|
|
63
|
+
ticks: `edit${prefix}AxisTicks`,
|
|
64
|
+
labels: `edit${prefix}AxisLabels`,
|
|
65
|
+
group: `edit${prefix}AxisTicksAndLabels`,
|
|
66
|
+
title: `edit${prefix}AxisTitle`
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function makeEditAxis(channel) {
|
|
71
|
+
const operation = names(channel);
|
|
72
|
+
return action(
|
|
73
|
+
{
|
|
74
|
+
op: operation.operation,
|
|
75
|
+
description: `Edit selected existing ${channel}-axis components.`
|
|
76
|
+
},
|
|
77
|
+
function (args = {}) {
|
|
78
|
+
validateArgs(args, operation.operation);
|
|
79
|
+
const shared = Object.hasOwn(args, "position")
|
|
80
|
+
? { position: args.position }
|
|
81
|
+
: {};
|
|
82
|
+
const has = id => this.graphicSpec.objects[id] !== undefined;
|
|
83
|
+
let next = this;
|
|
84
|
+
|
|
85
|
+
if (args.line !== undefined || (args.position !== undefined && has(`${channel}AxisLine`))) {
|
|
86
|
+
next = next[operation.line]({ ...shared, ...(args.line ?? {}) });
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (args.ticksAndLabels !== undefined) {
|
|
90
|
+
next = next[operation.group]({
|
|
91
|
+
...shared,
|
|
92
|
+
...args.ticksAndLabels
|
|
93
|
+
});
|
|
94
|
+
} else if (args.ticks !== undefined || args.labels !== undefined) {
|
|
95
|
+
if (args.ticks !== undefined) {
|
|
96
|
+
next = next[operation.ticks]({ ...shared, ...args.ticks });
|
|
97
|
+
}
|
|
98
|
+
if (args.labels !== undefined) {
|
|
99
|
+
next = next[operation.labels]({ ...shared, ...args.labels });
|
|
100
|
+
}
|
|
101
|
+
} else if (args.position !== undefined) {
|
|
102
|
+
const hasTicks = has(`${channel}AxisTicks`);
|
|
103
|
+
const hasLabels = has(`${channel}AxisLabels`);
|
|
104
|
+
if (hasTicks && hasLabels) {
|
|
105
|
+
next = next[operation.group](shared);
|
|
106
|
+
} else {
|
|
107
|
+
if (hasTicks) next = next[operation.ticks](shared);
|
|
108
|
+
if (hasLabels) next = next[operation.labels](shared);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (args.title !== undefined || (args.position !== undefined && has(`${channel}AxisTitle`))) {
|
|
113
|
+
next = next[operation.title]({ ...shared, ...(args.title ?? {}) });
|
|
114
|
+
}
|
|
115
|
+
return next;
|
|
116
|
+
}
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const editXAxis = makeEditAxis("x");
|
|
121
|
+
const editYAxis = makeEditAxis("y");
|
|
122
|
+
|
|
123
|
+
export function registerCompleteAxisEditActions(ProgramClass) {
|
|
124
|
+
ProgramClass.prototype.editXAxis = editXAxis;
|
|
125
|
+
ProgramClass.prototype.editYAxis = editYAxis;
|
|
126
|
+
}
|
|
@@ -5,6 +5,13 @@ import { registerAxisTickGroupActions } from "./tickGroups.js";
|
|
|
5
5
|
import { registerAxisTickActions } from "./ticks.js";
|
|
6
6
|
import { registerAxisTitleActions } from "./titles.js";
|
|
7
7
|
import { registerAxisActions } from "./axis.js";
|
|
8
|
+
import { registerCompleteAxisEditActions } from "./edit.js";
|
|
9
|
+
import {
|
|
10
|
+
removeRadialAxis,
|
|
11
|
+
removeThetaAxis,
|
|
12
|
+
removeXAxis,
|
|
13
|
+
removeYAxis
|
|
14
|
+
} from "./remove.js";
|
|
8
15
|
|
|
9
16
|
export function registerGuideAxisActions(ProgramClass) {
|
|
10
17
|
registerAxisLineActions(ProgramClass);
|
|
@@ -14,4 +21,9 @@ export function registerGuideAxisActions(ProgramClass) {
|
|
|
14
21
|
registerAxisTitleActions(ProgramClass);
|
|
15
22
|
registerAxisActions(ProgramClass);
|
|
16
23
|
registerAxisCollectionActions(ProgramClass);
|
|
24
|
+
registerCompleteAxisEditActions(ProgramClass);
|
|
25
|
+
ProgramClass.prototype.removeXAxis = removeXAxis;
|
|
26
|
+
ProgramClass.prototype.removeYAxis = removeYAxis;
|
|
27
|
+
ProgramClass.prototype.removeThetaAxis = removeThetaAxis;
|
|
28
|
+
ProgramClass.prototype.removeRadialAxis = removeRadialAxis;
|
|
17
29
|
}
|
|
@@ -2,6 +2,7 @@ import { action } from "../../../core/action.js";
|
|
|
2
2
|
import { validateUserId } from "../../../core/identifiers.js";
|
|
3
3
|
import {
|
|
4
4
|
sameOrderedValues,
|
|
5
|
+
validateOptionObject,
|
|
5
6
|
validateNonEmptyString,
|
|
6
7
|
validateNonNegativeFinite,
|
|
7
8
|
validatePositiveFinite
|
|
@@ -12,8 +13,8 @@ import {
|
|
|
12
13
|
mapContinuousScaleValues,
|
|
13
14
|
formatTransformedTick,
|
|
14
15
|
mapOrdinalPositionValues
|
|
15
|
-
} from "../../../grammar/scales.js";
|
|
16
|
-
import { formatTimeTick } from "../../../grammar/ticks.js";
|
|
16
|
+
} from "../../../grammar/scales/index.js";
|
|
17
|
+
import { formatTimeTick, formatTimeTicks } from "../../../grammar/ticks.js";
|
|
17
18
|
import { valuesFromTickConfig } from "../tickValues.js";
|
|
18
19
|
import { DEFAULT_COLORS, DEFAULT_FONT_FAMILY } from
|
|
19
20
|
"../../../theme/defaults.js";
|
|
@@ -24,7 +25,7 @@ import {
|
|
|
24
25
|
validateAxisFormat,
|
|
25
26
|
validateAxisPosition
|
|
26
27
|
} from "./policy.js";
|
|
27
|
-
import { resolvePlotGraphicPlacement } from
|
|
28
|
+
import { findCanvasGraphic, resolvePlotGraphicPlacement } from
|
|
28
29
|
"../../../materialization/graphicHierarchy.js";
|
|
29
30
|
|
|
30
31
|
const OPTIONS = Object.freeze([
|
|
@@ -41,11 +42,11 @@ const DEFAULTS = Object.freeze({
|
|
|
41
42
|
});
|
|
42
43
|
|
|
43
44
|
function validateOptions(args, operation, create) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
validateOptionObject(
|
|
46
|
+
args,
|
|
47
|
+
create ? OPTIONS : OPTIONS.filter(key => key !== "scale"),
|
|
48
|
+
operation
|
|
49
|
+
);
|
|
49
50
|
if (Object.hasOwn(args, "count") && Object.hasOwn(args, "values")) {
|
|
50
51
|
throw new Error(`${operation} cannot use count and values together.`);
|
|
51
52
|
}
|
|
@@ -97,16 +98,18 @@ function resolve(program, channel, config) {
|
|
|
97
98
|
const positions = discrete
|
|
98
99
|
? mapOrdinalPositionValues(values, scale)
|
|
99
100
|
: mapContinuousScaleValues(values, scale);
|
|
100
|
-
const text =
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
101
|
+
const text = scale.type === "time" && config.format === "auto"
|
|
102
|
+
? formatTimeTicks(values, scale.domain)
|
|
103
|
+
: values.map(value => formatAxisValue(
|
|
104
|
+
value,
|
|
105
|
+
scale.type,
|
|
106
|
+
config.format,
|
|
107
|
+
item => scale.type === "time"
|
|
108
|
+
? formatTimeTick(item, scale.domain)
|
|
109
|
+
: isTransformedScaleType(scale.type)
|
|
110
|
+
? formatTransformedTick(scale.type, item)
|
|
111
|
+
: String(item)
|
|
112
|
+
));
|
|
110
113
|
const resolved = {
|
|
111
114
|
values,
|
|
112
115
|
text,
|
|
@@ -118,7 +121,7 @@ function resolve(program, channel, config) {
|
|
|
118
121
|
offset: config.offset
|
|
119
122
|
})
|
|
120
123
|
};
|
|
121
|
-
const canvas = program
|
|
124
|
+
const canvas = findCanvasGraphic(program)?.properties;
|
|
122
125
|
const maximumTextWidth = Math.max(
|
|
123
126
|
0,
|
|
124
127
|
...text.map(value => [...value].length * config.fontSize * 0.6)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { action } from "../../../core/action.js";
|
|
2
|
+
import { validateKeys } from "../../../core/validation.js";
|
|
3
|
+
import { axisGraphicIds } from
|
|
4
|
+
"../../../materialization/guides/resources.js";
|
|
5
|
+
|
|
6
|
+
const OPTIONS = Object.freeze(["coordinate", "scale"]);
|
|
7
|
+
|
|
8
|
+
function makeRemoveAxis(channel) {
|
|
9
|
+
const prefix = channel === "x"
|
|
10
|
+
? "X"
|
|
11
|
+
: channel === "y"
|
|
12
|
+
? "Y"
|
|
13
|
+
: channel === "theta"
|
|
14
|
+
? "Theta"
|
|
15
|
+
: "Radial";
|
|
16
|
+
const operation = `remove${prefix}Axis`;
|
|
17
|
+
const graphicIds = axisGraphicIds(channel);
|
|
18
|
+
return action(
|
|
19
|
+
{
|
|
20
|
+
op: operation,
|
|
21
|
+
description: `Remove the complete ${channel}-axis resource.`
|
|
22
|
+
},
|
|
23
|
+
function (args = {}) {
|
|
24
|
+
validateKeys(args, OPTIONS, operation);
|
|
25
|
+
const semantic = this.semanticSpec.guides.axis?.[channel];
|
|
26
|
+
const config = this.guideConfigs.axis?.[channel];
|
|
27
|
+
const hasGraphic = graphicIds.some(
|
|
28
|
+
id => this.graphicSpec.objects[id] !== undefined
|
|
29
|
+
);
|
|
30
|
+
if (semantic === undefined && config === undefined && !hasGraphic) {
|
|
31
|
+
throw new Error(`${operation} requires an existing ${channel}-axis.`);
|
|
32
|
+
}
|
|
33
|
+
if (args.scale !== undefined && semantic?.scale !== args.scale) {
|
|
34
|
+
throw new Error(`${operation} found no axis for scale "${args.scale}".`);
|
|
35
|
+
}
|
|
36
|
+
if (args.coordinate !== undefined && semantic?.coordinate !== args.coordinate) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
`${operation} found no axis for coordinate "${args.coordinate}".`
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
let next = semantic === undefined
|
|
42
|
+
? this
|
|
43
|
+
: this.editSemantic({
|
|
44
|
+
property: `guide.axis.${channel}`,
|
|
45
|
+
remove: true
|
|
46
|
+
});
|
|
47
|
+
for (const id of graphicIds) {
|
|
48
|
+
if (next.graphicSpec.objects[id] !== undefined) {
|
|
49
|
+
next = next.editGraphics({ target: id, remove: true });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return next._withoutMaterializationConfig(["guides", "axis", channel]);
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const removeXAxis = makeRemoveAxis("x");
|
|
58
|
+
export const removeYAxis = makeRemoveAxis("y");
|
|
59
|
+
export const removeThetaAxis = makeRemoveAxis("theta");
|
|
60
|
+
export const removeRadialAxis = makeRemoveAxis("radius");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { action } from "../../../core/action.js";
|
|
2
2
|
import { isPlainObject } from "../../../core/immutable.js";
|
|
3
|
-
import { validateKeys } from "../../../core/validation.js";
|
|
3
|
+
import { validateKeys, validateOptionObject } from "../../../core/validation.js";
|
|
4
4
|
|
|
5
5
|
const CREATE_OPTIONS = Object.freeze([
|
|
6
6
|
"scale",
|
|
@@ -38,7 +38,12 @@ function validateNested(value, supported, label) {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
function validateArgs(args, operation, create) {
|
|
41
|
-
|
|
41
|
+
validateOptionObject(
|
|
42
|
+
args,
|
|
43
|
+
create ? CREATE_OPTIONS : EDIT_OPTIONS,
|
|
44
|
+
operation,
|
|
45
|
+
{ allowEmpty: create }
|
|
46
|
+
);
|
|
42
47
|
|
|
43
48
|
if (Object.hasOwn(args, "count") && Object.hasOwn(args, "values")) {
|
|
44
49
|
throw new Error(`${operation} cannot use count and values together.`);
|
|
@@ -52,9 +57,6 @@ function validateArgs(args, operation, create) {
|
|
|
52
57
|
validateNested(args.labels, LABEL_OPTIONS, `${operation}.labels`);
|
|
53
58
|
}
|
|
54
59
|
|
|
55
|
-
if (!create && Object.keys(args).length === 0) {
|
|
56
|
-
throw new TypeError(`${operation} requires at least one option.`);
|
|
57
|
-
}
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
function select(source, keys) {
|
|
@@ -2,14 +2,15 @@ import { action } from "../../../core/action.js";
|
|
|
2
2
|
import { validateUserId } from "../../../core/identifiers.js";
|
|
3
3
|
import {
|
|
4
4
|
validateNonEmptyString,
|
|
5
|
-
validateNonNegativeFinite
|
|
5
|
+
validateNonNegativeFinite,
|
|
6
|
+
validateOptionObject
|
|
6
7
|
} from "../../../core/validation.js";
|
|
7
8
|
import { resolveGraphicBounds } from "../../../layout/canvas.js";
|
|
8
9
|
import {
|
|
9
10
|
isTransformedScaleType,
|
|
10
11
|
mapContinuousScaleValues,
|
|
11
12
|
mapOrdinalPositionValues
|
|
12
|
-
} from "../../../grammar/scales.js";
|
|
13
|
+
} from "../../../grammar/scales/index.js";
|
|
13
14
|
import {
|
|
14
15
|
DEFAULT_TICK_COUNT,
|
|
15
16
|
inferHistogramBoundaries,
|
|
@@ -21,7 +22,7 @@ import {
|
|
|
21
22
|
resolveAxisTickGeometry,
|
|
22
23
|
validateAxisPosition
|
|
23
24
|
} from "./policy.js";
|
|
24
|
-
import { resolvePlotGraphicPlacement } from
|
|
25
|
+
import { findCanvasGraphic, resolvePlotGraphicPlacement } from
|
|
25
26
|
"../../../materialization/graphicHierarchy.js";
|
|
26
27
|
|
|
27
28
|
const OPTIONS = Object.freeze(["scale", "position", "count", "values", "length", "color", "lineWidth"]);
|
|
@@ -33,7 +34,11 @@ const DEFAULTS = Object.freeze({
|
|
|
33
34
|
});
|
|
34
35
|
|
|
35
36
|
function validateOptions(args, operation, create) {
|
|
36
|
-
|
|
37
|
+
validateOptionObject(
|
|
38
|
+
args,
|
|
39
|
+
create ? OPTIONS : OPTIONS.filter(key => key !== "scale"),
|
|
40
|
+
operation
|
|
41
|
+
);
|
|
37
42
|
if (Object.hasOwn(args, "count") && Object.hasOwn(args, "values")) throw new Error(`${operation} cannot use count and values together.`);
|
|
38
43
|
}
|
|
39
44
|
|
|
@@ -84,7 +89,7 @@ function geometry(program, channel, config) {
|
|
|
84
89
|
length: config.length
|
|
85
90
|
})
|
|
86
91
|
};
|
|
87
|
-
const canvas = program
|
|
92
|
+
const canvas = findCanvasGraphic(program)?.properties;
|
|
88
93
|
const coordinates = channel === "x"
|
|
89
94
|
? [...resolved.x1, ...resolved.x2, resolved.y1, resolved.y2]
|
|
90
95
|
: [resolved.x1, resolved.x2, ...resolved.y1, ...resolved.y2];
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
isTransformedScaleType,
|
|
12
12
|
mapContinuousScaleValues,
|
|
13
13
|
mapOrdinalPositionValues
|
|
14
|
-
} from "../../../grammar/scales.js";
|
|
14
|
+
} from "../../../grammar/scales/index.js";
|
|
15
15
|
import { DEFAULT_COLORS, DEFAULT_FONT_FAMILY } from
|
|
16
16
|
"../../../theme/defaults.js";
|
|
17
17
|
import { findDataset } from "../../../selectors/datasets.js";
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
resolveAxisTitleGeometry,
|
|
23
23
|
validateAxisPosition
|
|
24
24
|
} from "./policy.js";
|
|
25
|
-
import { resolvePlotGraphicPlacement } from
|
|
25
|
+
import { findCanvasGraphic, resolvePlotGraphicPlacement } from
|
|
26
26
|
"../../../materialization/graphicHierarchy.js";
|
|
27
27
|
|
|
28
28
|
const CREATE_OPTIONS = Object.freeze([
|
|
@@ -52,7 +52,7 @@ function validateConfig(channel, config) {
|
|
|
52
52
|
if (typeof config.fontWeight !== "string" && !Number.isFinite(config.fontWeight)) throw new TypeError("Axis title fontWeight must be a string or number.");
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
function
|
|
55
|
+
export function inferAxisTitleText(program, channel, scaleId) {
|
|
56
56
|
const titles = new Set();
|
|
57
57
|
const primaryTitles = new Set();
|
|
58
58
|
for (const layer of program.semanticSpec.layers) {
|
|
@@ -138,7 +138,7 @@ function resolveGeometry(program, channel, config) {
|
|
|
138
138
|
const sine = Math.sin(config.rotation);
|
|
139
139
|
const extentX = Math.abs(cosine) * width / 2 + Math.abs(sine) * height / 2;
|
|
140
140
|
const extentY = Math.abs(sine) * width / 2 + Math.abs(cosine) * height / 2;
|
|
141
|
-
const canvas = program
|
|
141
|
+
const canvas = findCanvasGraphic(program)?.properties;
|
|
142
142
|
const newEdgeFits = config.position === "top"
|
|
143
143
|
? geometry.y - extentY >= 0
|
|
144
144
|
: config.position === "right"
|
|
@@ -184,7 +184,7 @@ function makeEdit(channel) {
|
|
|
184
184
|
const resolvedTitle = explicitText
|
|
185
185
|
? validateText(text)
|
|
186
186
|
: config.inferredText === true
|
|
187
|
-
?
|
|
187
|
+
? inferAxisTitleText(this, channel, config.scale)
|
|
188
188
|
: this.semanticSpec.guides.axis?.[channel]?.title;
|
|
189
189
|
if (resolvedTitle !== this.semanticSpec.guides.axis?.[channel]?.title) {
|
|
190
190
|
next = next.editSemantic({
|
|
@@ -218,7 +218,9 @@ function makeCreate(channel) {
|
|
|
218
218
|
if (guideScale && guideScale !== scale) throw new Error(`${operation.create} conflicts with the existing axis scale.`);
|
|
219
219
|
if (this.graphicSpec.objects[operation.graphic]) throw new Error(`${operation.create} requires a missing axis title.`);
|
|
220
220
|
const inferredText = !Object.hasOwn(args, "text");
|
|
221
|
-
const text = validateText(
|
|
221
|
+
const text = validateText(
|
|
222
|
+
args.text ?? inferAxisTitleText(this, channel, scale)
|
|
223
|
+
);
|
|
222
224
|
const position = args.position ?? defaultAxisPosition(channel);
|
|
223
225
|
const inferredRotation = !Object.hasOwn(args, "rotation");
|
|
224
226
|
const config = {
|