ggaction 0.0.9 → 0.0.11
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 +84 -0
- package/README.md +5 -4
- package/knowledge/action-card.schema.json +1 -0
- package/knowledge/action-cards.json +1 -10941
- package/knowledge/action-cards.schema.json +1 -0
- package/knowledge/extension-authoring.md +248 -0
- package/knowledge/intent-taxonomy.json +1 -183
- package/knowledge/intent-taxonomy.schema.json +1 -0
- package/knowledge/mcp-resources.json +1 -95
- package/knowledge/mcp-resources.schema.json +1 -0
- package/knowledge/task-packet.schema.json +1 -159
- package/knowledge/task-resolver.js +1 -1230
- package/package.json +19 -2
- package/src/actions/boxPlots/edit.js +45 -298
- package/src/actions/boxPlots/options.js +46 -77
- package/src/actions/boxPlots/resolve.js +1 -1
- package/src/actions/charts/bar.js +3 -0
- package/src/actions/charts/heatmap.js +73 -83
- package/src/actions/charts/index.js +1 -1
- package/src/actions/charts/parallel.js +35 -1
- package/src/actions/charts/shared.js +20 -0
- package/src/actions/composition/actions.js +46 -49
- package/src/actions/data/bin2d.js +105 -2
- package/src/actions/data/box.js +17 -17
- package/src/actions/data/density.js +42 -78
- package/src/actions/data/gradientProfile.js +22 -52
- package/src/actions/data/horizon.js +16 -41
- package/src/actions/data/index.js +10 -10
- package/src/actions/data/interval.js +16 -38
- package/src/actions/data/intervalEdit.js +55 -0
- package/src/actions/data/regression.js +7 -17
- package/src/actions/data/shared.js +47 -0
- package/src/actions/data/timeUnit.js +14 -38
- package/src/actions/data/window.js +14 -38
- package/src/actions/distributions/revision.js +154 -34
- package/src/actions/encodings/angle.js +9 -10
- package/src/actions/encodings/appearance.js +17 -51
- package/src/actions/encodings/density/resolve.js +3 -1
- package/src/actions/encodings/density.js +1 -15
- package/src/actions/encodings/horizon/resolve.js +13 -3
- package/src/actions/encodings/horizon.js +8 -7
- package/src/actions/encodings/index.js +9 -9
- package/src/actions/encodings/offset.js +8 -24
- package/src/actions/encodings/parallel.js +9 -10
- package/src/actions/encodings/pathOrder.js +6 -13
- package/src/actions/encodings/position/policies/index.js +4 -4
- package/src/actions/encodings/position/policies/line.js +47 -17
- package/src/actions/encodings/position/resolve.js +24 -5
- package/src/actions/encodings/ranged.js +18 -23
- package/src/actions/encodings/remove.js +15 -25
- package/src/actions/encodings/ruleAppearance.js +9 -24
- package/src/actions/encodings/shared.js +37 -0
- package/src/actions/encodings/strokeDash.js +8 -23
- package/src/actions/encodings/text.js +9 -10
- package/src/actions/errorBands/create.js +105 -39
- package/src/actions/errorBands/edit.js +89 -93
- package/src/actions/errorBands/index.js +1 -2
- package/src/actions/errorBars/create.js +78 -27
- package/src/actions/errorBars/edit.js +81 -48
- package/src/actions/errorBars/index.js +1 -2
- package/src/actions/facets/actions.js +45 -69
- package/src/actions/facets/derive.js +38 -53
- package/src/actions/facets/replay.js +0 -1
- package/src/actions/gradientPlots/components.js +59 -48
- package/src/actions/gradientPlots/edit.js +35 -216
- package/src/actions/gradientPlots/index.js +11 -10
- package/src/actions/gradientPlots/materialize.js +21 -35
- package/src/actions/gradientPlots/options.js +5 -7
- package/src/actions/gradientPlots/paint.js +9 -6
- package/src/actions/gradientPlots/resolve.js +1 -1
- package/src/actions/guides/axes/index.js +3 -3
- package/src/actions/guides/axes/labels.js +68 -26
- package/src/actions/guides/axes/lines.js +22 -2
- package/src/actions/guides/axes/policy.js +28 -8
- package/src/actions/guides/axes/ticks.js +27 -9
- package/src/actions/guides/axes/titles.js +113 -65
- package/src/actions/guides/grids/resolve.js +14 -0
- package/src/actions/guides/guides.js +2 -4
- package/src/actions/guides/index.js +2 -2
- package/src/actions/guides/legends/categorical/actions.js +44 -68
- package/src/actions/guides/legends/categorical/components.js +6 -1
- package/src/actions/guides/legends/categorical/index.js +2 -2
- package/src/actions/guides/legends/categorical/layout.js +148 -175
- package/src/actions/guides/legends/categorical/recipes.js +5 -0
- package/src/actions/guides/legends/categorical/resolve.js +8 -1
- package/src/actions/guides/legends/continuous/common.js +117 -66
- package/src/actions/guides/legends/continuous/gradient.js +38 -37
- package/src/actions/guides/legends/continuous/interval.js +97 -76
- package/src/actions/guides/legends/continuous/opacity.js +28 -38
- package/src/actions/guides/legends/edit.js +14 -1
- package/src/actions/guides/legends/index.js +1 -1
- package/src/actions/guides/legends/lane.js +2 -1
- package/src/actions/guides/legends/remove.js +8 -0
- package/src/actions/guides/legends/size.js +36 -43
- package/src/actions/guides/legends/strokeWidth.js +20 -26
- package/src/actions/guides/polar/axes/labels.js +17 -1
- package/src/actions/guides/polar/axes/shared.js +7 -1
- package/src/actions/guides/polar/axes/titles.js +19 -2
- package/src/actions/guides/polar/grids.js +4 -0
- package/src/actions/guides/polar/resolve.js +6 -0
- package/src/actions/index.js +13 -13
- package/src/actions/intervals/resolve.js +87 -117
- package/src/actions/marks/arc/actions.js +31 -36
- package/src/actions/marks/area/actions.js +23 -31
- package/src/actions/marks/index.js +6 -6
- package/src/actions/marks/line/actions.js +33 -52
- package/src/actions/marks/line/materialize.js +7 -5
- package/src/actions/marks/point/materialize.js +5 -3
- package/src/actions/marks/remove.js +21 -0
- package/src/actions/marks/rule/actions.js +53 -53
- package/src/actions/marks/shared.js +8 -4
- package/src/actions/marks/text/actions.js +14 -6
- package/src/actions/marks/text/layout.js +21 -45
- package/src/actions/marks/tick/actions.js +22 -21
- package/src/actions/primitives/createGraphics.js +4 -0
- package/src/actions/primitives/editGraphics.js +4 -0
- package/src/actions/primitives/index.js +3 -3
- package/src/actions/primitives/semantic.js +1 -1
- package/src/actions/primitives/semanticValidation/index.js +2 -2
- package/src/actions/primitives/semanticValidation/layer.js +1 -1
- package/src/actions/regression/edit.js +3 -43
- package/src/actions/scales/consumers/families.js +24 -4
- package/src/actions/scales/consumers/index.js +1 -1
- package/src/actions/scales/consumers/seriesLayout.js +17 -0
- package/src/actions/scales/definitions.js +100 -103
- package/src/actions/scales/materialize.js +5 -0
- package/src/actions/selection/actions.js +79 -121
- package/src/actions/titles/actions.js +48 -94
- package/src/actions/titles/resolve.js +14 -27
- package/src/actions/violinPlots/create.js +37 -44
- package/src/core/action.js +20 -5
- package/src/core/extensionRegistry.js +104 -0
- package/src/core/textMetrics.js +74 -37
- package/src/core/validation.js +18 -0
- package/src/extension.js +8 -1
- package/src/grammar/aggregate.js +48 -28
- package/src/grammar/arcs.js +58 -11
- package/src/grammar/areaSeries.js +14 -16
- package/src/grammar/bin2d.js +37 -23
- package/src/grammar/boxPlot.js +14 -13
- package/src/grammar/categoricalDensity.js +14 -14
- package/src/grammar/categoryOrder.js +8 -14
- package/src/grammar/curveCommands.js +87 -20
- package/src/grammar/density.js +162 -141
- package/src/grammar/facets/index.js +14 -0
- package/src/grammar/facets/scales.js +7 -4
- package/src/grammar/gradientProfile.js +12 -15
- package/src/grammar/histogram.js +145 -83
- package/src/grammar/horizon.js +60 -23
- package/src/grammar/interval.js +14 -13
- package/src/grammar/lineSeries.js +91 -61
- package/src/grammar/numeric.js +290 -0
- package/src/grammar/paint.js +3 -4
- package/src/grammar/palettes.js +21 -32
- package/src/grammar/pathCommands.js +6 -0
- package/src/grammar/polarPaths.js +5 -5
- package/src/grammar/regression/derive.js +48 -9
- package/src/grammar/regression/models.js +248 -69
- package/src/grammar/regression/parameters.js +8 -0
- package/src/grammar/scales/color.js +35 -26
- package/src/grammar/scales/continuous.js +71 -24
- package/src/grammar/scales/definition.js +1 -1
- package/src/grammar/scales/discretized.js +42 -12
- package/src/grammar/scales/fields.js +21 -4
- package/src/grammar/scales/ordinal.js +117 -61
- package/src/grammar/scales/temporal.js +40 -11
- package/src/grammar/scales/transformed.js +199 -52
- package/src/grammar/schemas/graphicBounds.js +270 -79
- package/src/grammar/seriesLayout.js +50 -18
- package/src/grammar/ticks.js +123 -53
- package/src/grammar/timeUnit.js +18 -8
- package/src/grammar/transforms.js +4 -1
- package/src/grammar/window.js +70 -57
- package/src/index.js +1 -1
- package/src/layout/canvas.js +4 -8
- package/src/layout/composition.js +39 -40
- package/src/layout/facets.js +10 -14
- package/src/layout/labels.js +91 -15
- package/src/layout/legend.js +22 -20
- package/src/layout/legendLane.js +54 -40
- package/src/layout/title.js +7 -7
- package/src/materialization/bars/aggregate.js +14 -40
- package/src/materialization/bars/grouped.js +7 -18
- package/src/materialization/bars/histogram.js +2 -19
- package/src/materialization/bars/ranged.js +20 -15
- package/src/materialization/bars/resolve.js +21 -0
- package/src/materialization/facetGuides/legacyCategorical.js +2 -1
- package/src/materialization/facetGuides/placement.js +34 -19
- package/src/materialization/facetGuides/preparation.js +9 -5
- package/src/materialization/facets.js +39 -27
- package/src/materialization/guides/resources.js +2 -1
- package/src/materialization/marks/capabilities.js +5 -2
- package/src/materialization/marks/index.js +24 -4
- package/src/materialization/scales/policies/bar.js +61 -59
- package/src/materialization/scales/policies/binnedPosition.js +34 -0
- package/src/materialization/scales/policies/series.js +13 -7
- package/src/materialization/scales/resolve.js +4 -5
- package/src/materialization/selection/items/arc.js +16 -2
- package/src/materialization/selection/items/bar.js +4 -2
- package/src/materialization/selection/items/common.js +21 -1
- package/src/materialization/selection/items/path.js +19 -2
- package/src/materialization/selection/items/point.js +4 -16
- package/src/materialization/selection/items/tick.js +13 -21
- package/src/materialization/selection/point.js +11 -4
- package/src/materialization/selection/styles.js +19 -27
- package/src/materialization/text.js +4 -1
- package/src/mcp/adapter.js +8 -3
- package/src/renderers/canvas/fill.js +2 -2
- package/src/renderers/canvas/index.js +35 -2
- package/src/renderers/canvas/native.js +160 -0
- package/src/renderers/pdf.js +14 -0
- package/src/renderers/svg.js +37 -4
- package/types/extension.d.ts +19 -0
- package/types/index.d.ts +3 -0
- package/types/program.d.ts +509 -131
- package/types/svg.d.ts +1 -0
- package/src/actions/errorBands/components.js +0 -81
- package/src/actions/errorBands/options.js +0 -39
- package/src/actions/errorBands/resolve.js +0 -28
- package/src/actions/errorBars/components.js +0 -62
- package/src/actions/errorBars/options.js +0 -58
- package/src/actions/errorBars/resolve.js +0 -25
|
@@ -22,49 +22,70 @@ import {
|
|
|
22
22
|
SCALE_ROLES,
|
|
23
23
|
validateScalePropertyForType,
|
|
24
24
|
validateScaleTypeForRole,
|
|
25
|
-
isDiscretePositionScaleType
|
|
25
|
+
isDiscretePositionScaleType,
|
|
26
|
+
withScaleUnknown
|
|
26
27
|
} from "../../grammar/scales/index.js";
|
|
27
28
|
import {
|
|
28
29
|
validateRadialRange,
|
|
29
30
|
validateThetaRange
|
|
30
31
|
} from "../../grammar/polar.js";
|
|
31
|
-
import { withScaleUnknown } from "../../grammar/scales/index.js";
|
|
32
32
|
import { findSemanticScale } from "../../selectors/scales.js";
|
|
33
33
|
|
|
34
|
-
const BASE_OPTIONS =
|
|
35
|
-
const
|
|
34
|
+
const BASE_OPTIONS = ["id", "type", "domain", "range"];
|
|
35
|
+
const UNKNOWN_OPTIONS = [...BASE_OPTIONS, "unknown"];
|
|
36
|
+
const CLAMP_REVERSE = ["clamp", "reverse"];
|
|
37
|
+
const BOOLEAN_OPTIONS = ["nice", "zero", ...CLAMP_REVERSE];
|
|
38
|
+
const TRANSFORM_OPTIONS = ["base", "exponent", "constant"];
|
|
39
|
+
const POSITION_OPTIONS = [
|
|
36
40
|
...BASE_OPTIONS,
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"clamp",
|
|
40
|
-
"reverse",
|
|
41
|
-
"base",
|
|
42
|
-
"exponent",
|
|
43
|
-
"constant",
|
|
41
|
+
...BOOLEAN_OPTIONS,
|
|
42
|
+
...TRANSFORM_OPTIONS,
|
|
44
43
|
"paddingInner",
|
|
45
44
|
"paddingOuter",
|
|
46
45
|
"padding",
|
|
47
46
|
"align",
|
|
48
47
|
"unknown"
|
|
49
|
-
]
|
|
50
|
-
const COLOR_OPTIONS =
|
|
51
|
-
const SEQUENTIAL_COLOR_OPTIONS =
|
|
48
|
+
];
|
|
49
|
+
const COLOR_OPTIONS = [...BASE_OPTIONS, "palette", "unknown"];
|
|
50
|
+
const SEQUENTIAL_COLOR_OPTIONS = [
|
|
52
51
|
...COLOR_OPTIONS,
|
|
53
52
|
"interpolate",
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
]
|
|
57
|
-
const
|
|
58
|
-
...
|
|
59
|
-
|
|
60
|
-
"reverse"
|
|
61
|
-
]);
|
|
53
|
+
...CLAMP_REVERSE
|
|
54
|
+
];
|
|
55
|
+
const OPACITY_OPTIONS = [...BASE_OPTIONS, ...BOOLEAN_OPTIONS, "unknown"];
|
|
56
|
+
const STROKE_WIDTH_OPTIONS = [
|
|
57
|
+
...BASE_OPTIONS, ...BOOLEAN_OPTIONS, ...TRANSFORM_OPTIONS
|
|
58
|
+
];
|
|
62
59
|
|
|
63
60
|
function optionsObject(options) {
|
|
64
61
|
if (!isPlainObject(options)) {
|
|
65
62
|
throw new TypeError("Encoding scale must be a plain object.");
|
|
66
63
|
}
|
|
67
|
-
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function validateBooleanOptions(options, properties, type) {
|
|
67
|
+
for (const property of properties) {
|
|
68
|
+
if (options[property] !== undefined && typeof options[property] !== "boolean") {
|
|
69
|
+
throw new TypeError(`Scale ${property} must be a boolean.`);
|
|
70
|
+
}
|
|
71
|
+
if (type !== undefined && options[property] !== undefined) {
|
|
72
|
+
validateScalePropertyForType(type, property);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function assignOptions(scale, options, existing, properties) {
|
|
78
|
+
for (const property of properties) {
|
|
79
|
+
const value = options[property] ?? existing?.[property];
|
|
80
|
+
if (value !== undefined) scale[property] = value;
|
|
81
|
+
}
|
|
82
|
+
return scale;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function validatePaletteRange(options) {
|
|
86
|
+
if (options.palette !== undefined && options.range !== undefined) {
|
|
87
|
+
throw new Error("Color scale cannot specify both palette and range.");
|
|
88
|
+
}
|
|
68
89
|
}
|
|
69
90
|
|
|
70
91
|
export function resolvePositionScaleDefinition(
|
|
@@ -78,14 +99,15 @@ export function resolvePositionScaleDefinition(
|
|
|
78
99
|
validateKeys(options, POSITION_OPTIONS, "scale");
|
|
79
100
|
const id = validateUserId(options.id ?? channel, "Scale id");
|
|
80
101
|
const existing = findSemanticScale(program, id);
|
|
102
|
+
const discrete = ["ordinal", "nominal"].includes(fieldType);
|
|
81
103
|
const expectedType = fieldType === "temporal"
|
|
82
104
|
? "time"
|
|
83
|
-
:
|
|
105
|
+
: discrete
|
|
84
106
|
? defaults.discreteType ?? "point"
|
|
85
107
|
: "linear";
|
|
86
108
|
const type = options.type ?? existing?.type ?? expectedType;
|
|
87
109
|
if (fieldType === "temporal") validateTimeScaleType(type);
|
|
88
|
-
else if (
|
|
110
|
+
else if (discrete) {
|
|
89
111
|
if (!isDiscretePositionScaleType(type)) {
|
|
90
112
|
throw new Error(
|
|
91
113
|
`Scale type "${type}" is not valid for discrete position.`
|
|
@@ -104,14 +126,17 @@ export function resolvePositionScaleDefinition(
|
|
|
104
126
|
...(existing === undefined && defaults.nice !== undefined
|
|
105
127
|
? { nice: defaults.nice }
|
|
106
128
|
: {}),
|
|
107
|
-
...(
|
|
129
|
+
...(
|
|
130
|
+
existing === undefined &&
|
|
131
|
+
defaults.zero !== undefined &&
|
|
132
|
+
(type !== "log" || defaults.zero)
|
|
108
133
|
? { zero: defaults.zero }
|
|
109
134
|
: {})
|
|
110
135
|
},
|
|
111
136
|
retainCoreOnTypeChange: true,
|
|
112
137
|
retainCompatibleOnTypeChange: true,
|
|
113
138
|
validateDomain: (_scaleType, value) =>
|
|
114
|
-
|
|
139
|
+
discrete
|
|
115
140
|
? validateOrdinalDomain(value)
|
|
116
141
|
: validateScaleDomain(value),
|
|
117
142
|
validateRange: (_scaleType, value) => channel === "theta"
|
|
@@ -127,9 +152,7 @@ export function resolvePositionScaleDefinition(
|
|
|
127
152
|
export function resolveColorScaleDefinition(program, options) {
|
|
128
153
|
optionsObject(options);
|
|
129
154
|
validateKeys(options, COLOR_OPTIONS, "scale");
|
|
130
|
-
|
|
131
|
-
throw new Error("Color scale cannot specify both palette and range.");
|
|
132
|
-
}
|
|
155
|
+
validatePaletteRange(options);
|
|
133
156
|
const id = validateUserId(options.id ?? "color", "Scale id");
|
|
134
157
|
const existing = findSemanticScale(program, id);
|
|
135
158
|
const range = options.palette === undefined
|
|
@@ -165,20 +188,14 @@ export function resolveSequentialColorScaleDefinition(
|
|
|
165
188
|
) {
|
|
166
189
|
optionsObject(options);
|
|
167
190
|
validateKeys(options, SEQUENTIAL_COLOR_OPTIONS, "scale");
|
|
168
|
-
|
|
169
|
-
throw new Error("Color scale cannot specify both palette and range.");
|
|
170
|
-
}
|
|
191
|
+
validatePaletteRange(options);
|
|
171
192
|
const id = validateUserId(options.id ?? "color", "Scale id");
|
|
172
193
|
const existing = findSemanticScale(program, id);
|
|
173
194
|
const type = options.type ?? existing?.type ?? "sequential";
|
|
174
195
|
if (type !== "sequential") {
|
|
175
196
|
throw new Error(`Unsupported continuous color scale type "${type}".`);
|
|
176
197
|
}
|
|
177
|
-
|
|
178
|
-
if (options[property] !== undefined && typeof options[property] !== "boolean") {
|
|
179
|
-
throw new TypeError(`Scale ${property} must be a boolean.`);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
198
|
+
validateBooleanOptions(options, CLAMP_REVERSE);
|
|
182
199
|
const requestedRange = options.palette === undefined
|
|
183
200
|
? options.range
|
|
184
201
|
: { palette: options.palette };
|
|
@@ -193,11 +210,11 @@ export function resolveSequentialColorScaleDefinition(
|
|
|
193
210
|
options.interpolate ?? existing?.interpolate ?? "rgb"
|
|
194
211
|
)
|
|
195
212
|
};
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
213
|
+
return withScaleUnknown(
|
|
214
|
+
assignOptions(scale, options, existing, CLAMP_REVERSE),
|
|
215
|
+
{ ...existing, ...options },
|
|
216
|
+
"color"
|
|
217
|
+
);
|
|
201
218
|
}
|
|
202
219
|
|
|
203
220
|
export function resolveQuantitativeColorScaleDefinition(
|
|
@@ -213,26 +230,16 @@ export function resolveQuantitativeColorScaleDefinition(
|
|
|
213
230
|
if (type === "sequential") {
|
|
214
231
|
return resolveSequentialColorScaleDefinition(program, fieldType, options);
|
|
215
232
|
}
|
|
216
|
-
validateKeys(options, [
|
|
217
|
-
...COLOR_OPTIONS,
|
|
218
|
-
"clamp",
|
|
219
|
-
"reverse"
|
|
220
|
-
], "scale");
|
|
233
|
+
validateKeys(options, [...COLOR_OPTIONS, ...CLAMP_REVERSE], "scale");
|
|
221
234
|
if (fieldType !== "quantitative") {
|
|
222
235
|
throw new Error(`Scale type "${type}" requires quantitative color.`);
|
|
223
236
|
}
|
|
224
237
|
validateScaleTypeForRole(type, SCALE_ROLES.discretizedColor);
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
}
|
|
228
|
-
for (const property of ["clamp", "reverse"]) {
|
|
229
|
-
if (options[property] !== undefined && typeof options[property] !== "boolean") {
|
|
230
|
-
throw new TypeError(`Scale ${property} must be a boolean.`);
|
|
231
|
-
}
|
|
232
|
-
if (options[property] !== undefined) validateScalePropertyForType(type, property);
|
|
233
|
-
}
|
|
238
|
+
validatePaletteRange(options);
|
|
239
|
+
validateBooleanOptions(options, CLAMP_REVERSE, type);
|
|
234
240
|
const id = validateUserId(options.id ?? "color", "Scale id");
|
|
235
241
|
const existing = findSemanticScale(program, id);
|
|
242
|
+
const previous = existing?.type === type ? existing : undefined;
|
|
236
243
|
const requestedRange = options.palette === undefined
|
|
237
244
|
? options.range
|
|
238
245
|
: { palette: options.palette };
|
|
@@ -241,35 +248,41 @@ export function resolveQuantitativeColorScaleDefinition(
|
|
|
241
248
|
type,
|
|
242
249
|
domain: validateDiscretizedColorDomain(
|
|
243
250
|
type,
|
|
244
|
-
options.domain ??
|
|
251
|
+
options.domain ?? previous?.domain ?? "auto"
|
|
245
252
|
),
|
|
246
253
|
range: validateDiscretizedColorRange(
|
|
247
|
-
requestedRange ??
|
|
254
|
+
requestedRange ?? previous?.range ?? { palette: "viridis" }
|
|
248
255
|
)
|
|
249
256
|
};
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
257
|
+
return withScaleUnknown(
|
|
258
|
+
assignOptions(scale, options, previous, CLAMP_REVERSE),
|
|
259
|
+
{ ...existing, ...options },
|
|
260
|
+
"color"
|
|
261
|
+
);
|
|
255
262
|
}
|
|
256
263
|
|
|
257
|
-
|
|
264
|
+
function resolveOrdinalScaleDefinition(program, options, channel, rangeValidator) {
|
|
258
265
|
optionsObject(options);
|
|
259
|
-
validateKeys(options,
|
|
260
|
-
const id = validateUserId(options.id ??
|
|
266
|
+
validateKeys(options, UNKNOWN_OPTIONS, "scale");
|
|
267
|
+
const id = validateUserId(options.id ?? channel, "Scale id");
|
|
261
268
|
const existing = findSemanticScale(program, id);
|
|
262
269
|
return withScaleUnknown({
|
|
263
270
|
id,
|
|
264
271
|
type: validateOrdinalScaleType(options.type ?? existing?.type ?? "ordinal"),
|
|
265
272
|
domain: validateOrdinalDomain(options.domain ?? existing?.domain ?? "auto"),
|
|
266
|
-
range:
|
|
267
|
-
}, { ...existing, ...options },
|
|
273
|
+
range: rangeValidator(options.range ?? existing?.range ?? "auto")
|
|
274
|
+
}, { ...existing, ...options }, channel);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export function resolveStrokeDashScaleDefinition(program, options) {
|
|
278
|
+
return resolveOrdinalScaleDefinition(
|
|
279
|
+
program, options, "strokeDash", validateStrokeDashRange
|
|
280
|
+
);
|
|
268
281
|
}
|
|
269
282
|
|
|
270
283
|
export function resolveAppearanceScaleDefinition(program, channel, options) {
|
|
271
284
|
optionsObject(options);
|
|
272
|
-
validateKeys(options,
|
|
285
|
+
validateKeys(options, UNKNOWN_OPTIONS, "scale");
|
|
273
286
|
const id = validateUserId(options.id ?? channel, "Scale id");
|
|
274
287
|
const existing = findSemanticScale(program, id);
|
|
275
288
|
const shape = channel === "shape";
|
|
@@ -293,38 +306,31 @@ export function resolveOpacityScaleDefinition(program, options) {
|
|
|
293
306
|
const id = validateUserId(options.id ?? "opacity", "Scale id");
|
|
294
307
|
const existing = findSemanticScale(program, id);
|
|
295
308
|
const type = validateLinearScaleType(options.type ?? existing?.type ?? "linear");
|
|
296
|
-
|
|
297
|
-
if (options[property] !== undefined && typeof options[property] !== "boolean") {
|
|
298
|
-
throw new TypeError(`Scale ${property} must be a boolean.`);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
309
|
+
validateBooleanOptions(options, BOOLEAN_OPTIONS, type);
|
|
301
310
|
const scale = {
|
|
302
311
|
id,
|
|
303
312
|
type,
|
|
304
313
|
domain: validateScaleDomain(options.domain ?? existing?.domain ?? "auto"),
|
|
305
314
|
range: validateOpacityRange(options.range ?? existing?.range ?? "auto")
|
|
306
315
|
};
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
if (zero !== undefined) scale.zero = zero;
|
|
313
|
-
if (clamp !== undefined) scale.clamp = clamp;
|
|
314
|
-
if (reverse !== undefined) scale.reverse = reverse;
|
|
315
|
-
return withScaleUnknown(scale, { ...existing, ...options }, "opacity");
|
|
316
|
+
return withScaleUnknown(
|
|
317
|
+
assignOptions(scale, options, existing, BOOLEAN_OPTIONS),
|
|
318
|
+
{ ...existing, ...options },
|
|
319
|
+
"opacity"
|
|
320
|
+
);
|
|
316
321
|
}
|
|
317
322
|
|
|
318
323
|
export function resolveStrokeWidthScaleDefinition(program, options) {
|
|
319
324
|
optionsObject(options);
|
|
320
|
-
validateKeys(options,
|
|
325
|
+
validateKeys(options, STROKE_WIDTH_OPTIONS, "scale");
|
|
321
326
|
const id = validateUserId(options.id ?? "strokeWidth", "Scale id");
|
|
322
327
|
const existing = findSemanticScale(program, id);
|
|
323
328
|
const type = options.type ?? existing?.type ?? "linear";
|
|
324
329
|
validateScaleTypeForRole(type, SCALE_ROLES.quantitativePosition);
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
330
|
+
validateBooleanOptions(options, BOOLEAN_OPTIONS, type);
|
|
331
|
+
for (const property of TRANSFORM_OPTIONS) {
|
|
332
|
+
if (options[property] !== undefined) {
|
|
333
|
+
validateScalePropertyForType(type, property);
|
|
328
334
|
}
|
|
329
335
|
}
|
|
330
336
|
const domain = validateScaleDomain(options.domain ?? existing?.domain ?? "auto");
|
|
@@ -337,22 +343,13 @@ export function resolveStrokeWidthScaleDefinition(program, options) {
|
|
|
337
343
|
domain,
|
|
338
344
|
range: validateStrokeWidthRange(options.range ?? existing?.range ?? "auto")
|
|
339
345
|
};
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
}
|
|
344
|
-
return scale;
|
|
346
|
+
return assignOptions(
|
|
347
|
+
scale, options, existing, [...BOOLEAN_OPTIONS, ...TRANSFORM_OPTIONS]
|
|
348
|
+
);
|
|
345
349
|
}
|
|
346
350
|
|
|
347
351
|
export function resolveOffsetScaleDefinition(program, options, channel = "xOffset") {
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
const existing = findSemanticScale(program, id);
|
|
352
|
-
return withScaleUnknown({
|
|
353
|
-
id,
|
|
354
|
-
type: validateOrdinalScaleType(options.type ?? existing?.type ?? "ordinal"),
|
|
355
|
-
domain: validateOrdinalDomain(options.domain ?? existing?.domain ?? "auto"),
|
|
356
|
-
range: validateScaleRange(options.range ?? existing?.range ?? "auto")
|
|
357
|
-
}, { ...existing, ...options }, channel);
|
|
352
|
+
return resolveOrdinalScaleDefinition(
|
|
353
|
+
program, options, channel, validateScaleRange
|
|
354
|
+
);
|
|
358
355
|
}
|
|
@@ -100,6 +100,11 @@ export const rematerializeScale = action(
|
|
|
100
100
|
continue;
|
|
101
101
|
}
|
|
102
102
|
if (materializationMode === "defer") continue;
|
|
103
|
+
if (Object.values(this.materializationConfigs.highlights ?? {})
|
|
104
|
+
.some(config => config.target === consumer.layer.id)) {
|
|
105
|
+
next = next.rematerializePointMark({ id: consumer.layer.id });
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
103
108
|
next = next.editGraphics({
|
|
104
109
|
target: consumer.layer.id,
|
|
105
110
|
property: channel === "color" ? "fill" : channel,
|
|
@@ -121,6 +121,57 @@ function selectedKeys(args, resolved) {
|
|
|
121
121
|
return args.keys;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
+
function selectedGraphicItems(program, resolved, keys) {
|
|
125
|
+
const selected = new Set(keys);
|
|
126
|
+
const graphic = program.graphicSpec.objects[resolved.definition.target];
|
|
127
|
+
const keyByGraphic = new Map(resolved.items.flatMap(item =>
|
|
128
|
+
item.graphicIds.map(id => [id, item.key])
|
|
129
|
+
));
|
|
130
|
+
return {
|
|
131
|
+
selected,
|
|
132
|
+
target: resolved.definition.target,
|
|
133
|
+
items: graphic.items.map(child => ({
|
|
134
|
+
key: keyByGraphic.get(child.id),
|
|
135
|
+
child: { type: child.type ?? graphic.type, properties: child.properties }
|
|
136
|
+
}))
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function editSelectedItems(
|
|
141
|
+
program,
|
|
142
|
+
resolved,
|
|
143
|
+
keys,
|
|
144
|
+
transform,
|
|
145
|
+
complement = false
|
|
146
|
+
) {
|
|
147
|
+
const { selected, target, items } = selectedGraphicItems(program, resolved, keys);
|
|
148
|
+
return program.editGraphics({
|
|
149
|
+
target,
|
|
150
|
+
property: "items",
|
|
151
|
+
value: items.map(item => selected.has(item.key) !== complement
|
|
152
|
+
? transform(item.child)
|
|
153
|
+
: item.child)
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function applyHighlight(program, config, keys) {
|
|
158
|
+
let next = program[requireSelectionPolicy(config.markType).applyHighlightOp]({
|
|
159
|
+
selection: config.selection,
|
|
160
|
+
style: config.style,
|
|
161
|
+
keys
|
|
162
|
+
});
|
|
163
|
+
if (config.dimOthers !== false) {
|
|
164
|
+
next = next.dimUnselectedMarkItems({
|
|
165
|
+
selection: config.selection,
|
|
166
|
+
opacity: config.dimOthers.opacity,
|
|
167
|
+
keys
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
return config.bringToFront
|
|
171
|
+
? next.placeSelectedMarkItemsLast({ selection: config.selection, keys })
|
|
172
|
+
: next;
|
|
173
|
+
}
|
|
174
|
+
|
|
124
175
|
export const selectMarks = action(
|
|
125
176
|
{ op: "selectMarks", description: "Create one reusable selection over final mark items." },
|
|
126
177
|
function (args = {}) {
|
|
@@ -217,24 +268,9 @@ export const applyPointHighlight = action(
|
|
|
217
268
|
throw new Error("applyPointHighlight requires a point selection.");
|
|
218
269
|
}
|
|
219
270
|
if (keys.length === 0) return this;
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
const keyByGraphic = new Map(resolved.items.flatMap(item =>
|
|
223
|
-
item.graphicIds.map(id => [id, item.key])
|
|
224
|
-
));
|
|
225
|
-
const items = graphic.items.map(child =>
|
|
226
|
-
selected.has(keyByGraphic.get(child.id))
|
|
227
|
-
? transformPointHighlightChild({
|
|
228
|
-
type: child.type ?? graphic.type,
|
|
229
|
-
properties: child.properties
|
|
230
|
-
}, args.style)
|
|
231
|
-
: { type: child.type ?? graphic.type, properties: child.properties }
|
|
271
|
+
return editSelectedItems(this, resolved, keys, item =>
|
|
272
|
+
transformPointHighlightChild(item, args.style)
|
|
232
273
|
);
|
|
233
|
-
return this.editGraphics({
|
|
234
|
-
target: resolved.definition.target,
|
|
235
|
-
property: "items",
|
|
236
|
-
value: items
|
|
237
|
-
});
|
|
238
274
|
}
|
|
239
275
|
);
|
|
240
276
|
|
|
@@ -265,21 +301,12 @@ function applyRectangularHighlight(
|
|
|
265
301
|
throw new Error(`${operation} requires a ${markType} selection.`);
|
|
266
302
|
}
|
|
267
303
|
if (keys.length === 0) return program;
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
target: resolved.definition.target,
|
|
275
|
-
property: "items",
|
|
276
|
-
value: graphic.items.map(child => ({
|
|
277
|
-
type: child.type ?? graphic.type,
|
|
278
|
-
properties: selected.has(keyByGraphic.get(child.id))
|
|
279
|
-
? transformRectangularProperties(child.properties, args.style, offset)
|
|
280
|
-
: child.properties
|
|
281
|
-
}))
|
|
282
|
-
});
|
|
304
|
+
return editSelectedItems(program, resolved, keys, item => ({
|
|
305
|
+
...item,
|
|
306
|
+
properties: transformRectangularProperties(
|
|
307
|
+
item.properties, args.style, offset
|
|
308
|
+
)
|
|
309
|
+
}));
|
|
283
310
|
}
|
|
284
311
|
|
|
285
312
|
export const applyBarHighlight = action(
|
|
@@ -316,21 +343,10 @@ export const applyPathHighlight = action(
|
|
|
316
343
|
throw new Error("applyPathHighlight requires a path selection policy.");
|
|
317
344
|
}
|
|
318
345
|
if (keys.length === 0) return this;
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
));
|
|
324
|
-
return this.editGraphics({
|
|
325
|
-
target: resolved.definition.target,
|
|
326
|
-
property: "items",
|
|
327
|
-
value: graphic.items.map(child => ({
|
|
328
|
-
type: child.type ?? graphic.type,
|
|
329
|
-
properties: selected.has(keyByGraphic.get(child.id))
|
|
330
|
-
? transformPathHighlightProperties(child.properties, args.style)
|
|
331
|
-
: child.properties
|
|
332
|
-
}))
|
|
333
|
-
});
|
|
346
|
+
return editSelectedItems(this, resolved, keys, item => ({
|
|
347
|
+
...item,
|
|
348
|
+
properties: transformPathHighlightProperties(item.properties, args.style)
|
|
349
|
+
}));
|
|
334
350
|
}
|
|
335
351
|
);
|
|
336
352
|
|
|
@@ -347,21 +363,10 @@ export const applyRuleHighlight = action(
|
|
|
347
363
|
throw new Error("applyRuleHighlight requires a rule or Tick selection.");
|
|
348
364
|
}
|
|
349
365
|
if (keys.length === 0) return this;
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
));
|
|
355
|
-
return this.editGraphics({
|
|
356
|
-
target: resolved.definition.target,
|
|
357
|
-
property: "items",
|
|
358
|
-
value: graphic.items.map(child => ({
|
|
359
|
-
type: child.type ?? graphic.type,
|
|
360
|
-
properties: selected.has(keyByGraphic.get(child.id))
|
|
361
|
-
? transformRuleHighlightProperties(child.properties, args.style)
|
|
362
|
-
: child.properties
|
|
363
|
-
}))
|
|
364
|
-
});
|
|
366
|
+
return editSelectedItems(this, resolved, keys, item => ({
|
|
367
|
+
...item,
|
|
368
|
+
properties: transformRuleHighlightProperties(item.properties, args.style)
|
|
369
|
+
}));
|
|
365
370
|
}
|
|
366
371
|
);
|
|
367
372
|
|
|
@@ -371,21 +376,11 @@ export const dimUnselectedMarkItems = action(
|
|
|
371
376
|
validateKeys(args, INTERNAL_DIM_OPTIONS, "dimUnselectedMarkItems");
|
|
372
377
|
const opacity = validateUnitInterval(args.opacity, "Dim opacity");
|
|
373
378
|
const resolved = resolveStoredSelection(this, args.selection);
|
|
374
|
-
const
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
item.
|
|
378
|
-
));
|
|
379
|
-
return this.editGraphics({
|
|
380
|
-
target: resolved.definition.target,
|
|
381
|
-
property: "items",
|
|
382
|
-
value: graphic.items.map(child => ({
|
|
383
|
-
type: child.type ?? graphic.type,
|
|
384
|
-
properties: selected.has(keyByGraphic.get(child.id))
|
|
385
|
-
? child.properties
|
|
386
|
-
: { ...child.properties, opacity }
|
|
387
|
-
}))
|
|
388
|
-
});
|
|
379
|
+
const keys = selectedKeys(args, resolved);
|
|
380
|
+
return editSelectedItems(this, resolved, keys, item => ({
|
|
381
|
+
...item,
|
|
382
|
+
properties: { ...item.properties, opacity }
|
|
383
|
+
}), true);
|
|
389
384
|
}
|
|
390
385
|
);
|
|
391
386
|
|
|
@@ -398,17 +393,9 @@ export const placeSelectedMarkItemsLast = action(
|
|
|
398
393
|
if (keys.length === 0 || keys.length === resolved.items.length) {
|
|
399
394
|
return this;
|
|
400
395
|
}
|
|
401
|
-
const selected =
|
|
402
|
-
const graphic = this.graphicSpec.objects[resolved.definition.target];
|
|
403
|
-
const keyByGraphic = new Map(resolved.items.flatMap(item =>
|
|
404
|
-
item.graphicIds.map(id => [id, item.key])
|
|
405
|
-
));
|
|
406
|
-
const items = graphic.items.map(child => ({
|
|
407
|
-
key: keyByGraphic.get(child.id),
|
|
408
|
-
child: { type: child.type ?? graphic.type, properties: child.properties }
|
|
409
|
-
}));
|
|
396
|
+
const { selected, target, items } = selectedGraphicItems(this, resolved, keys);
|
|
410
397
|
return this.editGraphics({
|
|
411
|
-
target
|
|
398
|
+
target,
|
|
412
399
|
property: "items",
|
|
413
400
|
value: [
|
|
414
401
|
...items.filter(item => !selected.has(item.key)),
|
|
@@ -433,26 +420,7 @@ export const rematerializeMarkHighlights = action(
|
|
|
433
420
|
}));
|
|
434
421
|
let next = this;
|
|
435
422
|
for (const { id, config, keys } of prepared) {
|
|
436
|
-
|
|
437
|
-
next = next[policy.applyHighlightOp]({
|
|
438
|
-
selection: config.selection,
|
|
439
|
-
style: config.style,
|
|
440
|
-
keys
|
|
441
|
-
});
|
|
442
|
-
if (config.dimOthers !== false) {
|
|
443
|
-
next = next.dimUnselectedMarkItems({
|
|
444
|
-
selection: config.selection,
|
|
445
|
-
opacity: config.dimOthers.opacity,
|
|
446
|
-
keys
|
|
447
|
-
});
|
|
448
|
-
}
|
|
449
|
-
if (config.bringToFront) {
|
|
450
|
-
next = next.placeSelectedMarkItemsLast({
|
|
451
|
-
selection: config.selection,
|
|
452
|
-
keys
|
|
453
|
-
});
|
|
454
|
-
}
|
|
455
|
-
next = next._withHighlightConfig(id, config);
|
|
423
|
+
next = applyHighlight(next, config, keys)._withHighlightConfig(id, config);
|
|
456
424
|
}
|
|
457
425
|
return prepared.some(({ config }) =>
|
|
458
426
|
hasLegendSelection(next, config.target, config.selection)
|
|
@@ -519,26 +487,16 @@ export const highlightMarks = action(
|
|
|
519
487
|
next = next._withoutMaterializationConfig(["highlights", selection]);
|
|
520
488
|
next = rebuildTargetHighlights(next, resolved.definition.target);
|
|
521
489
|
}
|
|
522
|
-
const
|
|
523
|
-
next = next[policy.applyHighlightOp]({ selection, style, keys });
|
|
524
|
-
if (dimOthers !== false) {
|
|
525
|
-
next = next.dimUnselectedMarkItems({
|
|
526
|
-
selection,
|
|
527
|
-
opacity: dimOthers.opacity,
|
|
528
|
-
keys
|
|
529
|
-
});
|
|
530
|
-
}
|
|
531
|
-
if (bringToFront) {
|
|
532
|
-
next = next.placeSelectedMarkItemsLast({ selection, keys });
|
|
533
|
-
}
|
|
534
|
-
next = next._withHighlightConfig(selection, {
|
|
490
|
+
const config = {
|
|
535
491
|
target: resolved.definition.target,
|
|
536
492
|
selection,
|
|
537
493
|
markType: layer.mark.type,
|
|
538
494
|
style,
|
|
539
495
|
dimOthers,
|
|
540
496
|
bringToFront
|
|
541
|
-
}
|
|
497
|
+
};
|
|
498
|
+
next = applyHighlight(next, config, keys)
|
|
499
|
+
._withHighlightConfig(selection, config);
|
|
542
500
|
return hasLegendSelection(next, resolved.definition.target, selection)
|
|
543
501
|
? next.rematerializeLegendHighlights()
|
|
544
502
|
: next;
|