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
|
@@ -4,13 +4,18 @@ import {
|
|
|
4
4
|
} from "../../core/identifiers.js";
|
|
5
5
|
import { findDataset } from "../../selectors/datasets.js";
|
|
6
6
|
import { findLayer, hasLayer } from "../../selectors/layers.js";
|
|
7
|
+
import { findSemanticScale } from "../../selectors/scales.js";
|
|
8
|
+
import { resolveMarkPositionPolicy } from "../encodings/position/policies/index.js";
|
|
9
|
+
import { getMarkMaterializationStep } from "../../materialization/marks/index.js";
|
|
10
|
+
import {
|
|
11
|
+
POLAR_POSITION_CHANNELS,
|
|
12
|
+
CARTESIAN_POSITION_CHANNELS,
|
|
13
|
+
POSITION_CHANNELS
|
|
14
|
+
} from "../../core/vocabulary.js";
|
|
15
|
+
import { validateOptionObject } from "../../core/validation.js";
|
|
7
16
|
|
|
8
17
|
export function validateMarkOptions(args, supported, operation) {
|
|
9
|
-
|
|
10
|
-
if (!supported.includes(key)) {
|
|
11
|
-
throw new Error(`Unknown ${operation} option "${key}".`);
|
|
12
|
-
}
|
|
13
|
-
}
|
|
18
|
+
validateOptionObject(args, supported, operation);
|
|
14
19
|
}
|
|
15
20
|
|
|
16
21
|
export function resolveMarkData(program, requested) {
|
|
@@ -60,30 +65,120 @@ export function assertMarkAvailable(program, id) {
|
|
|
60
65
|
}
|
|
61
66
|
}
|
|
62
67
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
68
|
+
const POSITION_POLICY_PROPERTIES = Object.freeze(["aggregate", "bin", "stack"]);
|
|
69
|
+
const POSITION_POLICY_SUBSETS = Object.freeze([
|
|
70
|
+
Object.freeze(["aggregate", "bin", "stack"]),
|
|
71
|
+
Object.freeze(["aggregate", "bin"]),
|
|
72
|
+
Object.freeze(["aggregate", "stack"]),
|
|
73
|
+
Object.freeze(["bin", "stack"]),
|
|
74
|
+
Object.freeze(["aggregate"]),
|
|
75
|
+
Object.freeze(["bin"]),
|
|
76
|
+
Object.freeze(["stack"]),
|
|
77
|
+
Object.freeze([])
|
|
78
|
+
]);
|
|
79
|
+
|
|
80
|
+
function inheritedPositionEncodings(encoding) {
|
|
81
|
+
if (encoding?.field === undefined) return undefined;
|
|
82
|
+
const base = Object.fromEntries(
|
|
83
|
+
["field", "fieldType", "scale", "title"]
|
|
67
84
|
.filter(property => Object.hasOwn(encoding, property))
|
|
68
85
|
.map(property => [property, encoding[property]])
|
|
69
86
|
);
|
|
87
|
+
const available = POSITION_POLICY_PROPERTIES.filter(property =>
|
|
88
|
+
Object.hasOwn(encoding, property)
|
|
89
|
+
);
|
|
90
|
+
const seen = new Set();
|
|
91
|
+
return POSITION_POLICY_SUBSETS.map(properties => ({
|
|
92
|
+
...base,
|
|
93
|
+
...Object.fromEntries(properties
|
|
94
|
+
.filter(property => available.includes(property))
|
|
95
|
+
.map(property => [property, encoding[property]]))
|
|
96
|
+
})).filter(candidate => {
|
|
97
|
+
const key = JSON.stringify(candidate);
|
|
98
|
+
if (seen.has(key)) return false;
|
|
99
|
+
seen.add(key);
|
|
100
|
+
return true;
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function scaleSupportsEncoding(program, markType, channel, encoding) {
|
|
105
|
+
const scale = findSemanticScale(program, encoding.scale);
|
|
106
|
+
if (scale === undefined) return false;
|
|
107
|
+
const categorical = ["nominal", "ordinal"].includes(encoding.fieldType);
|
|
108
|
+
if (categorical) {
|
|
109
|
+
if (["bar", "rect"].includes(markType)) return scale.type === "band";
|
|
110
|
+
if (markType === "arc") return scale.type === "band";
|
|
111
|
+
return ["ordinal", "band", "point"].includes(scale.type);
|
|
112
|
+
}
|
|
113
|
+
if (encoding.fieldType === "temporal") return scale.type === "time";
|
|
114
|
+
return ["linear", "log", "pow", "sqrt", "symlog"].includes(scale.type);
|
|
70
115
|
}
|
|
71
116
|
|
|
72
|
-
function
|
|
117
|
+
export function resolveCompatibleEncodings(program, source, markType) {
|
|
118
|
+
const dataset = findDataset(program, source.data);
|
|
119
|
+
if (dataset === undefined) return {};
|
|
120
|
+
const channels = POLAR_POSITION_CHANNELS.some(
|
|
121
|
+
channel => source.encoding?.[channel] !== undefined
|
|
122
|
+
)
|
|
123
|
+
? POLAR_POSITION_CHANNELS
|
|
124
|
+
: CARTESIAN_POSITION_CHANNELS;
|
|
125
|
+
const pending = new Map(channels.map(channel => [
|
|
126
|
+
channel,
|
|
127
|
+
inheritedPositionEncodings(source.encoding?.[channel])
|
|
128
|
+
]).filter(([, encodings]) => encodings !== undefined));
|
|
129
|
+
const candidate = {
|
|
130
|
+
id: "layered-inference",
|
|
131
|
+
data: source.data,
|
|
132
|
+
mark: { type: markType },
|
|
133
|
+
encoding: {}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
for (let pass = 0; pass < 2; pass += 1) {
|
|
137
|
+
for (const [channel, encodings] of pending) {
|
|
138
|
+
for (const encoding of encodings) {
|
|
139
|
+
if (!scaleSupportsEncoding(program, markType, channel, encoding)) continue;
|
|
140
|
+
try {
|
|
141
|
+
const policy = resolveMarkPositionPolicy({
|
|
142
|
+
program,
|
|
143
|
+
layer: candidate,
|
|
144
|
+
dataset,
|
|
145
|
+
channel,
|
|
146
|
+
args: encoding,
|
|
147
|
+
field: encoding.field,
|
|
148
|
+
fieldType: encoding.fieldType
|
|
149
|
+
});
|
|
150
|
+
candidate.encoding[channel] = {
|
|
151
|
+
...encoding,
|
|
152
|
+
...Object.fromEntries(
|
|
153
|
+
Object.entries(policy).filter(([, value]) => value !== undefined)
|
|
154
|
+
)
|
|
155
|
+
};
|
|
156
|
+
pending.delete(channel);
|
|
157
|
+
break;
|
|
158
|
+
} catch {
|
|
159
|
+
// Try the next target-compatible policy subset. Another channel may
|
|
160
|
+
// also make a richer subset resolvable on the next pass.
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return candidate.encoding;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function eligibleLayeredSource(program, layer, requestedData, markType) {
|
|
73
169
|
if (layer?.data === undefined) return false;
|
|
74
170
|
if (requestedData !== undefined && layer.data !== requestedData) return false;
|
|
75
|
-
return
|
|
171
|
+
return Object.keys(resolveCompatibleEncodings(program, layer, markType)).length > 0;
|
|
76
172
|
}
|
|
77
173
|
|
|
78
|
-
export function resolveLayeredMarkInheritance(program, requested = {}) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
: undefined;
|
|
174
|
+
export function resolveLayeredMarkInheritance(program, requested = {}, markType) {
|
|
175
|
+
if (Object.hasOwn(requested, "data")) return undefined;
|
|
176
|
+
const requestedData = program.context.currentData;
|
|
82
177
|
const eligible = program.semanticSpec.layers.filter(layer =>
|
|
83
|
-
eligibleLayeredSource(layer, requestedData)
|
|
178
|
+
eligibleLayeredSource(program, layer, requestedData, markType)
|
|
84
179
|
);
|
|
85
180
|
const current = findLayer(program, program.context.currentMark);
|
|
86
|
-
const source = eligibleLayeredSource(current, requestedData)
|
|
181
|
+
const source = eligibleLayeredSource(program, current, requestedData, markType)
|
|
87
182
|
? current
|
|
88
183
|
: eligible.length === 1
|
|
89
184
|
? eligible[0]
|
|
@@ -95,18 +190,54 @@ export function resolveLayeredMarkInheritance(program, requested = {}) {
|
|
|
95
190
|
);
|
|
96
191
|
}
|
|
97
192
|
if (source === undefined) return undefined;
|
|
193
|
+
if (
|
|
194
|
+
markType === "bar" &&
|
|
195
|
+
(
|
|
196
|
+
source.encoding?.x?.bin !== undefined ||
|
|
197
|
+
source.encoding?.y?.bin !== undefined ||
|
|
198
|
+
(source.encoding?.x?.stack !== undefined && source.encoding.x.stack !== null) ||
|
|
199
|
+
(source.encoding?.y?.stack !== undefined && source.encoding.y.stack !== null) ||
|
|
200
|
+
source.encoding?.xOffset !== undefined ||
|
|
201
|
+
source.encoding?.yOffset !== undefined ||
|
|
202
|
+
source.encoding?.color?.layout !== undefined
|
|
203
|
+
)
|
|
204
|
+
) {
|
|
205
|
+
return undefined;
|
|
206
|
+
}
|
|
98
207
|
|
|
99
208
|
return {
|
|
100
209
|
source: source.id,
|
|
101
210
|
data: source.data,
|
|
102
211
|
coordinate: source.coordinate,
|
|
103
|
-
encoding:
|
|
104
|
-
["x", "y"]
|
|
105
|
-
.map(channel => [
|
|
106
|
-
channel,
|
|
107
|
-
inheritedPositionEncoding(source.encoding?.[channel])
|
|
108
|
-
])
|
|
109
|
-
.filter(([, encoding]) => encoding !== undefined)
|
|
110
|
-
)
|
|
212
|
+
encoding: resolveCompatibleEncodings(program, source, markType)
|
|
111
213
|
};
|
|
112
214
|
}
|
|
215
|
+
|
|
216
|
+
export function applyLayeredMarkInheritance(program, id, inherited) {
|
|
217
|
+
let next = program;
|
|
218
|
+
if (inherited?.coordinate !== undefined) {
|
|
219
|
+
next = next.editSemantic({
|
|
220
|
+
property: `layer[${id}].coordinate`,
|
|
221
|
+
value: inherited.coordinate
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
for (const channel of POSITION_CHANNELS) {
|
|
225
|
+
for (const [property, value] of Object.entries(
|
|
226
|
+
inherited?.encoding[channel] ?? {}
|
|
227
|
+
)) {
|
|
228
|
+
next = next.editSemantic({
|
|
229
|
+
property: `layer[${id}].encoding.${channel}.${property}`,
|
|
230
|
+
value
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return next;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export function materializeInheritedMark(program, id) {
|
|
238
|
+
const layer = findLayer(program, id);
|
|
239
|
+
const step = layer === undefined
|
|
240
|
+
? undefined
|
|
241
|
+
: getMarkMaterializationStep(program, layer);
|
|
242
|
+
return step === undefined ? program : program[step.op](step.args);
|
|
243
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { action } from "../../../core/action.js";
|
|
2
|
+
import { validateUserId } from "../../../core/identifiers.js";
|
|
3
|
+
import {
|
|
4
|
+
DEFAULT_TEXT_MARK,
|
|
5
|
+
normalizeTextMarkConfig
|
|
6
|
+
} from "../../../grammar/text.js";
|
|
7
|
+
import { canMaterializeText } from "../../../materialization/marks/index.js";
|
|
8
|
+
import { resolveTextGraphicItems } from "../../../materialization/text.js";
|
|
9
|
+
import { findLayer, resolveEligibleLayer } from "../../../selectors/layers.js";
|
|
10
|
+
import {
|
|
11
|
+
applyLayeredMarkInheritance,
|
|
12
|
+
assertMarkAvailable,
|
|
13
|
+
resolveCompatibleEncodings,
|
|
14
|
+
resolveMarkData,
|
|
15
|
+
resolveMarkId,
|
|
16
|
+
validateMarkOptions
|
|
17
|
+
} from "../shared.js";
|
|
18
|
+
import { resolveMarkGraphicPlacement } from
|
|
19
|
+
"../../../materialization/graphicHierarchy.js";
|
|
20
|
+
|
|
21
|
+
const STYLE_OPTIONS = Object.freeze([
|
|
22
|
+
"fill", "opacity", "fontSize", "fontFamily", "fontWeight",
|
|
23
|
+
"align", "baseline", "rotation", "dx", "dy"
|
|
24
|
+
]);
|
|
25
|
+
const CREATE_OPTIONS = Object.freeze(["id", "data", "text", ...STYLE_OPTIONS]);
|
|
26
|
+
const EDIT_OPTIONS = Object.freeze(["target", ...STYLE_OPTIONS]);
|
|
27
|
+
const REMATERIALIZE_OPTIONS = Object.freeze(["id"]);
|
|
28
|
+
const SOURCE_TYPES = new Set(["point", "bar", "rule", "rect"]);
|
|
29
|
+
|
|
30
|
+
function eligibleSource(program, layer, requestedData) {
|
|
31
|
+
if (
|
|
32
|
+
!SOURCE_TYPES.has(layer?.mark?.type) ||
|
|
33
|
+
layer.data === undefined ||
|
|
34
|
+
(requestedData !== undefined && layer.data !== requestedData)
|
|
35
|
+
) return false;
|
|
36
|
+
const encodings = resolveCompatibleEncodings(program, layer, "text");
|
|
37
|
+
return encodings.x !== undefined && encodings.y !== undefined;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function resolveTextInheritance(program, args) {
|
|
41
|
+
if (Object.hasOwn(args, "data")) return undefined;
|
|
42
|
+
const requestedData = program.context.currentData;
|
|
43
|
+
const candidates = program.semanticSpec.layers.filter(layer =>
|
|
44
|
+
eligibleSource(program, layer, requestedData)
|
|
45
|
+
);
|
|
46
|
+
const current = findLayer(program, program.context.currentMark);
|
|
47
|
+
const source = eligibleSource(program, current, requestedData)
|
|
48
|
+
? current
|
|
49
|
+
: candidates.length === 1
|
|
50
|
+
? candidates[0]
|
|
51
|
+
: undefined;
|
|
52
|
+
if (source === undefined && candidates.length > 1) {
|
|
53
|
+
throw new Error(
|
|
54
|
+
"Text source inference is ambiguous; provide data and encode its position explicitly."
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
if (source === undefined) return undefined;
|
|
58
|
+
return {
|
|
59
|
+
source: source.id,
|
|
60
|
+
data: source.data,
|
|
61
|
+
coordinate: source.coordinate,
|
|
62
|
+
encoding: resolveCompatibleEncodings(program, source, "text")
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function requireTextLayer(program, requested, operation) {
|
|
67
|
+
const id = requested === undefined ? undefined : validateUserId(requested, "Text mark id");
|
|
68
|
+
return resolveEligibleLayer(program, {
|
|
69
|
+
target: id,
|
|
70
|
+
predicate: layer => layer.mark?.type === "text",
|
|
71
|
+
label: `${operation} text mark`
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const createTextMark = action(
|
|
76
|
+
{
|
|
77
|
+
op: "createTextMark",
|
|
78
|
+
description: "Create a semantic text annotation layer."
|
|
79
|
+
},
|
|
80
|
+
function (args = {}) {
|
|
81
|
+
validateMarkOptions(args, CREATE_OPTIONS, "createTextMark");
|
|
82
|
+
const id = resolveMarkId(this, args.id, {
|
|
83
|
+
defaultId: "text",
|
|
84
|
+
label: "Text mark id",
|
|
85
|
+
markType: "text",
|
|
86
|
+
operation: "createTextMark"
|
|
87
|
+
});
|
|
88
|
+
const inherited = resolveTextInheritance(this, args);
|
|
89
|
+
const { data } = resolveMarkData(this, {
|
|
90
|
+
...args,
|
|
91
|
+
...(args.data === undefined && inherited?.data !== undefined
|
|
92
|
+
? { data: inherited.data }
|
|
93
|
+
: {})
|
|
94
|
+
});
|
|
95
|
+
assertMarkAvailable(this, id);
|
|
96
|
+
|
|
97
|
+
let next = this
|
|
98
|
+
.editSemantic({ property: `layer[${id}].mark.type`, value: "text" })
|
|
99
|
+
.editSemantic({ property: `layer[${id}].data`, value: data });
|
|
100
|
+
if (inherited?.source !== undefined) {
|
|
101
|
+
next = next.editSemantic({
|
|
102
|
+
property: `layer[${id}].source`,
|
|
103
|
+
value: inherited.source
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
next = applyLayeredMarkInheritance(next, id, inherited)
|
|
107
|
+
.createGraphics({
|
|
108
|
+
id,
|
|
109
|
+
type: "text",
|
|
110
|
+
length: 0,
|
|
111
|
+
...resolveMarkGraphicPlacement(next, { data, markType: "text" })
|
|
112
|
+
})
|
|
113
|
+
._withMarkConfig(id, { ...DEFAULT_TEXT_MARK, fillExplicit: false });
|
|
114
|
+
|
|
115
|
+
const style = Object.fromEntries(
|
|
116
|
+
STYLE_OPTIONS.filter(option => Object.hasOwn(args, option))
|
|
117
|
+
.map(option => [option, args[option]])
|
|
118
|
+
);
|
|
119
|
+
if (Object.keys(style).length > 0) {
|
|
120
|
+
next = next.editTextMark({ target: id, ...style });
|
|
121
|
+
}
|
|
122
|
+
return Object.hasOwn(args, "text")
|
|
123
|
+
? next.encodeText({ target: id, value: args.text })
|
|
124
|
+
: next;
|
|
125
|
+
}
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
const rematerializeTextMark = action(
|
|
129
|
+
{
|
|
130
|
+
op: "rematerializeTextMark",
|
|
131
|
+
description: "Recompute concrete text content, anchors, and typography."
|
|
132
|
+
},
|
|
133
|
+
function (args = {}) {
|
|
134
|
+
validateMarkOptions(args, REMATERIALIZE_OPTIONS, "rematerializeTextMark");
|
|
135
|
+
const id = validateUserId(args.id, "Text mark id");
|
|
136
|
+
const layer = findLayer(this, id);
|
|
137
|
+
const graphic = this.graphicSpec.objects[id];
|
|
138
|
+
if (layer?.mark?.type !== "text") throw new Error(`Unknown text mark "${id}".`);
|
|
139
|
+
if (graphic?.type !== "text" || !Array.isArray(graphic.items)) {
|
|
140
|
+
throw new Error(`Text mark "${id}" requires text collection graphics.`);
|
|
141
|
+
}
|
|
142
|
+
if (!canMaterializeText(this, layer)) {
|
|
143
|
+
return graphic.items.length === 0
|
|
144
|
+
? this
|
|
145
|
+
: this.editGraphics({ target: id, property: "length", value: 0 });
|
|
146
|
+
}
|
|
147
|
+
return this.editGraphics({
|
|
148
|
+
target: id,
|
|
149
|
+
property: "items",
|
|
150
|
+
value: resolveTextGraphicItems(
|
|
151
|
+
this,
|
|
152
|
+
layer,
|
|
153
|
+
this.markConfigs[id] ?? DEFAULT_TEXT_MARK
|
|
154
|
+
)
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
const editTextMark = action(
|
|
160
|
+
{
|
|
161
|
+
op: "editTextMark",
|
|
162
|
+
description: "Edit text typography, alignment, rotation, and offsets."
|
|
163
|
+
},
|
|
164
|
+
function (args = {}) {
|
|
165
|
+
validateMarkOptions(args, EDIT_OPTIONS, "editTextMark");
|
|
166
|
+
if (!STYLE_OPTIONS.some(option => Object.hasOwn(args, option))) {
|
|
167
|
+
throw new Error("editTextMark requires at least one editable property.");
|
|
168
|
+
}
|
|
169
|
+
const layer = requireTextLayer(this, args.target, "editTextMark");
|
|
170
|
+
const next = this._withMarkConfig(
|
|
171
|
+
layer.id,
|
|
172
|
+
{
|
|
173
|
+
...normalizeTextMarkConfig(
|
|
174
|
+
args,
|
|
175
|
+
this.markConfigs[layer.id] ?? DEFAULT_TEXT_MARK
|
|
176
|
+
),
|
|
177
|
+
fillExplicit: Object.hasOwn(args, "fill")
|
|
178
|
+
? true
|
|
179
|
+
: this.markConfigs[layer.id]?.fillExplicit ?? false
|
|
180
|
+
}
|
|
181
|
+
);
|
|
182
|
+
return canMaterializeText(next, layer)
|
|
183
|
+
? next.rematerializeTextMark({ id: layer.id })
|
|
184
|
+
: next;
|
|
185
|
+
}
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
export function registerTextMarkActions(ProgramClass) {
|
|
189
|
+
ProgramClass.prototype.createTextMark = createTextMark;
|
|
190
|
+
ProgramClass.prototype.editTextMark = editTextMark;
|
|
191
|
+
ProgramClass.prototype.rematerializeTextMark = rematerializeTextMark;
|
|
192
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { registerTextMarkActions } from "./actions.js";
|
|
@@ -61,8 +61,11 @@ function resolvePlacement(graphicSpec, { id, type, parent, before, after }) {
|
|
|
61
61
|
if (!isGraphicContainerType(parentGraphic.type)) {
|
|
62
62
|
throw new Error(`Graphic parent "${parent}" must be a canvas or collection.`);
|
|
63
63
|
}
|
|
64
|
-
if (
|
|
65
|
-
|
|
64
|
+
if (
|
|
65
|
+
type === "canvas" &&
|
|
66
|
+
!graphicSpec.order.some(id => graphicSpec.objects[id]?.type === "canvas")
|
|
67
|
+
) {
|
|
68
|
+
throw new Error("A nested Canvas requires an ordered root Canvas.");
|
|
66
69
|
}
|
|
67
70
|
}
|
|
68
71
|
const anchor = before ?? after;
|
|
@@ -83,7 +86,7 @@ function resolvePlacement(graphicSpec, { id, type, parent, before, after }) {
|
|
|
83
86
|
if (parent === undefined && before === "canvas") {
|
|
84
87
|
throw new Error("createGraphics cannot place a graphic before the canvas.");
|
|
85
88
|
}
|
|
86
|
-
if (type === "canvas") {
|
|
89
|
+
if (type === "canvas" && parent === undefined) {
|
|
87
90
|
throw new Error("Canvas graphics do not accept before or after placement.");
|
|
88
91
|
}
|
|
89
92
|
}
|
|
@@ -99,7 +102,8 @@ function insertSibling(siblings, id, { before, after }) {
|
|
|
99
102
|
const createGraphics = action(
|
|
100
103
|
{
|
|
101
104
|
op: "createGraphics",
|
|
102
|
-
description: "Create and optionally attach a concrete graphic."
|
|
105
|
+
description: "Create and optionally attach a concrete graphic.",
|
|
106
|
+
scope: "any"
|
|
103
107
|
},
|
|
104
108
|
function ({ id, type, length, parent, before, after } = {}) {
|
|
105
109
|
if (typeof id !== "string" || !GRAPHIC_ID_PATTERN.test(id)) {
|
|
@@ -131,9 +135,12 @@ const createGraphics = action(
|
|
|
131
135
|
}
|
|
132
136
|
if (
|
|
133
137
|
validatedType === "canvas" &&
|
|
134
|
-
|
|
138
|
+
parent === undefined &&
|
|
139
|
+
this.graphicSpec.order.some(
|
|
140
|
+
objectId => this.graphicSpec.objects[objectId]?.type === "canvas"
|
|
141
|
+
)
|
|
135
142
|
) {
|
|
136
|
-
throw new Error("graphicSpec supports exactly one canvas.");
|
|
143
|
+
throw new Error("graphicSpec supports exactly one ordered canvas.");
|
|
137
144
|
}
|
|
138
145
|
|
|
139
146
|
const objects = {
|
|
@@ -110,14 +110,17 @@ function editItems(owner, property, value, id) {
|
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
function removeGraphicTree(graphicSpec, id) {
|
|
113
|
-
|
|
113
|
+
const parent = findGraphicParent(graphicSpec, id);
|
|
114
|
+
if (
|
|
115
|
+
graphicSpec.objects[id].type === "canvas" &&
|
|
116
|
+
parent?.kind !== "parent"
|
|
117
|
+
) {
|
|
114
118
|
throw new Error("The canvas root cannot be removed.");
|
|
115
119
|
}
|
|
116
120
|
const removedIds = new Set(collectGraphicSubtreeIds(graphicSpec, id));
|
|
117
121
|
const objects = Object.fromEntries(
|
|
118
122
|
Object.entries(graphicSpec.objects).filter(([objectId]) => !removedIds.has(objectId))
|
|
119
123
|
);
|
|
120
|
-
const parent = findGraphicParent(graphicSpec, id);
|
|
121
124
|
let order = graphicSpec.order;
|
|
122
125
|
if (parent?.kind === "parent") {
|
|
123
126
|
objects[parent.id] = freezeOwned({
|
|
@@ -131,7 +134,11 @@ function removeGraphicTree(graphicSpec, id) {
|
|
|
131
134
|
}
|
|
132
135
|
|
|
133
136
|
const editGraphics = action(
|
|
134
|
-
{
|
|
137
|
+
{
|
|
138
|
+
op: "editGraphics",
|
|
139
|
+
description: "Replace or remove concrete graphic state.",
|
|
140
|
+
scope: "any"
|
|
141
|
+
},
|
|
135
142
|
function ({ target, property, value, remove = false } = {}) {
|
|
136
143
|
if (typeof target !== "string" || target.length === 0) {
|
|
137
144
|
throw new TypeError("editGraphics requires a non-empty target string.");
|
|
@@ -76,6 +76,13 @@ function removeEntity(spec, parsed) {
|
|
|
76
76
|
const collection = spec[parsed.collection];
|
|
77
77
|
const index = collection.findIndex(item => item.id === parsed.id);
|
|
78
78
|
if (index === -1) return spec;
|
|
79
|
+
if (parsed.kind === "layer" && parsed.path.length === 0) {
|
|
80
|
+
const nextCollection = collection.filter((_, itemIndex) => itemIndex !== index);
|
|
81
|
+
return freezeOwned({
|
|
82
|
+
...spec,
|
|
83
|
+
[parsed.collection]: freezeOwned(nextCollection)
|
|
84
|
+
});
|
|
85
|
+
}
|
|
79
86
|
if (parsed.kind === "dataset" && parsed.path.length === 0) {
|
|
80
87
|
const dataset = collection[index];
|
|
81
88
|
if (dataset.source === undefined) {
|
|
@@ -115,7 +122,8 @@ function removeRootProperty(spec, root, path) {
|
|
|
115
122
|
const editSemantic = action(
|
|
116
123
|
{
|
|
117
124
|
op: "editSemantic",
|
|
118
|
-
description: "Create, replace, or remove one semantic property."
|
|
125
|
+
description: "Create, replace, or remove one semantic property.",
|
|
126
|
+
scope: "any"
|
|
119
127
|
},
|
|
120
128
|
function ({ property, value, remove = false } = {}) {
|
|
121
129
|
if (typeof remove !== "boolean") {
|
|
@@ -129,6 +137,13 @@ const editSemantic = action(
|
|
|
129
137
|
}
|
|
130
138
|
|
|
131
139
|
const parsed = parseSemanticPath(property, { allowContainer: remove });
|
|
140
|
+
if (this.compositionSpec !== undefined && !(
|
|
141
|
+
this.compositionSpec.type === "facet" && parsed.kind === "title"
|
|
142
|
+
)) {
|
|
143
|
+
throw new Error(
|
|
144
|
+
"editSemantic on a composition parent currently supports only facet title state."
|
|
145
|
+
);
|
|
146
|
+
}
|
|
132
147
|
if (remove) {
|
|
133
148
|
const semanticSpec = parsed.kind === "guide"
|
|
134
149
|
? removeRootProperty(this.semanticSpec, "guides", parsed.path)
|
|
@@ -140,10 +155,18 @@ const editSemantic = action(
|
|
|
140
155
|
parsed.kind === "dataset" &&
|
|
141
156
|
parsed.path.length === 0 &&
|
|
142
157
|
this.context.currentData === parsed.id;
|
|
158
|
+
const clearsCurrentMark =
|
|
159
|
+
parsed.kind === "layer" &&
|
|
160
|
+
parsed.path.length === 0 &&
|
|
161
|
+
this.context.currentMark === parsed.id;
|
|
143
162
|
return this._clone({
|
|
144
163
|
semanticSpec,
|
|
145
|
-
...(clearsCurrentData
|
|
146
|
-
? { context: freezeOwned({
|
|
164
|
+
...(clearsCurrentData || clearsCurrentMark
|
|
165
|
+
? { context: freezeOwned({
|
|
166
|
+
...this.context,
|
|
167
|
+
...(clearsCurrentData ? { currentData: undefined } : {}),
|
|
168
|
+
...(clearsCurrentMark ? { currentMark: undefined } : {})
|
|
169
|
+
}) }
|
|
147
170
|
: {})
|
|
148
171
|
});
|
|
149
172
|
}
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
MARK_TYPES
|
|
8
8
|
} from "../../core/vocabulary.js";
|
|
9
9
|
import { validateAggregate } from "../../grammar/aggregate.js";
|
|
10
|
-
import { findSemanticScale, hasDataset } from "../../selectors/index.js";
|
|
10
|
+
import { findLayer, findSemanticScale, hasDataset } from "../../selectors/index.js";
|
|
11
11
|
import { validateCoordinateType } from "../../grammar/coordinates.js";
|
|
12
12
|
import {
|
|
13
13
|
normalizeHistogramBin,
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
validateSemanticScaleDomain,
|
|
23
23
|
validateSemanticScaleRange,
|
|
24
24
|
validateSemanticScaleType
|
|
25
|
-
} from "../../grammar/scales.js";
|
|
25
|
+
} from "../../grammar/scales/index.js";
|
|
26
26
|
|
|
27
27
|
function nonEmptyString(value, label) {
|
|
28
28
|
if (typeof value !== "string" || value.length === 0) {
|
|
@@ -74,6 +74,21 @@ export function validateSemanticValue(program, parsed, value) {
|
|
|
74
74
|
}
|
|
75
75
|
if (parsed.kind === "layer") {
|
|
76
76
|
const property = parsed.path.join(".");
|
|
77
|
+
if (property === "source") {
|
|
78
|
+
validateUserId(value, "Layer source id");
|
|
79
|
+
if (value === parsed.id) {
|
|
80
|
+
throw new Error("A layer cannot use itself as its source.");
|
|
81
|
+
}
|
|
82
|
+
const source = findLayer(program, value);
|
|
83
|
+
if (source === undefined) {
|
|
84
|
+
throw new Error(`Unknown source layer "${value}".`);
|
|
85
|
+
}
|
|
86
|
+
if (!["point", "bar", "rule", "rect"].includes(source.mark?.type)) {
|
|
87
|
+
throw new Error(
|
|
88
|
+
`Layer source "${value}" must be a point, bar, rule, or rect mark.`
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
77
92
|
if (property.endsWith(".title")) {
|
|
78
93
|
nonEmptyString(value, "Encoding title");
|
|
79
94
|
}
|
|
@@ -173,8 +188,15 @@ export function validateSemanticValue(program, parsed, value) {
|
|
|
173
188
|
if (parsed.kind === "guide" && parsed.id === "legend.series") {
|
|
174
189
|
validateLegend(parsed.path.at(-1), value);
|
|
175
190
|
}
|
|
176
|
-
if (parsed.kind === "guide" &&
|
|
177
|
-
|
|
191
|
+
if (parsed.kind === "guide" && (
|
|
192
|
+
parsed.id.startsWith("grid.") || parsed.id.startsWith("axis.")
|
|
193
|
+
)) {
|
|
194
|
+
const property = parsed.path.at(-1);
|
|
195
|
+
if (property === "title") nonEmptyString(value, "Axis title");
|
|
196
|
+
else {
|
|
197
|
+
const guideKind = parsed.id.startsWith("grid.") ? "Grid" : "Axis";
|
|
198
|
+
validateUserId(value, `${guideKind} ${property} id`);
|
|
199
|
+
}
|
|
178
200
|
}
|
|
179
201
|
if (parsed.kind === "title") {
|
|
180
202
|
nonEmptyString(value, `Chart title ${parsed.path[0]}`);
|
|
@@ -7,8 +7,8 @@ import { resolveEligibleLayer } from "../../selectors/layers.js";
|
|
|
7
7
|
import {
|
|
8
8
|
REGRESSION_LOWER_FIELD,
|
|
9
9
|
REGRESSION_UPPER_FIELD
|
|
10
|
-
} from "../../grammar/regression.js";
|
|
11
|
-
import { validateAreaCreateOutline } from "../marks/area.js";
|
|
10
|
+
} from "../../grammar/regression/index.js";
|
|
11
|
+
import { validateAreaCreateOutline } from "../marks/area/index.js";
|
|
12
12
|
|
|
13
13
|
const BAND_OPTIONS = Object.freeze([
|
|
14
14
|
"id", "data", "x", "lower", "upper", "groupBy", "coordinate", "xScale", "yScale",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { action } from "../../core/action.js";
|
|
2
2
|
import { validateKeys } from "../../core/validation.js";
|
|
3
|
-
import { normalizeRegressionParameters } from "../../grammar/regression.js";
|
|
3
|
+
import { normalizeRegressionParameters } from "../../grammar/regression/index.js";
|
|
4
4
|
import {
|
|
5
5
|
findRegressionPoint,
|
|
6
6
|
inferRegressionGroup,
|
|
@@ -87,7 +87,7 @@ export const createRegression = action(
|
|
|
87
87
|
...band
|
|
88
88
|
});
|
|
89
89
|
}
|
|
90
|
-
|
|
90
|
+
next = next.createRegressionLine({
|
|
91
91
|
id: lineId,
|
|
92
92
|
data: dataId,
|
|
93
93
|
x,
|
|
@@ -98,5 +98,22 @@ export const createRegression = action(
|
|
|
98
98
|
yScale,
|
|
99
99
|
...line
|
|
100
100
|
});
|
|
101
|
+
return next._withMarkConfig(point.id, {
|
|
102
|
+
...next.markConfigs[point.id],
|
|
103
|
+
regression: {
|
|
104
|
+
source: point.data,
|
|
105
|
+
x,
|
|
106
|
+
y,
|
|
107
|
+
groupBy,
|
|
108
|
+
coordinate,
|
|
109
|
+
xScale,
|
|
110
|
+
yScale,
|
|
111
|
+
colorScale,
|
|
112
|
+
dataId,
|
|
113
|
+
bandId: band === false ? undefined : bandId,
|
|
114
|
+
lineId,
|
|
115
|
+
parameters
|
|
116
|
+
}
|
|
117
|
+
});
|
|
101
118
|
}
|
|
102
119
|
);
|