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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { validateOptionObject } from "../../../../core/validation.js";
|
|
2
2
|
import { mapOrdinalValues } from "../../../../grammar/scales/index.js";
|
|
3
3
|
import { DEFAULT_COLORS } from "../../../../theme/defaults.js";
|
|
4
4
|
import { formatVisibleText } from "../../../../core/textMetrics.js";
|
|
@@ -10,25 +10,57 @@ import {
|
|
|
10
10
|
import { resolveLegendItemLayout } from "../../../../layout/legendItems.js";
|
|
11
11
|
import { createPointShapeGraphic } from "../../../../grammar/pointShapes.js";
|
|
12
12
|
import { resolveConcreteGraphicBounds } from "../../../../grammar/schemas/graphicBounds.js";
|
|
13
|
+
import { resolveEffectiveLegendBlockConfig } from "../blocks.js";
|
|
14
|
+
import { resolveDisplayLabel } from "../../../../grammar/displayLabels.js";
|
|
15
|
+
import {
|
|
16
|
+
materializeRectItem,
|
|
17
|
+
requestedRectStyleDetails
|
|
18
|
+
} from "../../../../grammar/roundedRect.js";
|
|
19
|
+
import { requestedStrokeDetails } from "../../../../grammar/strokeStyle.js";
|
|
20
|
+
|
|
21
|
+
const CATEGORICAL_KINDS = Object.freeze(["series", "color", "stroke"]);
|
|
13
22
|
|
|
14
|
-
export function activeConfig(program) {
|
|
15
|
-
|
|
23
|
+
export function activeConfig(program, requested) {
|
|
24
|
+
if (requested !== undefined && !CATEGORICAL_KINDS.includes(requested)) {
|
|
25
|
+
throw new Error(`Unknown categorical legend kind "${requested}".`);
|
|
26
|
+
}
|
|
27
|
+
const kinds = CATEGORICAL_KINDS
|
|
16
28
|
.filter(kind => program.guideConfigs.legend?.[kind] !== undefined);
|
|
29
|
+
if (requested !== undefined) {
|
|
30
|
+
if (!kinds.includes(requested)) {
|
|
31
|
+
throw new Error(`Missing categorical legend config "${requested}".`);
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
kind: requested,
|
|
35
|
+
config: resolveEffectiveLegendBlockConfig(
|
|
36
|
+
program,
|
|
37
|
+
requested,
|
|
38
|
+
program.guideConfigs.legend[requested]
|
|
39
|
+
)
|
|
40
|
+
};
|
|
41
|
+
}
|
|
17
42
|
if (kinds.length !== 1) {
|
|
18
43
|
throw new Error("Legend component requires one categorical legend config.");
|
|
19
44
|
}
|
|
20
45
|
const kind = kinds[0];
|
|
21
|
-
return {
|
|
46
|
+
return {
|
|
47
|
+
kind,
|
|
48
|
+
config: resolveEffectiveLegendBlockConfig(
|
|
49
|
+
program,
|
|
50
|
+
kind,
|
|
51
|
+
program.guideConfigs.legend[kind]
|
|
52
|
+
)
|
|
53
|
+
};
|
|
22
54
|
}
|
|
23
55
|
|
|
24
56
|
function prefix(config) {
|
|
25
|
-
return config.kind
|
|
57
|
+
return `${config.kind}Legend`;
|
|
26
58
|
}
|
|
27
59
|
|
|
28
60
|
export function symbolGraphic(config, type) {
|
|
29
61
|
const onlyDefault = config.symbol.layers.length === 1 &&
|
|
30
62
|
((config.kind === "series" && type === "line") ||
|
|
31
|
-
(config.kind
|
|
63
|
+
(["color", "stroke"].includes(config.kind) && type === "swatch"));
|
|
32
64
|
if (onlyDefault) return `${prefix(config)}Symbols`;
|
|
33
65
|
const suffix = { line: "Lines", point: "Points", swatch: "Swatches" }[type];
|
|
34
66
|
return `${prefix(config)}Symbol${suffix}`;
|
|
@@ -46,6 +78,14 @@ export function symbolWidth(config) {
|
|
|
46
78
|
}));
|
|
47
79
|
}
|
|
48
80
|
|
|
81
|
+
export function categoricalLegendLabels(config) {
|
|
82
|
+
return config.domain.map(value => resolveDisplayLabel(
|
|
83
|
+
value,
|
|
84
|
+
config.labelMap,
|
|
85
|
+
formatVisibleText
|
|
86
|
+
));
|
|
87
|
+
}
|
|
88
|
+
|
|
49
89
|
function resolveSampleBounds(program, config, width) {
|
|
50
90
|
const appearance = resolveAppearance(program, config);
|
|
51
91
|
return config.domain.map((_, index) => {
|
|
@@ -53,21 +93,56 @@ function resolveSampleBounds(program, config, width) {
|
|
|
53
93
|
if (layer.type === "line") {
|
|
54
94
|
const x = (width - layer.length) / 2;
|
|
55
95
|
return { type: "line", properties: { x1: x, x2: x + layer.length, y1: 0, y2: 0,
|
|
56
|
-
|
|
96
|
+
stroke: layer.stroke ?? (config.channels.includes("stroke")
|
|
97
|
+
? appearance.strokes[index]
|
|
98
|
+
: appearance.colors[index]),
|
|
99
|
+
strokeWidth: layer.lineWidth, opacity: layer.opacity,
|
|
100
|
+
...requestedStrokeDetails(layer, "Legend line symbol") } };
|
|
57
101
|
}
|
|
58
102
|
if (layer.type === "swatch") {
|
|
59
|
-
|
|
60
|
-
|
|
103
|
+
const details = requestedRectStyleDetails(layer, "Legend swatch symbol");
|
|
104
|
+
return materializeRectItem({ x: (width - layer.width) / 2, y: -layer.height / 2,
|
|
105
|
+
width: layer.width, height: layer.height, strokeWidth: layer.strokeWidth,
|
|
106
|
+
stroke: config.channels.includes("stroke")
|
|
107
|
+
? appearance.strokes[index]
|
|
108
|
+
: layer.stroke,
|
|
109
|
+
opacity: layer.opacity,
|
|
110
|
+
...requestedStrokeDetails(details, "Legend swatch symbol")
|
|
111
|
+
}, details.cornerRadius ?? 0);
|
|
61
112
|
}
|
|
62
113
|
if (config.channels.includes("shape")) {
|
|
63
114
|
return createPointShapeGraphic({ shape: appearance.shapes[index], x: width / 2, y: 0,
|
|
64
115
|
area: Math.PI * layer.size ** 2, fill: layer.fill ?? appearance.colors[index],
|
|
65
|
-
stroke: layer.stroke, strokeWidth: layer.strokeWidth
|
|
116
|
+
stroke: layer.stroke, strokeWidth: layer.strokeWidth, opacity: layer.opacity,
|
|
117
|
+
...requestedStrokeDetails(layer, "Legend point symbol") });
|
|
66
118
|
}
|
|
67
119
|
return { type: "circle", properties: { x: width / 2, y: 0, radius: layer.size,
|
|
68
|
-
|
|
120
|
+
stroke: config.channels.includes("stroke")
|
|
121
|
+
? appearance.strokes[index]
|
|
122
|
+
: layer.stroke,
|
|
123
|
+
strokeWidth: layer.strokeWidth, opacity: layer.opacity,
|
|
124
|
+
...requestedStrokeDetails(layer, "Legend point symbol") } };
|
|
69
125
|
});
|
|
70
|
-
return
|
|
126
|
+
return items.map((item, itemIndex) => {
|
|
127
|
+
const bounds = resolveConcreteGraphicBounds({
|
|
128
|
+
objects: { sample: item },
|
|
129
|
+
order: ["sample"]
|
|
130
|
+
}, "sample");
|
|
131
|
+
if (config.symbol.layers[itemIndex].type !== "line") return bounds;
|
|
132
|
+
const extent = config.symbol.layers[itemIndex].lineWidth / 2;
|
|
133
|
+
return {
|
|
134
|
+
...bounds,
|
|
135
|
+
left: bounds.left - extent,
|
|
136
|
+
right: bounds.right + extent
|
|
137
|
+
};
|
|
138
|
+
}).reduce((result, bounds) => result === undefined
|
|
139
|
+
? bounds
|
|
140
|
+
: {
|
|
141
|
+
left: Math.min(result.left, bounds.left),
|
|
142
|
+
right: Math.max(result.right, bounds.right),
|
|
143
|
+
top: Math.min(result.top, bounds.top),
|
|
144
|
+
bottom: Math.max(result.bottom, bounds.bottom)
|
|
145
|
+
}, undefined);
|
|
71
146
|
});
|
|
72
147
|
}
|
|
73
148
|
|
|
@@ -75,7 +150,7 @@ export function resolveLayout(program, config) {
|
|
|
75
150
|
const { plot, canvas } = resolveContinuousBounds(program,
|
|
76
151
|
"Legend layout requires Canvas bounds, width, and height.");
|
|
77
152
|
const width = symbolWidth(config);
|
|
78
|
-
const layout = resolveLegendItemLayout(plot, config, config
|
|
153
|
+
const layout = resolveLegendItemLayout(plot, config, categoricalLegendLabels(config), {
|
|
79
154
|
width, height: 0, itemBounds: resolveSampleBounds(program, config, width)
|
|
80
155
|
}, canvas);
|
|
81
156
|
assertLegendBoundsInsideCanvas(layout.bounds, canvas, "Categorical legend layout", config);
|
|
@@ -86,19 +161,26 @@ export function resolveLayout(program, config) {
|
|
|
86
161
|
|
|
87
162
|
export function resolveAppearance(program, config) {
|
|
88
163
|
let colors = config.domain.map(() => DEFAULT_COLORS.mark);
|
|
164
|
+
let strokes = config.domain.map(() => "white");
|
|
89
165
|
let dashes = config.domain.map(() => []);
|
|
90
166
|
let shapes = config.domain.map(() => "circle");
|
|
91
167
|
for (let index = 0; index < config.channels.length; index += 1) {
|
|
92
168
|
const scale = program.resolvedScales[config.scales[index]];
|
|
93
169
|
const values = mapOrdinalValues(config.domain, scale.domain, scale.range);
|
|
94
170
|
if (config.channels[index] === "color") colors = values;
|
|
171
|
+
if (config.channels[index] === "stroke") strokes = values;
|
|
95
172
|
if (config.channels[index] === "strokeDash") dashes = values;
|
|
96
173
|
if (config.channels[index] === "shape") shapes = values;
|
|
97
174
|
}
|
|
98
|
-
return { colors, dashes, shapes };
|
|
175
|
+
return { colors, strokes, dashes, shapes };
|
|
99
176
|
}
|
|
100
177
|
|
|
101
|
-
export
|
|
178
|
+
export function noOptions(args, operation) {
|
|
179
|
+
validateOptionObject(args, ["kind"], operation);
|
|
180
|
+
if (args.kind !== undefined && !CATEGORICAL_KINDS.includes(args.kind)) {
|
|
181
|
+
throw new Error(`Unknown categorical legend kind "${args.kind}".`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
102
184
|
|
|
103
185
|
export function layerFor(config, type) {
|
|
104
186
|
const layer = config.symbol.layers.find(item => item.type === type);
|
|
@@ -88,7 +88,7 @@ function normalizeBorder(border) {
|
|
|
88
88
|
return normalized;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
export function normalizeOptions(args, kind) {
|
|
91
|
+
export function normalizeOptions(args, kind, { internalSymbol = false } = {}) {
|
|
92
92
|
validateOptionObject(args, OPTIONS, "createLegend");
|
|
93
93
|
if (Object.hasOwn(args, "labels")) {
|
|
94
94
|
validateObject(args.labels, TEXT_OPTIONS, "createLegend.labels");
|
|
@@ -191,7 +191,7 @@ export function normalizeOptions(args, kind) {
|
|
|
191
191
|
offset,
|
|
192
192
|
titlePosition,
|
|
193
193
|
title: args.title,
|
|
194
|
-
symbol: normalizeRecipe(args.symbol, kind),
|
|
194
|
+
symbol: normalizeRecipe(args.symbol, kind, { internal: internalSymbol }),
|
|
195
195
|
labels,
|
|
196
196
|
titleStyle,
|
|
197
197
|
itemGap,
|
|
@@ -5,6 +5,15 @@ import {
|
|
|
5
5
|
positive,
|
|
6
6
|
validateKeys
|
|
7
7
|
} from "./validation.js";
|
|
8
|
+
import {
|
|
9
|
+
requestedRectStyleDetails,
|
|
10
|
+
RECT_STYLE_PROPERTIES
|
|
11
|
+
} from "../../../../grammar/roundedRect.js";
|
|
12
|
+
import {
|
|
13
|
+
requestedStrokeDetails,
|
|
14
|
+
STROKE_STYLE_PROPERTIES
|
|
15
|
+
} from "../../../../grammar/strokeStyle.js";
|
|
16
|
+
import { findLayerMatching } from "../../../../selectors/layers.js";
|
|
8
17
|
|
|
9
18
|
const LAYER_OPTIONS = Object.freeze({
|
|
10
19
|
line: Object.freeze(["type", "length", "lineWidth"]),
|
|
@@ -24,19 +33,101 @@ const LAYER_OPTIONS = Object.freeze({
|
|
|
24
33
|
"strokeWidth"
|
|
25
34
|
])
|
|
26
35
|
});
|
|
36
|
+
const INTERNAL_LAYER_OPTIONS = Object.freeze({
|
|
37
|
+
line: Object.freeze([...LAYER_OPTIONS.line, ...STROKE_STYLE_PROPERTIES]),
|
|
38
|
+
point: Object.freeze([...LAYER_OPTIONS.point, ...STROKE_STYLE_PROPERTIES]),
|
|
39
|
+
swatch: Object.freeze([...LAYER_OPTIONS.swatch, ...RECT_STYLE_PROPERTIES])
|
|
40
|
+
});
|
|
27
41
|
|
|
28
|
-
export function resolveLegendSymbol(program, layer, channels, requested) {
|
|
42
|
+
export function resolveLegendSymbol(program, layer, channels, requested, kind) {
|
|
29
43
|
if (requested !== undefined && requested !== "auto") return requested;
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
44
|
+
const config = program.markConfigs[layer.id] ?? {};
|
|
45
|
+
const bar = config.barAppearance ?? {};
|
|
46
|
+
const styleOwner = layer.mark.type === "bar" ? bar : config;
|
|
47
|
+
const strokeDetails = requestedStrokeDetails(styleOwner, "Legend source");
|
|
48
|
+
const rectDetails = ["bar", "rect"].includes(layer.mark.type)
|
|
49
|
+
? requestedRectStyleDetails(styleOwner, "Legend source")
|
|
50
|
+
: strokeDetails;
|
|
51
|
+
if (channels?.length === 1 && channels[0] === "stroke") {
|
|
52
|
+
const strokeWidth = bar.strokeWidth ?? config.strokeWidth ?? ({
|
|
53
|
+
point: 1,
|
|
54
|
+
line: 2,
|
|
55
|
+
area: 1,
|
|
56
|
+
bar: 0.5,
|
|
57
|
+
rect: 1,
|
|
58
|
+
arc: 1,
|
|
59
|
+
rule: 2,
|
|
60
|
+
tick: 2
|
|
61
|
+
}[layer.mark.type] ?? 1);
|
|
62
|
+
if (["line", "rule", "tick"].includes(layer.mark.type)) {
|
|
63
|
+
return { layers: [{
|
|
64
|
+
type: "line",
|
|
65
|
+
length: 32,
|
|
66
|
+
lineWidth: strokeWidth,
|
|
67
|
+
...strokeDetails
|
|
68
|
+
}] };
|
|
69
|
+
}
|
|
70
|
+
if (layer.mark.type === "point") {
|
|
71
|
+
return { layers: [{
|
|
72
|
+
type: "point",
|
|
73
|
+
size: 5,
|
|
74
|
+
fill: config.fill ?? "#4c78a8",
|
|
75
|
+
stroke: "white",
|
|
76
|
+
strokeWidth,
|
|
77
|
+
...strokeDetails
|
|
78
|
+
}] };
|
|
79
|
+
}
|
|
80
|
+
return { layers: [{
|
|
81
|
+
type: "swatch",
|
|
82
|
+
width: 14,
|
|
83
|
+
height: 12,
|
|
84
|
+
stroke: "white",
|
|
85
|
+
strokeWidth,
|
|
86
|
+
...rectDetails
|
|
87
|
+
}] };
|
|
88
|
+
}
|
|
89
|
+
if (layer.mark?.type === "point" && channels?.includes("shape")) {
|
|
90
|
+
const color = channels.includes("color") ? layer.encoding?.color : undefined;
|
|
91
|
+
const matchingLine = color?.scale === undefined
|
|
92
|
+
? undefined
|
|
93
|
+
: findLayerMatching(program, candidate =>
|
|
94
|
+
candidate.mark?.type === "line" &&
|
|
95
|
+
candidate.encoding?.color?.field === color.field &&
|
|
96
|
+
candidate.encoding?.color?.scale === color.scale
|
|
97
|
+
);
|
|
98
|
+
const lineDetails = matchingLine === undefined
|
|
99
|
+
? {}
|
|
100
|
+
: requestedStrokeDetails(
|
|
101
|
+
program.markConfigs[matchingLine.id] ?? {},
|
|
102
|
+
"Legend line source"
|
|
103
|
+
);
|
|
104
|
+
return { layers: [
|
|
105
|
+
...(matchingLine !== undefined ? [{
|
|
106
|
+
type: "line",
|
|
107
|
+
length: 32,
|
|
108
|
+
lineWidth: 3,
|
|
109
|
+
...lineDetails
|
|
110
|
+
}] : []),
|
|
111
|
+
{
|
|
112
|
+
type: "point",
|
|
113
|
+
size: Math.sqrt(64 / Math.PI),
|
|
114
|
+
stroke: "white",
|
|
115
|
+
strokeWidth: 0,
|
|
116
|
+
...strokeDetails
|
|
117
|
+
}
|
|
118
|
+
] };
|
|
119
|
+
}
|
|
120
|
+
if (Object.keys(rectDetails).length === 0) return undefined;
|
|
121
|
+
return kind === "series"
|
|
122
|
+
? { layers: [{ type: "line", length: 32, lineWidth: 2, ...strokeDetails }] }
|
|
123
|
+
: { layers: [{
|
|
124
|
+
type: "swatch",
|
|
125
|
+
width: 14,
|
|
126
|
+
height: 12,
|
|
127
|
+
stroke: "white",
|
|
128
|
+
strokeWidth: 0.5,
|
|
129
|
+
...rectDetails
|
|
130
|
+
}] };
|
|
40
131
|
}
|
|
41
132
|
|
|
42
133
|
function defaultRecipe(kind) {
|
|
@@ -53,20 +144,20 @@ function defaultRecipe(kind) {
|
|
|
53
144
|
};
|
|
54
145
|
}
|
|
55
146
|
|
|
56
|
-
function normalizeLayer(layer) {
|
|
147
|
+
function normalizeLayer(layer, internal) {
|
|
57
148
|
if (!isPlainObject(layer) || !Object.hasOwn(LAYER_OPTIONS, layer.type)) {
|
|
58
149
|
throw new Error("Legend symbol layer type must be line, point, or swatch.");
|
|
59
150
|
}
|
|
60
151
|
validateKeys(
|
|
61
152
|
layer,
|
|
62
|
-
LAYER_OPTIONS[layer.type],
|
|
153
|
+
internal ? INTERNAL_LAYER_OPTIONS[layer.type] : LAYER_OPTIONS[layer.type],
|
|
63
154
|
`createLegend.symbol.${layer.type}`
|
|
64
155
|
);
|
|
65
156
|
if (layer.type === "line") {
|
|
66
157
|
const normalized = { length: 32, lineWidth: 2, ...layer };
|
|
67
158
|
positive(normalized.length, "Legend line symbol length");
|
|
68
159
|
nonNegative(normalized.lineWidth, "Legend line symbol lineWidth");
|
|
69
|
-
return normalized;
|
|
160
|
+
return { ...normalized, ...requestedStrokeDetails(normalized, "Legend line symbol") };
|
|
70
161
|
}
|
|
71
162
|
if (layer.type === "point") {
|
|
72
163
|
const normalized = {
|
|
@@ -85,7 +176,7 @@ function normalizeLayer(layer) {
|
|
|
85
176
|
if (normalized.fill !== undefined) {
|
|
86
177
|
nonEmptyString(normalized.fill, "Legend point symbol fill");
|
|
87
178
|
}
|
|
88
|
-
return normalized;
|
|
179
|
+
return { ...normalized, ...requestedStrokeDetails(normalized, "Legend point symbol") };
|
|
89
180
|
}
|
|
90
181
|
const normalized = {
|
|
91
182
|
width: 14,
|
|
@@ -98,10 +189,10 @@ function normalizeLayer(layer) {
|
|
|
98
189
|
positive(normalized.height, "Legend swatch height");
|
|
99
190
|
nonEmptyString(normalized.stroke, "Legend swatch stroke");
|
|
100
191
|
nonNegative(normalized.strokeWidth, "Legend swatch strokeWidth");
|
|
101
|
-
return normalized;
|
|
192
|
+
return { ...normalized, ...requestedRectStyleDetails(normalized, "Legend swatch symbol") };
|
|
102
193
|
}
|
|
103
194
|
|
|
104
|
-
export function normalizeRecipe(symbol, kind) {
|
|
195
|
+
export function normalizeRecipe(symbol, kind, { internal = false } = {}) {
|
|
105
196
|
if (symbol === undefined || symbol === "auto") return defaultRecipe(kind);
|
|
106
197
|
if (!isPlainObject(symbol)) {
|
|
107
198
|
throw new TypeError('createLegend.symbol must be "auto" or a plain object.');
|
|
@@ -118,17 +209,17 @@ export function normalizeRecipe(symbol, kind) {
|
|
|
118
209
|
"Legend symbol recipe supports at most one layer per type."
|
|
119
210
|
);
|
|
120
211
|
}
|
|
121
|
-
layers = symbol.layers.map(normalizeLayer);
|
|
212
|
+
layers = symbol.layers.map(layer => normalizeLayer(layer, internal));
|
|
122
213
|
} else if (kind === "series") {
|
|
123
214
|
validateKeys(symbol, ["length", "lineWidth"], "createLegend.symbol");
|
|
124
|
-
layers = [normalizeLayer({ type: "line", ...symbol })];
|
|
215
|
+
layers = [normalizeLayer({ type: "line", ...symbol }, internal)];
|
|
125
216
|
} else {
|
|
126
217
|
validateKeys(
|
|
127
218
|
symbol,
|
|
128
219
|
["width", "height", "stroke", "strokeWidth"],
|
|
129
220
|
"createLegend.symbol"
|
|
130
221
|
);
|
|
131
|
-
layers = [normalizeLayer({ type: "swatch", ...symbol })];
|
|
222
|
+
layers = [normalizeLayer({ type: "swatch", ...symbol }, internal)];
|
|
132
223
|
}
|
|
133
224
|
|
|
134
225
|
const types = layers.map(layer => layer.type);
|
|
@@ -17,8 +17,11 @@ function isCategoricalTarget(layer) {
|
|
|
17
17
|
channel => layer.encoding?.[channel]?.scale !== undefined
|
|
18
18
|
);
|
|
19
19
|
}
|
|
20
|
-
return ["bar", "area", "arc", "rect"].includes(
|
|
21
|
-
layer
|
|
20
|
+
return ["bar", "area", "arc", "rect", "rule", "tick"].includes(
|
|
21
|
+
layer?.mark?.type
|
|
22
|
+
) && ["color", "stroke"].some(
|
|
23
|
+
channel => layer.encoding?.[channel]?.scale !== undefined
|
|
24
|
+
);
|
|
22
25
|
}
|
|
23
26
|
|
|
24
27
|
export function resolveTarget(program, requested) {
|
|
@@ -45,6 +48,7 @@ export function resolveTarget(program, requested) {
|
|
|
45
48
|
export const sameValues = sameOrderedValues;
|
|
46
49
|
|
|
47
50
|
function resolveLegendKind(layer, requestedChannels) {
|
|
51
|
+
if (sameValues(requestedChannels, ["stroke"])) return "stroke";
|
|
48
52
|
if (["bar", "area", "arc", "rect"].includes(layer.mark.type)) return "color";
|
|
49
53
|
if (
|
|
50
54
|
layer.mark.type === "point" &&
|
|
@@ -78,9 +82,9 @@ function resolveOrdinalScales(program, scaleIds) {
|
|
|
78
82
|
}
|
|
79
83
|
|
|
80
84
|
export function resolveDefinition(program, layer, requestedChannels, requestedTitle, order) {
|
|
81
|
-
const channels = requestedChannels ??
|
|
82
|
-
|
|
83
|
-
|
|
85
|
+
const channels = requestedChannels ?? CHANNELS.filter(
|
|
86
|
+
channel => layer.encoding?.[channel]?.scale !== undefined
|
|
87
|
+
);
|
|
84
88
|
const kind = resolveLegendKind(layer, channels);
|
|
85
89
|
if (
|
|
86
90
|
!Array.isArray(channels) ||
|
|
@@ -92,8 +96,8 @@ export function resolveDefinition(program, layer, requestedChannels, requestedTi
|
|
|
92
96
|
"Legend channels must be a non-empty unique color/strokeDash/shape array."
|
|
93
97
|
);
|
|
94
98
|
}
|
|
95
|
-
if (
|
|
96
|
-
throw new Error(
|
|
99
|
+
if (["color", "stroke"].includes(kind) && !sameValues(channels, [kind])) {
|
|
100
|
+
throw new Error(`${kind} legends currently support only the ${kind} channel.`);
|
|
97
101
|
}
|
|
98
102
|
|
|
99
103
|
const encodings = channels.map(channel => {
|
|
@@ -138,7 +142,7 @@ export function resolveCurrentDefinition(program, config) {
|
|
|
138
142
|
if (guide === undefined) {
|
|
139
143
|
throw new Error("Legend rematerialization requires semantic guide state.");
|
|
140
144
|
}
|
|
141
|
-
const channels = config.kind === "series" ? guide.channels : [
|
|
145
|
+
const channels = config.kind === "series" ? guide.channels : [config.kind];
|
|
142
146
|
return resolveDefinition(
|
|
143
147
|
program,
|
|
144
148
|
layer,
|
|
@@ -11,6 +11,18 @@ import {
|
|
|
11
11
|
} from "./layout.js";
|
|
12
12
|
import { resolveCategoricalLegendPlacement } from "../lifecycle.js";
|
|
13
13
|
import { createPointShapeGraphic } from "../../../../grammar/pointShapes.js";
|
|
14
|
+
import {
|
|
15
|
+
materializeRectItem,
|
|
16
|
+
requestedRectStyleDetails
|
|
17
|
+
} from "../../../../grammar/roundedRect.js";
|
|
18
|
+
import { requestedStrokeDetails } from "../../../../grammar/strokeStyle.js";
|
|
19
|
+
import { replaceGraphicItems } from "../../../primitives/editGraphics.js";
|
|
20
|
+
|
|
21
|
+
function applyLayerOpacity(program, id, opacity) {
|
|
22
|
+
return opacity === undefined
|
|
23
|
+
? program
|
|
24
|
+
: program.editGraphics({ target: id, property: "opacity", value: opacity });
|
|
25
|
+
}
|
|
14
26
|
|
|
15
27
|
function makeEditSymbol(type) {
|
|
16
28
|
const suffix = { line: "Lines", point: "Points", swatch: "Swatches" }[type];
|
|
@@ -19,20 +31,23 @@ function makeEditSymbol(type) {
|
|
|
19
31
|
{ op, description: `Rematerialize categorical legend ${type} symbols.` },
|
|
20
32
|
function (args = {}) {
|
|
21
33
|
noOptions(args, op);
|
|
22
|
-
const { config } = activeConfig(this);
|
|
34
|
+
const { config } = activeConfig(this, args.kind);
|
|
23
35
|
const layer = layerFor(config, type);
|
|
24
36
|
const id = symbolGraphic(config, type);
|
|
25
37
|
const dynamicPoint = type === "point" && config.channels.includes("shape");
|
|
26
|
-
const
|
|
38
|
+
const styledSwatch = type === "swatch" && Object.hasOwn(layer, "cornerRadius");
|
|
39
|
+
const expected = dynamicPoint || styledSwatch
|
|
27
40
|
? "collection"
|
|
28
41
|
: { line: "line", point: "circle", swatch: "rect" }[type];
|
|
29
|
-
|
|
42
|
+
const actual = this.graphicSpec.objects[id]?.type;
|
|
43
|
+
const compatibleStyledSwatch = styledSwatch && ["rect", "collection"].includes(actual);
|
|
44
|
+
if (actual !== expected && !compatibleStyledSwatch) {
|
|
30
45
|
throw new Error(`${op} requires existing ${type} symbols.`);
|
|
31
46
|
}
|
|
32
47
|
const layout = resolveLayout(this, config);
|
|
33
48
|
const appearance = resolveAppearance(this, config);
|
|
34
49
|
let next = this;
|
|
35
|
-
if (!dynamicPoint) {
|
|
50
|
+
if (!dynamicPoint && !styledSwatch) {
|
|
36
51
|
next = next.editGraphics({
|
|
37
52
|
target: id,
|
|
38
53
|
property: "length",
|
|
@@ -43,7 +58,7 @@ function makeEditSymbol(type) {
|
|
|
43
58
|
const x1 = layout.symbolX.map(
|
|
44
59
|
value => value + (symbolWidth(config) - layer.length) / 2
|
|
45
60
|
);
|
|
46
|
-
|
|
61
|
+
next = next
|
|
47
62
|
.editGraphics({ target: id, property: "x1", value: x1 })
|
|
48
63
|
.editGraphics({ target: id, property: "y1", value: layout.itemY })
|
|
49
64
|
.editGraphics({
|
|
@@ -52,9 +67,21 @@ function makeEditSymbol(type) {
|
|
|
52
67
|
value: x1.map(value => value + layer.length)
|
|
53
68
|
})
|
|
54
69
|
.editGraphics({ target: id, property: "y2", value: layout.itemY })
|
|
55
|
-
.editGraphics({
|
|
70
|
+
.editGraphics({
|
|
71
|
+
target: id,
|
|
72
|
+
property: "stroke",
|
|
73
|
+
value: layer.stroke ?? (config.channels.includes("stroke")
|
|
74
|
+
? appearance.strokes
|
|
75
|
+
: appearance.colors)
|
|
76
|
+
})
|
|
56
77
|
.editGraphics({ target: id, property: "strokeWidth", value: layer.lineWidth })
|
|
57
78
|
.editGraphics({ target: id, property: "strokeDash", value: appearance.dashes });
|
|
79
|
+
for (const [property, value] of Object.entries(
|
|
80
|
+
requestedStrokeDetails(layer, "Legend line symbol")
|
|
81
|
+
)) {
|
|
82
|
+
next = next.editGraphics({ target: id, property, value });
|
|
83
|
+
}
|
|
84
|
+
return applyLayerOpacity(next, id, layer.opacity);
|
|
58
85
|
}
|
|
59
86
|
if (type === "point") {
|
|
60
87
|
const x = layout.symbolX.map(value => value + symbolWidth(config) / 2);
|
|
@@ -67,8 +94,12 @@ function makeEditSymbol(type) {
|
|
|
67
94
|
y: layout.itemY[index],
|
|
68
95
|
area: Math.PI * layer.size ** 2,
|
|
69
96
|
fill,
|
|
70
|
-
stroke:
|
|
71
|
-
|
|
97
|
+
stroke: config.channels.includes("stroke")
|
|
98
|
+
? appearance.strokes[index]
|
|
99
|
+
: layer.stroke,
|
|
100
|
+
strokeWidth: layer.strokeWidth,
|
|
101
|
+
opacity: layer.opacity,
|
|
102
|
+
...requestedStrokeDetails(layer, "Legend point symbol")
|
|
72
103
|
});
|
|
73
104
|
});
|
|
74
105
|
return next.editGraphics({
|
|
@@ -77,7 +108,7 @@ function makeEditSymbol(type) {
|
|
|
77
108
|
value: items
|
|
78
109
|
});
|
|
79
110
|
}
|
|
80
|
-
|
|
111
|
+
next = next
|
|
81
112
|
.editGraphics({ target: id, property: "x", value: x })
|
|
82
113
|
.editGraphics({ target: id, property: "y", value: layout.itemY })
|
|
83
114
|
.editGraphics({ target: id, property: "radius", value: layer.size })
|
|
@@ -86,24 +117,47 @@ function makeEditSymbol(type) {
|
|
|
86
117
|
property: "fill",
|
|
87
118
|
value: layer.fill ?? appearance.colors
|
|
88
119
|
})
|
|
89
|
-
.editGraphics({
|
|
120
|
+
.editGraphics({
|
|
121
|
+
target: id,
|
|
122
|
+
property: "stroke",
|
|
123
|
+
value: config.channels.includes("stroke")
|
|
124
|
+
? appearance.strokes
|
|
125
|
+
: layer.stroke
|
|
126
|
+
})
|
|
90
127
|
.editGraphics({ target: id, property: "strokeWidth", value: layer.strokeWidth });
|
|
128
|
+
for (const [property, value] of Object.entries(
|
|
129
|
+
requestedStrokeDetails(layer, "Legend point symbol")
|
|
130
|
+
)) {
|
|
131
|
+
next = next.editGraphics({ target: id, property, value });
|
|
132
|
+
}
|
|
133
|
+
return applyLayerOpacity(next, id, layer.opacity);
|
|
91
134
|
}
|
|
92
135
|
const x = layout.symbolX.map(
|
|
93
136
|
value => value + (symbolWidth(config) - layer.width) / 2
|
|
94
137
|
);
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
.
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
.
|
|
138
|
+
const details = requestedRectStyleDetails(layer, "Legend swatch symbol");
|
|
139
|
+
const fills = layer.fill === undefined
|
|
140
|
+
? appearance.colors
|
|
141
|
+
: config.domain.map(() => layer.fill);
|
|
142
|
+
const strokes = config.channels.includes("stroke")
|
|
143
|
+
? appearance.strokes
|
|
144
|
+
: config.domain.map(() => layer.stroke);
|
|
145
|
+
return replaceGraphicItems(
|
|
146
|
+
next,
|
|
147
|
+
id,
|
|
148
|
+
"rect",
|
|
149
|
+
config.domain.map((_, index) => materializeRectItem({
|
|
150
|
+
x: x[index],
|
|
151
|
+
y: layout.itemY[index] - layer.height / 2,
|
|
152
|
+
width: layer.width,
|
|
153
|
+
height: layer.height,
|
|
154
|
+
fill: fills[index],
|
|
155
|
+
stroke: strokes[index],
|
|
156
|
+
strokeWidth: layer.strokeWidth,
|
|
157
|
+
...(layer.opacity === undefined ? {} : { opacity: layer.opacity }),
|
|
158
|
+
...requestedStrokeDetails(details, "Legend swatch symbol")
|
|
159
|
+
}, details.cornerRadius ?? 0))
|
|
160
|
+
);
|
|
107
161
|
}
|
|
108
162
|
);
|
|
109
163
|
}
|
|
@@ -115,13 +169,14 @@ function makeCreateSymbol(type, edit) {
|
|
|
115
169
|
{ op, description: `Create categorical legend ${type} symbols.` },
|
|
116
170
|
function (args = {}) {
|
|
117
171
|
noOptions(args, op);
|
|
118
|
-
const { config } = activeConfig(this);
|
|
119
|
-
layerFor(config, type);
|
|
172
|
+
const { config } = activeConfig(this, args.kind);
|
|
173
|
+
const layer = layerFor(config, type);
|
|
120
174
|
const id = symbolGraphic(config, type);
|
|
121
175
|
if (this.graphicSpec.objects[id] !== undefined) {
|
|
122
176
|
throw new Error(`${op} requires missing ${type} symbols.`);
|
|
123
177
|
}
|
|
124
|
-
const graphicType = type === "point" && config.channels.includes("shape")
|
|
178
|
+
const graphicType = type === "point" && config.channels.includes("shape") ||
|
|
179
|
+
type === "swatch" && (layer.cornerRadius ?? 0) > 0
|
|
125
180
|
? "collection"
|
|
126
181
|
: { line: "line", point: "circle", swatch: "rect" }[type];
|
|
127
182
|
return this
|
|
@@ -136,7 +191,7 @@ function makeCreateSymbol(type, edit) {
|
|
|
136
191
|
? {}
|
|
137
192
|
: { length: config.domain.length })
|
|
138
193
|
})
|
|
139
|
-
[edit]();
|
|
194
|
+
[edit]({ kind: config.kind });
|
|
140
195
|
}
|
|
141
196
|
);
|
|
142
197
|
}
|
|
@@ -161,7 +216,7 @@ export const createLegendSymbols = action(
|
|
|
161
216
|
{ op: "createLegendSymbols", description: "Create layered legend symbols." },
|
|
162
217
|
function (args = {}) {
|
|
163
218
|
noOptions(args, "createLegendSymbols");
|
|
164
|
-
const { config } = activeConfig(this);
|
|
219
|
+
const { config } = activeConfig(this, args.kind);
|
|
165
220
|
let next = this;
|
|
166
221
|
for (const layer of config.symbol.layers) {
|
|
167
222
|
const operation = {
|
|
@@ -169,7 +224,7 @@ export const createLegendSymbols = action(
|
|
|
169
224
|
point: "createLegendSymbolPoints",
|
|
170
225
|
swatch: "createLegendSymbolSwatches"
|
|
171
226
|
}[layer.type];
|
|
172
|
-
next = next[operation]();
|
|
227
|
+
next = next[operation]({ kind: config.kind });
|
|
173
228
|
}
|
|
174
229
|
return next;
|
|
175
230
|
}
|
|
@@ -179,7 +234,7 @@ export const rematerializeLegendSymbols = action(
|
|
|
179
234
|
{ op: "rematerializeLegendSymbols", description: "Rematerialize layered legend symbols." },
|
|
180
235
|
function (args = {}) {
|
|
181
236
|
noOptions(args, "rematerializeLegendSymbols");
|
|
182
|
-
const { config } = activeConfig(this);
|
|
237
|
+
const { config } = activeConfig(this, args.kind);
|
|
183
238
|
let next = this;
|
|
184
239
|
for (const layer of config.symbol.layers) {
|
|
185
240
|
const operation = {
|
|
@@ -187,7 +242,7 @@ export const rematerializeLegendSymbols = action(
|
|
|
187
242
|
point: "rematerializeLegendSymbolPoints",
|
|
188
243
|
swatch: "rematerializeLegendSymbolSwatches"
|
|
189
244
|
}[layer.type];
|
|
190
|
-
next = next[operation]();
|
|
245
|
+
next = next[operation]({ kind: config.kind });
|
|
191
246
|
}
|
|
192
247
|
return next;
|
|
193
248
|
}
|