ggaction 0.0.13 → 0.0.15
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 +78 -0
- package/README.md +20 -11
- package/knowledge/action-cards.json +1 -1
- package/knowledge/intent-taxonomy.json +1 -1
- package/knowledge/mcp-resources.json +1 -1
- package/package.json +8 -3
- package/src/actions/basic.js +2 -2
- package/src/actions/boxPlots/components.js +14 -4
- package/src/actions/boxPlots/edit.js +31 -5
- package/src/actions/boxPlots/materialize.js +11 -3
- package/src/actions/boxPlots/options.js +48 -16
- package/src/actions/categoryOrder/index.js +15 -4
- package/src/actions/charts/area.js +5 -1
- package/src/actions/charts/bar.js +2 -1
- package/src/actions/charts/density.js +6 -2
- package/src/actions/charts/ecdf.js +4 -1
- package/src/actions/charts/endpoints.js +25 -3
- package/src/actions/charts/heatmap.js +2 -1
- package/src/actions/charts/histogram.js +4 -3
- package/src/actions/charts/horizon.js +3 -1
- package/src/actions/charts/interval-regression.js +5 -2
- package/src/actions/charts/line.js +3 -1
- package/src/actions/charts/parallel.js +8 -2
- package/src/actions/charts/pie.js +10 -2
- package/src/actions/charts/polar.js +15 -4
- package/src/actions/charts/radar.js +8 -2
- package/src/actions/charts/radial.js +10 -2
- package/src/actions/charts/rug-strip.js +6 -2
- package/src/actions/charts/scatter.js +3 -1
- package/src/actions/composition/actions.js +13 -4
- package/src/actions/coordinates/actions.js +1 -1
- package/src/actions/coordinates/edit.js +76 -0
- package/src/actions/data/bin.js +1 -1
- package/src/actions/data/bin2d.js +57 -185
- package/src/actions/data/complete.js +26 -0
- package/src/actions/data/computed.js +2 -2
- package/src/actions/data/create.js +2 -2
- package/src/actions/data/density.js +12 -20
- package/src/actions/data/derived.js +47 -17
- package/src/actions/data/edit.js +539 -0
- package/src/actions/data/filter.js +14 -2
- package/src/actions/data/impute.js +26 -0
- package/src/actions/data/index.js +14 -0
- package/src/actions/data/normalize.js +27 -0
- package/src/actions/data/regression.js +16 -21
- package/src/actions/data/revisionBindings.js +145 -0
- package/src/actions/data/shared.js +20 -4
- package/src/actions/data/summary.js +1 -1
- package/src/actions/data/timeUnit.js +4 -1
- package/src/actions/data/window.js +1 -1
- package/src/actions/encodings/channels.js +415 -0
- package/src/actions/encodings/color/index.js +2 -1
- package/src/actions/encodings/density.js +5 -2
- package/src/actions/encodings/histogram.js +3 -2
- package/src/actions/encodings/index.js +4 -0
- package/src/actions/encodings/offset.js +20 -13
- package/src/actions/encodings/position/apply.js +53 -7
- package/src/actions/encodings/position/index.js +10 -2
- package/src/actions/encodings/position/policies/bar.js +9 -0
- package/src/actions/encodings/position/resolve.js +24 -7
- package/src/actions/encodings/remove.js +9 -4
- package/src/actions/encodings/ruleAppearance.js +1 -23
- package/src/actions/encodings/seriesLayout.js +1 -0
- package/src/actions/encodings/shared.js +82 -14
- package/src/actions/encodings/stroke.js +226 -0
- package/src/actions/errorBands/create.js +23 -3
- package/src/actions/errorBands/edit.js +21 -6
- package/src/actions/errorBars/create.js +35 -7
- package/src/actions/errorBars/edit.js +19 -4
- package/src/actions/facets/actions.js +237 -57
- package/src/actions/facets/derive.js +91 -26
- package/src/actions/gradientPlots/components.js +10 -2
- package/src/actions/gradientPlots/edit.js +14 -4
- package/src/actions/gradientPlots/materialize.js +3 -1
- package/src/actions/gradientPlots/options.js +10 -2
- package/src/actions/guides/applicability.js +8 -2
- package/src/actions/guides/axes/labels.js +58 -12
- package/src/actions/guides/axes/lines.js +6 -2
- package/src/actions/guides/axes/parallel/resolve.js +6 -2
- package/src/actions/guides/axes/tickGroups.js +2 -1
- package/src/actions/guides/axes/ticks.js +6 -2
- package/src/actions/guides/axes/titles.js +11 -3
- package/src/actions/guides/grids/resolve.js +7 -2
- package/src/actions/guides/guides.js +19 -3
- package/src/actions/guides/legends/blocks.js +426 -0
- package/src/actions/guides/legends/categorical/actions.js +134 -48
- package/src/actions/guides/legends/categorical/components.js +17 -12
- package/src/actions/guides/legends/categorical/index.js +37 -33
- package/src/actions/guides/legends/categorical/layout.js +97 -15
- package/src/actions/guides/legends/categorical/options.js +2 -2
- package/src/actions/guides/legends/categorical/recipes.js +111 -20
- package/src/actions/guides/legends/categorical/resolve.js +12 -8
- package/src/actions/guides/legends/categorical/symbols.js +85 -30
- package/src/actions/guides/legends/continuous/common.js +30 -12
- package/src/actions/guides/legends/continuous/gradient.js +10 -7
- package/src/actions/guides/legends/continuous/index.js +2 -0
- package/src/actions/guides/legends/continuous/interval.js +8 -3
- package/src/actions/guides/legends/continuous/opacity.js +8 -5
- package/src/actions/guides/legends/continuous/stroke.js +525 -0
- package/src/actions/guides/legends/creation.js +14 -1
- package/src/actions/guides/legends/edit.js +158 -59
- package/src/actions/guides/legends/index.js +2 -0
- package/src/actions/guides/legends/lane.js +54 -19
- package/src/actions/guides/legends/lifecycle.js +19 -8
- package/src/actions/guides/legends/remove.js +13 -3
- package/src/actions/guides/legends/sampling.js +140 -0
- package/src/actions/guides/legends/size.js +93 -40
- package/src/actions/guides/legends/strokeWidth.js +34 -28
- package/src/actions/guides/legends/target.js +112 -1
- package/src/actions/guides/legends/transition.js +165 -16
- package/src/actions/guides/polar/axes/facade.js +4 -4
- package/src/actions/guides/polar/axes/labels.js +24 -5
- package/src/actions/guides/polar/axes/lines.js +10 -4
- package/src/actions/guides/polar/axes/shared.js +16 -0
- package/src/actions/guides/polar/axes/ticks.js +1 -1
- package/src/actions/guides/polar/axes/titles.js +1 -1
- package/src/actions/guides/polar/grids.js +1 -1
- package/src/actions/guides/polar/resolve.js +12 -8
- package/src/actions/index.js +14 -3
- package/src/actions/marks/arc/actions.js +77 -19
- package/src/actions/marks/area/actions.js +34 -9
- package/src/actions/marks/area/materialize.js +17 -1
- package/src/actions/marks/bar/create.js +11 -2
- package/src/actions/marks/bar/edit.js +20 -3
- package/src/actions/marks/bar/materialize.js +22 -2
- package/src/actions/marks/index.js +10 -1
- package/src/actions/marks/line/actions.js +37 -14
- package/src/actions/marks/line/materialize.js +29 -10
- package/src/actions/marks/point/create.js +8 -2
- package/src/actions/marks/point/edit.js +18 -6
- package/src/actions/marks/point/materialize.js +49 -15
- package/src/actions/marks/rect/actions.js +33 -12
- package/src/actions/marks/references.js +601 -30
- package/src/actions/marks/remove.js +71 -1
- package/src/actions/marks/rule/actions.js +47 -10
- package/src/actions/marks/shared.js +1 -1
- package/src/actions/marks/text/actions.js +74 -9
- package/src/actions/marks/text/index.js +4 -0
- package/src/actions/marks/text/layout.js +154 -6
- package/src/actions/marks/text/removal.js +173 -0
- package/src/actions/marks/text/selection.js +73 -0
- package/src/actions/marks/tick/actions.js +22 -5
- package/src/actions/primitives/editGraphics.js +26 -0
- package/src/actions/primitives/semantic.js +2 -1
- package/src/actions/primitives/semanticAction.js +83 -10
- package/src/actions/primitives/semanticValidation/guide.js +1 -1
- package/src/actions/primitives/semanticValidation/index.js +9 -1
- package/src/actions/primitives/semanticValidation/layer.js +5 -0
- package/src/actions/primitives/semanticValidation/scale.js +28 -7
- package/src/actions/regression/components.js +26 -11
- package/src/actions/regression/create.js +10 -2
- package/src/actions/regression/edit.js +21 -7
- package/src/actions/resources/index.js +7 -0
- package/src/actions/resources/remove.js +172 -0
- package/src/actions/scales/channels.js +212 -0
- package/src/actions/scales/consumers/common.js +1 -1
- package/src/actions/scales/consumers/families.js +3 -2
- package/src/actions/scales/consumers/index.js +19 -2
- package/src/actions/scales/consumers/seriesLayout.js +28 -14
- package/src/actions/scales/create.js +54 -22
- package/src/actions/scales/definitions.js +94 -40
- package/src/actions/scales/edit.js +66 -11
- package/src/actions/scales/editPolicy.js +81 -11
- package/src/actions/scales/index.js +6 -0
- package/src/actions/scales/materialize.js +4 -3
- package/src/actions/scales/offset.js +48 -0
- package/src/actions/scales/parallel.js +71 -0
- package/src/actions/scales/preview.js +111 -10
- package/src/actions/selection/actions.js +154 -30
- package/src/actions/selection/index.js +3 -0
- package/src/actions/theme/actions.js +68 -13
- package/src/actions/theme/composition.js +238 -0
- package/src/actions/theme/index.js +9 -2
- package/src/actions/theme/reconcile.js +315 -67
- package/src/actions/theme/state.js +160 -0
- package/src/actions/violinPlots/create.js +5 -2
- package/src/actions/violinPlots/edit.js +1 -0
- package/src/core/action.js +10 -0
- package/src/core/compositionState.js +6 -1
- package/src/core/programState.js +20 -0
- package/src/core/resourceReferences.js +332 -0
- package/src/core/vocabulary.js +16 -3
- package/src/grammar/bars/aggregate.js +4 -1
- package/src/grammar/bars/geometry.js +44 -0
- package/src/grammar/bin.js +54 -9
- package/src/grammar/complete.js +279 -0
- package/src/grammar/computed.js +265 -64
- package/src/grammar/coordinates.js +37 -0
- package/src/grammar/density.js +239 -37
- package/src/grammar/displayLabels.js +57 -0
- package/src/grammar/facets/dependencies.js +133 -1
- package/src/grammar/facets/guides.js +3 -0
- package/src/grammar/facets/index.js +7 -47
- package/src/grammar/facets/scales.js +15 -18
- package/src/grammar/impute.js +340 -0
- package/src/grammar/markLabelSelection.js +57 -0
- package/src/grammar/markLabels.js +18 -1
- package/src/grammar/normalize.js +366 -0
- package/src/grammar/pathSeries.js +26 -8
- package/src/grammar/pointShapes.js +13 -4
- package/src/grammar/polar.js +90 -8
- package/src/grammar/regression/index.js +1 -0
- package/src/grammar/regression/parameters.js +23 -0
- package/src/grammar/roundedRect.js +180 -0
- package/src/grammar/rules.js +1 -0
- package/src/grammar/scales/colorConsumers.js +20 -1
- package/src/grammar/scales/continuous.js +2 -2
- package/src/grammar/scales/definition.js +27 -4
- package/src/grammar/scales/index.js +1 -0
- package/src/grammar/scales/ordinal.js +12 -3
- package/src/grammar/scales/policies.js +2 -2
- package/src/grammar/scales/size.js +456 -0
- package/src/grammar/schemas/concreteGraphic.js +14 -2
- package/src/grammar/schemas/graphic.js +15 -0
- package/src/grammar/schemas/graphicBounds.js +179 -30
- package/src/grammar/schemas/semanticPath.js +7 -2
- package/src/grammar/statisticalReference.js +103 -0
- package/src/grammar/strokeStyle.js +77 -0
- package/src/grammar/summary.js +78 -16
- package/src/grammar/timeUnit.js +216 -5
- package/src/grammar/transformTopology.js +4 -0
- package/src/grammar/transforms.js +394 -28
- package/src/grammar/weightedStatistics.js +374 -0
- package/src/grammar/window.js +221 -16
- package/src/layout/aspect.js +80 -0
- package/src/layout/facets.js +59 -5
- package/src/layout/labels.js +343 -0
- package/src/materialization/bars/aggregate.js +25 -2
- package/src/materialization/bars/histogram.js +84 -24
- package/src/materialization/bars/ranged.js +19 -3
- package/src/materialization/bars/resolve.js +7 -4
- package/src/materialization/composition.js +3 -1
- package/src/materialization/coordinateBounds.js +204 -0
- package/src/materialization/dependencies.js +36 -0
- package/src/materialization/facetGuides/legacyCategorical.js +3 -0
- package/src/materialization/facetGuides/placement.js +8 -2
- package/src/materialization/facetGuides/preparation.js +12 -7
- package/src/materialization/facetHeaders.js +346 -0
- package/src/materialization/facets.js +15 -65
- package/src/materialization/guides/resources.js +26 -0
- package/src/materialization/legends.js +1 -1
- package/src/materialization/marks/index.js +11 -5
- package/src/materialization/marks/policies.js +2 -1
- package/src/materialization/rect.js +25 -12
- package/src/materialization/rectConfig.js +5 -1
- package/src/materialization/scales/map.js +5 -3
- package/src/materialization/scales/policies/binnedPosition.js +2 -2
- package/src/materialization/scales/policies/offset.js +50 -8
- package/src/materialization/scales/policies/series.js +4 -1
- package/src/materialization/scales/resolve.js +22 -6
- package/src/materialization/selection/items/arc.js +28 -4
- package/src/materialization/selection/items/bar.js +30 -3
- package/src/materialization/selection/items/common.js +19 -4
- package/src/materialization/selection/items/rect.js +28 -4
- package/src/materialization/selection/path.js +1 -1
- package/src/materialization/selection/point.js +3 -1
- package/src/materialization/text.js +265 -14
- package/src/renderers/canvas/circle.js +2 -3
- package/src/renderers/canvas/line.js +2 -3
- package/src/renderers/canvas/native.js +4 -4
- package/src/renderers/canvas/path.js +2 -3
- package/src/renderers/canvas/rect.js +2 -3
- package/src/renderers/canvas/stroke.js +11 -0
- package/src/renderers/svg.js +19 -0
- package/src/selectors/datasets.js +32 -1
- package/src/selectors/layers.js +4 -0
- package/src/theme/defaults.js +101 -0
- package/types/basic.d.ts +6 -0
- package/types/index.d.ts +85 -0
- package/types/program.d.ts +910 -117
|
@@ -11,6 +11,8 @@ import {
|
|
|
11
11
|
resolveFacetDefinition,
|
|
12
12
|
resolveFacetGridDefinition
|
|
13
13
|
} from "../../grammar/facets/index.js";
|
|
14
|
+
import { collectFacetScaleBindings, resolveFacetFamily } from
|
|
15
|
+
"../../grammar/facets/dependencies.js";
|
|
14
16
|
import {
|
|
15
17
|
FACET_SCALE_CHANNELS,
|
|
16
18
|
normalizeFacetScalePolicies
|
|
@@ -18,14 +20,21 @@ import {
|
|
|
18
20
|
import { resolveFacetLayout } from "../../layout/facets.js";
|
|
19
21
|
import { compositionChildDescriptor } from
|
|
20
22
|
"../../materialization/composition.js";
|
|
21
|
-
import { DEFAULT_COLORS, DEFAULT_FONT_FAMILY } from "../../theme/defaults.js";
|
|
22
23
|
import { deriveFacetChildren } from "./derive.js";
|
|
23
24
|
import { resolveFacetChildrenScales } from "./derive.js";
|
|
24
25
|
import { replayDerivedData } from "./replay.js";
|
|
25
26
|
import { composeFacetGuides } from "./guides.js";
|
|
26
27
|
import { applyCompositionState } from "../composition/actions.js";
|
|
28
|
+
import { replayCompositionThemeState } from "../theme/composition.js";
|
|
27
29
|
import { findDataset } from "../../selectors/datasets.js";
|
|
28
30
|
import { findLayer } from "../../selectors/layers.js";
|
|
31
|
+
import { findSemanticScale } from "../../selectors/scales.js";
|
|
32
|
+
import { normalizeDisplayLabelMap } from "../../grammar/displayLabels.js";
|
|
33
|
+
import {
|
|
34
|
+
createDefaultFacetHeaders,
|
|
35
|
+
normalizeFacetHeadersConfig,
|
|
36
|
+
resolveFacetHeaderConfig
|
|
37
|
+
} from "../../materialization/facetHeaders.js";
|
|
29
38
|
|
|
30
39
|
const FACET_OPTIONS = Object.freeze([
|
|
31
40
|
"id", "field", "data", "values", "columns", "gap", "align", "padding", "scales",
|
|
@@ -42,15 +51,12 @@ const REPEAT_OPTIONS = Object.freeze([
|
|
|
42
51
|
const SOURCE_EDIT_OPTIONS = Object.freeze(["program"]);
|
|
43
52
|
const GUIDE_OPTIONS = Object.freeze(["axes", "legend"]);
|
|
44
53
|
const HEADER_OPTIONS = Object.freeze([
|
|
45
|
-
"fontSize", "fontFamily", "fontWeight", "color", "offset"
|
|
54
|
+
"fontSize", "fontFamily", "fontWeight", "color", "offset", "role",
|
|
55
|
+
"labelMap", "side", "align"
|
|
56
|
+
]);
|
|
57
|
+
const HEADER_STYLE_OPTIONS = Object.freeze([
|
|
58
|
+
"fontSize", "fontFamily", "fontWeight", "color", "offset", "align"
|
|
46
59
|
]);
|
|
47
|
-
const DEFAULT_HEADERS = Object.freeze({
|
|
48
|
-
fontSize: 12,
|
|
49
|
-
fontFamily: DEFAULT_FONT_FAMILY,
|
|
50
|
-
fontWeight: 600,
|
|
51
|
-
color: DEFAULT_COLORS.strongText,
|
|
52
|
-
offset: 10
|
|
53
|
-
});
|
|
54
60
|
|
|
55
61
|
function normalizeGuides(guides) {
|
|
56
62
|
if (guides === undefined) return { axes: "each", legend: false };
|
|
@@ -74,11 +80,14 @@ function requireFacetProgram(program, operation) {
|
|
|
74
80
|
}
|
|
75
81
|
|
|
76
82
|
function usedFacetScalePolicies(program, policies) {
|
|
77
|
-
|
|
78
|
-
program.semanticSpec.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
83
|
+
const used = new Set(
|
|
84
|
+
collectFacetScaleBindings(program.semanticSpec).map(binding => binding.policyKey)
|
|
85
|
+
);
|
|
86
|
+
return Object.fromEntries(
|
|
87
|
+
FACET_SCALE_CHANNELS.flatMap(channel =>
|
|
88
|
+
used.has(channel) ? [[channel, policies[channel]]] : []
|
|
89
|
+
)
|
|
90
|
+
);
|
|
82
91
|
}
|
|
83
92
|
|
|
84
93
|
function facetUnitTemplate(program) {
|
|
@@ -96,6 +105,9 @@ function facetUnitTemplate(program) {
|
|
|
96
105
|
resolvedScales: program.resolvedScales,
|
|
97
106
|
materializationConfigs: freezeOwned({
|
|
98
107
|
...unitConfigs,
|
|
108
|
+
...(seed.materializationConfigs.theme === undefined
|
|
109
|
+
? {}
|
|
110
|
+
: { theme: seed.materializationConfigs.theme }),
|
|
99
111
|
...(seed.materializationConfigs.guides === undefined
|
|
100
112
|
? {}
|
|
101
113
|
: { guides: seed.materializationConfigs.guides }),
|
|
@@ -145,11 +157,7 @@ function rederiveFacet(program, { scales, guides }) {
|
|
|
145
157
|
columns: current.facet.grid.columns,
|
|
146
158
|
combinations: current.facet.grid.combinations
|
|
147
159
|
});
|
|
148
|
-
const request =
|
|
149
|
-
program.semanticSpec.layers.some(
|
|
150
|
-
layer => layer.encoding?.[channel]?.scale !== undefined
|
|
151
|
-
) ? [[channel, scales[channel]]] : []
|
|
152
|
-
));
|
|
160
|
+
const request = usedFacetScalePolicies(program, scales);
|
|
153
161
|
const normalized = normalizeFacetScalePolicies(program.semanticSpec, request);
|
|
154
162
|
const template = facetUnitTemplate(program);
|
|
155
163
|
const derived = deriveFacetChildren(template, definition, {
|
|
@@ -184,6 +192,9 @@ export const facet = action(
|
|
|
184
192
|
validateOptionObject(args, FACET_OPTIONS, "facet");
|
|
185
193
|
const guides = normalizeGuides(args.guides);
|
|
186
194
|
const definition = resolveFacetDefinition(this.semanticSpec, args);
|
|
195
|
+
if (definition.family !== "cartesian" && guides.axes === "outer") {
|
|
196
|
+
throw new Error("Polar and Parallel facets do not support outer axes.");
|
|
197
|
+
}
|
|
187
198
|
const scalePolicies = normalizeFacetScalePolicies(
|
|
188
199
|
this.semanticSpec,
|
|
189
200
|
args.scales ?? {}
|
|
@@ -222,7 +233,7 @@ export const facet = action(
|
|
|
222
233
|
};
|
|
223
234
|
return applyCompositionState(
|
|
224
235
|
this._withMaterializationConfig(["facets", definition.id], {
|
|
225
|
-
headers:
|
|
236
|
+
headers: createDefaultFacetHeaders()
|
|
226
237
|
}),
|
|
227
238
|
{
|
|
228
239
|
children: derived.children,
|
|
@@ -238,8 +249,20 @@ function resolveRepeatDefinition(program, args) {
|
|
|
238
249
|
throw new TypeError("repeatCharts options must be a plain object.");
|
|
239
250
|
}
|
|
240
251
|
const id = validateUserId(args.id ?? "repeat", "Repeat id");
|
|
241
|
-
|
|
242
|
-
|
|
252
|
+
const parallelChannel = isPlainObject(args.channel);
|
|
253
|
+
if (parallelChannel) {
|
|
254
|
+
const keys = Object.keys(args.channel);
|
|
255
|
+
if (keys.length !== 1 || keys[0] !== "parallelDimension" ||
|
|
256
|
+
typeof args.channel.parallelDimension !== "string" ||
|
|
257
|
+
args.channel.parallelDimension.length === 0) {
|
|
258
|
+
throw new Error(
|
|
259
|
+
"repeatCharts Parallel channel must contain exactly one non-empty parallelDimension."
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
} else if (!["x", "y", "theta", "r"].includes(args.channel)) {
|
|
263
|
+
throw new Error(
|
|
264
|
+
'repeatCharts channel must be "x", "y", "theta", "r", or one parallelDimension.'
|
|
265
|
+
);
|
|
243
266
|
}
|
|
244
267
|
if (!Array.isArray(args.fields) || args.fields.length === 0 ||
|
|
245
268
|
args.fields.some(field => typeof field !== "string" || field.length === 0)) {
|
|
@@ -248,29 +271,53 @@ function resolveRepeatDefinition(program, args) {
|
|
|
248
271
|
if (new Set(args.fields).size !== args.fields.length) {
|
|
249
272
|
throw new Error("repeatCharts fields must be unique.");
|
|
250
273
|
}
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
274
|
+
const family = resolveFacetFamily(program.semanticSpec).family;
|
|
275
|
+
const semanticChannel = args.channel === "r" ? "radius" : args.channel;
|
|
276
|
+
const eligible = program.semanticSpec.layers.filter(layer => {
|
|
277
|
+
if (parallelChannel) {
|
|
278
|
+
const matches = (layer.encoding?.parallel?.dimensions ?? []).filter(
|
|
279
|
+
dimension => dimension.field === args.channel.parallelDimension
|
|
280
|
+
);
|
|
281
|
+
return family === "parallel" && layer.mark?.type === "line" && matches.length === 1;
|
|
282
|
+
}
|
|
283
|
+
if (["x", "y"].includes(args.channel)) {
|
|
284
|
+
return family === "cartesian" &&
|
|
285
|
+
layer.encoding?.x?.scale !== undefined &&
|
|
286
|
+
layer.encoding?.y?.scale !== undefined &&
|
|
287
|
+
layer.encoding?.[args.channel]?.field !== undefined &&
|
|
288
|
+
["point", "line", "area", "bar", "rule", "tick", "rect"].includes(layer.mark?.type);
|
|
289
|
+
}
|
|
290
|
+
const role = program.markConfigs[layer.id]?.compositionRole;
|
|
291
|
+
if (family !== "polar" || ["pie", "radar"].includes(role)) return false;
|
|
292
|
+
return ["point", "line", "arc"].includes(layer.mark?.type) &&
|
|
293
|
+
layer.encoding?.[semanticChannel]?.field !== undefined;
|
|
294
|
+
});
|
|
257
295
|
let target;
|
|
258
296
|
if (args.target !== undefined) {
|
|
259
297
|
target = validateUserId(args.target, "Repeat target");
|
|
260
298
|
if (!eligible.some(layer => layer.id === target)) {
|
|
261
|
-
throw new Error(
|
|
299
|
+
throw new Error(
|
|
300
|
+
`repeatCharts target "${target}" is not eligible for the requested field role.`
|
|
301
|
+
);
|
|
262
302
|
}
|
|
263
303
|
} else if (eligible.length === 1) {
|
|
264
304
|
target = eligible[0].id;
|
|
265
305
|
} else {
|
|
266
306
|
throw new Error(
|
|
267
307
|
eligible.length === 0
|
|
268
|
-
? "repeatCharts requires one complete
|
|
308
|
+
? "repeatCharts requires one eligible complete mark for the requested field role."
|
|
269
309
|
: "repeatCharts target is ambiguous; provide target."
|
|
270
310
|
);
|
|
271
311
|
}
|
|
272
|
-
|
|
273
|
-
|
|
312
|
+
const unrelatedLayers = program.semanticSpec.layers.filter(layer => {
|
|
313
|
+
if (layer.id === target) return false;
|
|
314
|
+
const attachedLabel = layer.mark?.type === "text" && layer.source === target;
|
|
315
|
+
const statisticalReference = program.markConfigs[layer.id]
|
|
316
|
+
?.statisticalReference?.source === target;
|
|
317
|
+
return !attachedLabel && !statisticalReference;
|
|
318
|
+
});
|
|
319
|
+
if (unrelatedLayers.length > 0) {
|
|
320
|
+
throw new Error("repeatCharts currently supports one direct mark only.");
|
|
274
321
|
}
|
|
275
322
|
const config = program.markConfigs[target] ?? {};
|
|
276
323
|
const composite = [
|
|
@@ -285,14 +332,23 @@ function resolveRepeatDefinition(program, args) {
|
|
|
285
332
|
if (dataset?.transform?.length > 0) {
|
|
286
333
|
throw new Error("repeatCharts does not rewrite a derived dataset dependency.");
|
|
287
334
|
}
|
|
288
|
-
const encoding =
|
|
335
|
+
const encoding = parallelChannel
|
|
336
|
+
? layer.encoding.parallel
|
|
337
|
+
: layer.encoding[semanticChannel];
|
|
338
|
+
const dimensionIndex = parallelChannel
|
|
339
|
+
? encoding.dimensions.findIndex(
|
|
340
|
+
dimension => dimension.field === args.channel.parallelDimension
|
|
341
|
+
)
|
|
342
|
+
: undefined;
|
|
289
343
|
return {
|
|
290
344
|
id,
|
|
291
345
|
target,
|
|
292
|
-
channel: args.channel,
|
|
346
|
+
channel: parallelChannel ? { ...args.channel } : args.channel,
|
|
347
|
+
policyKey: parallelChannel ? "parallelDimensions" : args.channel,
|
|
293
348
|
fields: [...args.fields],
|
|
294
349
|
data: layer.data,
|
|
295
350
|
encoding,
|
|
351
|
+
...(dimensionIndex === undefined ? {} : { dimensionIndex }),
|
|
296
352
|
cells: args.fields.map((field, index) => ({
|
|
297
353
|
id: `${id}-field-${index + 1}`,
|
|
298
354
|
field,
|
|
@@ -316,15 +372,52 @@ function repeatEncodingArgs(definition, field) {
|
|
|
316
372
|
};
|
|
317
373
|
}
|
|
318
374
|
|
|
375
|
+
function parallelRepeatDimensions(program, definition, field) {
|
|
376
|
+
return definition.encoding.dimensions.map((dimension, index) => {
|
|
377
|
+
const semanticScale = findSemanticScale(program, dimension.scale);
|
|
378
|
+
if (semanticScale === undefined) {
|
|
379
|
+
throw new Error(`repeatCharts Parallel dimension scale "${dimension.scale}" is missing.`);
|
|
380
|
+
}
|
|
381
|
+
const { id: _id, ...scale } = semanticScale;
|
|
382
|
+
void _id;
|
|
383
|
+
const replacement = index === definition.dimensionIndex;
|
|
384
|
+
return {
|
|
385
|
+
field: replacement ? field : dimension.field,
|
|
386
|
+
fieldType: dimension.fieldType,
|
|
387
|
+
title: replacement && dimension.title === dimension.field
|
|
388
|
+
? field
|
|
389
|
+
: dimension.title,
|
|
390
|
+
scale
|
|
391
|
+
};
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function deriveRepeatedProgram(template, definition, field) {
|
|
396
|
+
if (isPlainObject(definition.channel)) {
|
|
397
|
+
return template.encodeParallelCoordinates({
|
|
398
|
+
target: definition.target,
|
|
399
|
+
coordinate: findLayer(template, definition.target).coordinate,
|
|
400
|
+
dimensions: parallelRepeatDimensions(template, definition, field),
|
|
401
|
+
...(definition.encoding.key === undefined ? {} : { key: definition.encoding.key }),
|
|
402
|
+
missing: definition.encoding.missing
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
const operation = {
|
|
406
|
+
x: "encodeX",
|
|
407
|
+
y: "encodeY",
|
|
408
|
+
theta: "encodeTheta",
|
|
409
|
+
r: "encodeR"
|
|
410
|
+
}[definition.channel];
|
|
411
|
+
return template[operation](repeatEncodingArgs(definition, field));
|
|
412
|
+
}
|
|
413
|
+
|
|
319
414
|
function deriveRepeatChildren(base, definition, scales, closeInheritedAction) {
|
|
320
415
|
const template = base.semanticSpec.title.text === undefined
|
|
321
416
|
? base
|
|
322
417
|
: base.removeTitle();
|
|
323
418
|
const independentlyResolved = Object.fromEntries(definition.cells.map(cell => [
|
|
324
419
|
cell.id,
|
|
325
|
-
template
|
|
326
|
-
repeatEncodingArgs(definition, cell.field)
|
|
327
|
-
)
|
|
420
|
+
deriveRepeatedProgram(template, definition, cell.field)
|
|
328
421
|
]));
|
|
329
422
|
return resolveFacetChildrenScales(
|
|
330
423
|
template,
|
|
@@ -338,7 +431,7 @@ function deriveRepeatChildren(base, definition, scales, closeInheritedAction) {
|
|
|
338
431
|
export const repeatCharts = action(
|
|
339
432
|
{
|
|
340
433
|
op: "repeatCharts",
|
|
341
|
-
description: "Repeat one direct
|
|
434
|
+
description: "Repeat one direct chart across an ordered field-role list."
|
|
342
435
|
},
|
|
343
436
|
function (args = {}) {
|
|
344
437
|
validateOptionObject(args, REPEAT_OPTIONS, "repeatCharts");
|
|
@@ -349,7 +442,7 @@ export const repeatCharts = action(
|
|
|
349
442
|
const definition = resolveRepeatDefinition(this, args);
|
|
350
443
|
const requestedScales = {
|
|
351
444
|
...(args.scales ?? {}),
|
|
352
|
-
[definition.
|
|
445
|
+
[definition.policyKey]: args.scales?.[definition.policyKey] ?? "independent"
|
|
353
446
|
};
|
|
354
447
|
const scalePolicies = normalizeFacetScalePolicies(
|
|
355
448
|
this.semanticSpec,
|
|
@@ -394,7 +487,7 @@ export const repeatCharts = action(
|
|
|
394
487
|
};
|
|
395
488
|
return applyCompositionState(
|
|
396
489
|
this._withMaterializationConfig(["facets", definition.id], {
|
|
397
|
-
headers:
|
|
490
|
+
headers: createDefaultFacetHeaders()
|
|
398
491
|
}),
|
|
399
492
|
{ children: derived.children, compositionSpec },
|
|
400
493
|
compositionSpec.children
|
|
@@ -411,6 +504,9 @@ export const facetGrid = action(
|
|
|
411
504
|
validateOptionObject(args, FACET_GRID_OPTIONS, "facetGrid");
|
|
412
505
|
const guides = normalizeGuides(args.guides);
|
|
413
506
|
const definition = resolveFacetGridDefinition(this.semanticSpec, args);
|
|
507
|
+
if (definition.family !== "cartesian" && guides.axes === "outer") {
|
|
508
|
+
throw new Error("Polar and Parallel facets do not support outer axes.");
|
|
509
|
+
}
|
|
414
510
|
const scalePolicies = normalizeFacetScalePolicies(
|
|
415
511
|
this.semanticSpec,
|
|
416
512
|
args.scales ?? {}
|
|
@@ -461,7 +557,7 @@ export const facetGrid = action(
|
|
|
461
557
|
};
|
|
462
558
|
return applyCompositionState(
|
|
463
559
|
this._withMaterializationConfig(["facets", definition.id], {
|
|
464
|
-
headers:
|
|
560
|
+
headers: createDefaultFacetHeaders()
|
|
465
561
|
}),
|
|
466
562
|
{ children: derived.children, compositionSpec },
|
|
467
563
|
compositionSpec.children
|
|
@@ -486,18 +582,73 @@ export const editFacetHeaders = action(
|
|
|
486
582
|
allowEmpty: false,
|
|
487
583
|
emptyMessage: "editFacetHeaders requires at least one change."
|
|
488
584
|
});
|
|
489
|
-
const
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
585
|
+
const role = args.role ?? "all";
|
|
586
|
+
if (!["all", "row", "column"].includes(role)) {
|
|
587
|
+
throw new Error(`Unknown facet header role "${role}".`);
|
|
588
|
+
}
|
|
589
|
+
if (role === "all" && Object.hasOwn(args, "side")) {
|
|
590
|
+
throw new Error("editFacetHeaders side requires an explicit row or column role.");
|
|
591
|
+
}
|
|
592
|
+
if (role === "row" && this.compositionSpec.facet.grid === undefined) {
|
|
593
|
+
throw new Error("editFacetHeaders row role requires a row-column facet grid.");
|
|
594
|
+
}
|
|
595
|
+
if (Object.hasOwn(args, "side")) {
|
|
596
|
+
const sides = role === "row" ? ["left", "right"] : ["top", "bottom"];
|
|
597
|
+
if (!sides.includes(args.side)) {
|
|
598
|
+
throw new Error(`editFacetHeaders ${role} side must be ${sides.join(" or ")}.`);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
if (Object.hasOwn(args, "align") &&
|
|
602
|
+
!["start", "center", "end"].includes(args.align)) {
|
|
603
|
+
throw new Error("editFacetHeaders align must be start, center, or end.");
|
|
604
|
+
}
|
|
605
|
+
if (role === "all" && Object.keys(args).every(key => key === "role")) {
|
|
606
|
+
throw new Error("editFacetHeaders requires at least one change.");
|
|
607
|
+
}
|
|
608
|
+
const current = normalizeFacetHeadersConfig(config.headers);
|
|
609
|
+
const owner = role === "all" ? "common" : role;
|
|
610
|
+
const patch = { ...current[owner] };
|
|
611
|
+
for (const key of HEADER_STYLE_OPTIONS) {
|
|
612
|
+
if (Object.hasOwn(args, key)) patch[key] = args[key];
|
|
613
|
+
}
|
|
614
|
+
if (Object.hasOwn(args, "side")) patch.side = args.side;
|
|
615
|
+
if (Object.hasOwn(args, "labelMap")) {
|
|
616
|
+
if (args.labelMap === "auto") delete patch.labelMap;
|
|
617
|
+
else {
|
|
618
|
+
patch.labelMap = normalizeDisplayLabelMap(
|
|
619
|
+
args.labelMap,
|
|
620
|
+
`editFacetHeaders ${role} labelMap`
|
|
621
|
+
);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
const headers = {
|
|
625
|
+
...current,
|
|
626
|
+
...(role === "all" ? {} : { mode: "roles" }),
|
|
627
|
+
[owner]: patch
|
|
628
|
+
};
|
|
629
|
+
for (const headerRole of ["all", "row", "column"]) {
|
|
630
|
+
const resolved = resolveFacetHeaderConfig(headers, headerRole);
|
|
631
|
+
validatePositiveFinite(resolved.fontSize, `Facet ${headerRole} header fontSize`);
|
|
632
|
+
validateNonEmptyString(resolved.fontFamily, `Facet ${headerRole} header fontFamily`);
|
|
633
|
+
validateNonEmptyString(resolved.color, `Facet ${headerRole} header color`);
|
|
634
|
+
validateNonNegativeFinite(resolved.offset, `Facet ${headerRole} header offset`);
|
|
635
|
+
if (!(
|
|
636
|
+
(typeof resolved.fontWeight === "string" && resolved.fontWeight.length > 0) ||
|
|
637
|
+
Number.isFinite(resolved.fontWeight)
|
|
638
|
+
)) {
|
|
639
|
+
throw new TypeError(
|
|
640
|
+
`Facet ${headerRole} header fontWeight must be a non-empty string or number.`
|
|
641
|
+
);
|
|
642
|
+
}
|
|
643
|
+
if (!["start", "center", "end"].includes(resolved.align)) {
|
|
644
|
+
throw new Error(`Facet ${headerRole} header align must be start, center, or end.`);
|
|
645
|
+
}
|
|
646
|
+
if (resolved.labelMap !== undefined) {
|
|
647
|
+
normalizeDisplayLabelMap(
|
|
648
|
+
resolved.labelMap,
|
|
649
|
+
`Facet ${headerRole} header labelMap`
|
|
650
|
+
);
|
|
651
|
+
}
|
|
501
652
|
}
|
|
502
653
|
return this
|
|
503
654
|
._withMaterializationConfig(["facets", id], { ...config, headers })
|
|
@@ -518,8 +669,8 @@ export const editFacetScales = action(
|
|
|
518
669
|
emptyMessage: "editFacetScales requires at least one channel policy change."
|
|
519
670
|
});
|
|
520
671
|
for (const channel of Object.keys(args)) {
|
|
521
|
-
if (!this.semanticSpec.
|
|
522
|
-
|
|
672
|
+
if (!collectFacetScaleBindings(this.semanticSpec).some(
|
|
673
|
+
binding => binding.policyKey === channel
|
|
523
674
|
)) {
|
|
524
675
|
throw new Error(
|
|
525
676
|
`Facet scale channel "${channel}" is not used by an affected layer.`
|
|
@@ -554,6 +705,10 @@ export const editFacetGuides = action(
|
|
|
554
705
|
});
|
|
555
706
|
const current = this.compositionSpec.facet;
|
|
556
707
|
const guides = normalizeGuides({ ...current.guides, ...args });
|
|
708
|
+
const family = resolveFacetFamily(this.semanticSpec).family;
|
|
709
|
+
if (family !== "cartesian" && guides.axes === "outer") {
|
|
710
|
+
throw new Error("Polar and Parallel facets do not support outer axes.");
|
|
711
|
+
}
|
|
557
712
|
if (current.repeat !== undefined && guides.axes === "outer") {
|
|
558
713
|
throw new Error("repeatCharts does not promote axes across different repeated fields.");
|
|
559
714
|
}
|
|
@@ -574,11 +729,33 @@ function adoptUnitState(program, actionOwner) {
|
|
|
574
729
|
if (program.actionStack.length !== 0) {
|
|
575
730
|
throw new Error("editFacetSource program has an unfinished action stack.");
|
|
576
731
|
}
|
|
732
|
+
const seedId = actionOwner.compositionSpec.children.find(id =>
|
|
733
|
+
actionOwner.children[id]?.semanticSpec.layers.length > 0
|
|
734
|
+
) ?? actionOwner.compositionSpec.children[0];
|
|
735
|
+
const retained = actionOwner.children[seedId]?.materializationConfigs ?? {};
|
|
736
|
+
const {
|
|
737
|
+
canvas: _retainedCanvas,
|
|
738
|
+
theme: _retainedTheme,
|
|
739
|
+
facets: _retainedFacets,
|
|
740
|
+
...retainedRecipe
|
|
741
|
+
} = retained;
|
|
742
|
+
void _retainedCanvas;
|
|
743
|
+
void _retainedTheme;
|
|
744
|
+
void _retainedFacets;
|
|
577
745
|
return new actionOwner.constructor({
|
|
578
746
|
semanticSpec: program.semanticSpec,
|
|
579
747
|
graphicSpec: program.graphicSpec,
|
|
580
748
|
resolvedScales: program.resolvedScales,
|
|
581
|
-
materializationConfigs:
|
|
749
|
+
materializationConfigs: freezeOwned({
|
|
750
|
+
...program.materializationConfigs,
|
|
751
|
+
...retainedRecipe,
|
|
752
|
+
...(program.materializationConfigs.canvas === undefined
|
|
753
|
+
? {}
|
|
754
|
+
: { canvas: program.materializationConfigs.canvas }),
|
|
755
|
+
...(program.materializationConfigs.theme === undefined
|
|
756
|
+
? {}
|
|
757
|
+
: { theme: program.materializationConfigs.theme })
|
|
758
|
+
}),
|
|
582
759
|
children: {},
|
|
583
760
|
context: program.context,
|
|
584
761
|
trace: actionOwner.trace,
|
|
@@ -653,7 +830,10 @@ export const editFacetSource = action(
|
|
|
653
830
|
...this.semanticSpec.title
|
|
654
831
|
});
|
|
655
832
|
}
|
|
656
|
-
return
|
|
833
|
+
return replayCompositionThemeState(
|
|
834
|
+
revised,
|
|
835
|
+
this.materializationConfigs.theme
|
|
836
|
+
);
|
|
657
837
|
}
|
|
658
838
|
);
|
|
659
839
|
|