ggaction 0.0.12 → 0.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +127 -0
- package/README.md +20 -11
- package/knowledge/action-card.schema.json +1 -1
- package/knowledge/action-cards.json +1 -1
- package/knowledge/action-cards.schema.json +1 -1
- package/knowledge/extension-authoring.md +2 -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 +10 -3
- package/src/actions/basic.js +4 -2
- package/src/actions/boxPlots/components.js +14 -4
- package/src/actions/boxPlots/edit.js +31 -5
- package/src/actions/boxPlots/materialize.js +13 -4
- package/src/actions/boxPlots/options.js +48 -16
- package/src/actions/canvas/actions.js +3 -1
- package/src/actions/canvas/fitting.js +211 -0
- package/src/actions/canvas/index.js +2 -0
- package/src/actions/categoryOrder/index.js +18 -6
- package/src/actions/charts/area.js +51 -0
- package/src/actions/charts/bar.js +13 -6
- package/src/actions/charts/beeswarm.js +70 -0
- package/src/actions/charts/density.js +48 -0
- package/src/actions/charts/ecdf.js +186 -0
- package/src/actions/charts/endpoints.js +473 -0
- package/src/actions/charts/heatmap.js +4 -3
- package/src/actions/charts/histogram.js +5 -4
- package/src/actions/charts/horizon.js +73 -0
- package/src/actions/charts/index.js +37 -0
- package/src/actions/charts/interval-regression.js +172 -0
- package/src/actions/charts/line.js +8 -6
- package/src/actions/charts/parallel.js +8 -2
- package/src/actions/charts/pie.js +52 -0
- package/src/actions/charts/polar.js +134 -0
- package/src/actions/charts/radar.js +305 -0
- package/src/actions/charts/radial.js +50 -0
- package/src/actions/charts/raincloud.js +422 -0
- package/src/actions/charts/rug-strip.js +283 -0
- package/src/actions/charts/scatter.js +11 -3
- package/src/actions/charts/shared.js +69 -10
- package/src/actions/composition/actions.js +129 -3
- package/src/actions/coordinates/actions.js +1 -1
- package/src/actions/coordinates/edit.js +76 -0
- package/src/actions/data/bin.js +29 -0
- package/src/actions/data/bin2d.js +57 -185
- package/src/actions/data/complete.js +26 -0
- package/src/actions/data/computed.js +24 -0
- package/src/actions/data/create.js +2 -2
- package/src/actions/data/density.js +12 -20
- package/src/actions/data/derived.js +111 -15
- package/src/actions/data/ecdf.js +38 -0
- package/src/actions/data/edit.js +539 -0
- package/src/actions/data/filter.js +226 -30
- package/src/actions/data/fold.js +24 -0
- package/src/actions/data/impute.js +26 -0
- package/src/actions/data/index.js +38 -0
- package/src/actions/data/interval.js +2 -0
- package/src/actions/data/intervalEdit.js +86 -4
- package/src/actions/data/normalize.js +27 -0
- package/src/actions/data/regression.js +19 -21
- package/src/actions/data/revisionBindings.js +145 -0
- package/src/actions/data/shared.js +20 -4
- package/src/actions/data/stack.js +26 -0
- package/src/actions/data/summary.js +26 -0
- package/src/actions/data/timeUnit.js +4 -1
- package/src/actions/data/window.js +1 -1
- package/src/actions/encodings/appearance.js +32 -13
- package/src/actions/encodings/barWidth.js +8 -0
- package/src/actions/encodings/basic.js +2 -0
- package/src/actions/encodings/channels.js +415 -0
- package/src/actions/encodings/color/continuous.js +19 -59
- package/src/actions/encodings/color/index.js +30 -23
- package/src/actions/encodings/color/layout.js +3 -97
- package/src/actions/encodings/color/policy.js +5 -8
- package/src/actions/encodings/density/resolve.js +37 -5
- package/src/actions/encodings/density.js +124 -34
- package/src/actions/encodings/histogram.js +3 -2
- package/src/actions/encodings/horizon/resolve.js +8 -4
- package/src/actions/encodings/horizon.js +18 -8
- package/src/actions/encodings/index.js +6 -0
- package/src/actions/encodings/offset.js +30 -29
- package/src/actions/encodings/parallel.js +2 -8
- package/src/actions/encodings/position/apply.js +63 -11
- package/src/actions/encodings/position/index.js +52 -6
- package/src/actions/encodings/position/policies/arc.js +14 -0
- package/src/actions/encodings/position/policies/area.js +4 -42
- package/src/actions/encodings/position/policies/bar.js +57 -13
- package/src/actions/encodings/position/resolve.js +88 -26
- package/src/actions/encodings/ranged.js +123 -97
- package/src/actions/encodings/remove.js +44 -34
- package/src/actions/encodings/ruleAppearance.js +24 -32
- package/src/actions/encodings/seriesLayout.js +105 -0
- package/src/actions/encodings/shared.js +107 -37
- package/src/actions/encodings/stroke.js +226 -0
- package/src/actions/encodings/strokeDash.js +9 -6
- package/src/actions/encodings/temporal.js +7 -0
- package/src/actions/encodings/text.js +30 -15
- package/src/actions/errorBands/create.js +41 -8
- package/src/actions/errorBands/edit.js +360 -25
- package/src/actions/errorBars/create.js +56 -17
- package/src/actions/errorBars/edit.js +372 -21
- package/src/actions/facets/actions.js +619 -41
- package/src/actions/facets/derive.js +139 -25
- package/src/actions/gradientPlots/components.js +43 -13
- package/src/actions/gradientPlots/edit.js +14 -4
- package/src/actions/gradientPlots/materialize.js +8 -8
- package/src/actions/gradientPlots/options.js +10 -2
- package/src/actions/guides/applicability.js +28 -19
- package/src/actions/guides/axes/axes.js +4 -3
- package/src/actions/guides/axes/axis.js +18 -18
- package/src/actions/guides/axes/components.js +43 -0
- package/src/actions/guides/axes/edit.js +16 -51
- package/src/actions/guides/axes/labels.js +184 -28
- package/src/actions/guides/axes/lines.js +13 -7
- package/src/actions/guides/axes/parallel/lifecycle.js +117 -0
- package/src/actions/guides/axes/parallel/policy.js +137 -0
- package/src/actions/guides/axes/parallel/resolve.js +50 -25
- package/src/actions/guides/axes/parallel.js +93 -217
- package/src/actions/guides/axes/policy.js +21 -67
- package/src/actions/guides/axes/tickGroups.js +15 -8
- package/src/actions/guides/axes/ticks.js +11 -6
- package/src/actions/guides/axes/titles.js +32 -11
- package/src/actions/guides/basic.js +2 -0
- package/src/actions/guides/facade.js +106 -0
- package/src/actions/guides/facadeAxes.js +200 -0
- package/src/actions/guides/facadeLegend.js +63 -0
- package/src/actions/guides/grids/grid.js +37 -33
- package/src/actions/guides/grids/resolve.js +17 -4
- package/src/actions/guides/guides.js +54 -31
- package/src/actions/guides/legends/blocks.js +426 -0
- package/src/actions/guides/legends/categorical/actions.js +309 -279
- package/src/actions/guides/legends/categorical/components.js +21 -17
- package/src/actions/guides/legends/categorical/index.js +37 -33
- package/src/actions/guides/legends/categorical/layout.js +123 -354
- package/src/actions/guides/legends/categorical/options.js +32 -16
- package/src/actions/guides/legends/categorical/recipes.js +114 -9
- package/src/actions/guides/legends/categorical/resolve.js +30 -15
- package/src/actions/guides/legends/categorical/symbols.js +87 -33
- package/src/actions/guides/legends/continuous/common.js +99 -26
- package/src/actions/guides/legends/continuous/gradient.js +120 -89
- package/src/actions/guides/legends/continuous/index.js +8 -2
- package/src/actions/guides/legends/continuous/interval.js +79 -95
- package/src/actions/guides/legends/continuous/opacity.js +113 -89
- package/src/actions/guides/legends/continuous/stroke.js +525 -0
- package/src/actions/guides/legends/creation.js +40 -0
- package/src/actions/guides/legends/edit.js +321 -90
- package/src/actions/guides/legends/focused.js +2 -2
- package/src/actions/guides/legends/index.js +2 -0
- package/src/actions/guides/legends/lane.js +149 -102
- package/src/actions/guides/legends/lifecycle.js +143 -0
- package/src/actions/guides/legends/remove.js +43 -78
- package/src/actions/guides/legends/sampling.js +140 -0
- package/src/actions/guides/legends/size.js +213 -116
- package/src/actions/guides/legends/strokeWidth.js +121 -73
- package/src/actions/guides/legends/target.js +123 -0
- package/src/actions/guides/legends/transition.js +192 -0
- package/src/actions/guides/polar/axes/facade.js +81 -66
- package/src/actions/guides/polar/axes/labels.js +28 -8
- package/src/actions/guides/polar/axes/lines.js +12 -5
- package/src/actions/guides/polar/axes/shared.js +22 -0
- package/src/actions/guides/polar/axes/ticks.js +6 -5
- package/src/actions/guides/polar/axes/titles.js +6 -5
- package/src/actions/guides/polar/grids.js +8 -8
- package/src/actions/guides/polar/resolve.js +12 -8
- package/src/actions/guides/reuse.js +44 -0
- package/src/actions/guides/tickValues.js +19 -21
- package/src/actions/index.js +15 -2
- package/src/actions/intervals/resolve.js +35 -15
- package/src/actions/marks/arc/actions.js +78 -20
- package/src/actions/marks/area/actions.js +51 -12
- package/src/actions/marks/area/materialize.js +22 -3
- package/src/actions/marks/bar/create.js +11 -2
- package/src/actions/marks/bar/edit.js +20 -3
- package/src/actions/marks/bar/materialize.js +33 -9
- package/src/actions/marks/index.js +14 -0
- package/src/actions/marks/line/actions.js +71 -113
- package/src/actions/marks/line/materialize.js +57 -56
- package/src/actions/marks/point/create.js +8 -2
- package/src/actions/marks/point/edit.js +29 -21
- package/src/actions/marks/point/index.js +1 -0
- package/src/actions/marks/point/jitter.js +3 -0
- package/src/actions/marks/point/materialize.js +154 -16
- package/src/actions/marks/point/packing.js +68 -0
- package/src/actions/marks/rect/actions.js +33 -12
- package/src/actions/marks/references.js +658 -0
- package/src/actions/marks/remove.js +87 -4
- package/src/actions/marks/rule/actions.js +77 -9
- package/src/actions/marks/rule/appearance.js +26 -0
- package/src/actions/marks/shared.js +13 -13
- package/src/actions/marks/text/actions.js +296 -13
- package/src/actions/marks/text/index.js +4 -0
- package/src/actions/marks/text/layout.js +156 -8
- package/src/actions/marks/text/removal.js +173 -0
- package/src/actions/marks/text/selection.js +73 -0
- package/src/actions/marks/tick/actions.js +22 -5
- package/src/actions/primitives/createGraphics.js +3 -1
- package/src/actions/primitives/editGraphics.js +26 -0
- package/src/actions/primitives/semantic.js +3 -2
- package/src/actions/primitives/semanticAction.js +120 -10
- package/src/actions/primitives/semanticValidation/guide.js +10 -0
- package/src/actions/primitives/semanticValidation/index.js +9 -1
- package/src/actions/primitives/semanticValidation/layer.js +23 -1
- package/src/actions/primitives/semanticValidation/scale.js +38 -8
- package/src/actions/regression/components.js +26 -11
- package/src/actions/regression/create.js +11 -3
- package/src/actions/regression/edit.js +40 -13
- package/src/actions/regression/resolve.js +1 -1
- package/src/actions/resources/index.js +7 -0
- package/src/actions/resources/remove.js +172 -0
- package/src/actions/scales/channels.js +212 -0
- package/src/actions/scales/consumers/common.js +21 -54
- package/src/actions/scales/consumers/families.js +28 -14
- package/src/actions/scales/consumers/index.js +42 -4
- package/src/actions/scales/consumers/seriesLayout.js +57 -12
- package/src/actions/scales/create.js +58 -26
- package/src/actions/scales/definitions.js +109 -39
- package/src/actions/scales/edit.js +101 -34
- package/src/actions/scales/editPolicy.js +95 -35
- package/src/actions/scales/index.js +6 -0
- package/src/actions/scales/materialize.js +14 -49
- package/src/actions/scales/offset.js +48 -0
- package/src/actions/scales/parallel.js +71 -0
- package/src/actions/scales/patch.js +1 -1
- package/src/actions/scales/preview.js +140 -0
- package/src/actions/scales/quantitativeColor.js +9 -5
- package/src/actions/selection/actions.js +154 -30
- package/src/actions/selection/index.js +3 -0
- package/src/actions/theme/actions.js +98 -0
- package/src/actions/theme/composition.js +238 -0
- package/src/actions/theme/index.js +16 -0
- package/src/actions/theme/reconcile.js +1130 -0
- package/src/actions/theme/state.js +160 -0
- package/src/actions/titles/actions.js +11 -10
- package/src/actions/titles/resolve.js +1 -40
- package/src/actions/violinPlots/create.js +90 -43
- package/src/actions/violinPlots/edit.js +192 -0
- package/src/actions/violinPlots/index.js +2 -0
- package/src/core/ChartProgram.js +7 -0
- package/src/core/action.js +32 -2
- package/src/core/compositionState.js +141 -8
- package/src/core/programState.js +20 -0
- package/src/core/resourceReferences.js +332 -0
- package/src/core/vocabulary.js +16 -3
- package/src/grammar/aggregate.js +66 -23
- package/src/grammar/arcs.js +58 -1
- package/src/grammar/areaEndpoints.js +46 -0
- package/src/grammar/areaSeries.js +87 -41
- package/src/grammar/bars/aggregate.js +37 -13
- package/src/grammar/bars/geometry.js +44 -0
- package/src/grammar/bars/policy.js +9 -7
- package/src/grammar/bin.js +226 -0
- package/src/grammar/categoryOrder.js +34 -0
- package/src/grammar/complete.js +279 -0
- package/src/grammar/computed.js +351 -0
- package/src/grammar/coordinates.js +37 -0
- package/src/grammar/density.js +239 -37
- package/src/grammar/displayLabels.js +57 -0
- package/src/grammar/ecdf.js +198 -0
- package/src/grammar/facets/dependencies.js +133 -1
- package/src/grammar/facets/guides.js +3 -0
- package/src/grammar/facets/index.js +123 -52
- package/src/grammar/facets/scales.js +15 -18
- package/src/grammar/fold.js +123 -0
- package/src/grammar/horizon.js +6 -4
- package/src/grammar/impute.js +340 -0
- package/src/grammar/interval.js +37 -20
- package/src/grammar/lineSeries.js +20 -63
- package/src/grammar/markFilter.js +22 -4
- package/src/grammar/markLabelSelection.js +57 -0
- package/src/grammar/markLabels.js +110 -0
- package/src/grammar/normalize.js +366 -0
- package/src/grammar/parallelAxisTitles.js +16 -0
- package/src/grammar/pathSeries.js +109 -0
- package/src/grammar/pointPacking.js +183 -0
- package/src/grammar/pointShapes.js +13 -4
- package/src/grammar/polar.js +90 -8
- package/src/grammar/positionDatum.js +12 -0
- package/src/grammar/rects.js +9 -1
- package/src/grammar/regression/derive.js +4 -0
- package/src/grammar/regression/index.js +8 -2
- package/src/grammar/regression/models.js +22 -7
- package/src/grammar/regression/parameters.js +63 -13
- package/src/grammar/rotation.js +29 -0
- package/src/grammar/roundedRect.js +180 -0
- package/src/grammar/rules.js +8 -21
- package/src/grammar/scales/color.js +23 -1
- package/src/grammar/scales/colorConsumers.js +57 -0
- package/src/grammar/scales/continuous.js +2 -2
- package/src/grammar/scales/definition.js +42 -5
- package/src/grammar/scales/discretized.js +11 -5
- package/src/grammar/scales/fields.js +38 -8
- package/src/grammar/scales/index.js +1 -0
- package/src/grammar/scales/mapping.js +2 -0
- package/src/grammar/scales/ordinal.js +12 -3
- package/src/grammar/scales/policies.js +2 -2
- package/src/grammar/scales/radial.js +55 -0
- package/src/grammar/scales/size.js +456 -0
- package/src/grammar/scales/types.js +1 -0
- package/src/grammar/schemas/concreteGraphic.js +14 -2
- package/src/grammar/schemas/graphic.js +15 -0
- package/src/grammar/schemas/graphicBounds.js +179 -30
- package/src/grammar/schemas/semanticPath.js +23 -4
- package/src/grammar/stack.js +170 -0
- package/src/grammar/statisticalReference.js +103 -0
- package/src/grammar/statistics/confidenceInterval.js +101 -0
- package/src/grammar/strokeStyle.js +77 -0
- package/src/grammar/summary.js +245 -0
- package/src/grammar/text.js +22 -16
- package/src/grammar/timeUnit.js +234 -13
- package/src/grammar/transformTopology.js +10 -0
- package/src/grammar/transforms.js +418 -16
- package/src/grammar/valueFormat.js +112 -0
- package/src/grammar/weightedStatistics.js +374 -0
- package/src/grammar/window.js +232 -21
- package/src/layout/aspect.js +80 -0
- package/src/layout/facets.js +100 -12
- package/src/layout/guideCollisions.js +12 -0
- package/src/layout/labels.js +343 -0
- package/src/layout/legend.js +2 -10
- package/src/layout/legendItems.js +102 -0
- package/src/layout/legendLane.js +100 -11
- package/src/materialization/bars/aggregate.js +56 -147
- package/src/materialization/bars/histogram.js +104 -20
- package/src/materialization/bars/ranged.js +19 -3
- package/src/materialization/bars/resolve.js +7 -4
- package/src/materialization/categorySlotOffset.js +40 -0
- package/src/materialization/composition.js +3 -1
- package/src/materialization/compositionSnapshot.js +1 -1
- package/src/materialization/coordinateBounds.js +204 -0
- package/src/materialization/dependencies.js +93 -16
- package/src/materialization/encodings.js +1 -1
- package/src/materialization/facetGuides/legacyCategorical.js +3 -0
- package/src/materialization/facetGuides/placement.js +8 -2
- package/src/materialization/facetGuides/preparation.js +12 -7
- package/src/materialization/facetHeaders.js +346 -0
- package/src/materialization/facets.js +23 -66
- package/src/materialization/guides/layout.js +57 -0
- package/src/materialization/guides/resources.js +28 -2
- package/src/materialization/legends.js +11 -2
- package/src/materialization/marks/capabilities.js +20 -5
- package/src/materialization/marks/grouping.js +52 -0
- package/src/materialization/marks/index.js +68 -27
- package/src/materialization/marks/policies.js +4 -1
- package/src/materialization/rect.js +55 -42
- package/src/materialization/rectConfig.js +5 -1
- package/src/materialization/rowEncoding.js +13 -13
- package/src/materialization/scaleGuideDependencies.js +8 -5
- package/src/materialization/scales/map.js +6 -3
- package/src/materialization/scales/policies/arc.js +42 -0
- package/src/materialization/scales/policies/binnedPosition.js +7 -4
- package/src/materialization/scales/policies/offset.js +50 -8
- package/src/materialization/scales/policies/series.js +5 -1
- package/src/materialization/scales/resolve.js +65 -17
- package/src/materialization/selection/compatibility.js +11 -0
- package/src/materialization/selection/items/arc.js +28 -4
- package/src/materialization/selection/items/bar.js +42 -71
- package/src/materialization/selection/items/common.js +27 -6
- package/src/materialization/selection/items/path.js +3 -13
- package/src/materialization/selection/items/rect.js +35 -8
- package/src/materialization/selection/path.js +1 -1
- package/src/materialization/selection/point.js +3 -1
- package/src/materialization/text.js +324 -14
- package/src/mcp/adapter.js +2 -2
- package/src/renderers/canvas/circle.js +2 -3
- package/src/renderers/canvas/line.js +2 -3
- package/src/renderers/canvas/native.js +4 -4
- package/src/renderers/canvas/path.js +2 -3
- package/src/renderers/canvas/rect.js +2 -3
- package/src/renderers/canvas/stroke.js +11 -0
- package/src/renderers/svg.js +19 -0
- package/src/selectors/datasets.js +44 -0
- package/src/selectors/layers.js +7 -2
- package/src/selectors/scales.js +6 -0
- package/src/theme/defaults.js +140 -0
- package/types/basic.d.ts +17 -2
- package/types/index.d.ts +211 -0
- package/types/program.d.ts +2182 -204
- package/src/materialization/bars/grouped.js +0 -144
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { resolveLegendItemLayout } from "../../../../layout/legendItems.js";
|
|
1
2
|
import { action } from "../../../../core/action.js";
|
|
2
3
|
import { isPlainObject } from "../../../../core/immutable.js";
|
|
3
4
|
import {
|
|
4
5
|
validateKeys,
|
|
5
|
-
validateOptionObject
|
|
6
|
+
validateOptionObject,
|
|
7
|
+
validateNonEmptyString
|
|
6
8
|
} from "../../../../core/validation.js";
|
|
7
9
|
import { formatDiscretizedIntervals } from "../../../../grammar/scales/index.js";
|
|
8
10
|
import { DEFAULT_COLORS, DEFAULT_FONT_FAMILY } from
|
|
@@ -12,21 +14,23 @@ import {
|
|
|
12
14
|
editGraphicProperties,
|
|
13
15
|
editLegendBackground,
|
|
14
16
|
normalizeLegendBorder,
|
|
17
|
+
normalizeItemLegendLayout,
|
|
15
18
|
normalizeLegendTextOptions,
|
|
19
|
+
normalizeLegendTitleOptions,
|
|
16
20
|
resolveContinuousBounds,
|
|
17
|
-
|
|
21
|
+
resolveContinuousColorLayer,
|
|
18
22
|
resolveLegendBackgroundFromBounds,
|
|
19
|
-
resolveLegendTextBounds,
|
|
20
23
|
styleContinuousText,
|
|
21
24
|
validateNonNegative,
|
|
22
25
|
validatePositive
|
|
23
26
|
} from "./common.js";
|
|
24
27
|
import { resolveLegendGraphicPlacement } from
|
|
25
28
|
"../../../../materialization/graphicHierarchy.js";
|
|
29
|
+
import { resolveEffectiveLegendBlockConfig } from "../blocks.js";
|
|
26
30
|
|
|
27
31
|
const OPTIONS = [
|
|
28
32
|
"target", "channels", "position", "align", "offset", "title",
|
|
29
|
-
"symbol", "labels", "titleStyle", "itemGap", "direction", "border"
|
|
33
|
+
"symbol", "labels", "titleStyle", "itemGap", "direction", "border", "layout", "columns", "titlePosition"
|
|
30
34
|
];
|
|
31
35
|
const SYMBOL_OPTIONS = [
|
|
32
36
|
"width", "height", "stroke", "strokeWidth"
|
|
@@ -41,12 +45,7 @@ export function normalizeIntervalLegend(args) {
|
|
|
41
45
|
)) {
|
|
42
46
|
throw new Error('Interval legend requires channels: ["color"].');
|
|
43
47
|
}
|
|
44
|
-
|
|
45
|
-
throw new Error('Interval legends currently support position "right".');
|
|
46
|
-
}
|
|
47
|
-
if ((args.direction ?? "vertical") !== "vertical") {
|
|
48
|
-
throw new Error('Interval legends currently support direction "vertical".');
|
|
49
|
-
}
|
|
48
|
+
const layout = normalizeItemLegendLayout(args);
|
|
50
49
|
if (args.symbol !== undefined && !isPlainObject(args.symbol)) {
|
|
51
50
|
throw new TypeError("createLegend.symbol must be a plain object.");
|
|
52
51
|
}
|
|
@@ -61,17 +60,11 @@ export function normalizeIntervalLegend(args) {
|
|
|
61
60
|
validatePositive(symbol.width, "Legend symbol width");
|
|
62
61
|
validatePositive(symbol.height, "Legend symbol height");
|
|
63
62
|
validateNonNegative(symbol.strokeWidth, "Legend symbol strokeWidth");
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
validateNonNegative(offset, "Legend offset");
|
|
67
|
-
validatePositive(itemGap, "Legend itemGap");
|
|
63
|
+
if (args.title !== undefined) validateNonEmptyString(args.title, "Legend title");
|
|
64
|
+
validateNonEmptyString(symbol.stroke, "Legend symbol stroke");
|
|
68
65
|
return {
|
|
69
66
|
target: args.target,
|
|
70
|
-
|
|
71
|
-
direction: "vertical",
|
|
72
|
-
align: args.align ?? "center",
|
|
73
|
-
offset,
|
|
74
|
-
itemGap,
|
|
67
|
+
...layout,
|
|
75
68
|
title: args.title,
|
|
76
69
|
inferredTitle: args.title === undefined,
|
|
77
70
|
titleVisible: true,
|
|
@@ -83,7 +76,7 @@ export function normalizeIntervalLegend(args) {
|
|
|
83
76
|
fontFamily: DEFAULT_FONT_FAMILY,
|
|
84
77
|
fontWeight: "normal"
|
|
85
78
|
}),
|
|
86
|
-
titleStyle:
|
|
79
|
+
titleStyle: normalizeLegendTitleOptions(args.titleStyle, "createLegend.titleStyle", {
|
|
87
80
|
color: DEFAULT_COLORS.text,
|
|
88
81
|
fontSize: 13,
|
|
89
82
|
fontFamily: DEFAULT_FONT_FAMILY,
|
|
@@ -93,8 +86,8 @@ export function normalizeIntervalLegend(args) {
|
|
|
93
86
|
};
|
|
94
87
|
}
|
|
95
88
|
|
|
96
|
-
function resolveIntervalConfig(program, stored) {
|
|
97
|
-
const layer =
|
|
89
|
+
export function resolveIntervalConfig(program, stored) {
|
|
90
|
+
const layer = resolveContinuousColorLayer(program, stored.target);
|
|
98
91
|
const encoding = layer.encoding.color;
|
|
99
92
|
if (encoding.fieldType !== "quantitative") {
|
|
100
93
|
throw new Error("Interval legend requires quantitative color.");
|
|
@@ -111,47 +104,26 @@ function resolveIntervalConfig(program, stored) {
|
|
|
111
104
|
} };
|
|
112
105
|
}
|
|
113
106
|
|
|
114
|
-
function resolveIntervalLayout(program, config, scale) {
|
|
107
|
+
export function resolveIntervalLayout(program, config, scale) {
|
|
115
108
|
const { plot, canvas } = resolveContinuousBounds(program);
|
|
116
|
-
const labels = formatDiscretizedIntervals(scale.thresholds);
|
|
117
|
-
const
|
|
118
|
-
const
|
|
119
|
-
const labelX = symbolX + config.symbol.width + config.labels.offset;
|
|
120
|
-
const title = { x: symbolX, y: plot.y + 20 };
|
|
121
|
-
const strokeExtent = config.symbol.strokeWidth / 2;
|
|
122
|
-
const occupiedBounds = [
|
|
123
|
-
resolveLegendTextBounds(
|
|
124
|
-
{ ...title, align: "left" },
|
|
125
|
-
config.title,
|
|
126
|
-
config.titleStyle
|
|
127
|
-
),
|
|
128
|
-
...labels.map((label, index) => resolveLegendTextBounds(
|
|
129
|
-
{ x: labelX, y: itemY[index], align: "left" },
|
|
130
|
-
label,
|
|
131
|
-
config.labels
|
|
132
|
-
)),
|
|
133
|
-
...itemY.map(y => ({
|
|
134
|
-
left: symbolX - strokeExtent,
|
|
135
|
-
right: symbolX + config.symbol.width + strokeExtent,
|
|
136
|
-
top: y - config.symbol.height / 2 - strokeExtent,
|
|
137
|
-
bottom: y + config.symbol.height / 2 + strokeExtent
|
|
138
|
-
}))
|
|
139
|
-
];
|
|
109
|
+
const labels = formatDiscretizedIntervals(scale.thresholds, config.labels.format);
|
|
110
|
+
const layout = resolveLegendItemLayout(plot, config, labels, config.symbol);
|
|
111
|
+
const occupiedBounds = layout.bounds;
|
|
140
112
|
assertLegendBoundsInsideCanvas(
|
|
141
113
|
occupiedBounds,
|
|
142
114
|
canvas,
|
|
143
|
-
"Interval legend layout"
|
|
115
|
+
"Interval legend layout", config
|
|
144
116
|
);
|
|
145
117
|
const background = resolveLegendBackgroundFromBounds(
|
|
146
118
|
occupiedBounds,
|
|
147
119
|
config.border,
|
|
148
120
|
canvas,
|
|
149
|
-
"Interval legend"
|
|
121
|
+
"Interval legend", config
|
|
150
122
|
);
|
|
151
|
-
return { labels,
|
|
123
|
+
return { labels, ...layout, background };
|
|
152
124
|
}
|
|
153
125
|
|
|
154
|
-
export const rematerializeIntervalLegend = action(
|
|
126
|
+
export const rematerializeIntervalLegend = /* @__PURE__ */ action(
|
|
155
127
|
{
|
|
156
128
|
op: "rematerializeIntervalLegend",
|
|
157
129
|
description: "Rematerialize a discretized color interval legend."
|
|
@@ -162,12 +134,13 @@ export const rematerializeIntervalLegend = action(
|
|
|
162
134
|
if (stored === undefined) {
|
|
163
135
|
throw new Error("Interval legend requires stored configuration.");
|
|
164
136
|
}
|
|
165
|
-
const { encoding, scale, config } = resolveIntervalConfig(this, stored);
|
|
137
|
+
const { encoding, scale, config: currentConfig } = resolveIntervalConfig(this, stored);
|
|
138
|
+
const config = resolveEffectiveLegendBlockConfig(this, "interval", currentConfig);
|
|
166
139
|
const layout = resolveIntervalLayout(this, config, scale);
|
|
167
140
|
let next = editGraphicProperties(this
|
|
168
141
|
.editSemantic({ property: "guide.legend.color.scale", value: encoding.scale })
|
|
169
142
|
.editSemantic({ property: "guide.legend.color.title", value: config.title })
|
|
170
|
-
._withLegendConfig("interval",
|
|
143
|
+
._withLegendConfig("interval", currentConfig), "colorLegendSymbols", {
|
|
171
144
|
length: scale.range.length,
|
|
172
145
|
x: layout.symbolX,
|
|
173
146
|
y: layout.itemY.map(value => value - config.symbol.height / 2),
|
|
@@ -175,7 +148,10 @@ export const rematerializeIntervalLegend = action(
|
|
|
175
148
|
height: config.symbol.height,
|
|
176
149
|
fill: scale.range,
|
|
177
150
|
stroke: config.symbol.stroke,
|
|
178
|
-
strokeWidth: config.symbol.strokeWidth
|
|
151
|
+
strokeWidth: config.symbol.strokeWidth,
|
|
152
|
+
...(config.blockSymbol?.opacity === undefined
|
|
153
|
+
? {}
|
|
154
|
+
: { opacity: config.blockSymbol.opacity })
|
|
179
155
|
});
|
|
180
156
|
next = editGraphicProperties(next, "colorLegendLabels", {
|
|
181
157
|
length: layout.labels.length,
|
|
@@ -196,11 +172,57 @@ export const rematerializeIntervalLegend = action(
|
|
|
196
172
|
y: layout.title.y,
|
|
197
173
|
text: config.title
|
|
198
174
|
});
|
|
199
|
-
return styleContinuousText(next, "colorLegendTitle", config.titleStyle);
|
|
175
|
+
return styleContinuousText(next, "colorLegendTitle", config.titleStyle, { align: layout.title.align });
|
|
200
176
|
}
|
|
201
177
|
);
|
|
202
178
|
|
|
203
|
-
export
|
|
179
|
+
export function createIntervalLegendFromConfig(program, config) {
|
|
180
|
+
const resolved = resolveIntervalConfig(program, config);
|
|
181
|
+
resolveIntervalLayout(program, resolved.config, resolved.scale);
|
|
182
|
+
let next = program
|
|
183
|
+
.editSemantic({
|
|
184
|
+
property: "guide.legend.color.scale",
|
|
185
|
+
value: resolved.encoding.scale
|
|
186
|
+
})
|
|
187
|
+
.editSemantic({
|
|
188
|
+
property: "guide.legend.color.title",
|
|
189
|
+
value: resolved.config.title
|
|
190
|
+
})
|
|
191
|
+
._withLegendConfig("interval", resolved.config);
|
|
192
|
+
const placement = resolveLegendGraphicPlacement(next);
|
|
193
|
+
if (resolved.config.border !== false) {
|
|
194
|
+
next = next.createGraphics({
|
|
195
|
+
id: "colorLegendBackground",
|
|
196
|
+
type: "rect",
|
|
197
|
+
...placement
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
next = next
|
|
201
|
+
.createGraphics({
|
|
202
|
+
id: "colorLegendSymbols",
|
|
203
|
+
type: "rect",
|
|
204
|
+
length: 0,
|
|
205
|
+
...resolveLegendGraphicPlacement(next, resolved.config.border === false
|
|
206
|
+
? {}
|
|
207
|
+
: { after: "colorLegendBackground" })
|
|
208
|
+
})
|
|
209
|
+
.createGraphics({
|
|
210
|
+
id: "colorLegendLabels",
|
|
211
|
+
type: "text",
|
|
212
|
+
length: 0,
|
|
213
|
+
...placement
|
|
214
|
+
});
|
|
215
|
+
if (resolved.config.titleVisible !== false) {
|
|
216
|
+
next = next.createGraphics({
|
|
217
|
+
id: "colorLegendTitle",
|
|
218
|
+
type: "text",
|
|
219
|
+
...placement
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
return next.rematerializeIntervalLegend();
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export const createIntervalLegend = /* @__PURE__ */ action(
|
|
204
226
|
{
|
|
205
227
|
op: "createIntervalLegend",
|
|
206
228
|
description: "Create a discretized color interval legend."
|
|
@@ -212,44 +234,6 @@ export const createIntervalLegend = action(
|
|
|
212
234
|
if (this.graphicSpec.objects.colorLegendSymbols !== undefined) {
|
|
213
235
|
throw new Error("createIntervalLegend requires a missing interval legend.");
|
|
214
236
|
}
|
|
215
|
-
|
|
216
|
-
.editSemantic({
|
|
217
|
-
property: "guide.legend.color.scale",
|
|
218
|
-
value: resolved.encoding.scale
|
|
219
|
-
})
|
|
220
|
-
.editSemantic({
|
|
221
|
-
property: "guide.legend.color.title",
|
|
222
|
-
value: resolved.config.title
|
|
223
|
-
})
|
|
224
|
-
._withLegendConfig("interval", resolved.config);
|
|
225
|
-
const placement = resolveLegendGraphicPlacement(next);
|
|
226
|
-
if (resolved.config.border !== false) {
|
|
227
|
-
next = next.createGraphics({
|
|
228
|
-
id: "colorLegendBackground",
|
|
229
|
-
type: "rect",
|
|
230
|
-
...placement
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
return next
|
|
234
|
-
.createGraphics({
|
|
235
|
-
id: "colorLegendSymbols",
|
|
236
|
-
type: "rect",
|
|
237
|
-
length: 0,
|
|
238
|
-
...resolveLegendGraphicPlacement(next, resolved.config.border === false
|
|
239
|
-
? {}
|
|
240
|
-
: { after: "colorLegendBackground" })
|
|
241
|
-
})
|
|
242
|
-
.createGraphics({
|
|
243
|
-
id: "colorLegendLabels",
|
|
244
|
-
type: "text",
|
|
245
|
-
length: 0,
|
|
246
|
-
...placement
|
|
247
|
-
})
|
|
248
|
-
.createGraphics({
|
|
249
|
-
id: "colorLegendTitle",
|
|
250
|
-
type: "text",
|
|
251
|
-
...placement
|
|
252
|
-
})
|
|
253
|
-
.rematerializeIntervalLegend();
|
|
237
|
+
return createIntervalLegendFromConfig(this, resolved.config);
|
|
254
238
|
}
|
|
255
239
|
);
|
|
@@ -11,16 +11,17 @@ import {
|
|
|
11
11
|
normalizeContinuousLegend,
|
|
12
12
|
requireResolvedLegendScale,
|
|
13
13
|
resolveContinuousBounds,
|
|
14
|
-
|
|
14
|
+
resolveContinuousLegendLayer,
|
|
15
15
|
resolveLegendBackgroundFromBounds,
|
|
16
16
|
resolveLegendTextBounds,
|
|
17
|
-
sampleContinuousValues,
|
|
18
17
|
styleContinuousText,
|
|
19
18
|
validateNonNegative,
|
|
20
19
|
validatePositive
|
|
21
20
|
} from "./common.js";
|
|
22
21
|
import { resolveLegendGraphicPlacement } from
|
|
23
22
|
"../../../../materialization/graphicHierarchy.js";
|
|
23
|
+
import { resolveLegendSampleValues } from "../sampling.js";
|
|
24
|
+
import { resolveEffectiveLegendBlockConfig } from "../blocks.js";
|
|
24
25
|
|
|
25
26
|
const SYMBOL_OPTIONS = Object.freeze([
|
|
26
27
|
"type", "radius", "fill", "stroke", "strokeWidth"
|
|
@@ -62,7 +63,7 @@ export function normalizeOpacitySymbol(value) {
|
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
function resolveOpacityConfig(program, config) {
|
|
65
|
-
const layer =
|
|
66
|
+
const layer = resolveContinuousLegendLayer(program, config.target, "opacity");
|
|
66
67
|
const encoding = layer.encoding.opacity;
|
|
67
68
|
if (encoding.fieldType !== "quantitative") {
|
|
68
69
|
throw new Error("Opacity legend requires quantitative field opacity.");
|
|
@@ -85,9 +86,15 @@ function resolveOpacityConfig(program, config) {
|
|
|
85
86
|
function resolveOpacityLayout(program, config, scale) {
|
|
86
87
|
const { plot, canvas } = resolveContinuousBounds(program);
|
|
87
88
|
const vertical = ["right", "left"].includes(config.position);
|
|
88
|
-
const values =
|
|
89
|
-
const texts = formatContinuousValues(
|
|
90
|
-
|
|
89
|
+
const values = resolveLegendSampleValues(config, scale, "Opacity legend");
|
|
90
|
+
const texts = formatContinuousValues(
|
|
91
|
+
values,
|
|
92
|
+
scale.domain,
|
|
93
|
+
"quantitative",
|
|
94
|
+
config.labels.format
|
|
95
|
+
);
|
|
96
|
+
const symbolExtent = config.symbol.radius + (config.symbol.strokeWidth ?? 0) / 2;
|
|
97
|
+
const labelWidths = texts.map(text => measureTextWidth(text, config.labels));
|
|
91
98
|
let symbols;
|
|
92
99
|
let labels;
|
|
93
100
|
let title;
|
|
@@ -95,57 +102,61 @@ function resolveOpacityLayout(program, config, scale) {
|
|
|
95
102
|
const baseX = config.position === "right"
|
|
96
103
|
? plot.x + plot.width + config.offset
|
|
97
104
|
: plot.x - config.offset;
|
|
98
|
-
const
|
|
105
|
+
const itemHeight = Math.max(symbolExtent * 2, config.labels.fontSize);
|
|
106
|
+
const pitch = Math.max(config.itemGap, itemHeight);
|
|
107
|
+
const firstY = Math.max(plot.y + 46, config.titleVisible === false ? plot.y + 46
|
|
108
|
+
: plot.y + 20 + config.titleStyle.fontSize / 2 + 12 + itemHeight / 2);
|
|
109
|
+
const y = values.map((_, index) => firstY + index * pitch);
|
|
99
110
|
symbols = y.map(itemY => ({
|
|
100
|
-
x: config.position === "right" ? baseX +
|
|
111
|
+
x: config.position === "right" ? baseX + symbolExtent : baseX - symbolExtent,
|
|
101
112
|
y: itemY
|
|
102
113
|
}));
|
|
103
114
|
labels = y.map(itemY => ({
|
|
104
115
|
x: config.position === "right"
|
|
105
|
-
? baseX +
|
|
106
|
-
: baseX -
|
|
116
|
+
? baseX + symbolExtent * 2 + config.labels.offset
|
|
117
|
+
: baseX - symbolExtent * 2 - config.labels.offset,
|
|
107
118
|
y: itemY,
|
|
108
119
|
align: config.position === "right" ? "left" : "right"
|
|
109
120
|
}));
|
|
110
121
|
title = {
|
|
111
|
-
x: config.position === "right" ? baseX : baseX -
|
|
122
|
+
x: config.position === "right" ? baseX : baseX - symbolExtent * 2,
|
|
112
123
|
y: plot.y + 20,
|
|
113
124
|
align: config.position === "right" ? "left" : "right"
|
|
114
125
|
};
|
|
115
126
|
} else if (config.titlePosition === "left") {
|
|
116
|
-
const titleWidth = measureTextWidth(config.title, config.titleStyle);
|
|
117
|
-
const
|
|
118
|
-
measureTextWidth(text, config.labels)
|
|
119
|
-
);
|
|
127
|
+
const titleWidth = config.titleVisible === false ? 0 : measureTextWidth(config.title, config.titleStyle);
|
|
128
|
+
const titlePrefix = config.titleVisible === false ? 0 : titleWidth + 20;
|
|
120
129
|
const samplesWidth = labelWidths.reduce(
|
|
121
|
-
(sum, width) => sum +
|
|
130
|
+
(sum, width) => sum + symbolExtent * 2 + config.labels.offset + width,
|
|
122
131
|
0
|
|
123
132
|
) + config.itemGap * (values.length - 1);
|
|
124
|
-
const width =
|
|
133
|
+
const width = titlePrefix + samplesWidth;
|
|
125
134
|
const startX = config.align === "left" ? plot.x
|
|
126
135
|
: config.align === "right" ? plot.x + plot.width - width
|
|
127
136
|
: plot.x + (plot.width - width) / 2;
|
|
128
137
|
const height = Math.max(
|
|
129
|
-
|
|
138
|
+
symbolExtent * 2,
|
|
130
139
|
config.labels.fontSize,
|
|
131
|
-
config.titleStyle.fontSize
|
|
140
|
+
config.titleVisible === false ? 0 : config.titleStyle.fontSize
|
|
132
141
|
);
|
|
133
142
|
const centerY = config.position === "top"
|
|
134
143
|
? plot.y - config.offset - height / 2
|
|
135
144
|
: plot.y + plot.height + config.offset + height / 2;
|
|
136
145
|
title = { x: startX, y: centerY, align: "left" };
|
|
137
|
-
let cursor = startX +
|
|
146
|
+
let cursor = startX + titlePrefix;
|
|
138
147
|
symbols = [];
|
|
139
148
|
labels = [];
|
|
140
149
|
for (let index = 0; index < values.length; index += 1) {
|
|
141
|
-
const symbolX = cursor +
|
|
150
|
+
const symbolX = cursor + symbolExtent;
|
|
142
151
|
symbols.push({ x: symbolX, y: centerY });
|
|
143
|
-
const labelX = symbolX +
|
|
152
|
+
const labelX = symbolX + symbolExtent + config.labels.offset;
|
|
144
153
|
labels.push({ x: labelX, y: centerY, align: "left" });
|
|
145
154
|
cursor = labelX + labelWidths[index] + config.itemGap;
|
|
146
155
|
}
|
|
147
156
|
} else {
|
|
148
|
-
const
|
|
157
|
+
const pitch = Math.max(56, config.itemGap * 2,
|
|
158
|
+
symbolExtent * 2 + config.itemGap, Math.max(...labelWidths) + config.itemGap);
|
|
159
|
+
const width = (values.length - 1) * pitch;
|
|
149
160
|
const startX = config.align === "left" ? plot.x
|
|
150
161
|
: config.align === "right" ? plot.x + plot.width - width
|
|
151
162
|
: plot.x + (plot.width - width) / 2;
|
|
@@ -153,27 +164,26 @@ function resolveOpacityLayout(program, config, scale) {
|
|
|
153
164
|
const labelHalf = config.labels.fontSize / 2;
|
|
154
165
|
const y = config.position === "top"
|
|
155
166
|
? plot.y - config.offset - config.labels.fontSize -
|
|
156
|
-
config.labels.offset -
|
|
167
|
+
config.labels.offset - symbolExtent
|
|
157
168
|
: plot.y + plot.height + config.offset +
|
|
158
|
-
config.titleStyle.fontSize + 12 +
|
|
169
|
+
(config.titleVisible === false ? 0 : config.titleStyle.fontSize + 12) + symbolExtent;
|
|
170
|
+
const samplePitch = values.length === 1 ? 0 : width / (values.length - 1);
|
|
159
171
|
symbols = values.map((_, index) => ({
|
|
160
|
-
x: startX + index *
|
|
172
|
+
x: startX + index * samplePitch,
|
|
161
173
|
y
|
|
162
174
|
}));
|
|
163
175
|
labels = symbols.map(symbol => ({
|
|
164
176
|
x: symbol.x,
|
|
165
|
-
y: symbol.y +
|
|
177
|
+
y: symbol.y + symbolExtent + config.labels.offset + labelHalf,
|
|
166
178
|
align: "center"
|
|
167
179
|
}));
|
|
168
180
|
title = {
|
|
169
181
|
x: startX + width / 2,
|
|
170
|
-
y: y -
|
|
182
|
+
y: y - symbolExtent - 12 - titleHalf,
|
|
171
183
|
align: "center"
|
|
172
184
|
};
|
|
173
185
|
}
|
|
174
|
-
const
|
|
175
|
-
const symbolExtent = radius + strokeExtent;
|
|
176
|
-
const titleBounds = resolveLegendTextBounds(
|
|
186
|
+
const titleBounds = config.titleVisible === false ? undefined : resolveLegendTextBounds(
|
|
177
187
|
title,
|
|
178
188
|
config.title,
|
|
179
189
|
config.titleStyle
|
|
@@ -187,22 +197,22 @@ function resolveOpacityLayout(program, config, scale) {
|
|
|
187
197
|
top: symbol.y - symbolExtent,
|
|
188
198
|
bottom: symbol.y + symbolExtent
|
|
189
199
|
}));
|
|
190
|
-
const occupiedBounds = [titleBounds, ...labelBounds, ...symbolBounds];
|
|
200
|
+
const occupiedBounds = [...(config.titleVisible === false ? [] : [titleBounds]), ...labelBounds, ...symbolBounds];
|
|
191
201
|
assertLegendBoundsInsideCanvas(
|
|
192
202
|
occupiedBounds,
|
|
193
203
|
canvas,
|
|
194
|
-
"Opacity legend layout"
|
|
204
|
+
"Opacity legend layout", config
|
|
195
205
|
);
|
|
196
206
|
const background = resolveLegendBackgroundFromBounds(
|
|
197
207
|
occupiedBounds,
|
|
198
208
|
config.border,
|
|
199
209
|
canvas,
|
|
200
|
-
"Opacity legend"
|
|
210
|
+
"Opacity legend", config
|
|
201
211
|
);
|
|
202
212
|
return { values, texts, symbols, labels, title, background };
|
|
203
213
|
}
|
|
204
214
|
|
|
205
|
-
export const rematerializeOpacityLegend = action(
|
|
215
|
+
export const rematerializeOpacityLegend = /* @__PURE__ */ action(
|
|
206
216
|
{
|
|
207
217
|
op: "rematerializeOpacityLegend",
|
|
208
218
|
description: "Rematerialize a field-opacity sample legend."
|
|
@@ -213,7 +223,8 @@ export const rematerializeOpacityLegend = action(
|
|
|
213
223
|
if (stored === undefined) {
|
|
214
224
|
throw new Error("Opacity legend requires stored configuration.");
|
|
215
225
|
}
|
|
216
|
-
const { encoding, scale, config } = resolveOpacityConfig(this, stored);
|
|
226
|
+
const { encoding, scale, config: currentConfig } = resolveOpacityConfig(this, stored);
|
|
227
|
+
const config = resolveEffectiveLegendBlockConfig(this, "opacity", currentConfig);
|
|
217
228
|
const layout = resolveOpacityLayout(this, config, scale);
|
|
218
229
|
const opacities = mapLinearValues(layout.values, scale.domain, scale.range, {
|
|
219
230
|
clamp: scale.clamp ?? false
|
|
@@ -227,7 +238,7 @@ export const rematerializeOpacityLegend = action(
|
|
|
227
238
|
property: "guide.legend.opacity.title",
|
|
228
239
|
value: config.title
|
|
229
240
|
})
|
|
230
|
-
._withLegendConfig("opacity",
|
|
241
|
+
._withLegendConfig("opacity", currentConfig)
|
|
231
242
|
.editGraphics({
|
|
232
243
|
target: "opacityLegendSymbols",
|
|
233
244
|
property: "length",
|
|
@@ -330,72 +341,85 @@ export const rematerializeOpacityLegend = action(
|
|
|
330
341
|
}
|
|
331
342
|
);
|
|
332
343
|
|
|
333
|
-
|
|
344
|
+
|
|
345
|
+
export function resolveOpacityLegendCreation(program, args = {}) {
|
|
346
|
+
const config = normalizeContinuousLegend(args, "opacity");
|
|
347
|
+
if (args.channels !== undefined && (
|
|
348
|
+
!Array.isArray(args.channels) ||
|
|
349
|
+
args.channels.length !== 1 ||
|
|
350
|
+
args.channels[0] !== "opacity"
|
|
351
|
+
)) {
|
|
352
|
+
throw new Error('Opacity legend requires channels: ["opacity"].');
|
|
353
|
+
}
|
|
354
|
+
config.symbol = normalizeOpacitySymbol(args.symbol);
|
|
355
|
+
config.titleVisible = true;
|
|
356
|
+
const resolved = resolveOpacityConfig(program, config);
|
|
357
|
+
return resolved;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
export function createOpacityLegendFromConfig(program, config) {
|
|
361
|
+
const resolved = resolveOpacityConfig(program, config);
|
|
362
|
+
resolveOpacityLayout(program, resolved.config, resolved.scale);
|
|
363
|
+
let next = program
|
|
364
|
+
.editSemantic({
|
|
365
|
+
property: "guide.legend.opacity.scale",
|
|
366
|
+
value: resolved.encoding.scale
|
|
367
|
+
})
|
|
368
|
+
.editSemantic({
|
|
369
|
+
property: "guide.legend.opacity.title",
|
|
370
|
+
value: resolved.config.title
|
|
371
|
+
})
|
|
372
|
+
._withLegendConfig("opacity", resolved.config);
|
|
373
|
+
if (resolved.config.border !== false) {
|
|
374
|
+
next = next.createGraphics({
|
|
375
|
+
id: "opacityLegendBackground",
|
|
376
|
+
type: "rect",
|
|
377
|
+
...resolveLegendGraphicPlacement(next)
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
next = next
|
|
381
|
+
.createGraphics({
|
|
382
|
+
id: "opacityLegendSymbols",
|
|
383
|
+
type: "circle",
|
|
384
|
+
length: 0,
|
|
385
|
+
...resolveLegendGraphicPlacement(next, resolved.config.border === false
|
|
386
|
+
? {}
|
|
387
|
+
: { after: "opacityLegendBackground" })
|
|
388
|
+
})
|
|
389
|
+
.createGraphics({
|
|
390
|
+
id: "opacityLegendLabels",
|
|
391
|
+
type: "text",
|
|
392
|
+
length: 0,
|
|
393
|
+
...resolveLegendGraphicPlacement(next)
|
|
394
|
+
});
|
|
395
|
+
if (resolved.config.titleVisible !== false) {
|
|
396
|
+
next = next.createGraphics({
|
|
397
|
+
id: "opacityLegendTitle",
|
|
398
|
+
type: "text",
|
|
399
|
+
...resolveLegendGraphicPlacement(next)
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
return next.rematerializeOpacityLegend();
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
export const createOpacityLegend = /* @__PURE__ */ action(
|
|
334
406
|
{
|
|
335
407
|
op: "createOpacityLegend",
|
|
336
408
|
description: "Create a field-opacity sample legend."
|
|
337
409
|
},
|
|
338
410
|
function (args = {}) {
|
|
339
|
-
const
|
|
340
|
-
if (args.channels !== undefined && (
|
|
341
|
-
!Array.isArray(args.channels) ||
|
|
342
|
-
args.channels.length !== 1 ||
|
|
343
|
-
args.channels[0] !== "opacity"
|
|
344
|
-
)) {
|
|
345
|
-
throw new Error('Opacity legend requires channels: ["opacity"].');
|
|
346
|
-
}
|
|
347
|
-
config.symbol = normalizeOpacitySymbol(args.symbol);
|
|
348
|
-
config.titleVisible = true;
|
|
349
|
-
const resolved = resolveOpacityConfig(this, config);
|
|
411
|
+
const resolved = resolveOpacityLegendCreation(this, args);
|
|
350
412
|
resolveOpacityLayout(this, resolved.config, resolved.scale);
|
|
351
413
|
if (this.graphicSpec.objects.opacityLegendSymbols !== undefined) {
|
|
352
414
|
throw new Error(
|
|
353
415
|
"createOpacityLegend requires a missing opacity legend."
|
|
354
416
|
);
|
|
355
417
|
}
|
|
356
|
-
|
|
357
|
-
.editSemantic({
|
|
358
|
-
property: "guide.legend.opacity.scale",
|
|
359
|
-
value: resolved.encoding.scale
|
|
360
|
-
})
|
|
361
|
-
.editSemantic({
|
|
362
|
-
property: "guide.legend.opacity.title",
|
|
363
|
-
value: resolved.config.title
|
|
364
|
-
})
|
|
365
|
-
._withLegendConfig("opacity", resolved.config);
|
|
366
|
-
if (resolved.config.border !== false) {
|
|
367
|
-
next = next.createGraphics({
|
|
368
|
-
id: "opacityLegendBackground",
|
|
369
|
-
type: "rect",
|
|
370
|
-
...resolveLegendGraphicPlacement(next)
|
|
371
|
-
});
|
|
372
|
-
}
|
|
373
|
-
return next
|
|
374
|
-
.createGraphics({
|
|
375
|
-
id: "opacityLegendSymbols",
|
|
376
|
-
type: "circle",
|
|
377
|
-
length: 0,
|
|
378
|
-
...resolveLegendGraphicPlacement(next),
|
|
379
|
-
...(resolved.config.border === false
|
|
380
|
-
? {}
|
|
381
|
-
: { after: "opacityLegendBackground" })
|
|
382
|
-
})
|
|
383
|
-
.createGraphics({
|
|
384
|
-
id: "opacityLegendLabels",
|
|
385
|
-
type: "text",
|
|
386
|
-
length: 0,
|
|
387
|
-
...resolveLegendGraphicPlacement(next)
|
|
388
|
-
})
|
|
389
|
-
.createGraphics({
|
|
390
|
-
id: "opacityLegendTitle",
|
|
391
|
-
type: "text",
|
|
392
|
-
...resolveLegendGraphicPlacement(next)
|
|
393
|
-
})
|
|
394
|
-
.rematerializeOpacityLegend();
|
|
418
|
+
return createOpacityLegendFromConfig(this, resolved.config);
|
|
395
419
|
}
|
|
396
420
|
);
|
|
397
421
|
|
|
398
|
-
export const removeOpacityLegend = action(
|
|
422
|
+
export const removeOpacityLegend = /* @__PURE__ */ action(
|
|
399
423
|
{
|
|
400
424
|
op: "removeOpacityLegend",
|
|
401
425
|
description: "Remove a field-opacity legend after switching to constant opacity."
|