ggaction 0.0.17 → 0.0.18
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 +46 -0
- package/README.md +2 -2
- package/knowledge/action-card.schema.json +1 -1
- package/knowledge/action-cards.json +1 -1
- package/knowledge/intent-taxonomy.json +1 -1
- package/knowledge/mcp-resources.json +1 -1
- package/knowledge/task-resolver.js +1 -1
- package/package.json +3 -3
- package/src/BasicChartProgram.js +1 -1
- package/src/accessibility.js +3 -2
- package/src/actions/boxPlots/create.js +14 -4
- package/src/actions/boxPlots/edit.js +39 -33
- package/src/actions/boxPlots/materialize.js +7 -3
- package/src/actions/boxPlots/options.js +37 -11
- package/src/actions/canvas/actions.js +70 -95
- package/src/actions/canvas/fitting.js +16 -1
- package/src/actions/canvas/index.js +3 -0
- package/src/actions/canvas/plotBorder.js +28 -0
- package/src/actions/charts/area.js +2 -2
- package/src/actions/charts/bar.js +34 -7
- package/src/actions/charts/ecdf.js +4 -18
- package/src/actions/charts/endpoints.js +4 -18
- package/src/actions/charts/heatmap.js +4 -4
- package/src/actions/charts/index.js +2 -0
- package/src/actions/charts/interval-regression.js +2 -2
- package/src/actions/charts/line.js +1 -1
- package/src/actions/charts/pie.js +3 -47
- package/src/actions/charts/radial.js +34 -12
- package/src/actions/charts/scatter.js +16 -2
- package/src/actions/charts/shared.js +2 -2
- package/src/actions/charts/text.js +35 -0
- package/src/actions/composition/actions.js +6 -2
- package/src/actions/data/box.js +2 -1
- package/src/actions/data/edit.js +1 -1
- package/src/actions/data/regression.js +2 -1
- package/src/actions/data/shared.js +3 -2
- package/src/actions/data/stack.js +3 -1
- package/src/actions/encodings/appearance.js +37 -29
- package/src/actions/encodings/barWidth.js +36 -11
- package/src/actions/encodings/channels.js +0 -1
- package/src/actions/encodings/color/continuous.js +1 -1
- package/src/actions/encodings/color/index.js +1 -1
- package/src/actions/encodings/color/policy.js +10 -3
- package/src/actions/encodings/density/resolve.js +1 -6
- package/src/actions/encodings/position/policies/arc.js +12 -10
- package/src/actions/encodings/position/policies/area.js +3 -3
- package/src/actions/encodings/position/policies/bar.js +15 -6
- package/src/actions/encodings/position/policies/line.js +17 -6
- package/src/actions/encodings/position/resolve.js +3 -2
- package/src/actions/encodings/ranged.js +17 -0
- package/src/actions/encodings/remove.js +3 -2
- package/src/actions/encodings/seriesLayout.js +1 -0
- package/src/actions/encodings/text.js +6 -3
- package/src/actions/errorBands/create.js +2 -2
- package/src/actions/errorBands/edit.js +2 -1
- package/src/actions/facets/actions.js +158 -240
- package/src/actions/gradientPlots/resolve.js +2 -17
- package/src/actions/guides/applicability.js +1 -1
- package/src/actions/guides/axes/axis.js +16 -4
- package/src/actions/guides/axes/edit.js +4 -4
- package/src/actions/guides/axes/labels.js +120 -46
- package/src/actions/guides/axes/lines.js +6 -5
- package/src/actions/guides/axes/parallel/lifecycle.js +1 -1
- package/src/actions/guides/axes/parallel/policy.js +8 -7
- package/src/actions/guides/axes/remove.js +3 -2
- package/src/actions/guides/axes/tickGroups.js +5 -2
- package/src/actions/guides/axes/ticks.js +6 -3
- package/src/actions/guides/axes/titles.js +71 -26
- package/src/actions/guides/basic.js +2 -0
- package/src/actions/guides/facadeLegend.js +10 -2
- package/src/actions/guides/grids/grid.js +7 -1
- package/src/actions/guides/grids/resolve.js +59 -12
- package/src/actions/guides/legends/blocks.js +3 -1
- package/src/actions/guides/legends/categorical/actions.js +70 -17
- package/src/actions/guides/legends/categorical/components.js +12 -8
- package/src/actions/guides/legends/categorical/index.js +6 -0
- package/src/actions/guides/legends/categorical/layout.js +37 -8
- package/src/actions/guides/legends/categorical/options.js +31 -20
- package/src/actions/guides/legends/categorical/resolve.js +2 -2
- package/src/actions/guides/legends/categorical/symbols.js +18 -12
- package/src/actions/guides/legends/categorical/validation.js +1 -6
- package/src/actions/guides/legends/continuous/colorSymbols.js +1 -0
- package/src/actions/guides/legends/continuous/common.js +1 -447
- package/src/actions/guides/legends/continuous/gradient.js +47 -34
- package/src/actions/guides/legends/continuous/interval.js +4 -4
- package/src/actions/guides/legends/continuous/opacity.js +19 -12
- package/src/actions/guides/legends/edit.js +1 -604
- package/src/actions/guides/legends/focused.js +2 -2
- package/src/actions/guides/legends/group.js +1 -0
- package/src/actions/guides/legends/lane.js +1 -516
- package/src/actions/guides/legends/remove.js +3 -0
- package/src/actions/guides/legends/sampling.js +1 -1
- package/src/actions/guides/legends/size.js +51 -43
- package/src/actions/guides/legends/strokeWidth.js +11 -29
- package/src/actions/guides/legends/target.js +3 -3
- package/src/actions/guides/legends/transition.js +21 -7
- package/src/actions/guides/polar/axes/labels.js +4 -2
- package/src/actions/guides/polar/axes/titles.js +7 -5
- package/src/actions/guides/polar/resolve.js +6 -14
- package/src/actions/index.js +16 -16
- package/src/actions/marks/arc/actions.js +3 -10
- package/src/actions/marks/area/actions.js +8 -5
- package/src/actions/marks/area/materialize.js +8 -9
- package/src/actions/marks/bar/create.js +10 -4
- package/src/actions/marks/bar/edit.js +8 -4
- package/src/actions/marks/bar/materialize.js +9 -6
- package/src/actions/marks/line/actions.js +11 -4
- package/src/actions/marks/line/materialize.js +9 -5
- package/src/actions/marks/point/edit.js +1 -1
- package/src/actions/marks/rect/actions.js +3 -3
- package/src/actions/marks/remove.js +2 -0
- package/src/actions/marks/rule/actions.js +7 -4
- package/src/actions/marks/text/actions.js +17 -5
- package/src/actions/marks/text/layout.js +3 -0
- package/src/actions/primitives/semanticValidation/layer.js +4 -0
- package/src/actions/regression/create.js +1 -1
- package/src/actions/regression/edit.js +3 -3
- package/src/actions/regression/resolve.js +2 -6
- package/src/actions/scales/channels.js +9 -4
- package/src/actions/scales/consumers/families.js +16 -5
- package/src/actions/scales/create.js +8 -3
- package/src/actions/scales/definitions.js +1 -425
- package/src/actions/scales/edit.js +2 -2
- package/src/actions/scales/editPolicy.js +9 -3
- package/src/actions/scales/materialize.js +1 -1
- package/src/actions/scales/preview.js +21 -1
- package/src/actions/scales/quantitativeColor.js +3 -3
- package/src/actions/selection/actions.js +1 -13
- package/src/actions/theme/reconcile.js +81 -27
- package/src/actions/theme/state.js +12 -0
- package/src/actions/titles/actions.js +5 -18
- package/src/actions/titles/resolve.js +41 -19
- package/src/actions/violinPlots/edit.js +3 -20
- package/src/core/compositionState.js +4 -1
- package/src/core/font.js +9 -2
- package/src/core/optionRequirements.js +1 -0
- package/src/core/resourceReferences.js +3 -0
- package/src/core/textMetricProfile.js +3 -2
- package/src/core/textMetrics.js +41 -4
- package/src/core/validation.js +26 -0
- package/src/core/vocabulary.js +4 -0
- package/src/grammar/aggregate.js +4 -18
- package/src/grammar/arcs.js +1 -386
- package/src/grammar/areaPaint.js +10 -0
- package/src/grammar/areaSeries.js +18 -10
- package/src/grammar/bars/geometry.js +30 -3
- package/src/grammar/bars/policy.js +12 -1
- package/src/grammar/bin.js +2 -12
- package/src/grammar/bin2d.js +1 -6
- package/src/grammar/boxPlot.js +56 -9
- package/src/grammar/categoryOrder.js +1 -6
- package/src/grammar/complete.js +15 -19
- package/src/grammar/computed.js +92 -25
- package/src/grammar/curveCommands.js +29 -16
- package/src/grammar/datasetSchema.js +3 -7
- package/src/grammar/displayLabels.js +28 -0
- package/src/grammar/ecdf.js +2 -9
- package/src/grammar/facets/dependencies.js +6 -2
- package/src/grammar/facets/guides.js +5 -0
- package/src/grammar/facets/index.js +2 -5
- package/src/grammar/fold.js +2 -12
- package/src/grammar/gradientProfile.js +1 -6
- package/src/grammar/impute.js +21 -48
- package/src/grammar/interval.js +2 -9
- package/src/grammar/legendGradient.js +18 -0
- package/src/grammar/legendGroups.js +1 -0
- package/src/grammar/legendTitleStyle.js +22 -0
- package/src/grammar/lineSeries.js +32 -17
- package/src/grammar/markLabels.js +5 -2
- package/src/grammar/normalize.js +4 -54
- package/src/grammar/numeric.js +1 -290
- package/src/grammar/parallelCoordinates.js +1 -6
- package/src/grammar/parseTime.js +41 -0
- package/src/grammar/pathCommands.js +11 -4
- package/src/grammar/polar.js +1 -184
- package/src/grammar/positionCompatibility.js +3 -3
- package/src/grammar/regression/derive.js +7 -1
- package/src/grammar/regression/models.js +83 -55
- package/src/grammar/regression/parameters.js +14 -3
- package/src/grammar/roundedRect.js +42 -54
- package/src/grammar/scales/appearance.js +1 -206
- package/src/grammar/scales/color.js +1 -382
- package/src/grammar/scales/colorConsumers.js +5 -2
- package/src/grammar/scales/definition.js +1 -197
- package/src/grammar/scales/discretized.js +1 -195
- package/src/grammar/scales/size.js +1 -456
- package/src/grammar/scales/temporal.js +2 -13
- package/src/grammar/scales/transformed.js +1 -383
- package/src/grammar/scales/types.js +1 -1
- package/src/grammar/scales/validation.js +1 -66
- package/src/grammar/schemas/concreteGraphic.js +11 -0
- package/src/grammar/schemas/graphic.js +3 -0
- package/src/grammar/schemas/semanticPath.js +2 -1
- package/src/grammar/stack.js +2 -10
- package/src/grammar/statisticalReference.js +1 -6
- package/src/grammar/summary.js +3 -26
- package/src/grammar/text.js +19 -0
- package/src/grammar/timeUnit.js +16 -7
- package/src/grammar/transformKeys.js +47 -0
- package/src/grammar/transforms.js +4 -1
- package/src/grammar/valueFormat.js +17 -5
- package/src/grammar/weightedStatistics.js +1 -6
- package/src/grammar/window.js +4 -58
- package/src/inspectionDescriptors.js +1 -1
- package/src/layout/canvas.js +1 -110
- package/src/layout/facets.js +1 -244
- package/src/layout/labels.js +1 -0
- package/src/layout/legend.js +13 -0
- package/src/layout/legendGroup.js +1 -0
- package/src/layout/legendItems.js +59 -15
- package/src/layout/legendLane.js +1 -441
- package/src/layout/legendText.js +1 -0
- package/src/layout/title.js +35 -13
- package/src/materialization/bars/aggregate.js +11 -5
- package/src/materialization/bars/histogram.js +4 -1
- package/src/materialization/bars/ranged.js +19 -6
- package/src/materialization/bars/resolve.js +22 -1
- package/src/materialization/coordinateBounds.js +8 -0
- package/src/materialization/dependencies.js +4 -44
- package/src/materialization/facetGuides/layout.js +1 -0
- package/src/materialization/facetGuides/placement.js +19 -8
- package/src/materialization/facetGuides/preparation.js +15 -7
- package/src/materialization/facetHeaders.js +1 -347
- package/src/materialization/facets.js +1 -185
- package/src/materialization/graphicHierarchy.js +1 -1
- package/src/materialization/guides/layout.js +1 -57
- package/src/materialization/guides/resources.js +1 -123
- package/src/materialization/layout.js +53 -0
- package/src/materialization/legendGroups.js +1 -0
- package/src/materialization/legendText.js +1 -0
- package/src/materialization/legends.js +1 -1
- package/src/materialization/marks/capabilities.js +5 -6
- package/src/materialization/rect.js +1 -1
- package/src/materialization/scaleGuideDependencies.js +1 -1
- package/src/materialization/scales/map.js +3 -0
- package/src/materialization/scales/policies/arc.js +8 -7
- package/src/materialization/scales/policies/bar.js +19 -7
- package/src/materialization/scales/policies/series.js +2 -1
- package/src/materialization/scales/resolve.js +5 -3
- package/src/materialization/selection/items/arc.js +1 -1
- package/src/materialization/selection/items/bar.js +7 -6
- package/src/materialization/selection/items/path.js +7 -6
- package/src/materialization/selection/items/rect.js +2 -2
- package/src/materialization/text.js +13 -4
- package/src/materialization/textLines.js +15 -0
- package/src/persistence/codec.js +37 -6
- package/src/persistence/validation.js +47 -0
- package/src/persistence.js +18 -5
- package/src/renderers/canvas/circle.js +6 -3
- package/src/renderers/canvas/fill.js +7 -1
- package/src/renderers/canvas/index.js +2 -1
- package/src/renderers/canvas/line.js +1 -0
- package/src/renderers/canvas/path.js +1 -1
- package/src/renderers/canvas/rect.js +11 -8
- package/src/renderers/canvas/stroke.js +2 -1
- package/src/renderers/canvas/text.js +5 -3
- package/src/renderers/svg.js +4 -1
- package/src/selectors/layers.js +19 -0
- package/src/theme/defaults.js +8 -2
- package/src/version.js +1 -1
- package/types/basic.d.ts +1 -0
- package/types/index.d.ts +7 -0
- package/types/program.d.ts +347 -85
|
@@ -1,604 +1 @@
|
|
|
1
|
-
import { withGuideLayoutValidation } from "../../../materialization/guides/layout.js";
|
|
2
|
-
import { resolveLegendStepConfig } from "./creation.js";
|
|
3
|
-
import { normalizeLegendOrder } from "../../../grammar/categoryOrder.js";
|
|
4
|
-
import { resolveDefinition } from "./categorical/resolve.js";
|
|
5
|
-
import { action } from "../../../core/action.js";
|
|
6
|
-
import {
|
|
7
|
-
validateOptionObject
|
|
8
|
-
} from "../../../core/validation.js";
|
|
9
|
-
import { normalizeOptions } from "./categorical/options.js";
|
|
10
|
-
import { resolveLegendSymbol } from "./categorical/recipes.js";
|
|
11
|
-
import { resolveLayout } from "./categorical/layout.js";
|
|
12
|
-
import { resolveLegendCreationPlan } from "./categorical/actions.js";
|
|
13
|
-
import { categoricalSymbolIds, reconcileCategoricalSymbols, createCategoricalLegendFromConfig, resolveCategoricalLegendRevision, removeLegendKinds } from "./lifecycle.js";
|
|
14
|
-
import { createGradientLegendFromConfig } from "./continuous/gradient.js";
|
|
15
|
-
import {
|
|
16
|
-
normalizeItemLegendLayout,
|
|
17
|
-
normalizeLegendBorder,
|
|
18
|
-
normalizeLegendTextOptions,
|
|
19
|
-
normalizeLegendTitleOptions,
|
|
20
|
-
normalizeContinuousLegend,
|
|
21
|
-
validatePositive
|
|
22
|
-
} from "./continuous/common.js";
|
|
23
|
-
import { normalizeOpacitySymbol, createOpacityLegendFromConfig } from "./continuous/opacity.js";
|
|
24
|
-
import { normalizeIntervalLegend, resolveIntervalConfig, resolveIntervalLayout, createIntervalLegendFromConfig } from "./continuous/interval.js";
|
|
25
|
-
import {
|
|
26
|
-
createStrokeGradientLegendFromConfig,
|
|
27
|
-
createStrokeIntervalLegendFromConfig,
|
|
28
|
-
resolveStrokeIntervalConfig,
|
|
29
|
-
resolveStrokeIntervalLayout
|
|
30
|
-
} from "./continuous/stroke.js";
|
|
31
|
-
import { findLayer } from "../../../selectors/layers.js";
|
|
32
|
-
import { resolveLegendGraphicPlacement } from
|
|
33
|
-
"../../../materialization/graphicHierarchy.js";
|
|
34
|
-
import { resolveLegendTarget, validateLegendChannels } from "./target.js";
|
|
35
|
-
import { isDiscreteSizeScaleType } from "../../../grammar/scales/index.js";
|
|
36
|
-
import { SIZE_LEGEND_LABELS, SIZE_LEGEND_TITLE_STYLE, resolveSizeLegendLayout, createSizeLegendFromConfig } from "./size.js";
|
|
37
|
-
import {
|
|
38
|
-
STROKE_WIDTH_LEGEND_LABELS,
|
|
39
|
-
STROKE_WIDTH_LEGEND_TITLE_STYLE,
|
|
40
|
-
resolveStrokeWidthLegendLayout,
|
|
41
|
-
createStrokeWidthLegendFromConfig
|
|
42
|
-
} from "./strokeWidth.js";
|
|
43
|
-
import { normalizeLegendSampling } from "./sampling.js";
|
|
44
|
-
import { planLegendBlockTransitions } from "./transition.js";
|
|
45
|
-
|
|
46
|
-
const OPTIONS = Object.freeze([
|
|
47
|
-
"target", "channels", "position", "layout", "align", "direction", "columns", "offset",
|
|
48
|
-
"titlePosition", "title", "symbol", "labels", "titleStyle", "itemGap",
|
|
49
|
-
"border", "count", "values", "gradient", "order"
|
|
50
|
-
]);
|
|
51
|
-
|
|
52
|
-
function mergeObject(previous, patch) {
|
|
53
|
-
return patch === undefined
|
|
54
|
-
? previous
|
|
55
|
-
: { ...previous, ...patch };
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function mergeBorder(previous, patch) {
|
|
59
|
-
if (patch === undefined) return previous;
|
|
60
|
-
if (patch === false || patch === true) return patch;
|
|
61
|
-
return previous === false ? patch : { ...previous, ...patch };
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function reconcileGraphic(program, id, shouldExist, definition) {
|
|
65
|
-
const exists = program.graphicSpec.objects[id] !== undefined;
|
|
66
|
-
if (exists && !shouldExist) return program.editGraphics({ target: id, remove: true });
|
|
67
|
-
if (!exists && shouldExist) {
|
|
68
|
-
return program.createGraphics({
|
|
69
|
-
id,
|
|
70
|
-
...definition,
|
|
71
|
-
...resolveLegendGraphicPlacement(program, {
|
|
72
|
-
...(definition.before === undefined ? {} : { before: definition.before }),
|
|
73
|
-
...(definition.after === undefined ? {} : { after: definition.after })
|
|
74
|
-
})
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
return program;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function resolveContinuousEdit(program, kind, previous, args) {
|
|
81
|
-
const gradient = ["gradient", "strokeGradient"].includes(kind);
|
|
82
|
-
const allowed = gradient
|
|
83
|
-
? ["target", "position", "align", "offset", "title", "labels",
|
|
84
|
-
"titleStyle", "titlePosition", "border", "count", "gradient"]
|
|
85
|
-
: ["target", "position", "align", "offset", "title", "symbol", "labels",
|
|
86
|
-
"titleStyle", "titlePosition", "itemGap", "border", "count", "values"];
|
|
87
|
-
for (const key of Object.keys(args)) {
|
|
88
|
-
if (!allowed.includes(key)) throw new Error(`${kind} legend does not accept ${key}.`);
|
|
89
|
-
}
|
|
90
|
-
const titleMode = args.title;
|
|
91
|
-
const inferredTitle = titleMode === "auto"
|
|
92
|
-
? true
|
|
93
|
-
: typeof titleMode === "string" ? false : previous.inferredTitle;
|
|
94
|
-
const titleVisible = titleMode === false ? false
|
|
95
|
-
: titleMode === undefined ? previous.titleVisible !== false : true;
|
|
96
|
-
const layer = findLayer(program, previous.target);
|
|
97
|
-
const channel = kind === "gradient" ? "color"
|
|
98
|
-
: kind === "strokeGradient" ? "stroke" : "opacity";
|
|
99
|
-
const inferred = layer?.encoding?.[channel]?.field;
|
|
100
|
-
const title = titleMode === "auto"
|
|
101
|
-
? inferred
|
|
102
|
-
: typeof titleMode === "string" ? titleMode : previous.title;
|
|
103
|
-
const titlePosition = args.titlePosition ?? previous.titlePosition;
|
|
104
|
-
const enteringInline = kind === "opacity" &&
|
|
105
|
-
titlePosition === "left" && previous.titlePosition !== "left";
|
|
106
|
-
const labels = mergeObject(previous.labels, args.labels);
|
|
107
|
-
const sampling = gradient ? undefined : normalizeLegendSampling(args, {
|
|
108
|
-
previous,
|
|
109
|
-
operation: "edit",
|
|
110
|
-
label: "Opacity legend"
|
|
111
|
-
});
|
|
112
|
-
const normalized = normalizeContinuousLegend({
|
|
113
|
-
target: previous.target,
|
|
114
|
-
position: args.position ?? previous.position,
|
|
115
|
-
align: args.align ?? previous.align,
|
|
116
|
-
offset: args.offset ?? previous.offset,
|
|
117
|
-
...(gradient ? { count: args.count ?? previous.count } : {}),
|
|
118
|
-
titlePosition,
|
|
119
|
-
title,
|
|
120
|
-
labels: enteringInline && args.labels?.offset === undefined
|
|
121
|
-
? { ...labels, offset: 8 }
|
|
122
|
-
: labels,
|
|
123
|
-
titleStyle: mergeObject(previous.titleStyle, args.titleStyle),
|
|
124
|
-
border: mergeBorder(previous.border, args.border),
|
|
125
|
-
...(kind === "opacity" ? {
|
|
126
|
-
itemGap: args.itemGap ?? (enteringInline ? 20 : previous.itemGap),
|
|
127
|
-
symbol: args.symbol ?? previous.symbol
|
|
128
|
-
} : {})
|
|
129
|
-
}, gradient ? "gradient" : kind);
|
|
130
|
-
const { count: _legacyCount, ...canonicalPrevious } = previous;
|
|
131
|
-
const config = {
|
|
132
|
-
...(gradient ? previous : canonicalPrevious),
|
|
133
|
-
...normalized,
|
|
134
|
-
...(gradient ? {} : { sampling }),
|
|
135
|
-
inferredTitle,
|
|
136
|
-
titleVisible
|
|
137
|
-
};
|
|
138
|
-
if (gradient) {
|
|
139
|
-
const gradient = mergeObject(previous.gradient, args.gradient);
|
|
140
|
-
validatePositive(gradient.length, "Gradient length");
|
|
141
|
-
validatePositive(gradient.thickness, "Gradient thickness");
|
|
142
|
-
config.gradient = gradient;
|
|
143
|
-
} else {
|
|
144
|
-
config.symbol = normalizeOpacitySymbol(args.symbol ?? previous.symbol);
|
|
145
|
-
}
|
|
146
|
-
return { config, titleMode, title, titleVisible };
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function editContinuous(program, kind, previous, args) {
|
|
150
|
-
const { config, titleMode, title, titleVisible } = resolveContinuousEdit(program, kind, previous, args);
|
|
151
|
-
const prefix = kind === "gradient" ? "colorGradient"
|
|
152
|
-
: kind === "strokeGradient" ? "strokeGradient" : "opacityLegend";
|
|
153
|
-
const semanticKind = kind === "gradient" ? "color"
|
|
154
|
-
: kind === "strokeGradient" ? "stroke" : "opacity";
|
|
155
|
-
let next = program;
|
|
156
|
-
if (titleMode === "auto" || typeof titleMode === "string") {
|
|
157
|
-
next = next.editSemantic({
|
|
158
|
-
property: `guide.legend.${semanticKind}.title`,
|
|
159
|
-
value: title
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
next = next._withLegendConfig(kind, config);
|
|
163
|
-
next = reconcileGraphic(next, `${prefix}Background`, config.border !== false, {
|
|
164
|
-
type: "rect",
|
|
165
|
-
before: ["gradient", "strokeGradient"].includes(kind)
|
|
166
|
-
? `${prefix}Strips`
|
|
167
|
-
: "opacityLegendSymbols"
|
|
168
|
-
});
|
|
169
|
-
next = reconcileGraphic(next, `${prefix}Title`, titleVisible, { type: "text" });
|
|
170
|
-
return next.rematerializeLegend();
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
function resolveIntervalEdit(program, previous, args, channel = "color") {
|
|
174
|
-
for (const key of Object.keys(args)) {
|
|
175
|
-
if (![
|
|
176
|
-
"target", "position", "align", "direction", "offset", "title",
|
|
177
|
-
"symbol", "labels", "titleStyle", "itemGap", "border", "layout", "columns", "titlePosition"
|
|
178
|
-
].includes(key)) {
|
|
179
|
-
throw new Error(`interval legend does not accept ${key}.`);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
const titleMode = args.title;
|
|
183
|
-
const layer = findLayer(program, previous.target);
|
|
184
|
-
const inferredTitle = titleMode === "auto"
|
|
185
|
-
? true
|
|
186
|
-
: typeof titleMode === "string" ? false : previous.inferredTitle;
|
|
187
|
-
const titleVisible = titleMode === false
|
|
188
|
-
? false
|
|
189
|
-
: titleMode === undefined ? previous.titleVisible !== false : true;
|
|
190
|
-
const title = titleMode === "auto"
|
|
191
|
-
? layer?.encoding?.[channel]?.field
|
|
192
|
-
: typeof titleMode === "string" ? titleMode : previous.title;
|
|
193
|
-
const normalized = normalizeIntervalLegend({
|
|
194
|
-
target: previous.target,
|
|
195
|
-
position: args.position ?? previous.position,
|
|
196
|
-
align: args.align ?? previous.align,
|
|
197
|
-
direction: args.direction ?? (args.position !== undefined && args.position !== previous.position
|
|
198
|
-
? undefined : previous.direction),
|
|
199
|
-
layout: args.layout === undefined ? previous.layout : args.layout,
|
|
200
|
-
columns: args.columns ?? previous.columns,
|
|
201
|
-
titlePosition: args.titlePosition ?? previous.titlePosition,
|
|
202
|
-
offset: args.offset ?? previous.offset,
|
|
203
|
-
title,
|
|
204
|
-
symbol: mergeObject(previous.symbol, args.symbol),
|
|
205
|
-
labels: mergeObject(previous.labels, args.labels),
|
|
206
|
-
titleStyle: mergeObject(previous.titleStyle, args.titleStyle),
|
|
207
|
-
itemGap: args.itemGap ?? previous.itemGap,
|
|
208
|
-
border: mergeBorder(previous.border, args.border)
|
|
209
|
-
});
|
|
210
|
-
const config = {
|
|
211
|
-
...previous,
|
|
212
|
-
...normalized,
|
|
213
|
-
inferredTitle,
|
|
214
|
-
titleVisible,
|
|
215
|
-
...(channel === "stroke" && args.symbol?.strokeWidth !== undefined
|
|
216
|
-
? { inferredStrokeWidth: false }
|
|
217
|
-
: {})
|
|
218
|
-
};
|
|
219
|
-
const resolved = channel === "stroke"
|
|
220
|
-
? resolveStrokeIntervalConfig(program, config)
|
|
221
|
-
: resolveIntervalConfig(program, config);
|
|
222
|
-
(channel === "stroke" ? resolveStrokeIntervalLayout : resolveIntervalLayout)(
|
|
223
|
-
program,
|
|
224
|
-
resolved.config,
|
|
225
|
-
resolved.scale
|
|
226
|
-
);
|
|
227
|
-
return { normalized, config, titleMode, title, titleVisible };
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
function editInterval(program, previous, args, channel = "color") {
|
|
231
|
-
const kind = channel === "stroke" ? "strokeInterval" : "interval";
|
|
232
|
-
const prefix = channel === "stroke" ? "strokeInterval" : "colorLegend";
|
|
233
|
-
const { normalized, config, titleMode, title, titleVisible } = resolveIntervalEdit(program, previous, args, channel);
|
|
234
|
-
let next = program._withLegendConfig(kind, config);
|
|
235
|
-
if (titleMode === "auto" || typeof titleMode === "string") {
|
|
236
|
-
next = next.editSemantic({
|
|
237
|
-
property: `guide.legend.${channel}.title`,
|
|
238
|
-
value: title
|
|
239
|
-
});
|
|
240
|
-
}
|
|
241
|
-
next = reconcileGraphic(next, `${prefix}Title`, titleVisible, {
|
|
242
|
-
type: "text"
|
|
243
|
-
});
|
|
244
|
-
next = reconcileGraphic(
|
|
245
|
-
next,
|
|
246
|
-
`${prefix}Background`,
|
|
247
|
-
normalized.border !== false,
|
|
248
|
-
{ type: "rect", before: `${prefix}Symbols` }
|
|
249
|
-
);
|
|
250
|
-
return next.rematerializeLegend();
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
function resolveSampledLegendEdit(program, kind, previous, args) {
|
|
254
|
-
const size = kind === "size";
|
|
255
|
-
const label = size ? "size" : "stroke-width";
|
|
256
|
-
const allowed = ["target", "title", "count", "values", "labels", "titleStyle",
|
|
257
|
-
"position", "layout", "align", "direction", "columns", "titlePosition", "offset", "itemGap", "border"];
|
|
258
|
-
for (const key of Object.keys(args)) {
|
|
259
|
-
if (!allowed.includes(key)) {
|
|
260
|
-
throw new Error(`${label} legend does not accept ${key}.`);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
const discrete = size &&
|
|
264
|
-
isDiscreteSizeScaleType(program.resolvedScales[previous.scale]?.type);
|
|
265
|
-
if (discrete && (args.count !== undefined || args.values !== undefined)) {
|
|
266
|
-
throw new Error("Discrete size legends do not support count or exact values.");
|
|
267
|
-
}
|
|
268
|
-
const sampling = discrete ? undefined : normalizeLegendSampling(args, {
|
|
269
|
-
previous,
|
|
270
|
-
operation: "edit",
|
|
271
|
-
label: `${size ? "Size" : "Stroke-width"} legend`
|
|
272
|
-
});
|
|
273
|
-
const layer = findLayer(program, previous.target);
|
|
274
|
-
const titleMode = args.title;
|
|
275
|
-
const inferredTitle = titleMode === "auto"
|
|
276
|
-
? true
|
|
277
|
-
: typeof titleMode === "string" ? false : previous.inferredTitle;
|
|
278
|
-
const titleVisible = titleMode === false
|
|
279
|
-
? false
|
|
280
|
-
: titleMode === undefined ? previous.titleVisible !== false : true;
|
|
281
|
-
const title = titleMode === "auto"
|
|
282
|
-
? layer?.encoding?.[kind]?.field
|
|
283
|
-
: typeof titleMode === "string" ? titleMode : previous.title;
|
|
284
|
-
const { count: _legacyCount, ...continuousPrevious } = previous;
|
|
285
|
-
const canonicalPrevious = discrete ? previous : continuousPrevious;
|
|
286
|
-
const config = {
|
|
287
|
-
...canonicalPrevious,
|
|
288
|
-
title,
|
|
289
|
-
inferredTitle,
|
|
290
|
-
titleVisible,
|
|
291
|
-
...(discrete ? {} : { sampling }),
|
|
292
|
-
...(size ? { inheritAppearance: false } : {}),
|
|
293
|
-
labels: normalizeLegendTextOptions(
|
|
294
|
-
args.labels,
|
|
295
|
-
"editLegend.labels",
|
|
296
|
-
previous.labels ?? (size ? SIZE_LEGEND_LABELS : STROKE_WIDTH_LEGEND_LABELS)
|
|
297
|
-
),
|
|
298
|
-
titleStyle: normalizeLegendTitleOptions(
|
|
299
|
-
args.titleStyle,
|
|
300
|
-
"editLegend.titleStyle",
|
|
301
|
-
previous.titleStyle ?? (size ? SIZE_LEGEND_TITLE_STYLE : STROKE_WIDTH_LEGEND_TITLE_STYLE)
|
|
302
|
-
)
|
|
303
|
-
};
|
|
304
|
-
Object.assign(config, normalizeItemLegendLayout({ ...previous, ...args,
|
|
305
|
-
direction: args.direction ?? (args.position !== undefined && args.position !== previous.position
|
|
306
|
-
? undefined : previous.direction) }));
|
|
307
|
-
config.border = normalizeLegendBorder(mergeBorder(previous.border, args.border));
|
|
308
|
-
(size ? resolveSizeLegendLayout : resolveStrokeWidthLegendLayout)(program, config);
|
|
309
|
-
return { config, titleMode, title, titleVisible };
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
function editSampledLegend(program, kind, previous, args) {
|
|
313
|
-
const { config, titleMode, title, titleVisible } = resolveSampledLegendEdit(program, kind, previous, args);
|
|
314
|
-
const prefix = kind === "size" ? "sizeLegend" : "strokeWidthLegend";
|
|
315
|
-
let next = program;
|
|
316
|
-
if (titleMode === "auto" || typeof titleMode === "string") {
|
|
317
|
-
next = next.editSemantic({
|
|
318
|
-
property: `guide.legend.${kind}.title`,
|
|
319
|
-
value: title
|
|
320
|
-
});
|
|
321
|
-
}
|
|
322
|
-
next = next._withLegendConfig(kind, config);
|
|
323
|
-
next = reconcileGraphic(next, `${prefix}Background`, config.border !== false, {
|
|
324
|
-
type: "rect", before: `${prefix}Symbols`
|
|
325
|
-
});
|
|
326
|
-
next = reconcileGraphic(next, `${prefix}Title`, titleVisible, {
|
|
327
|
-
type: "text", after: `${prefix}Labels`
|
|
328
|
-
});
|
|
329
|
-
return next.rematerializeLegend();
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
function resolveCompanionSizeEdit(program, previous, size, args) {
|
|
333
|
-
if (size === undefined) return undefined;
|
|
334
|
-
const discrete = isDiscreteSizeScaleType(
|
|
335
|
-
program.resolvedScales[size.scale]?.type
|
|
336
|
-
);
|
|
337
|
-
if (discrete && (args.count !== undefined || args.values !== undefined)) {
|
|
338
|
-
throw new Error("Discrete size legends do not support count or exact values.");
|
|
339
|
-
}
|
|
340
|
-
const sampling = discrete ? undefined : normalizeLegendSampling(args, {
|
|
341
|
-
previous: size,
|
|
342
|
-
operation: "edit",
|
|
343
|
-
label: "Size legend"
|
|
344
|
-
});
|
|
345
|
-
const { count: _legacyCount, ...continuousSize } = size;
|
|
346
|
-
const canonicalSize = discrete ? size : continuousSize;
|
|
347
|
-
const config = {
|
|
348
|
-
...canonicalSize,
|
|
349
|
-
...(discrete ? {} : { sampling })
|
|
350
|
-
};
|
|
351
|
-
if (args.labels === undefined && args.titleStyle === undefined) return config;
|
|
352
|
-
const labels = size.inheritAppearance
|
|
353
|
-
? { ...previous.labels, offset: size.labels.offset } : size.labels ?? SIZE_LEGEND_LABELS;
|
|
354
|
-
const titleStyle = size.inheritAppearance
|
|
355
|
-
? previous.titleStyle : size.titleStyle ?? SIZE_LEGEND_TITLE_STYLE;
|
|
356
|
-
return { ...config, inheritAppearance: false,
|
|
357
|
-
labels: normalizeLegendTextOptions(args.labels, "editLegend.labels", labels),
|
|
358
|
-
titleStyle: normalizeLegendTitleOptions(args.titleStyle, "editLegend.titleStyle", titleStyle) };
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
function resolveCategoricalEdit(program, kind, previous, size, args, storedOrder = program.semanticSpec.guides.legend?.[kind]?.order) {
|
|
362
|
-
if (args.values !== undefined) {
|
|
363
|
-
throw new Error(
|
|
364
|
-
"Combined and categorical legends require a channel block selector for exact values."
|
|
365
|
-
);
|
|
366
|
-
}
|
|
367
|
-
if (args.gradient !== undefined) {
|
|
368
|
-
throw new Error("Categorical legends do not accept gradient.");
|
|
369
|
-
}
|
|
370
|
-
if (args.count !== undefined && size === undefined) {
|
|
371
|
-
throw new Error("Legend count requires an existing size legend.");
|
|
372
|
-
}
|
|
373
|
-
const titleMode = args.title;
|
|
374
|
-
const inferredTitle = titleMode === "auto"
|
|
375
|
-
? true
|
|
376
|
-
: typeof titleMode === "string" ? false : previous.inferredTitle;
|
|
377
|
-
const titleVisible = titleMode === false ? false
|
|
378
|
-
: titleMode === undefined ? previous.titleVisible !== false : true;
|
|
379
|
-
const title = titleMode === "auto"
|
|
380
|
-
? previous.field
|
|
381
|
-
: typeof titleMode === "string" ? titleMode : previous.title;
|
|
382
|
-
const mergedLabels = mergeObject(previous.labels, args.labels);
|
|
383
|
-
if (mergedLabels.format !== undefined && mergedLabels.format !== "auto" && size === undefined) {
|
|
384
|
-
throw new Error("Categorical legend labels do not accept format.");
|
|
385
|
-
}
|
|
386
|
-
const { format: _sizeFormat, ...categoricalLabels } = mergedLabels;
|
|
387
|
-
const inferredSymbol = args.symbol === undefined
|
|
388
|
-
? previous.inferredSymbol
|
|
389
|
-
: args.symbol === "auto";
|
|
390
|
-
const normalized = normalizeOptions({
|
|
391
|
-
target: previous.target,
|
|
392
|
-
channels: previous.channels,
|
|
393
|
-
layout: args.layout === undefined ? previous.layout : args.layout,
|
|
394
|
-
position: args.position ?? previous.position,
|
|
395
|
-
align: args.align ?? previous.align,
|
|
396
|
-
direction: args.direction ?? (args.position !== undefined && args.position !== previous.position
|
|
397
|
-
? undefined : previous.direction),
|
|
398
|
-
...(args.columns === undefined && previous.columns === undefined
|
|
399
|
-
? {} : { columns: args.columns ?? previous.columns }),
|
|
400
|
-
offset: args.offset ?? previous.offset,
|
|
401
|
-
titlePosition: args.titlePosition ?? previous.titlePosition,
|
|
402
|
-
title,
|
|
403
|
-
symbol: args.symbol === undefined ? previous.symbol
|
|
404
|
-
: resolveLegendSymbol(
|
|
405
|
-
program,
|
|
406
|
-
findLayer(program, previous.target),
|
|
407
|
-
previous.channels,
|
|
408
|
-
args.symbol,
|
|
409
|
-
kind
|
|
410
|
-
),
|
|
411
|
-
labels: categoricalLabels,
|
|
412
|
-
titleStyle: mergeObject(previous.titleStyle, args.titleStyle),
|
|
413
|
-
itemGap: args.itemGap ?? previous.itemGap,
|
|
414
|
-
border: mergeBorder(previous.border, args.border)
|
|
415
|
-
}, kind, { internalSymbol: inferredSymbol });
|
|
416
|
-
const config = {
|
|
417
|
-
...previous,
|
|
418
|
-
...normalized,
|
|
419
|
-
inferredTitle,
|
|
420
|
-
inferredSymbol,
|
|
421
|
-
titleVisible
|
|
422
|
-
};
|
|
423
|
-
const order = args.order === undefined
|
|
424
|
-
? storedOrder
|
|
425
|
-
: normalizeLegendOrder(args.order);
|
|
426
|
-
// Resolve the final domain before changing semantic/config/graphic state.
|
|
427
|
-
resolveDefinition(program, findLayer(program, previous.target), previous.channels, title, order);
|
|
428
|
-
return { config, order, titleMode, title, titleVisible, sizeConfig: resolveCompanionSizeEdit(program, previous, size, args) };
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
function editCategorical(program, kind, previous, size, args) {
|
|
432
|
-
const { config, order, titleMode, title, titleVisible, sizeConfig } = resolveCategoricalEdit(program, kind, previous, size, args);
|
|
433
|
-
const newSymbols = categoricalSymbolIds(config);
|
|
434
|
-
let next = program;
|
|
435
|
-
if (titleMode === "auto" || typeof titleMode === "string") {
|
|
436
|
-
next = next.editSemantic({
|
|
437
|
-
property: `guide.legend.${kind}.title`,
|
|
438
|
-
value: title
|
|
439
|
-
});
|
|
440
|
-
}
|
|
441
|
-
if (args.order !== undefined) {
|
|
442
|
-
if (order === "scale") {
|
|
443
|
-
if (program.semanticSpec.guides.legend?.[kind]?.order !== undefined) {
|
|
444
|
-
next = next.editSemantic({ property: `guide.legend.${kind}.order`, remove: true });
|
|
445
|
-
}
|
|
446
|
-
} else {
|
|
447
|
-
next = next.editSemantic({ property: `guide.legend.${kind}.order`, value: order });
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
next = next._withLegendConfig(kind, config);
|
|
451
|
-
next = reconcileCategoricalSymbols(next, previous, config);
|
|
452
|
-
next = reconcileGraphic(next, `${kind}LegendBackground`,
|
|
453
|
-
config.border !== false, {
|
|
454
|
-
type: "rect",
|
|
455
|
-
before: [...newSymbols][0]
|
|
456
|
-
});
|
|
457
|
-
const titleId = `${kind}LegendTitle`;
|
|
458
|
-
next = reconcileGraphic(next, titleId, titleVisible, {
|
|
459
|
-
type: "text",
|
|
460
|
-
...(next.graphicSpec.objects.sizeLegendSymbols === undefined
|
|
461
|
-
? {} : { before: next.graphicSpec.objects.sizeLegendBackground === undefined
|
|
462
|
-
? "sizeLegendSymbols" : "sizeLegendBackground" })
|
|
463
|
-
});
|
|
464
|
-
if (size !== undefined) {
|
|
465
|
-
next = next._withLegendConfig("size", sizeConfig);
|
|
466
|
-
}
|
|
467
|
-
return next.rematerializeLegend();
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
function editLegendContent(program, target, args) {
|
|
472
|
-
validateLegendChannels(args.channels, "editLegend");
|
|
473
|
-
const { channels, target: _target, ...patch } = args;
|
|
474
|
-
const previous = program.guideConfigs.legend ?? {};
|
|
475
|
-
const removed = Object.keys(previous).filter(kind => previous[kind].target === target);
|
|
476
|
-
let view = removed.reduce((next, kind) => next._withoutMaterializationConfig(["guides", "legend", kind]), program);
|
|
477
|
-
const creation = resolveLegendCreationPlan(view, { target, channels });
|
|
478
|
-
const oldCategorical = removed.find(kind =>
|
|
479
|
-
["series", "color", "stroke"].includes(kind)
|
|
480
|
-
);
|
|
481
|
-
let plans = creation.steps.map(step => {
|
|
482
|
-
const descriptor = resolveLegendStepConfig(view, step);
|
|
483
|
-
const { kind } = descriptor;
|
|
484
|
-
if (previous[kind] !== undefined && previous[kind].target !== target) {
|
|
485
|
-
throw new Error(`Legend ${kind} content already belongs to another target.`);
|
|
486
|
-
}
|
|
487
|
-
if (["series", "color", "stroke"].includes(kind) && oldCategorical !== undefined) {
|
|
488
|
-
const order = patch.order === undefined ? program.semanticSpec.guides.legend?.[oldCategorical]?.order
|
|
489
|
-
: normalizeLegendOrder(patch.order);
|
|
490
|
-
return { ...resolveCategoricalLegendRevision(view, oldCategorical, previous[oldCategorical],
|
|
491
|
-
descriptor.config.channels, { order, validateLayout: false }), kind };
|
|
492
|
-
}
|
|
493
|
-
return { ...descriptor, config: previous[kind]?.target === target ? previous[kind] : descriptor.config };
|
|
494
|
-
});
|
|
495
|
-
const categorical = plans.find(plan =>
|
|
496
|
-
["series", "color", "stroke"].includes(plan.kind)
|
|
497
|
-
);
|
|
498
|
-
const size = plans.find(plan => plan.kind === "size");
|
|
499
|
-
if (categorical !== undefined) {
|
|
500
|
-
if (size !== undefined && previous.size?.target !== target) {
|
|
501
|
-
size.config = { ...size.config, inheritAppearance: categorical.config.position === "left" ||
|
|
502
|
-
patch.position === "left" || patch.labels !== undefined || patch.titleStyle !== undefined };
|
|
503
|
-
}
|
|
504
|
-
const edited = resolveCategoricalEdit(view, categorical.kind, categorical.config, size?.config, patch, categorical.order);
|
|
505
|
-
categorical.config = edited.config;
|
|
506
|
-
categorical.order = edited.order;
|
|
507
|
-
if (size !== undefined) {
|
|
508
|
-
size.config = edited.sizeConfig;
|
|
509
|
-
if (categorical.config.layout === "legacy-bottom") {
|
|
510
|
-
throw new Error('Combined size legends require layout "edge".');
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
} else {
|
|
514
|
-
const plan = plans[0];
|
|
515
|
-
if (plan.kind === "size" || plan.kind === "strokeWidth") {
|
|
516
|
-
plan.config = resolveSampledLegendEdit(view, plan.kind, plan.config, patch).config;
|
|
517
|
-
} else if (["interval", "strokeInterval"].includes(plan.kind)) {
|
|
518
|
-
plan.config = resolveIntervalEdit(
|
|
519
|
-
view,
|
|
520
|
-
plan.config,
|
|
521
|
-
patch,
|
|
522
|
-
plan.kind === "strokeInterval" ? "stroke" : "color"
|
|
523
|
-
).config;
|
|
524
|
-
}
|
|
525
|
-
else plan.config = resolveContinuousEdit(view, plan.kind, plan.config, patch).config;
|
|
526
|
-
}
|
|
527
|
-
plans = planLegendBlockTransitions(program, target, plans);
|
|
528
|
-
// The complete final content and styles are validated before removing resources.
|
|
529
|
-
for (const { kind, config } of plans) view = view._withLegendConfig(kind, config);
|
|
530
|
-
if (categorical !== undefined) resolveLayout(view, categorical.config);
|
|
531
|
-
let next = removeLegendKinds(program, removed);
|
|
532
|
-
for (const { kind, config, order } of plans) {
|
|
533
|
-
if (["series", "color", "stroke"].includes(kind)) {
|
|
534
|
-
next = createCategoricalLegendFromConfig(next, config, order);
|
|
535
|
-
}
|
|
536
|
-
else next = ({ size: createSizeLegendFromConfig, strokeWidth: createStrokeWidthLegendFromConfig,
|
|
537
|
-
gradient: createGradientLegendFromConfig, opacity: createOpacityLegendFromConfig,
|
|
538
|
-
interval: createIntervalLegendFromConfig,
|
|
539
|
-
strokeGradient: createStrokeGradientLegendFromConfig,
|
|
540
|
-
strokeInterval: createStrokeIntervalLegendFromConfig })[kind](next, config);
|
|
541
|
-
}
|
|
542
|
-
return next.rematerializeLegend();
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
export const editLegend = /* @__PURE__ */ action(
|
|
546
|
-
{ op: "editLegend", description: "Edit one stable legend content, layout or appearance." },
|
|
547
|
-
withGuideLayoutValidation(function (args = {}) {
|
|
548
|
-
validateOptionObject(args, OPTIONS, "editLegend");
|
|
549
|
-
const changes = Object.keys(args).filter(key => key !== "target");
|
|
550
|
-
if (changes.length === 0) {
|
|
551
|
-
throw new Error("editLegend requires at least one change.");
|
|
552
|
-
}
|
|
553
|
-
if (args.title !== undefined && args.title !== false && args.title !== "auto" && (
|
|
554
|
-
typeof args.title !== "string" || args.title.length === 0
|
|
555
|
-
)) {
|
|
556
|
-
throw new TypeError('editLegend title must be a non-empty string, "auto", or false.');
|
|
557
|
-
}
|
|
558
|
-
const target = resolveLegendTarget(this, args.target, "editLegend");
|
|
559
|
-
if (args.channels !== undefined) return editLegendContent(this, target, args);
|
|
560
|
-
const configs = this.guideConfigs.legend ?? {};
|
|
561
|
-
const ownedKinds = Object.keys(configs).filter(
|
|
562
|
-
kind => configs[kind]?.target === target
|
|
563
|
-
);
|
|
564
|
-
if (args.values !== undefined && ownedKinds.length > 1) {
|
|
565
|
-
throw new Error(
|
|
566
|
-
"A target with multiple legend blocks requires a channel block selector for exact values."
|
|
567
|
-
);
|
|
568
|
-
}
|
|
569
|
-
const categoricalKinds = ["series", "color", "stroke"].filter(
|
|
570
|
-
kind => configs[kind]?.target === target
|
|
571
|
-
);
|
|
572
|
-
if (categoricalKinds.length > 1) {
|
|
573
|
-
throw new Error(
|
|
574
|
-
"editLegend requires channels when a target owns multiple categorical legend blocks."
|
|
575
|
-
);
|
|
576
|
-
}
|
|
577
|
-
const categoricalKind = categoricalKinds[0];
|
|
578
|
-
if (categoricalKind !== undefined) {
|
|
579
|
-
return editCategorical(
|
|
580
|
-
this,
|
|
581
|
-
categoricalKind,
|
|
582
|
-
configs[categoricalKind],
|
|
583
|
-
configs.size?.target === target ? configs.size : undefined,
|
|
584
|
-
args
|
|
585
|
-
);
|
|
586
|
-
}
|
|
587
|
-
if (configs.interval?.target === target) {
|
|
588
|
-
return editInterval(this, configs.interval, args);
|
|
589
|
-
}
|
|
590
|
-
if (configs.strokeInterval?.target === target) {
|
|
591
|
-
return editInterval(this, configs.strokeInterval, args, "stroke");
|
|
592
|
-
}
|
|
593
|
-
if (configs.strokeWidth?.target === target) {
|
|
594
|
-
return editSampledLegend(this, "strokeWidth", configs.strokeWidth, args);
|
|
595
|
-
}
|
|
596
|
-
if (configs.size?.target === target) {
|
|
597
|
-
return editSampledLegend(this, "size", configs.size, args);
|
|
598
|
-
}
|
|
599
|
-
const continuousKind = ["gradient", "strokeGradient", "opacity"].find(
|
|
600
|
-
kind => configs[kind]?.target === target
|
|
601
|
-
);
|
|
602
|
-
return editContinuous(this, continuousKind, configs[continuousKind], args);
|
|
603
|
-
})
|
|
604
|
-
);
|
|
1
|
+
import{mergeLegendTitleStyle as G}from"../../../grammar/legendTitleStyle.js";import{recordLegendOffsetIntent as X}from"./group.js";import{prepareLegendGroup as v}from"./group.js";import{PLOT_LEGEND_POSITIONS as $}from"../../../layout/legend.js";import{withGuideLayoutValidation as ee}from"../../../materialization/guides/layout.js";import{resolveLegendStepConfig as te}from"./creation.js";import{normalizeLegendOrder as j}from"../../../grammar/categoryOrder.js";import{resolveDefinition as ne}from"./categorical/resolve.js";import{action as oe}from"../../../core/action.js";import{validateOptionObject as x}from"../../../core/validation.js";import{normalizeLegendOverflow as V,normalizeOptions as ie}from"./categorical/options.js";import{resolveLegendSymbol as le}from"./categorical/recipes.js";import{resolveLayout as re}from"./categorical/layout.js";import{resolveLegendCreationPlan as de}from"./categorical/actions.js";import{categoricalSymbolIds as ce,reconcileCategoricalSymbols as se,createCategoricalLegendFromConfig as fe,resolveCategoricalLegendRevision as ae,removeLegendKinds as W}from"./lifecycle.js";import{normalizeColorSymbols as ue}from"./continuous/colorSymbols.js";import{readLegendSampling as me}from"./sampling.js";import{createGradientLegendFromConfig as M}from"./continuous/gradient.js";import{normalizeLegendGradient as B}from"../../../grammar/legendGradient.js";import{normalizeItemLegendLayout as ge,normalizeLegendBorder as ye,normalizeLegendTextOptions as q,normalizeLegendTitleOptions as D,normalizeContinuousLegend as A}from"./continuous/common.js";import{normalizeOpacitySymbol as be,createOpacityLegendFromConfig as pe}from"./continuous/opacity.js";import{normalizeIntervalLegend as Le,resolveIntervalConfig as Se,resolveIntervalLayout as he,createIntervalLegendFromConfig as we}from"./continuous/interval.js";import{createStrokeGradientLegendFromConfig as ke,createStrokeIntervalLegendFromConfig as ze,resolveStrokeIntervalConfig as Ee,resolveStrokeIntervalLayout as Ce}from"./continuous/stroke.js";import{findLayer as z}from"../../../selectors/layers.js";import{resolveLegendGraphicPlacement as Ge}from"../../../materialization/graphicHierarchy.js";import{resolveLegendTarget as Oe,validateLegendChannels as _e}from"./target.js";import{isEnumeratedSizeScaleType as F}from"../../../grammar/scales/index.js";import{SIZE_LEGEND_LABELS as K,SIZE_LEGEND_TITLE_STYLE as N,resolveSizeLegendLayout as Te,createSizeLegendFromConfig as Ie}from"./size.js";import{STROKE_WIDTH_LEGEND_LABELS as Pe,STROKE_WIDTH_LEGEND_TITLE_STYLE as $e,resolveStrokeWidthLegendLayout as je,createStrokeWidthLegendFromConfig as xe}from"./strokeWidth.js";import{normalizeLegendSampling as O}from"./sampling.js";import{planLegendBlockTransitions as Ve}from"./transition.js";const We=Object.freeze(["target","channels","position","layout","align","direction","columns","offset","titlePosition","title","symbol","labels","titleStyle","itemGap","border","count","values","gradient","representation","order","overflow","blockLayout","at"]);function E(o,t){return t===void 0?o:{...o,...t}}function C(o,t){return t===void 0?o:t===!1||t===!0||o===!1?t:{...o,...t}}function k(o,t,e,n){const l=o.graphicSpec.objects[t]!==void 0;return l&&!e?o.editGraphics({target:t,remove:!0}):!l&&e?o.createGraphics({id:t,...n,...Ge(o,{...n.before===void 0?{}:{before:n.before},...n.after===void 0?{}:{after:n.after}})}):o}function Me(o,t,e){const n=e.representation??t.representation??"gradient";if(!["gradient","symbols"].includes(n))throw new Error("Legend representation must be gradient or symbols.");const l=["target","representation","position","align","offset","title","labels","titleStyle","titlePosition","border","count",...n==="symbols"?["symbol","values","itemGap","layout","direction","columns"]:["gradient"]];for(const y of Object.keys(e))if(!l.includes(y))throw new Error(`Color ${n} legend does not accept ${y}.`);const d=(t.representation??"gradient")!==n,i=e.title,m=i==="auto"?z(o,t.target)?.encoding?.color?.field:typeof i=="string"?i:t.title,s={target:t.target,position:e.position??t.position,align:e.align??t.align,offset:e.offset??t.offset,title:m,labels:E(t.labels,e.labels),titleStyle:G(t.titleStyle,e.titleStyle,{edit:!0}),border:C(t.border,e.border),titlePosition:e.titlePosition??(d&&n==="gradient"?"top":t.titlePosition)};let u;if(n==="symbols"){e.symbol!==void 0&&e.symbol!=="auto"&&x(e.symbol,["type","radius","stroke","strokeWidth"],"Sampled color symbol");const y=d?{count:t.count}:{};for(const w of["values","count"])Object.hasOwn(e,w)&&(y[w]=e[w]);Object.hasOwn(e,"values")&&delete y.count,Object.hasOwn(e,"count")&&(y.count=e.count),u=ue({...s,...y,layout:e.layout??(d?void 0:t.layout),direction:e.direction??(d||e.position!==void 0&&e.position!==t.position?void 0:t.direction),columns:e.columns??(d?void 0:t.columns),itemGap:e.itemGap??(d?void 0:t.itemGap),symbol:e.symbol==="auto"?"auto":e.symbol===void 0?d?void 0:t.symbol:{...d?{}:t.symbol,...e.symbol}},d?{count:t.count}:t,"edit")}else{u=A({...s,count:e.count??(d?me(t).count:t.count)},"gradient");const y=B(e.gradient,d?void 0:t.gradient);u.gradient=y}const{representation:f,sampling:a,symbol:c,layout:b,direction:S,columns:h,gradient:r,count:p,...g}=t,L=d&&n==="gradient"&&t.blockOverrides!==void 0?Object.fromEntries(Object.entries(t.blockOverrides).map(([y,w])=>{const{symbol:qe,labelMap:De,...U}=w;return[y,U]})):t.blockOverrides;return{config:{...g,...u,...L===void 0?{}:{blockOverrides:L},inferredTitle:i==="auto"?!0:typeof i=="string"?!1:t.inferredTitle,titleVisible:i===!1?!1:i===void 0?t.titleVisible!==!1:!0},titleMode:i,title:m,titleVisible:i===!1?!1:i===void 0?t.titleVisible!==!1:!0}}function _(o,t,e,n){if(t==="gradient"&&(e.representation==="symbols"||n.representation!==void 0))return Me(o,e,n);const l=["gradient","strokeGradient"].includes(t),d=l?["target","position","align","offset","title","labels","titleStyle","titlePosition","border","count","gradient","representation"]:["target","position","align","offset","title","symbol","labels","titleStyle","titlePosition","itemGap","border","count","values"];for(const w of Object.keys(n))if(!d.includes(w))throw new Error(`${t} legend does not accept ${w}.`);const i=n.title,m=i==="auto"?!0:typeof i=="string"?!1:e.inferredTitle,s=i===!1?!1:i===void 0?e.titleVisible!==!1:!0,u=z(o,e.target),f=t==="gradient"?"color":t==="strokeGradient"?"stroke":"opacity",a=u?.encoding?.[f]?.field,c=i==="auto"?a:typeof i=="string"?i:e.title,b=n.titlePosition??e.titlePosition,S=t==="opacity"&&b==="left"&&e.titlePosition!=="left",h=E(e.labels,n.labels),r=l?void 0:O(n,{previous:e,operation:"edit",label:"Opacity legend"}),p=A({target:e.target,position:n.position??e.position,align:n.align??e.align,offset:n.offset??e.offset,...l?{count:n.count??e.count}:{},titlePosition:b,title:c,labels:S&&n.labels?.offset===void 0?{...h,offset:8}:h,titleStyle:G(e.titleStyle,n.titleStyle,{edit:!0}),border:C(e.border,n.border),...t==="opacity"?{itemGap:n.itemGap??(S?20:e.itemGap),symbol:n.symbol??e.symbol}:{}},l?"gradient":t),{count:g,...L}=e,y={...l?e:L,...p,...l?{}:{sampling:r},inferredTitle:m,titleVisible:s};if(l){const w=B(n.gradient,e.gradient);y.gradient=w}else y.symbol=be(n.symbol??e.symbol);return{config:y,titleMode:i,title:c,titleVisible:s}}function T(o,t,e,n){const{config:l,titleMode:d,title:i,titleVisible:m}=_(o,t,e,n);if(t==="gradient"&&(e.representation??"gradient")!==(l.representation??"gradient"))return M(W(o,[t]),l).rematerializeLegend();const s=t==="gradient"?"colorGradient":t==="strokeGradient"?"strokeGradient":"opacityLegend",u=t==="gradient"?"color":t==="strokeGradient"?"stroke":"opacity";let f=o;return(d==="auto"||typeof d=="string")&&(f=f.editSemantic({property:`guide.legend.${u}.title`,value:i})),f=f._withLegendConfig(t,l),f=k(f,`${s}Background`,l.border!==!1,{type:"rect",before:["gradient","strokeGradient"].includes(t)?`${s}${l.representation==="symbols"?"Symbols":"Strips"}`:"opacityLegendSymbols"}),f=k(f,`${s}Title`,m,{type:"text"}),f.rematerializeLegend()}function R(o,t,e,n="color"){for(const c of Object.keys(e))if(!["target","position","align","direction","offset","title","symbol","labels","titleStyle","itemGap","border","layout","columns","titlePosition"].includes(c))throw new Error(`interval legend does not accept ${c}.`);const l=e.title,d=z(o,t.target),i=l==="auto"?!0:typeof l=="string"?!1:t.inferredTitle,m=l===!1?!1:l===void 0?t.titleVisible!==!1:!0,s=l==="auto"?d?.encoding?.[n]?.field:typeof l=="string"?l:t.title,u=Le({target:t.target,position:e.position??t.position,align:e.align??t.align,direction:e.direction??(e.position!==void 0&&e.position!==t.position?void 0:t.direction),layout:e.layout===void 0?t.layout:e.layout,columns:e.columns??t.columns,titlePosition:e.titlePosition??t.titlePosition,offset:e.offset??t.offset,title:s,symbol:E(t.symbol,e.symbol),labels:E(t.labels,e.labels),titleStyle:G(t.titleStyle,e.titleStyle,{edit:!0}),itemGap:e.itemGap??t.itemGap,border:C(t.border,e.border)}),f={...t,...u,inferredTitle:i,titleVisible:m,...n==="stroke"&&e.symbol?.strokeWidth!==void 0?{inferredStrokeWidth:!1}:{}},a=n==="stroke"?Ee(o,f):Se(o,f);return(n==="stroke"?Ce:he)(o,a.config,a.scale),{normalized:u,config:f,titleMode:l,title:s,titleVisible:m}}function I(o,t,e,n="color"){const l=n==="stroke"?"strokeInterval":"interval",d=n==="stroke"?"strokeInterval":"colorLegend",{normalized:i,config:m,titleMode:s,title:u,titleVisible:f}=R(o,t,e,n);let a=o._withLegendConfig(l,m);return(s==="auto"||typeof s=="string")&&(a=a.editSemantic({property:`guide.legend.${n}.title`,value:u})),a=k(a,`${d}Title`,f,{type:"text"}),a=k(a,`${d}Background`,i.border!==!1,{type:"rect",before:`${d}Symbols`}),a.rematerializeLegend()}function H(o,t,e,n,l=!1){const d=t==="size",i=d?"size":"stroke-width",m=["target","title","count","values","labels","titleStyle","position","layout","align","direction","columns","titlePosition","offset","itemGap","border"];d&&m.push("overflow");for(const y of Object.keys(n))if(!m.includes(y))throw new Error(`${i} legend does not accept ${y}.`);const s=d&&F(o.resolvedScales[e.scale]?.type),u=d&&o.resolvedScales[e.scale]?.type==="ordinal";if(n.overflow!==void 0&&!u)throw new Error("Size legend overflow requires an ordinal scale.");if(s&&(n.count!==void 0||n.values!==void 0))throw new Error("Discrete size legends do not support count or exact values.");const f=s?void 0:O(n,{previous:e,operation:"edit",label:`${d?"Size":"Stroke-width"} legend`}),a=z(o,e.target),c=n.title,b=c==="auto"?!0:typeof c=="string"?!1:e.inferredTitle,S=c===!1?!1:c===void 0?e.titleVisible!==!1:!0,h=c==="auto"?a?.encoding?.[t]?.field:typeof c=="string"?c:e.title,{count:r,...p}=e,L={...s?e:p,title:h,inferredTitle:b,titleVisible:S,...s?{}:{sampling:f},...u&&(n.overflow!==void 0||e.overflow!==void 0)?{overflow:V(n.overflow===void 0?e.overflow:n.overflow)}:{},...d?{inheritAppearance:l&&e.inheritAppearance===!0,...n.position===void 0?{}:{inheritPosition:!1}}:{},labels:q(n.labels,"editLegend.labels",e.labels??(d?K:Pe)),titleStyle:D(n.titleStyle,"editLegend.titleStyle",e.titleStyle??(d?N:$e),!0)};return Object.assign(L,ge({...e,...n,titleStyle:L.titleStyle,direction:n.direction??(n.position!==void 0&&n.position!==e.position?void 0:e.direction)})),L.border=ye(C(e.border,n.border)),(d?Te:je)(o,L),{config:L,titleMode:c,title:h,titleVisible:S}}function P(o,t,e,n,l=!1){const{config:d,titleMode:i,title:m,titleVisible:s}=H(o,t,e,n,l),u=t==="size"?"sizeLegend":"strokeWidthLegend";let f=o;return(i==="auto"||typeof i=="string")&&(f=f.editSemantic({property:`guide.legend.${t}.title`,value:m})),f=f._withLegendConfig(t,d),f=k(f,`${u}Background`,d.border!==!1,{type:"rect",before:`${u}Symbols`}),f=k(f,`${u}Title`,s,{type:"text",after:`${u}Labels`}),f.rematerializeLegend()}function Be(o,t,e,n){if(e===void 0)return;const l=F(o.resolvedScales[e.scale]?.type);if(l&&(n.count!==void 0||n.values!==void 0))throw new Error("Discrete size legends do not support count or exact values.");const d=l?void 0:O(n,{previous:e,operation:"edit",label:"Size legend"}),{count:i,...m}=e,u={...l?e:m,...l?{}:{sampling:d},...n.overflow!==void 0&&o.resolvedScales[e.scale]?.type==="ordinal"?{overflow:V(n.overflow)}:{}};if(n.labels===void 0&&n.titleStyle===void 0)return u;const f=e.inheritAppearance?{...t.labels,offset:e.labels.offset}:e.labels??K,a=e.inheritAppearance?t.titleStyle:e.titleStyle??N;return{...u,inheritAppearance:!1,labels:q(n.labels,"editLegend.labels",f),titleStyle:D(n.titleStyle,"editLegend.titleStyle",a,!0)}}function Y(o,t,e,n,l,d=o.semanticSpec.guides.legend?.[t]?.order){if(l.values!==void 0)throw new Error("Combined and categorical legends require a channel block selector for exact values.");if(l.gradient!==void 0)throw new Error("Categorical legends do not accept gradient.");if(l.count!==void 0&&n===void 0)throw new Error("Legend count requires an existing size legend.");const i=l.title,m=i==="auto"?!0:typeof i=="string"?!1:e.inferredTitle,s=i===!1?!1:i===void 0?e.titleVisible!==!1:!0,u=i==="auto"?e.field:typeof i=="string"?i:e.title,f=E(e.labels,l.labels);if(f.format!==void 0&&f.format!=="auto"&&n===void 0)throw new Error("Categorical legend labels do not accept format.");const{format:a,...c}=f,b=l.symbol===void 0?e.inferredSymbol:l.symbol==="auto",S=ie({target:e.target,channels:e.channels,...l.overflow===void 0&&e.overflow===void 0?{}:{overflow:l.overflow===void 0?e.overflow:l.overflow},layout:l.layout===void 0?e.layout:l.layout,position:l.position??e.position,align:l.align??e.align,direction:l.direction??(l.position!==void 0&&l.position!==e.position?void 0:e.direction),...l.columns===void 0&&e.columns===void 0?{}:{columns:l.columns??e.columns},offset:l.offset??e.offset,titlePosition:l.titlePosition??e.titlePosition,title:u,symbol:l.symbol===void 0?e.symbol:le(o,z(o,e.target),e.channels,l.symbol,t),labels:c,titleStyle:G(e.titleStyle,l.titleStyle,{edit:!0}),itemGap:l.itemGap??e.itemGap,border:C(e.border,l.border)},t,{internalSymbol:b}),h={...e,...S,inferredTitle:m,inferredSymbol:b,titleVisible:s},r=l.order===void 0?d:j(l.order);return ne(o,z(o,e.target),e.channels,u,r),{config:h,order:r,titleMode:i,title:u,titleVisible:s,sizeConfig:Be(o,e,n,l)}}function Z(o,t,e,n,l){const{config:d,order:i,titleMode:m,title:s,titleVisible:u,sizeConfig:f}=Y(o,t,e,n,l),a=ce(d);let c=o;(m==="auto"||typeof m=="string")&&(c=c.editSemantic({property:`guide.legend.${t}.title`,value:s})),l.order!==void 0&&(i==="scale"?o.semanticSpec.guides.legend?.[t]?.order!==void 0&&(c=c.editSemantic({property:`guide.legend.${t}.order`,remove:!0})):c=c.editSemantic({property:`guide.legend.${t}.order`,value:i})),c=c._withLegendConfig(t,d),c=se(c,e,d),c=k(c,`${t}LegendBackground`,d.border!==!1,{type:"rect",before:[...a][0]});const b=`${t}LegendTitle`;return c=k(c,b,u,{type:"text",...c.graphicSpec.objects.sizeLegendSymbols===void 0?{}:{before:c.graphicSpec.objects.sizeLegendBackground===void 0?"sizeLegendSymbols":"sizeLegendBackground"}}),n!==void 0&&(c=c._withLegendConfig("size",f)),c.rematerializeLegend()}function J(o,t,e){_e(e.channels,"editLegend");const{channels:n,target:l,...d}=e,i=o.guideConfigs.legend??{},m=Object.keys(i).filter(r=>i[r].target===t);let s=m.reduce((r,p)=>r._withoutMaterializationConfig(["guides","legend",p]),o);const u=d.representation??(n.includes("color")&&i.gradient?.target===t?i.gradient.representation:void 0);if(d.representation!==void 0&&!n.includes("color"))throw new Error("Legend representation requires selected continuous color.");const f=de(s,{target:t,channels:n,...u===void 0?{}:{representation:u}}),a=m.find(r=>["series","color","stroke"].includes(r));let c=f.steps.map(r=>{const p=te(s,r),{kind:g}=p;if(i[g]!==void 0&&i[g].target!==t)throw new Error(`Legend ${g} content already belongs to another target.`);if(["series","color","stroke"].includes(g)&&a!==void 0){const L=d.order===void 0?o.semanticSpec.guides.legend?.[a]?.order:j(d.order);return{...ae(s,a,i[a],p.config.channels,{order:L,validateLayout:!1}),kind:g}}return{...p,config:i[g]?.target===t?i[g]:p.config}});const b=c.find(r=>["series","color","stroke"].includes(r.kind)),S=c.find(r=>r.kind==="size");if(d.representation!==void 0||c.some(r=>r.kind==="gradient"&&r.config.representation==="symbols")){const r=c.find(p=>p.kind==="gradient");if(r===void 0)throw new Error("Legend representation requires continuous color.");r.config=_(s,"gradient",r.config,d).config}else if(b!==void 0){S!==void 0&&i.size?.target!==t&&(S.config={...S.config,inheritAppearance:b.config.position==="left"||d.position==="left"||d.labels!==void 0||d.titleStyle!==void 0});const r=Y(s,b.kind,b.config,S?.config,d,b.order);if(b.config=r.config,b.order=r.order,S!==void 0&&(S.config=r.sizeConfig,b.config.layout==="legacy-bottom"))throw new Error('Combined size legends require layout "edge".')}else{const r=c[0];r.kind==="size"||r.kind==="strokeWidth"?r.config=H(s,r.kind,r.config,d).config:["interval","strokeInterval"].includes(r.kind)?r.config=R(s,r.config,d,r.kind==="strokeInterval"?"stroke":"color").config:r.config=_(s,r.kind,r.config,d).config}c=Ve(o,t,c);for(const{kind:r,config:p}of c)s=s._withLegendConfig(r,p);b!==void 0&&re(s,b.config);let h=W(o,m);for(const{kind:r,config:p,order:g}of c)["series","color","stroke"].includes(r)?h=fe(h,p,g):h={size:Ie,strokeWidth:xe,gradient:M,opacity:pe,interval:we,strokeGradient:ke,strokeInterval:ze}[r](h,p);return h.rematerializeLegend()}function Q(o={},t=!1){if(x(o,We,"editLegend"),Object.keys(o).filter(a=>a!=="target").length===0)throw new Error("editLegend requires at least one change.");if(o.title!==void 0&&o.title!==!1&&o.title!=="auto"&&(typeof o.title!="string"||o.title.length===0))throw new TypeError('editLegend title must be a non-empty string, "auto", or false.');const n=Oe(this,o.target,"editLegend"),l=this.guideConfigs.legendGroups?.[n];if(!t&&(l!==void 0||o.blockLayout!==void 0||o.at!==void 0||$.includes(o.position)&&Object.values(this.guideConfigs.legend??{}).some(a=>a.target===n&&a.kind===void 0))){const a=v(this,o,n,{corner:$.includes(o.position)}),{channels:c,...b}=a.options;if(c!==void 0)return J(a.program,n,a.options);const S=["position","layout","align","direction","columns","offset","titlePosition","itemGap"];if(Object.keys(b).every(r=>r==="target"||S.includes(r))){let r=a.program;const p=Object.entries(r.guideConfigs.legend??{}).filter(([,g])=>g.target===n);for(const[g]of p){const L=r.guideConfigs.legend[g],y={...b};if(["gradient","strokeGradient","opacity"].includes(g)&&L.representation!=="symbols")for(const w of["layout","columns","direction"]){if(Object.hasOwn(o,w))throw new Error(`${g} legend does not accept ${w}.`);delete y[w]}["gradient","strokeGradient"].includes(g)&&L.representation!=="symbols"&&delete y.itemGap,["series","color","stroke"].includes(g)?r=Z(r,g,L,void 0,y):["size","strokeWidth"].includes(g)?r=P(r,g,L,y,!0):["interval","strokeInterval"].includes(g)?r=I(r,L,y,g==="strokeInterval"?"stroke":"color"):r=T(r,g,L,y)}return r.rematerializeLegend()}return Q.call(a.program,a.options,!0)}if(o.channels!==void 0)return J(this,n,o);const i=this.guideConfigs.legend??{},m=Object.keys(i).filter(a=>i[a]?.target===n);if(o.representation!==void 0){if(i.gradient?.target!==n)throw new Error("Legend representation requires continuous color.");return T(this,"gradient",i.gradient,o)}if(i.gradient?.target===n&&i.gradient.representation==="symbols"&&m.length>1)throw new Error("Multiple legend blocks require editLegendBlock with an explicit channel for appearance or samples.");if(o.values!==void 0&&m.length>1)throw new Error("A target with multiple legend blocks requires a channel block selector for exact values.");const s=["series","color","stroke"].filter(a=>i[a]?.target===n);if(s.length>1)throw new Error("editLegend requires channels when a target owns multiple categorical legend blocks.");const u=s[0];if(u!==void 0)return Z(this,u,i[u],i.size?.target===n?i.size:void 0,o);if(i.interval?.target===n)return I(this,i.interval,o);if(i.strokeInterval?.target===n)return I(this,i.strokeInterval,o,"stroke");if(i.strokeWidth?.target===n)return P(this,"strokeWidth",i.strokeWidth,o);if(i.size?.target===n)return P(this,"size",i.size,o);const f=["gradient","strokeGradient","opacity"].find(a=>i[a]?.target===n);return T(this,f,i[f],o)}const St=oe({op:"editLegend",description:"Edit one stable legend content, layout or appearance."},ee(function(o={}){return X(this,Q.call(this,o),o)}));export{St as editLegend};
|
|
@@ -20,7 +20,7 @@ function makeFocusedLegendEdit({ name, options, map }) {
|
|
|
20
20
|
const editLegendLayout = /* @__PURE__ */ makeFocusedLegendEdit({
|
|
21
21
|
name: "editLegendLayout",
|
|
22
22
|
options: [
|
|
23
|
-
"position", "layout", "align", "direction", "columns", "offset",
|
|
23
|
+
"position", "layout", "align", "direction", "columns", "offset", "blockLayout", "at",
|
|
24
24
|
"titlePosition", "itemGap"
|
|
25
25
|
],
|
|
26
26
|
map: args => args
|
|
@@ -34,7 +34,7 @@ const editLegendLabels = /* @__PURE__ */ makeFocusedLegendEdit({
|
|
|
34
34
|
|
|
35
35
|
const editLegendTitle = /* @__PURE__ */ makeFocusedLegendEdit({
|
|
36
36
|
name: "editLegendTitle",
|
|
37
|
-
options: ["title", "color", "fontSize", "fontFamily", "fontWeight"],
|
|
37
|
+
options: ["title", "color", "fontSize", "fontFamily", "fontWeight", "align"],
|
|
38
38
|
map: ({ target, title, ...titleStyle }) => ({
|
|
39
39
|
target,
|
|
40
40
|
...(title === undefined ? {} : { title }),
|