ggaction 0.0.13 → 0.0.16
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 +146 -0
- package/README.md +41 -12
- package/knowledge/action-cards.json +1 -1
- package/knowledge/intent-taxonomy.json +1 -1
- package/knowledge/mcp-resources.json +1 -1
- package/knowledge/task-packet.schema.json +1 -1
- package/knowledge/task-resolver.js +1 -1
- package/package.json +39 -7
- package/src/BasicChartProgram.js +3 -1
- package/src/ChartProgram.js +2 -0
- package/src/accessibility.js +157 -0
- package/src/actions/basic.js +2 -2
- package/src/actions/boxPlots/components.js +20 -12
- package/src/actions/boxPlots/create.js +4 -5
- package/src/actions/boxPlots/edit.js +34 -9
- package/src/actions/boxPlots/materialize.js +12 -4
- package/src/actions/boxPlots/options.js +48 -16
- package/src/actions/canvas/actions.js +3 -3
- package/src/actions/canvas/fitting.js +1 -1
- package/src/actions/categoryOrder/index.js +21 -12
- package/src/actions/charts/area.js +6 -2
- package/src/actions/charts/bar.js +3 -2
- package/src/actions/charts/beeswarm.js +1 -1
- package/src/actions/charts/density.js +7 -3
- package/src/actions/charts/ecdf.js +6 -3
- package/src/actions/charts/endpoints.js +29 -7
- package/src/actions/charts/heatmap.js +3 -2
- package/src/actions/charts/histogram.js +5 -4
- package/src/actions/charts/horizon.js +4 -2
- package/src/actions/charts/interval-regression.js +7 -4
- package/src/actions/charts/line.js +4 -2
- package/src/actions/charts/parallel.js +9 -3
- package/src/actions/charts/pie.js +11 -3
- package/src/actions/charts/polar.js +17 -6
- package/src/actions/charts/radar.js +9 -3
- package/src/actions/charts/radial.js +12 -4
- package/src/actions/charts/raincloud.js +2 -2
- package/src/actions/charts/rug-strip.js +8 -4
- package/src/actions/charts/scatter.js +4 -2
- package/src/actions/composition/actions.js +29 -24
- package/src/actions/coordinates/actions.js +2 -2
- package/src/actions/coordinates/edit.js +76 -0
- package/src/actions/data/basicBin2d.js +4 -5
- package/src/actions/data/bin.js +3 -3
- package/src/actions/data/bin2d.js +63 -193
- package/src/actions/data/bin2dMaterialize.js +4 -5
- package/src/actions/data/box.js +4 -4
- package/src/actions/data/complete.js +26 -0
- package/src/actions/data/computed.js +4 -4
- package/src/actions/data/create.js +7 -14
- package/src/actions/data/density.js +15 -23
- package/src/actions/data/derived.js +57 -31
- package/src/actions/data/ecdf.js +2 -2
- package/src/actions/data/edit.js +551 -0
- package/src/actions/data/filter.js +28 -22
- package/src/actions/data/fold.js +2 -2
- package/src/actions/data/gradientProfile.js +2 -2
- package/src/actions/data/horizon.js +2 -2
- package/src/actions/data/impute.js +26 -0
- package/src/actions/data/index.js +62 -47
- package/src/actions/data/interval.js +2 -2
- package/src/actions/data/intervalEdit.js +26 -18
- package/src/actions/data/normalize.js +27 -0
- package/src/actions/data/regression.js +21 -27
- package/src/actions/data/revise.js +48 -0
- package/src/actions/data/revisionBindings.js +125 -0
- package/src/actions/data/shared.js +20 -4
- package/src/actions/data/stack.js +2 -2
- package/src/actions/data/summary.js +3 -3
- package/src/actions/data/timeUnit.js +6 -3
- package/src/actions/data/window.js +3 -3
- package/src/actions/encodings/angle.js +1 -1
- package/src/actions/encodings/appearance.js +4 -4
- package/src/actions/encodings/barWidth.js +1 -1
- package/src/actions/encodings/channels.js +415 -0
- package/src/actions/encodings/color/index.js +3 -2
- package/src/actions/encodings/density.js +7 -4
- package/src/actions/encodings/histogram.js +4 -3
- package/src/actions/encodings/horizon.js +6 -8
- package/src/actions/encodings/index.js +4 -0
- package/src/actions/encodings/offset.js +22 -15
- package/src/actions/encodings/parallel.js +4 -5
- package/src/actions/encodings/pathOrder.js +6 -11
- package/src/actions/encodings/position/apply.js +53 -7
- package/src/actions/encodings/position/index.js +14 -6
- package/src/actions/encodings/position/policies/bar.js +9 -0
- package/src/actions/encodings/position/resolve.js +24 -7
- package/src/actions/encodings/ranged.js +5 -5
- package/src/actions/encodings/remove.js +13 -9
- package/src/actions/encodings/ruleAppearance.js +2 -24
- package/src/actions/encodings/seriesLayout.js +2 -1
- package/src/actions/encodings/shared.js +82 -14
- package/src/actions/encodings/stroke.js +226 -0
- package/src/actions/encodings/strokeDash.js +2 -2
- package/src/actions/encodings/text.js +1 -1
- package/src/actions/errorBands/create.js +34 -20
- package/src/actions/errorBands/edit.js +34 -137
- package/src/actions/errorBars/create.js +43 -20
- package/src/actions/errorBars/edit.js +29 -125
- package/src/actions/facets/actions.js +267 -73
- package/src/actions/facets/derive.js +99 -28
- package/src/actions/facets/guides.js +4 -5
- package/src/actions/facets/replay.js +4 -5
- package/src/actions/gradientPlots/components.js +15 -8
- package/src/actions/gradientPlots/create.js +4 -5
- package/src/actions/gradientPlots/edit.js +18 -9
- package/src/actions/gradientPlots/materialize.js +5 -3
- package/src/actions/gradientPlots/options.js +10 -2
- package/src/actions/gradientPlots/rebind.js +4 -5
- package/src/actions/guides/applicability.js +8 -2
- package/src/actions/guides/axes/axes.js +23 -4
- package/src/actions/guides/axes/axis.js +2 -2
- package/src/actions/guides/axes/edit.js +2 -2
- package/src/actions/guides/axes/labels.js +67 -21
- package/src/actions/guides/axes/lines.js +15 -9
- package/src/actions/guides/axes/parallel/lifecycle.js +13 -13
- package/src/actions/guides/axes/parallel/resolve.js +6 -2
- package/src/actions/guides/axes/parallel.js +5 -5
- package/src/actions/guides/axes/remove.js +4 -4
- package/src/actions/guides/axes/tickGroups.js +6 -5
- package/src/actions/guides/axes/ticks.js +19 -6
- package/src/actions/guides/axes/titles.js +21 -13
- package/src/actions/guides/grids/grid.js +18 -29
- package/src/actions/guides/grids/resolve.js +7 -2
- package/src/actions/guides/guides.js +20 -4
- package/src/actions/guides/legends/blocks.js +426 -0
- package/src/actions/guides/legends/categorical/actions.js +138 -52
- package/src/actions/guides/legends/categorical/components.js +55 -45
- package/src/actions/guides/legends/categorical/index.js +37 -33
- package/src/actions/guides/legends/categorical/layout.js +98 -16
- 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 +104 -52
- package/src/actions/guides/legends/continuous/common.js +76 -20
- package/src/actions/guides/legends/continuous/gradient.js +95 -158
- package/src/actions/guides/legends/continuous/index.js +2 -0
- package/src/actions/guides/legends/continuous/interval.js +21 -16
- package/src/actions/guides/legends/continuous/opacity.js +37 -83
- package/src/actions/guides/legends/continuous/stroke.js +225 -0
- package/src/actions/guides/legends/creation.js +14 -1
- package/src/actions/guides/legends/edit.js +159 -60
- package/src/actions/guides/legends/focused.js +5 -5
- package/src/actions/guides/legends/index.js +2 -0
- package/src/actions/guides/legends/lane.js +59 -24
- package/src/actions/guides/legends/lifecycle.js +19 -8
- package/src/actions/guides/legends/remove.js +17 -8
- package/src/actions/guides/legends/sampling.js +140 -0
- package/src/actions/guides/legends/size.js +100 -108
- package/src/actions/guides/legends/strokeWidth.js +38 -92
- 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 +8 -8
- package/src/actions/guides/polar/axes/labels.js +29 -10
- package/src/actions/guides/polar/axes/lines.js +19 -19
- package/src/actions/guides/polar/axes/shared.js +16 -0
- package/src/actions/guides/polar/axes/ticks.js +10 -16
- package/src/actions/guides/polar/axes/titles.js +6 -6
- package/src/actions/guides/polar/grids.js +12 -14
- package/src/actions/guides/polar/resolve.js +12 -8
- package/src/actions/index.js +16 -3
- package/src/actions/intervals/revision.js +126 -0
- package/src/actions/marks/arc/actions.js +80 -22
- package/src/actions/marks/area/actions.js +38 -13
- package/src/actions/marks/area/materialize.js +17 -1
- package/src/actions/marks/bar/create.js +12 -3
- package/src/actions/marks/bar/edit.js +21 -4
- package/src/actions/marks/bar/materialize.js +34 -40
- package/src/actions/marks/index.js +10 -1
- package/src/actions/marks/line/actions.js +40 -17
- package/src/actions/marks/line/materialize.js +29 -10
- package/src/actions/marks/point/create.js +9 -3
- package/src/actions/marks/point/edit.js +19 -7
- package/src/actions/marks/point/jitter.js +6 -8
- package/src/actions/marks/point/materialize.js +48 -22
- package/src/actions/marks/point/packing.js +6 -8
- package/src/actions/marks/rect/actions.js +36 -15
- package/src/actions/marks/references.js +602 -33
- package/src/actions/marks/remove.js +67 -41
- package/src/actions/marks/rule/actions.js +51 -14
- package/src/actions/marks/shared.js +1 -1
- package/src/actions/marks/text/actions.js +79 -14
- package/src/actions/marks/text/index.js +4 -0
- package/src/actions/marks/text/layout.js +157 -17
- package/src/actions/marks/text/removal.js +173 -0
- package/src/actions/marks/text/selection.js +72 -0
- package/src/actions/marks/tick/actions.js +25 -8
- package/src/actions/primitives/createGraphics.js +5 -4
- package/src/actions/primitives/editGraphics.js +31 -4
- package/src/actions/primitives/graphicProperties.js +6 -0
- package/src/actions/primitives/semantic.js +11 -3
- package/src/actions/primitives/semanticAction.js +90 -14
- package/src/actions/primitives/semanticValidation/dataset.js +2 -4
- 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 +36 -25
- package/src/actions/regression/create.js +13 -6
- package/src/actions/regression/edit.js +24 -11
- package/src/actions/resources/index.js +7 -0
- package/src/actions/resources/remove.js +173 -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 +55 -23
- package/src/actions/scales/definitions.js +94 -40
- package/src/actions/scales/edit.js +67 -12
- package/src/actions/scales/editPolicy.js +81 -11
- package/src/actions/scales/index.js +6 -0
- package/src/actions/scales/materialize.js +5 -4
- 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/scales/quantitativeColor.js +4 -5
- package/src/actions/selection/actions.js +179 -66
- package/src/actions/selection/index.js +3 -0
- package/src/actions/textMetrics/index.js +56 -0
- package/src/actions/theme/actions.js +70 -15
- package/src/actions/theme/composition.js +238 -0
- package/src/actions/theme/index.js +9 -2
- package/src/actions/theme/reconcile.js +313 -121
- package/src/actions/theme/state.js +160 -0
- package/src/actions/titles/actions.js +8 -8
- package/src/actions/titles/resolve.js +3 -3
- package/src/actions/violinPlots/create.js +6 -3
- package/src/actions/violinPlots/edit.js +2 -1
- package/src/core/action.js +117 -35
- package/src/core/compositionState.js +6 -1
- package/src/core/diagnostics.js +16 -0
- package/src/core/extensionRegistry.js +27 -0
- package/src/core/font.js +18 -0
- package/src/core/identifiers.js +5 -4
- package/src/core/immutable.js +10 -3
- package/src/core/optionRequirements.js +8 -0
- package/src/core/programState.js +21 -1
- package/src/core/resourceReferences.js +341 -0
- package/src/core/textMetricProfile.js +28 -0
- package/src/core/textMetrics.js +24 -3
- package/src/core/validation.js +22 -10
- package/src/core/vocabulary.js +16 -3
- package/src/diagnostics.js +1 -0
- 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 +341 -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/scales/types.js +8 -0
- package/src/grammar/schemas/concreteGraphic.js +14 -2
- package/src/grammar/schemas/graphic.js +15 -0
- package/src/grammar/schemas/graphicBounds.js +190 -41
- 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 +351 -7
- package/src/layout/legend.js +4 -4
- package/src/layout/legendItems.js +6 -6
- package/src/layout/text.js +10 -9
- package/src/layout/title.js +9 -9
- 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 +9 -3
- package/src/materialization/facetGuides/preparation.js +13 -8
- package/src/materialization/facetHeaders.js +347 -0
- package/src/materialization/facets.js +15 -65
- package/src/materialization/guides/layout.js +3 -3
- 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/scaleGuideDependencies.js +10 -0
- 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 +20 -5
- 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/materialization/typography.js +31 -0
- package/src/mcp/cli.js +5 -3
- package/src/persistence/codec.js +65 -0
- package/src/persistence/validation.js +185 -0
- package/src/persistence.js +74 -0
- package/src/renderers/canvas/circle.js +2 -3
- package/src/renderers/canvas/index.js +5 -4
- 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/nativeBackend.js +10 -0
- package/src/renderers/options.js +13 -0
- package/src/renderers/pdf.js +25 -54
- package/src/renderers/png.js +22 -17
- package/src/renderers/svg.js +29 -14
- package/src/renderers/text.js +1 -11
- package/src/selectors/coordinates.js +3 -1
- package/src/selectors/datasets.js +38 -5
- package/src/selectors/layers.js +10 -4
- package/src/selectors/markOwners.js +46 -0
- package/src/selectors/scales.js +6 -4
- package/src/theme/defaults.js +103 -1
- package/src/version.js +2 -0
- package/types/accessibility.d.ts +37 -0
- package/types/basic.d.ts +10 -2
- package/types/diagnostics.d.ts +15 -0
- package/types/index.d.ts +90 -0
- package/types/pdf.d.ts +9 -0
- package/types/persistence.d.ts +7 -0
- package/types/png.d.ts +9 -0
- package/types/program.d.ts +944 -118
|
@@ -1,7 +1,27 @@
|
|
|
1
|
+
import { rematerializeTypography } from "../../materialization/typography.js";
|
|
1
2
|
import { themeTokens } from "../../theme/defaults.js";
|
|
3
|
+
import {
|
|
4
|
+
normalizeThemeState,
|
|
5
|
+
resolveEffectiveThemeTokens,
|
|
6
|
+
setThemeStateOverrides
|
|
7
|
+
} from "./state.js";
|
|
2
8
|
import { findLayer } from "../../selectors/layers.js";
|
|
3
9
|
|
|
4
|
-
|
|
10
|
+
let rematerializeThemeHighlights = program => program;
|
|
11
|
+
|
|
12
|
+
export function registerThemeHighlightReconciler(reconciler) {
|
|
13
|
+
if (typeof reconciler !== "function") {
|
|
14
|
+
throw new TypeError("Theme highlight reconciler must be a function.");
|
|
15
|
+
}
|
|
16
|
+
rematerializeThemeHighlights = reconciler;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const THEME_STYLE_PROPERTIES = Object.freeze([
|
|
20
|
+
"background",
|
|
21
|
+
"fill",
|
|
22
|
+
"stroke",
|
|
23
|
+
"fontFamily"
|
|
24
|
+
]);
|
|
5
25
|
const COLOR_TOKENS = Object.freeze([
|
|
6
26
|
"mark",
|
|
7
27
|
"text",
|
|
@@ -20,51 +40,57 @@ const COLOR_TOKENS = Object.freeze([
|
|
|
20
40
|
"gradientCenter"
|
|
21
41
|
]);
|
|
22
42
|
|
|
23
|
-
function tokenFor(value,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return
|
|
43
|
+
function tokenFor(value, sourceTokens, targetTokens) {
|
|
44
|
+
const matches = COLOR_TOKENS.filter(token =>
|
|
45
|
+
value === sourceTokens[token] || value === themeTokens("light")[token]
|
|
46
|
+
);
|
|
47
|
+
if (matches.length === 0) return undefined;
|
|
48
|
+
const targets = new Set(matches.map(token => targetTokens[token]));
|
|
49
|
+
if (targets.size !== 1) return undefined;
|
|
50
|
+
return matches[0];
|
|
31
51
|
}
|
|
32
52
|
|
|
33
|
-
function roleValues(role,
|
|
53
|
+
function roleValues(role, sourceTokens, legacy = []) {
|
|
34
54
|
return new Set([
|
|
35
|
-
|
|
55
|
+
sourceTokens[role],
|
|
36
56
|
themeTokens("light")[role],
|
|
37
57
|
...legacy.map(token => themeTokens("light")[token] ?? token)
|
|
38
58
|
]);
|
|
39
59
|
}
|
|
40
60
|
|
|
41
|
-
function mapRoleValue(value, role,
|
|
61
|
+
function mapRoleValue(value, role, sourceTokens, targetTokens, legacy) {
|
|
42
62
|
if (Array.isArray(value)) {
|
|
43
63
|
let changed = false;
|
|
44
64
|
const next = value.map(item => {
|
|
45
|
-
const mapped = mapRoleValue(
|
|
65
|
+
const mapped = mapRoleValue(
|
|
66
|
+
item,
|
|
67
|
+
role,
|
|
68
|
+
sourceTokens,
|
|
69
|
+
targetTokens,
|
|
70
|
+
legacy
|
|
71
|
+
);
|
|
46
72
|
changed ||= mapped.changed;
|
|
47
73
|
return mapped.value;
|
|
48
74
|
});
|
|
49
75
|
return { changed, value: changed ? next : value };
|
|
50
76
|
}
|
|
51
|
-
if (!roleValues(role,
|
|
77
|
+
if (!roleValues(role, sourceTokens, legacy).has(value)) {
|
|
52
78
|
return { changed: false, value };
|
|
53
79
|
}
|
|
54
80
|
return { changed: value !== targetTokens[role], value: targetTokens[role] };
|
|
55
81
|
}
|
|
56
82
|
|
|
57
|
-
function mapColorValue(value,
|
|
83
|
+
function mapColorValue(value, sourceTokens, targetTokens) {
|
|
58
84
|
if (Array.isArray(value)) {
|
|
59
85
|
let changed = false;
|
|
60
86
|
const next = value.map(item => {
|
|
61
|
-
const mapped = mapColorValue(item,
|
|
87
|
+
const mapped = mapColorValue(item, sourceTokens, targetTokens);
|
|
62
88
|
changed ||= mapped.changed;
|
|
63
89
|
return mapped.value;
|
|
64
90
|
});
|
|
65
91
|
return { changed, value: changed ? next : value };
|
|
66
92
|
}
|
|
67
|
-
const token = tokenFor(value,
|
|
93
|
+
const token = tokenFor(value, sourceTokens, targetTokens);
|
|
68
94
|
if (token === undefined) return { changed: false, value };
|
|
69
95
|
const next = targetTokens[token];
|
|
70
96
|
return { changed: next !== value, value: next };
|
|
@@ -73,7 +99,10 @@ function mapColorValue(value, names, targetTokens) {
|
|
|
73
99
|
function isFieldDrivenMark(program, id, property) {
|
|
74
100
|
if (!(["fill", "stroke"].includes(property))) return false;
|
|
75
101
|
const layer = findLayer(program, id);
|
|
76
|
-
return
|
|
102
|
+
return property === "stroke"
|
|
103
|
+
? layer?.encoding?.stroke?.field !== undefined ||
|
|
104
|
+
(layer?.mark?.type === "line" && layer.encoding?.color?.field !== undefined)
|
|
105
|
+
: layer?.encoding?.color?.field !== undefined;
|
|
77
106
|
}
|
|
78
107
|
|
|
79
108
|
function isRegressionBand(program, id) {
|
|
@@ -128,12 +157,16 @@ function componentRole(program, id, property) {
|
|
|
128
157
|
}
|
|
129
158
|
|
|
130
159
|
function graphicRole(program, id, property) {
|
|
160
|
+
if (property === "fontFamily") return "fontFamily";
|
|
131
161
|
if (id === "canvas" && property === "background") return "background";
|
|
162
|
+
if (id === `${program.compositionSpec?.id}-headers` && property === "fill") {
|
|
163
|
+
return "strongText";
|
|
164
|
+
}
|
|
132
165
|
const component = componentRole(program, id, property);
|
|
133
166
|
if (component !== undefined) return component;
|
|
134
167
|
const layer = findLayer(program, id);
|
|
135
168
|
if (layer !== undefined && ["fill", "stroke"].includes(property)) {
|
|
136
|
-
if (
|
|
169
|
+
if (isFieldDrivenMark(program, id, property)) return undefined;
|
|
137
170
|
if (property === "fill" && isRegressionBand(program, id)) {
|
|
138
171
|
return "regressionBand";
|
|
139
172
|
}
|
|
@@ -176,7 +209,7 @@ function isOverridden(overrides, key) {
|
|
|
176
209
|
}
|
|
177
210
|
|
|
178
211
|
function isFieldDrivenLegendSymbol(id) {
|
|
179
|
-
return /(?:color|series)Legend(?:Symbol|Symbols|
|
|
212
|
+
return /(?:color|series|stroke)(?:Legend|Gradient|Interval)(?:Symbol|Symbols|Strips|Swatches|Lines|Points)/iu
|
|
180
213
|
.test(id);
|
|
181
214
|
}
|
|
182
215
|
|
|
@@ -185,14 +218,14 @@ function mapGraphicProperties(
|
|
|
185
218
|
id,
|
|
186
219
|
type,
|
|
187
220
|
properties,
|
|
188
|
-
|
|
221
|
+
sourceTokens,
|
|
189
222
|
targetTokens,
|
|
190
223
|
overrides,
|
|
191
224
|
overrideId = id
|
|
192
225
|
) {
|
|
193
226
|
let changed = false;
|
|
194
227
|
const next = { ...properties };
|
|
195
|
-
for (const property of
|
|
228
|
+
for (const property of THEME_STYLE_PROPERTIES) {
|
|
196
229
|
if (!Object.hasOwn(properties, property)) continue;
|
|
197
230
|
if (isOverridden(overrides, `g:${overrideId}.${property}`) ||
|
|
198
231
|
isOverridden(overrides, `g:${id}.${property}`)) continue;
|
|
@@ -202,7 +235,7 @@ function mapGraphicProperties(
|
|
|
202
235
|
const mapped = mapRoleValue(
|
|
203
236
|
properties[property],
|
|
204
237
|
role,
|
|
205
|
-
|
|
238
|
+
sourceTokens,
|
|
206
239
|
targetTokens,
|
|
207
240
|
roleLegacy(role)
|
|
208
241
|
);
|
|
@@ -214,21 +247,28 @@ function mapGraphicProperties(
|
|
|
214
247
|
return { changed, properties: changed ? next : properties, type };
|
|
215
248
|
}
|
|
216
249
|
|
|
217
|
-
function recolorGraphic(
|
|
250
|
+
function recolorGraphic(
|
|
251
|
+
program,
|
|
252
|
+
id,
|
|
253
|
+
graphic,
|
|
254
|
+
sourceTokens,
|
|
255
|
+
targetTokens,
|
|
256
|
+
overrides
|
|
257
|
+
) {
|
|
218
258
|
if (graphic.items === undefined) {
|
|
219
259
|
const mapped = mapGraphicProperties(
|
|
220
260
|
program,
|
|
221
261
|
id,
|
|
222
262
|
graphic.type,
|
|
223
263
|
graphic.properties,
|
|
224
|
-
|
|
264
|
+
sourceTokens,
|
|
225
265
|
targetTokens,
|
|
226
266
|
overrides,
|
|
227
267
|
id
|
|
228
268
|
);
|
|
229
269
|
if (!mapped.changed) return program;
|
|
230
270
|
let next = program;
|
|
231
|
-
for (const property of
|
|
271
|
+
for (const property of THEME_STYLE_PROPERTIES) {
|
|
232
272
|
if (mapped.properties[property] !== graphic.properties[property]) {
|
|
233
273
|
next = next.editGraphics({
|
|
234
274
|
target: id,
|
|
@@ -248,7 +288,7 @@ function recolorGraphic(program, id, graphic, names, targetTokens, overrides) {
|
|
|
248
288
|
id,
|
|
249
289
|
type,
|
|
250
290
|
item.properties,
|
|
251
|
-
|
|
291
|
+
sourceTokens,
|
|
252
292
|
targetTokens,
|
|
253
293
|
overrides,
|
|
254
294
|
item.id
|
|
@@ -263,6 +303,13 @@ function recolorGraphic(program, id, graphic, names, targetTokens, overrides) {
|
|
|
263
303
|
|
|
264
304
|
function configRole(program, path) {
|
|
265
305
|
const property = path.at(-1);
|
|
306
|
+
if (property === "fontFamily") return "fontFamily";
|
|
307
|
+
if (path[0] === "facets" && path.includes("headers") &&
|
|
308
|
+
property === "color") {
|
|
309
|
+
return path[path.indexOf("headers") + 1] === "row"
|
|
310
|
+
? "mutedText"
|
|
311
|
+
: "strongText";
|
|
312
|
+
}
|
|
266
313
|
if (path[0] === "marks" && ["fill", "stroke"].includes(property)) {
|
|
267
314
|
if (path.includes("boxPlot") && path.includes("median") &&
|
|
268
315
|
property === "stroke") return "boxMedian";
|
|
@@ -271,7 +318,7 @@ function configRole(program, path) {
|
|
|
271
318
|
const component = componentRole(program, path[1], property);
|
|
272
319
|
if (component !== undefined) return component;
|
|
273
320
|
const layer = findLayer(program, path[1]);
|
|
274
|
-
if (
|
|
321
|
+
if (isFieldDrivenMark(program, path[1], property)) return undefined;
|
|
275
322
|
if (property === "fill" && isRegressionBand(program, path[1])) {
|
|
276
323
|
return "regressionBand";
|
|
277
324
|
}
|
|
@@ -298,7 +345,7 @@ function configRole(program, path) {
|
|
|
298
345
|
function recolorConfig(
|
|
299
346
|
program,
|
|
300
347
|
value,
|
|
301
|
-
|
|
348
|
+
sourceTokens,
|
|
302
349
|
targetTokens,
|
|
303
350
|
overrides,
|
|
304
351
|
path = []
|
|
@@ -309,7 +356,7 @@ function recolorConfig(
|
|
|
309
356
|
const mapped = recolorConfig(
|
|
310
357
|
program,
|
|
311
358
|
item,
|
|
312
|
-
|
|
359
|
+
sourceTokens,
|
|
313
360
|
targetTokens,
|
|
314
361
|
overrides,
|
|
315
362
|
[...path, index]
|
|
@@ -321,7 +368,8 @@ function recolorConfig(
|
|
|
321
368
|
}
|
|
322
369
|
if (value === null || typeof value !== "object") {
|
|
323
370
|
const property = path.at(-1);
|
|
324
|
-
if (!["color", "fill", "stroke", "background"]
|
|
371
|
+
if (!["color", "fill", "stroke", "background", "fontFamily"]
|
|
372
|
+
.includes(property)) {
|
|
325
373
|
return { changed: false, value };
|
|
326
374
|
}
|
|
327
375
|
if (isOverridden(overrides, `c:${path.join(".")}`)) {
|
|
@@ -332,8 +380,14 @@ function recolorConfig(
|
|
|
332
380
|
}
|
|
333
381
|
const role = configRole(program, path);
|
|
334
382
|
return role === undefined
|
|
335
|
-
? mapColorValue(value,
|
|
336
|
-
: mapRoleValue(
|
|
383
|
+
? mapColorValue(value, sourceTokens, targetTokens)
|
|
384
|
+
: mapRoleValue(
|
|
385
|
+
value,
|
|
386
|
+
role,
|
|
387
|
+
sourceTokens,
|
|
388
|
+
targetTokens,
|
|
389
|
+
roleLegacy(role)
|
|
390
|
+
);
|
|
337
391
|
}
|
|
338
392
|
if (["theme", "highlights", "selections"].includes(path[0])) {
|
|
339
393
|
return { changed: false, value };
|
|
@@ -344,7 +398,7 @@ function recolorConfig(
|
|
|
344
398
|
const mapped = recolorConfig(
|
|
345
399
|
program,
|
|
346
400
|
item,
|
|
347
|
-
|
|
401
|
+
sourceTokens,
|
|
348
402
|
targetTokens,
|
|
349
403
|
overrides,
|
|
350
404
|
[...path, key]
|
|
@@ -355,6 +409,11 @@ function recolorConfig(
|
|
|
355
409
|
return { changed, value: changed ? next : value };
|
|
356
410
|
}
|
|
357
411
|
|
|
412
|
+
function addMarkPropertyOverrides(overrides, id, property) {
|
|
413
|
+
overrides.add(`c:marks.${id}.${property}`);
|
|
414
|
+
overrides.add(`g:${id}.${property}`);
|
|
415
|
+
}
|
|
416
|
+
|
|
358
417
|
function addMarkOverrides(overrides, program, op, args) {
|
|
359
418
|
const match = /^(?:create|edit)(Point|Line|Area|Arc|Bar|Rect|Rule|Tick|Text)Mark$/u
|
|
360
419
|
.exec(op);
|
|
@@ -367,44 +426,45 @@ function addMarkOverrides(overrides, program, op, args) {
|
|
|
367
426
|
(op.startsWith("create") ? type : undefined) ??
|
|
368
427
|
(candidates.length === 1 ? candidates[0].id : program.context.currentMark);
|
|
369
428
|
if (typeof id !== "string") return;
|
|
370
|
-
for (const property of ["fill", "stroke"]) {
|
|
429
|
+
for (const property of ["fill", "stroke", "fontFamily"]) {
|
|
371
430
|
if (!Object.hasOwn(args, property)) continue;
|
|
372
|
-
overrides
|
|
373
|
-
overrides.add(`g:${id}.${property}`);
|
|
431
|
+
addMarkPropertyOverrides(overrides, id, property);
|
|
374
432
|
}
|
|
375
433
|
}
|
|
376
434
|
|
|
377
435
|
function addFacadeMarkOverrides(overrides, op, args) {
|
|
378
436
|
const facades = {
|
|
379
|
-
createScatterPlot: ["
|
|
380
|
-
createLinePlot: ["
|
|
381
|
-
createPolarScatterPlot: ["
|
|
382
|
-
createPolarLinePlot: ["
|
|
383
|
-
createRadarPlot: ["
|
|
384
|
-
createRugPlot: ["
|
|
385
|
-
createStripPlot: ["
|
|
386
|
-
createBeeswarmPlot: ["
|
|
387
|
-
createBarPlot: ["
|
|
388
|
-
createAreaPlot: ["
|
|
389
|
-
createPiePlot: ["
|
|
390
|
-
createRosePlot: ["
|
|
391
|
-
createRadialBarPlot: ["
|
|
392
|
-
createDensityPlot: ["
|
|
393
|
-
createParallelCoordinates: ["
|
|
394
|
-
createHistogram: ["
|
|
395
|
-
createHeatmap: ["
|
|
396
|
-
createHorizonPlot: ["
|
|
397
|
-
editHorizon: ["
|
|
398
|
-
createViolinPlot: ["
|
|
437
|
+
createScatterPlot: ["point", true],
|
|
438
|
+
createLinePlot: ["line", false],
|
|
439
|
+
createPolarScatterPlot: ["point", true],
|
|
440
|
+
createPolarLinePlot: ["line", false],
|
|
441
|
+
createRadarPlot: ["line", false],
|
|
442
|
+
createRugPlot: ["tick", false],
|
|
443
|
+
createStripPlot: ["point", true],
|
|
444
|
+
createBeeswarmPlot: ["point", true],
|
|
445
|
+
createBarPlot: ["bar", true],
|
|
446
|
+
createAreaPlot: ["area", true],
|
|
447
|
+
createPiePlot: ["arc", true],
|
|
448
|
+
createRosePlot: ["arc", true],
|
|
449
|
+
createRadialBarPlot: ["arc", true],
|
|
450
|
+
createDensityPlot: ["area", true],
|
|
451
|
+
createParallelCoordinates: ["line", false],
|
|
452
|
+
createHistogram: ["bar", true],
|
|
453
|
+
createHeatmap: ["rect", false],
|
|
454
|
+
createHorizonPlot: ["area", false],
|
|
455
|
+
editHorizon: ["area", false],
|
|
456
|
+
createViolinPlot: ["area", true]
|
|
399
457
|
};
|
|
400
458
|
const facade = facades[op];
|
|
401
459
|
if (facade === undefined) return;
|
|
402
|
-
const [
|
|
460
|
+
const [option, includesFill] = facade;
|
|
461
|
+
const subject = op === "editHorizon" ? "HorizonPlot" : op.slice(6);
|
|
462
|
+
const defaultId = subject[0].toLowerCase() + subject.slice(1);
|
|
463
|
+
const properties = includesFill ? ["fill", "stroke"] : ["stroke"];
|
|
403
464
|
const id = args.id ?? defaultId;
|
|
404
465
|
for (const property of properties) {
|
|
405
466
|
if (!Object.hasOwn(args[option] ?? {}, property)) continue;
|
|
406
|
-
overrides
|
|
407
|
-
overrides.add(`g:${id}.${property}`);
|
|
467
|
+
addMarkPropertyOverrides(overrides, id, property);
|
|
408
468
|
}
|
|
409
469
|
}
|
|
410
470
|
|
|
@@ -431,12 +491,13 @@ function resolveConfiguredOwner(program, key, requested, defaultId) {
|
|
|
431
491
|
}
|
|
432
492
|
|
|
433
493
|
function addTextFacadeOverrides(overrides, node) {
|
|
434
|
-
if (!["createAnnotation", "createMarkLabels"].includes(node.op)
|
|
435
|
-
!Object.hasOwn(node.args, "fill")) return;
|
|
494
|
+
if (!["createAnnotation", "createMarkLabels"].includes(node.op)) return;
|
|
436
495
|
const id = findDescendant(node, "createTextMark")?.args.id ?? node.args.id;
|
|
437
496
|
if (typeof id !== "string") return;
|
|
438
|
-
|
|
439
|
-
|
|
497
|
+
for (const property of ["fill", "fontFamily"]) {
|
|
498
|
+
if (!Object.hasOwn(node.args, property)) continue;
|
|
499
|
+
addMarkPropertyOverrides(overrides, id, property);
|
|
500
|
+
}
|
|
440
501
|
}
|
|
441
502
|
|
|
442
503
|
function addErrorBarOverrides(overrides, program, node) {
|
|
@@ -451,8 +512,7 @@ function addErrorBarOverrides(overrides, program, node) {
|
|
|
451
512
|
if (id === undefined) return;
|
|
452
513
|
const config = program.markConfigs[id].errorBar;
|
|
453
514
|
for (const target of [id, config.lowerCapId, config.upperCapId]) {
|
|
454
|
-
overrides
|
|
455
|
-
overrides.add(`g:${target}.stroke`);
|
|
515
|
+
addMarkPropertyOverrides(overrides, target, "stroke");
|
|
456
516
|
}
|
|
457
517
|
}
|
|
458
518
|
|
|
@@ -478,8 +538,7 @@ function addErrorBandOverrides(overrides, program, node) {
|
|
|
478
538
|
const targets = errorBandTargets(program, node);
|
|
479
539
|
if (targets === undefined) return;
|
|
480
540
|
if (Object.hasOwn(node.args, "fill") && node.args.fill !== false) {
|
|
481
|
-
overrides
|
|
482
|
-
overrides.add(`g:${targets.id}.fill`);
|
|
541
|
+
addMarkPropertyOverrides(overrides, targets.id, "fill");
|
|
483
542
|
}
|
|
484
543
|
const boundary = node.op === "editErrorBandBoundary"
|
|
485
544
|
? node.args
|
|
@@ -490,8 +549,7 @@ function addErrorBandOverrides(overrides, program, node) {
|
|
|
490
549
|
? [targets.lower, targets.upper]
|
|
491
550
|
: [(node.args.boundary === "lower" ? targets.lower : targets.upper)];
|
|
492
551
|
for (const id of ids) {
|
|
493
|
-
overrides
|
|
494
|
-
overrides.add(`g:${id}.stroke`);
|
|
552
|
+
addMarkPropertyOverrides(overrides, id, "stroke");
|
|
495
553
|
}
|
|
496
554
|
}
|
|
497
555
|
|
|
@@ -531,8 +589,7 @@ function addReferenceOverrides(overrides, op, args) {
|
|
|
531
589
|
const id = args.id ?? defaultId;
|
|
532
590
|
for (const property of properties) {
|
|
533
591
|
if (!Object.hasOwn(args, property)) continue;
|
|
534
|
-
overrides
|
|
535
|
-
overrides.add(`g:${id}.${property}`);
|
|
592
|
+
addMarkPropertyOverrides(overrides, id, property);
|
|
536
593
|
}
|
|
537
594
|
}
|
|
538
595
|
|
|
@@ -553,12 +610,10 @@ function addRegressionOverrides(overrides, node) {
|
|
|
553
610
|
: child?.args.id ?? child?.args.target;
|
|
554
611
|
if (typeof id !== "string") return;
|
|
555
612
|
if (Object.hasOwn(style ?? {}, "color")) {
|
|
556
|
-
overrides
|
|
557
|
-
overrides.add(`g:${id}.fill`);
|
|
613
|
+
addMarkPropertyOverrides(overrides, id, "fill");
|
|
558
614
|
}
|
|
559
615
|
if (Object.hasOwn(style ?? {}, "stroke")) {
|
|
560
|
-
overrides
|
|
561
|
-
overrides.add(`g:${id}.stroke`);
|
|
616
|
+
addMarkPropertyOverrides(overrides, id, "stroke");
|
|
562
617
|
}
|
|
563
618
|
}
|
|
564
619
|
|
|
@@ -651,7 +706,7 @@ function clearRecreatedOverrides(overrides, op, args) {
|
|
|
651
706
|
function addAxisOverrides(overrides, op, args) {
|
|
652
707
|
const match = /^(?:create|edit)(X|Y|Theta|Radial)Axis(Line|Ticks|Labels|Title)$/u
|
|
653
708
|
.exec(op);
|
|
654
|
-
if (match === null
|
|
709
|
+
if (match === null) return;
|
|
655
710
|
const channel = {
|
|
656
711
|
X: "x",
|
|
657
712
|
Y: "y",
|
|
@@ -660,10 +715,17 @@ function addAxisOverrides(overrides, op, args) {
|
|
|
660
715
|
}[match[1]];
|
|
661
716
|
const component = match[2].toLowerCase();
|
|
662
717
|
const idPrefix = match[1] === "Radial" ? "radial" : channel;
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
718
|
+
if (Object.hasOwn(args, "color")) {
|
|
719
|
+
overrides.add(`c:guides.axis.${channel}.${component}.color`);
|
|
720
|
+
overrides.add(
|
|
721
|
+
`g:${idPrefix}Axis${match[2]}.${component === "line" || component === "ticks" ? "stroke" : "fill"}`
|
|
722
|
+
);
|
|
723
|
+
}
|
|
724
|
+
if (["labels", "title"].includes(component) &&
|
|
725
|
+
Object.hasOwn(args, "fontFamily")) {
|
|
726
|
+
overrides.add(`c:guides.axis.${channel}.${component}.fontFamily`);
|
|
727
|
+
overrides.add(`g:${idPrefix}Axis${match[2]}.fontFamily`);
|
|
728
|
+
}
|
|
667
729
|
}
|
|
668
730
|
|
|
669
731
|
function addCompleteAxisOverride(overrides, name, args) {
|
|
@@ -680,12 +742,18 @@ function addCompleteAxisOverride(overrides, name, args) {
|
|
|
680
742
|
components.labels = args.ticksAndLabels.labels;
|
|
681
743
|
}
|
|
682
744
|
for (const [component, options] of Object.entries(components)) {
|
|
683
|
-
if (!Object.hasOwn(options ?? {}, "color")) continue;
|
|
684
745
|
const suffix = `${component[0].toUpperCase()}${component.slice(1)}`;
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
746
|
+
if (Object.hasOwn(options ?? {}, "color")) {
|
|
747
|
+
overrides.add(`c:guides.axis.${channel}.${component}.color`);
|
|
748
|
+
overrides.add(
|
|
749
|
+
`g:${idPrefix}Axis${suffix}.${component === "line" || component === "ticks" ? "stroke" : "fill"}`
|
|
750
|
+
);
|
|
751
|
+
}
|
|
752
|
+
if (["labels", "title"].includes(component) &&
|
|
753
|
+
Object.hasOwn(options ?? {}, "fontFamily")) {
|
|
754
|
+
overrides.add(`c:guides.axis.${channel}.${component}.fontFamily`);
|
|
755
|
+
overrides.add(`g:${idPrefix}Axis${suffix}.fontFamily`);
|
|
756
|
+
}
|
|
689
757
|
}
|
|
690
758
|
}
|
|
691
759
|
|
|
@@ -722,9 +790,14 @@ function addTitleOverrides(overrides, args) {
|
|
|
722
790
|
["titleStyle", "chartTitle"],
|
|
723
791
|
["subtitleStyle", "chartSubtitle"]
|
|
724
792
|
]) {
|
|
725
|
-
if (
|
|
726
|
-
|
|
727
|
-
|
|
793
|
+
if (Object.hasOwn(args[option] ?? {}, "color")) {
|
|
794
|
+
overrides.add(`c:title.${option}.color`);
|
|
795
|
+
overrides.add(`g:${id}.fill`);
|
|
796
|
+
}
|
|
797
|
+
if (Object.hasOwn(args[option] ?? {}, "fontFamily")) {
|
|
798
|
+
overrides.add(`c:title.${option}.fontFamily`);
|
|
799
|
+
overrides.add(`g:${id}.fontFamily`);
|
|
800
|
+
}
|
|
728
801
|
}
|
|
729
802
|
}
|
|
730
803
|
|
|
@@ -744,46 +817,110 @@ function addParallelOverrides(overrides, program, op, args) {
|
|
|
744
817
|
components.labels = args.ticksAndLabels.labels;
|
|
745
818
|
}
|
|
746
819
|
for (const [component, options] of Object.entries(components)) {
|
|
747
|
-
if (
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
820
|
+
if (Object.hasOwn(options ?? {}, "color")) {
|
|
821
|
+
overrides.add(
|
|
822
|
+
`c:guides.axis.parallel.axes.dimensions.${index}.${component}.color`
|
|
823
|
+
);
|
|
824
|
+
}
|
|
825
|
+
if (["labels", "title"].includes(component) &&
|
|
826
|
+
Object.hasOwn(options ?? {}, "fontFamily")) {
|
|
827
|
+
overrides.add(
|
|
828
|
+
`c:guides.axis.parallel.axes.dimensions.${index}.${component}.fontFamily`
|
|
829
|
+
);
|
|
830
|
+
}
|
|
751
831
|
}
|
|
752
832
|
}
|
|
753
833
|
|
|
754
834
|
function addLegendOverrides(overrides, program, args) {
|
|
755
|
-
const
|
|
756
|
-
|
|
835
|
+
for (const kind of Object.keys(program.guideConfigs.legend ?? {})) {
|
|
836
|
+
const prefix = legendPrefix(kind);
|
|
837
|
+
for (const [option, suffix, property, graphicProperty] of [
|
|
838
|
+
["labels", "Labels", "color", "fill"],
|
|
839
|
+
["labels", "Labels", "fontFamily", "fontFamily"],
|
|
840
|
+
["titleStyle", "Title", "color", "fill"],
|
|
841
|
+
["titleStyle", "Title", "fontFamily", "fontFamily"],
|
|
842
|
+
["border", "Background", "color", "stroke"]
|
|
843
|
+
]) {
|
|
844
|
+
if (!Object.hasOwn(args[option] ?? {}, property)) continue;
|
|
845
|
+
overrides.add(`c:guides.legend.${kind}.${option}.${property}`);
|
|
846
|
+
if (prefix) overrides.add(`g:${prefix}${suffix}.${graphicProperty}`);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
function legendPrefix(kind) {
|
|
852
|
+
return {
|
|
757
853
|
series: "seriesLegend",
|
|
758
854
|
color: "colorLegend",
|
|
855
|
+
stroke: "strokeLegend",
|
|
759
856
|
interval: "colorLegend",
|
|
760
857
|
gradient: "colorGradient",
|
|
858
|
+
strokeInterval: "strokeInterval",
|
|
859
|
+
strokeGradient: "strokeGradient",
|
|
761
860
|
size: "sizeLegend",
|
|
762
861
|
strokeWidth: "strokeWidthLegend",
|
|
763
862
|
opacity: "opacityLegend"
|
|
764
|
-
};
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
if (
|
|
771
|
-
|
|
772
|
-
|
|
863
|
+
}[kind];
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
function addLegendBlockOverrides(overrides, program, args) {
|
|
867
|
+
if (typeof args.target !== "string" || typeof args.channel !== "string") return;
|
|
868
|
+
for (const [kind, config] of Object.entries(program.guideConfigs.legend ?? {})) {
|
|
869
|
+
if (config.target !== args.target) continue;
|
|
870
|
+
const key = Object.keys(config.blockOverrides ?? {}).find(value => {
|
|
871
|
+
try {
|
|
872
|
+
return JSON.parse(value).includes(args.channel);
|
|
873
|
+
} catch {
|
|
874
|
+
return false;
|
|
875
|
+
}
|
|
876
|
+
});
|
|
877
|
+
if (key === undefined) continue;
|
|
878
|
+
const prefix = `c:guides.legend.${kind}.blockOverrides.${key}`;
|
|
879
|
+
const graphic = legendPrefix(kind);
|
|
880
|
+
for (const [property, graphicProperty] of [
|
|
881
|
+
["color", "fill"],
|
|
882
|
+
["fontFamily", "fontFamily"]
|
|
883
|
+
]) {
|
|
884
|
+
if (!Object.hasOwn(args.text ?? {}, property)) continue;
|
|
885
|
+
overrides.add(`${prefix}.text.${property}`);
|
|
886
|
+
if (graphic !== undefined) {
|
|
887
|
+
overrides.add(`g:${graphic}Labels.${graphicProperty}`);
|
|
888
|
+
}
|
|
773
889
|
}
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
890
|
+
for (const property of ["fill", "stroke"]) {
|
|
891
|
+
if (!Object.hasOwn(args.symbol ?? {}, property)) continue;
|
|
892
|
+
overrides.add(`${prefix}.symbol.${property}`);
|
|
893
|
+
if (graphic !== undefined) {
|
|
894
|
+
overrides.add(`g:${graphic}Symbols.${property}`);
|
|
895
|
+
}
|
|
777
896
|
}
|
|
778
897
|
}
|
|
779
898
|
}
|
|
780
899
|
|
|
900
|
+
function addFacetOverrides(overrides, program, args) {
|
|
901
|
+
const id = program.compositionSpec?.type === "facet"
|
|
902
|
+
? program.compositionSpec.id
|
|
903
|
+
: undefined;
|
|
904
|
+
if (id === undefined) return;
|
|
905
|
+
const owner = args.role === undefined || args.role === "all"
|
|
906
|
+
? "common"
|
|
907
|
+
: args.role;
|
|
908
|
+
for (const [property, graphicProperty] of [
|
|
909
|
+
["color", "fill"],
|
|
910
|
+
["fontFamily", "fontFamily"]
|
|
911
|
+
]) {
|
|
912
|
+
if (!Object.hasOwn(args, property)) continue;
|
|
913
|
+
overrides.add(`c:facets.${id}.headers.${owner}.${property}`);
|
|
914
|
+
overrides.add(`g:${id}-headers.${graphicProperty}`);
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
|
|
781
918
|
function explicitOverrides(program, node) {
|
|
782
919
|
const { op, args } = node;
|
|
783
920
|
const overrides = new Set();
|
|
784
921
|
if (op === "editGraphics" &&
|
|
785
922
|
typeof args.target === "string" &&
|
|
786
|
-
["background", "fill", "stroke"].includes(args.property)) {
|
|
923
|
+
["background", "fill", "stroke", "fontFamily"].includes(args.property)) {
|
|
787
924
|
overrides.add(`g:${args.target}.${args.property}`);
|
|
788
925
|
}
|
|
789
926
|
if (["createCanvas", "editCanvas"].includes(op) &&
|
|
@@ -818,6 +955,12 @@ function explicitOverrides(program, node) {
|
|
|
818
955
|
if (op === "editLegendBorder") {
|
|
819
956
|
addLegendOverrides(overrides, program, args);
|
|
820
957
|
}
|
|
958
|
+
if (op === "editLegendBlock") {
|
|
959
|
+
addLegendBlockOverrides(overrides, program, args);
|
|
960
|
+
}
|
|
961
|
+
if (op === "editFacetHeaders") {
|
|
962
|
+
addFacetOverrides(overrides, program, args);
|
|
963
|
+
}
|
|
821
964
|
return overrides;
|
|
822
965
|
}
|
|
823
966
|
|
|
@@ -836,22 +979,41 @@ function collectOverrides(program) {
|
|
|
836
979
|
}
|
|
837
980
|
|
|
838
981
|
export function reconcileProgramTheme(program, { source, metadata }) {
|
|
839
|
-
const
|
|
840
|
-
if (
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
982
|
+
const storedState = program.materializationConfigs.theme;
|
|
983
|
+
if (storedState === undefined) return program;
|
|
984
|
+
// Data/context/trace-only transitions cannot change authored appearance.
|
|
985
|
+
// Preserve all visual identities instead of rescanning every existing mark.
|
|
986
|
+
if (program.graphicSpec === source.graphicSpec &&
|
|
987
|
+
program.materializationConfigs === source.materializationConfigs &&
|
|
988
|
+
program.children === source.children &&
|
|
989
|
+
program.compositionSpec === source.compositionSpec &&
|
|
990
|
+
["layers", "scales", "coordinates", "guides", "title"].every(
|
|
991
|
+
key => program.semanticSpec[key] === source.semanticSpec[key]
|
|
992
|
+
)) return program;
|
|
993
|
+
const composition = program.compositionSpec !== undefined;
|
|
994
|
+
const state = normalizeThemeState(storedState, { composition });
|
|
995
|
+
const sourceState = normalizeThemeState(
|
|
996
|
+
source.materializationConfigs.theme,
|
|
997
|
+
{ composition }
|
|
998
|
+
);
|
|
999
|
+
const sourceRootTokens = resolveEffectiveThemeTokens(sourceState);
|
|
1000
|
+
const targetRootTokens = resolveEffectiveThemeTokens(state);
|
|
1001
|
+
const contentState = current => composition
|
|
1002
|
+
? { frames: current.frames.filter(frame => frame.scope === "descendants") }
|
|
1003
|
+
: current;
|
|
1004
|
+
const sourceTokens = resolveEffectiveThemeTokens(contentState(sourceState));
|
|
1005
|
+
const targetTokens = resolveEffectiveThemeTokens(contentState(state));
|
|
844
1006
|
const overrides = collectOverrides(program);
|
|
845
1007
|
|
|
846
|
-
let themed = program._withMaterializationConfig(
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
1008
|
+
let themed = program._withMaterializationConfig(
|
|
1009
|
+
["theme"],
|
|
1010
|
+
setThemeStateOverrides(state, [...overrides])
|
|
1011
|
+
);
|
|
850
1012
|
|
|
851
1013
|
const configs = recolorConfig(
|
|
852
1014
|
themed,
|
|
853
1015
|
themed.materializationConfigs,
|
|
854
|
-
|
|
1016
|
+
sourceTokens,
|
|
855
1017
|
targetTokens,
|
|
856
1018
|
overrides
|
|
857
1019
|
);
|
|
@@ -863,19 +1025,49 @@ export function reconcileProgramTheme(program, { source, metadata }) {
|
|
|
863
1025
|
if (configs.changed && configs.value.title !== undefined) {
|
|
864
1026
|
next = next._withMaterializationConfig(["title"], configs.value.title);
|
|
865
1027
|
}
|
|
1028
|
+
if (configs.changed && configs.value.facets !== undefined) {
|
|
1029
|
+
next = next._withMaterializationConfig(["facets"], configs.value.facets);
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
const facetsChanged = configs.value.facets !==
|
|
1033
|
+
themed.materializationConfigs.facets;
|
|
1034
|
+
const typographyChanged = sourceTokens.fontFamily !== targetTokens.fontFamily;
|
|
1035
|
+
if (facetsChanged && next.compositionSpec?.type === "facet") {
|
|
1036
|
+
next = next.materializeComposition();
|
|
1037
|
+
} else if (typographyChanged) {
|
|
1038
|
+
next = rematerializeTypography(next);
|
|
1039
|
+
}
|
|
866
1040
|
|
|
867
1041
|
const parallelChanged = configs.value.guides.axis?.parallel !==
|
|
868
1042
|
themed.guideConfigs.axis?.parallel;
|
|
869
|
-
if (parallelChanged && typeof next.rematerializeParallelAxes === "function") {
|
|
1043
|
+
if (parallelChanged && !typographyChanged && typeof next.rematerializeParallelAxes === "function") {
|
|
870
1044
|
next = next.rematerializeParallelAxes();
|
|
871
1045
|
}
|
|
872
1046
|
|
|
873
1047
|
for (const [id, graphic] of Object.entries(next.graphicSpec.objects)) {
|
|
874
1048
|
if (parallelChanged && id.startsWith("parallelAxis")) continue;
|
|
875
|
-
|
|
1049
|
+
const graphicSourceTokens = id === "canvas"
|
|
1050
|
+
? sourceRootTokens
|
|
1051
|
+
: sourceTokens;
|
|
1052
|
+
const graphicTargetTokens = id === "canvas"
|
|
1053
|
+
? targetRootTokens
|
|
1054
|
+
: targetTokens;
|
|
1055
|
+
next = recolorGraphic(
|
|
1056
|
+
next,
|
|
1057
|
+
id,
|
|
1058
|
+
graphic,
|
|
1059
|
+
graphicSourceTokens,
|
|
1060
|
+
graphicTargetTokens,
|
|
1061
|
+
overrides
|
|
1062
|
+
);
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
if (sourceTokens.highlight !== targetTokens.highlight) {
|
|
1066
|
+
next = rematerializeThemeHighlights(next);
|
|
876
1067
|
}
|
|
877
1068
|
|
|
878
|
-
if (
|
|
1069
|
+
if (metadata.op === "removeTheme" && state.frames.length === 0 &&
|
|
1070
|
+
(state.descendantFrames?.length ?? 0) === 0) {
|
|
879
1071
|
next = next._withoutMaterializationConfig(["theme"]);
|
|
880
1072
|
}
|
|
881
1073
|
return next;
|