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
|
@@ -9,24 +9,32 @@ import {
|
|
|
9
9
|
validateGeneratedItemLimit
|
|
10
10
|
} from "../../../../core/validation.js";
|
|
11
11
|
import { formatTimeTick } from "../../../../grammar/ticks.js";
|
|
12
|
+
import {
|
|
13
|
+
formatValue,
|
|
14
|
+
validateValueFormat
|
|
15
|
+
} from "../../../../grammar/valueFormat.js";
|
|
12
16
|
import {
|
|
13
17
|
formatDistinctNumericSamples,
|
|
14
18
|
sampleNumericRange
|
|
15
19
|
} from "../../../../grammar/numeric.js";
|
|
16
20
|
import { resolveGraphicBounds } from "../../../../layout/canvas.js";
|
|
21
|
+
import { isHorizontalEdgeLegend } from "../../../../layout/legendLane.js";
|
|
17
22
|
import { DEFAULT_COLORS, DEFAULT_FONT_FAMILY } from
|
|
18
23
|
"../../../../theme/defaults.js";
|
|
19
24
|
import { findLayer } from "../../../../selectors/layers.js";
|
|
25
|
+
import { isOpacityLegendLayer } from "../../../../materialization/legends.js";
|
|
20
26
|
import { findCanvasGraphic } from
|
|
21
27
|
"../../../../materialization/graphicHierarchy.js";
|
|
28
|
+
import { normalizeLegendSampling } from "../sampling.js";
|
|
22
29
|
|
|
23
30
|
const OPTIONS = [
|
|
24
31
|
"target", "channels", "position", "align", "offset", "title", "count",
|
|
32
|
+
"values",
|
|
25
33
|
"gradient", "symbol", "labels", "titleStyle", "itemGap", "border",
|
|
26
34
|
"direction", "columns", "titlePosition"
|
|
27
35
|
];
|
|
28
36
|
const TEXT_OPTIONS = [
|
|
29
|
-
"offset", "color", "fontSize", "fontFamily", "fontWeight"
|
|
37
|
+
"offset", "color", "fontSize", "fontFamily", "fontWeight", "format"
|
|
30
38
|
];
|
|
31
39
|
const BORDER_OPTIONS = [
|
|
32
40
|
"color", "lineWidth", "padding", "background"
|
|
@@ -82,9 +90,19 @@ export function normalizeLegendTextOptions(value, label, defaults) {
|
|
|
82
90
|
) {
|
|
83
91
|
throw new TypeError(`${label} fontWeight must be a string or finite number.`);
|
|
84
92
|
}
|
|
93
|
+
if (Object.hasOwn(result, "format")) {
|
|
94
|
+
result.format = validateValueFormat(result.format, `${label} format`);
|
|
95
|
+
}
|
|
85
96
|
return result;
|
|
86
97
|
}
|
|
87
98
|
|
|
99
|
+
export function normalizeLegendTitleOptions(value, label, defaults) {
|
|
100
|
+
if (value !== undefined) {
|
|
101
|
+
validateOptionObject(value, ["color", "fontSize", "fontFamily", "fontWeight"], label);
|
|
102
|
+
}
|
|
103
|
+
return normalizeLegendTextOptions(value, label, defaults);
|
|
104
|
+
}
|
|
105
|
+
|
|
88
106
|
export function normalizeLegendBorder(value) {
|
|
89
107
|
if (value === undefined || value === false) return false;
|
|
90
108
|
if (value !== true && !isPlainObject(value)) {
|
|
@@ -110,13 +128,27 @@ export function normalizeContinuousLegend(args, kind) {
|
|
|
110
128
|
if (!["left", "center", "right"].includes(align)) {
|
|
111
129
|
throw new Error(`Unsupported legend alignment "${align}".`);
|
|
112
130
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
);
|
|
131
|
+
if (["left", "right"].includes(position) && align !== "center") {
|
|
132
|
+
throw new Error("Side continuous legends require center alignment.");
|
|
133
|
+
}
|
|
134
|
+
if (kind === "gradient" && Object.hasOwn(args, "values")) {
|
|
135
|
+
throw new Error("Gradient legends do not support exact sample values.");
|
|
136
|
+
}
|
|
137
|
+
const sampling = kind === "opacity"
|
|
138
|
+
? normalizeLegendSampling(args, {
|
|
139
|
+
operation: "create",
|
|
140
|
+
label: "Opacity legend"
|
|
141
|
+
})
|
|
142
|
+
: undefined;
|
|
143
|
+
const count = kind === "gradient" ? args.count ?? 5 : undefined;
|
|
144
|
+
if (kind === "gradient") {
|
|
145
|
+
if (!Number.isInteger(count) || count < 2) {
|
|
146
|
+
throw new RangeError(
|
|
147
|
+
"Continuous legend count must be an integer of at least 2."
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
validateGeneratedItemLimit(count, "Continuous legend count");
|
|
118
151
|
}
|
|
119
|
-
validateGeneratedItemLimit(count, "Continuous legend count");
|
|
120
152
|
const offset = args.offset ?? 30;
|
|
121
153
|
validateNonNegative(offset, "Legend offset");
|
|
122
154
|
const titlePosition = args.titlePosition ?? "top";
|
|
@@ -152,7 +184,7 @@ export function normalizeContinuousLegend(args, kind) {
|
|
|
152
184
|
position,
|
|
153
185
|
align,
|
|
154
186
|
offset,
|
|
155
|
-
count,
|
|
187
|
+
...(kind === "gradient" ? { count } : { sampling }),
|
|
156
188
|
title: args.title,
|
|
157
189
|
inferredTitle: args.title === undefined,
|
|
158
190
|
labels: normalizeLegendTextOptions(
|
|
@@ -162,7 +194,7 @@ export function normalizeContinuousLegend(args, kind) {
|
|
|
162
194
|
? { ...DEFAULT_LABELS, offset: 8 }
|
|
163
195
|
: DEFAULT_LABELS
|
|
164
196
|
),
|
|
165
|
-
titleStyle:
|
|
197
|
+
titleStyle: normalizeLegendTitleOptions(
|
|
166
198
|
args.titleStyle,
|
|
167
199
|
"createLegend.titleStyle",
|
|
168
200
|
DEFAULT_TITLE
|
|
@@ -182,35 +214,40 @@ export function selectLegendLayer(program, requested, predicate) {
|
|
|
182
214
|
return candidates.includes(candidate) ? candidate : undefined;
|
|
183
215
|
}
|
|
184
216
|
|
|
185
|
-
export function
|
|
217
|
+
export function resolveContinuousLegendLayer(program, requested, channel) {
|
|
186
218
|
const layer = selectLegendLayer(
|
|
187
219
|
program,
|
|
188
220
|
requested,
|
|
189
|
-
|
|
190
|
-
candidate.encoding?.[channel]?.scale !== undefined
|
|
221
|
+
channel === "opacity" ? isOpacityLegendLayer : candidate =>
|
|
222
|
+
candidate.mark?.type === "point" && candidate.encoding?.[channel]?.scale !== undefined
|
|
191
223
|
);
|
|
192
224
|
if (layer === undefined) {
|
|
193
225
|
throw new Error(
|
|
194
226
|
requested === undefined
|
|
195
|
-
? `${channel} legend requires one eligible point mark.`
|
|
227
|
+
? `${channel} legend requires one eligible ${channel === "opacity" ? "point or line" : "point"} mark.`
|
|
196
228
|
: `Unknown ${channel} legend target "${requested}".`
|
|
197
229
|
);
|
|
198
230
|
}
|
|
199
231
|
return layer;
|
|
200
232
|
}
|
|
201
233
|
|
|
202
|
-
export function resolveContinuousColorLayer(program, requested) {
|
|
234
|
+
export function resolveContinuousColorLayer(program, requested, channel = "color") {
|
|
203
235
|
const layer = selectLegendLayer(
|
|
204
236
|
program,
|
|
205
237
|
requested,
|
|
206
|
-
candidate =>
|
|
207
|
-
|
|
238
|
+
candidate => (channel === "stroke"
|
|
239
|
+
? ["point", "line", "area", "bar", "rect", "arc", "rule", "tick"]
|
|
240
|
+
: ["point", "bar", "rect"]
|
|
241
|
+
).includes(candidate.mark?.type) &&
|
|
242
|
+
candidate.encoding?.[channel]?.scale !== undefined
|
|
208
243
|
);
|
|
209
244
|
if (layer === undefined) {
|
|
210
245
|
throw new Error(
|
|
211
246
|
requested === undefined
|
|
212
|
-
?
|
|
213
|
-
|
|
247
|
+
? channel === "color"
|
|
248
|
+
? "color legend requires one eligible point, bar, or rect mark."
|
|
249
|
+
: "stroke legend requires one eligible mark."
|
|
250
|
+
: `Unknown ${channel} legend target "${requested}".`
|
|
214
251
|
);
|
|
215
252
|
}
|
|
216
253
|
return layer;
|
|
@@ -251,10 +288,18 @@ export function sampleContinuousValues(domain, count) {
|
|
|
251
288
|
);
|
|
252
289
|
}
|
|
253
290
|
|
|
254
|
-
export function formatContinuousValues(values, domain, fieldType) {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
291
|
+
export function formatContinuousValues(values, domain, fieldType, format = "auto") {
|
|
292
|
+
const resolved = validateValueFormat(format, "Legend label format");
|
|
293
|
+
if (resolved === "auto") {
|
|
294
|
+
return fieldType === "temporal"
|
|
295
|
+
? values.map(value => formatTimeTick(value, domain))
|
|
296
|
+
: formatDistinctNumericSamples(values);
|
|
297
|
+
}
|
|
298
|
+
return values.map(value => formatValue(value, {
|
|
299
|
+
format: resolved,
|
|
300
|
+
valueType: fieldType === "temporal" ? "temporal" : "quantitative",
|
|
301
|
+
label: "Legend label format"
|
|
302
|
+
}));
|
|
258
303
|
}
|
|
259
304
|
|
|
260
305
|
export function styleContinuousText(
|
|
@@ -284,7 +329,10 @@ export function resolveLegendTextBounds(position, text, style) {
|
|
|
284
329
|
});
|
|
285
330
|
}
|
|
286
331
|
|
|
287
|
-
export function assertLegendBoundsInsideCanvas(bounds, canvas, label) {
|
|
332
|
+
export function assertLegendBoundsInsideCanvas(bounds, canvas, label, config) {
|
|
333
|
+
// Horizontal content is intrinsic until the shared lane places its actual
|
|
334
|
+
// concrete bounds. Only that final placement can determine Canvas fit.
|
|
335
|
+
if (isHorizontalEdgeLegend(config)) return;
|
|
288
336
|
if (bounds.some(item =>
|
|
289
337
|
item.left < 0 || item.right > canvas.width ||
|
|
290
338
|
item.top < 0 || item.bottom > canvas.height
|
|
@@ -297,7 +345,8 @@ export function resolveLegendBackgroundFromBounds(
|
|
|
297
345
|
bounds,
|
|
298
346
|
border,
|
|
299
347
|
canvas,
|
|
300
|
-
label
|
|
348
|
+
label,
|
|
349
|
+
config
|
|
301
350
|
) {
|
|
302
351
|
if (border === false) return undefined;
|
|
303
352
|
const strokeExtent = border.lineWidth / 2;
|
|
@@ -315,11 +364,11 @@ export function resolveLegendBackgroundFromBounds(
|
|
|
315
364
|
y -= border.padding;
|
|
316
365
|
right += border.padding;
|
|
317
366
|
bottom += border.padding;
|
|
318
|
-
if (
|
|
367
|
+
if (!isHorizontalEdgeLegend(config) && (
|
|
319
368
|
x - strokeExtent < 0 || y - strokeExtent < 0 ||
|
|
320
369
|
right + strokeExtent > canvas.width ||
|
|
321
370
|
bottom + strokeExtent > canvas.height
|
|
322
|
-
) {
|
|
371
|
+
)) {
|
|
323
372
|
throw new Error(`${label} background requires more Canvas margin space.`);
|
|
324
373
|
}
|
|
325
374
|
return { x, y, width: right - x, height: bottom - y };
|
|
@@ -334,3 +383,27 @@ export function editLegendBackground(program, id, bounds, border) {
|
|
|
334
383
|
strokeWidth: border.lineWidth
|
|
335
384
|
});
|
|
336
385
|
}
|
|
386
|
+
|
|
387
|
+
export function normalizeItemLegendLayout(args) {
|
|
388
|
+
const position = args.position ?? "right";
|
|
389
|
+
if (!["right", "left", "top", "bottom"].includes(position)) throw new Error(`Unsupported legend position "${position}".`);
|
|
390
|
+
const side = ["left", "right"].includes(position);
|
|
391
|
+
const layout = args.layout === undefined ? "edge" : args.layout;
|
|
392
|
+
if (layout !== "edge") throw new Error('This legend requires layout "edge".');
|
|
393
|
+
const align = args.align ?? "center";
|
|
394
|
+
if (!["left", "center", "right"].includes(align)) throw new Error(`Unsupported legend alignment "${align}".`);
|
|
395
|
+
const direction = args.direction ?? (side ? "vertical" : "horizontal");
|
|
396
|
+
if (!["horizontal", "vertical"].includes(direction)) throw new Error(`Unsupported legend direction "${direction}".`);
|
|
397
|
+
const columns = args.columns;
|
|
398
|
+
if (columns !== undefined && (!Number.isInteger(columns) || columns < 1)) throw new RangeError("Legend columns must be a positive integer.");
|
|
399
|
+
const titlePosition = args.titlePosition ?? "top";
|
|
400
|
+
if (!["top", "left"].includes(titlePosition)) throw new Error(`Unsupported legend titlePosition "${titlePosition}".`);
|
|
401
|
+
if (side && (direction !== "vertical" || align !== "center" || (columns !== undefined && columns !== 1) || titlePosition !== "top")) {
|
|
402
|
+
throw new Error("Side item legends require vertical direction, center alignment, top title and one column.");
|
|
403
|
+
}
|
|
404
|
+
const offset = args.offset ?? 30;
|
|
405
|
+
const itemGap = args.itemGap ?? 28;
|
|
406
|
+
validateNonNegative(offset, "Legend offset");
|
|
407
|
+
validatePositive(itemGap, "Legend itemGap");
|
|
408
|
+
return { position, layout, align, direction, columns, titlePosition, offset, itemGap };
|
|
409
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { action } from "../../../../core/action.js";
|
|
2
2
|
import { isPlainObject } from "../../../../core/immutable.js";
|
|
3
3
|
import { validateKeys } from "../../../../core/validation.js";
|
|
4
|
-
import {
|
|
4
|
+
import { mapScaleConsumerValues } from "../../../../materialization/scales/map.js";
|
|
5
|
+
import { inverseLerp, interpolateNumber } from "../../../../grammar/numeric.js";
|
|
5
6
|
import { DEFAULT_COLORS } from "../../../../theme/defaults.js";
|
|
6
7
|
import {
|
|
7
8
|
assertLegendBoundsInsideCanvas,
|
|
@@ -19,22 +20,26 @@ import {
|
|
|
19
20
|
} from "./common.js";
|
|
20
21
|
import { resolveLegendGraphicPlacement } from
|
|
21
22
|
"../../../../materialization/graphicHierarchy.js";
|
|
23
|
+
import { resolveEffectiveLegendBlockConfig } from "../blocks.js";
|
|
24
|
+
|
|
25
|
+
export const DEFAULT_GRADIENT_SIZE = Object.freeze({ length: 120, thickness: 12 });
|
|
22
26
|
|
|
23
27
|
const GRADIENT_OPTIONS = Object.freeze(["length", "thickness"]);
|
|
24
28
|
|
|
25
|
-
function resolveGradientLayout(program, config, scale) {
|
|
29
|
+
export function resolveGradientLayout(program, config, scale) {
|
|
26
30
|
const { plot, canvas } = resolveContinuousBounds(program);
|
|
27
31
|
const vertical = ["right", "left"].includes(config.position);
|
|
28
32
|
const length = config.gradient.length;
|
|
29
33
|
const thickness = config.gradient.thickness;
|
|
34
|
+
const titleGap = config.blockGap ?? 12;
|
|
30
35
|
let x;
|
|
31
36
|
let y;
|
|
32
37
|
if (config.position === "right") {
|
|
33
38
|
x = plot.x + plot.width + config.offset;
|
|
34
|
-
y = plot.y + 46;
|
|
39
|
+
y = plot.y + 46 + titleGap - 12;
|
|
35
40
|
} else if (config.position === "left") {
|
|
36
41
|
x = plot.x - config.offset - thickness;
|
|
37
|
-
y = plot.y + 46;
|
|
42
|
+
y = plot.y + 46 + titleGap - 12;
|
|
38
43
|
} else {
|
|
39
44
|
x = config.align === "left" ? plot.x
|
|
40
45
|
: config.align === "right" ? plot.x + plot.width - length
|
|
@@ -43,18 +48,29 @@ function resolveGradientLayout(program, config, scale) {
|
|
|
43
48
|
? plot.y - config.offset - thickness - config.labels.offset -
|
|
44
49
|
config.labels.fontSize
|
|
45
50
|
: plot.y + plot.height + config.offset +
|
|
46
|
-
config.titleStyle.fontSize +
|
|
51
|
+
(config.titleVisible === false ? 0 : config.titleStyle.fontSize + titleGap);
|
|
47
52
|
}
|
|
48
53
|
const title = vertical
|
|
49
54
|
? { x, y: plot.y + 20, align: "left" }
|
|
50
55
|
: {
|
|
51
56
|
x: x + length / 2,
|
|
52
|
-
y: y -
|
|
57
|
+
y: y - titleGap - config.titleStyle.fontSize / 2,
|
|
53
58
|
align: "center"
|
|
54
59
|
};
|
|
55
|
-
const values = sampleContinuousValues(scale.domain, config.count);
|
|
56
|
-
|
|
57
|
-
|
|
60
|
+
const values = [...sampleContinuousValues(scale.domain, config.count)];
|
|
61
|
+
if (scale.midpoint !== undefined && !values.includes(scale.midpoint)) {
|
|
62
|
+
values.push(scale.midpoint);
|
|
63
|
+
values.sort((a, b) => scale.domain[1] > scale.domain[0] ? a - b : b - a);
|
|
64
|
+
}
|
|
65
|
+
const texts = formatContinuousValues(
|
|
66
|
+
values,
|
|
67
|
+
scale.domain,
|
|
68
|
+
config.fieldType,
|
|
69
|
+
config.labels.format
|
|
70
|
+
);
|
|
71
|
+
const fractions = values.map((value, index) => scale.midpoint === undefined
|
|
72
|
+
? index / (values.length - 1)
|
|
73
|
+
: inverseLerp(value, ...scale.domain));
|
|
58
74
|
const labelOffset = config.labels.offset;
|
|
59
75
|
const labels = vertical
|
|
60
76
|
? fractions.map(fraction => ({
|
|
@@ -97,22 +113,23 @@ function resolveGradientLayout(program, config, scale) {
|
|
|
97
113
|
const labelBounds = labels.map((label, index) =>
|
|
98
114
|
resolveLegendTextBounds(label, texts[index], config.labels)
|
|
99
115
|
);
|
|
100
|
-
const titleBounds = resolveLegendTextBounds(
|
|
116
|
+
const titleBounds = config.titleVisible === false ? undefined : resolveLegendTextBounds(
|
|
101
117
|
title,
|
|
102
118
|
config.title,
|
|
103
119
|
config.titleStyle
|
|
104
120
|
);
|
|
105
|
-
const occupiedBounds = [stripBounds, ...tickBounds, ...labelBounds,
|
|
121
|
+
const occupiedBounds = [stripBounds, ...tickBounds, ...labelBounds,
|
|
122
|
+
...(config.titleVisible === false ? [] : [titleBounds])];
|
|
106
123
|
assertLegendBoundsInsideCanvas(
|
|
107
124
|
occupiedBounds,
|
|
108
125
|
canvas,
|
|
109
|
-
"Gradient legend layout"
|
|
126
|
+
"Gradient legend layout", config
|
|
110
127
|
);
|
|
111
128
|
const background = resolveLegendBackgroundFromBounds(
|
|
112
129
|
occupiedBounds,
|
|
113
130
|
config.border,
|
|
114
131
|
canvas,
|
|
115
|
-
"Gradient legend"
|
|
132
|
+
"Gradient legend", config
|
|
116
133
|
);
|
|
117
134
|
return {
|
|
118
135
|
vertical, x, y, length, thickness, values, texts, labels, ticks, title,
|
|
@@ -157,17 +174,18 @@ export const rematerializeGradientLegend = action(
|
|
|
157
174
|
if (stored === undefined) {
|
|
158
175
|
throw new Error("Gradient legend requires stored configuration.");
|
|
159
176
|
}
|
|
160
|
-
const { scale, encoding, config } = resolveGradientConfig(this, stored);
|
|
177
|
+
const { scale, encoding, config: currentConfig } = resolveGradientConfig(this, stored);
|
|
178
|
+
const config = resolveEffectiveLegendBlockConfig(this, "gradient", currentConfig);
|
|
161
179
|
const layout = resolveGradientLayout(this, config, scale);
|
|
162
180
|
const stripCount = 60;
|
|
163
181
|
const stripSize = layout.length / stripCount;
|
|
164
182
|
const strips = Array.from({ length: stripCount }, (_, index) => {
|
|
165
183
|
const fraction = (index + 0.5) / stripCount;
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
);
|
|
184
|
+
const position = layout.vertical ? 1 - fraction : fraction;
|
|
185
|
+
// Keep legacy uniform samples exact without a value/domain round trip.
|
|
186
|
+
const samplingScale = scale.midpoint === undefined ? { ...scale, domain: [0, 1] } : scale;
|
|
187
|
+
const value = scale.midpoint === undefined ? position : interpolateNumber(...scale.domain, position);
|
|
188
|
+
const [color] = mapScaleConsumerValues([value], samplingScale, "color");
|
|
171
189
|
return {
|
|
172
190
|
x: layout.x + (layout.vertical ? 0 : index * stripSize),
|
|
173
191
|
y: layout.y + (layout.vertical ? index * stripSize : 0),
|
|
@@ -187,7 +205,7 @@ export const rematerializeGradientLegend = action(
|
|
|
187
205
|
property: "guide.legend.color.title",
|
|
188
206
|
value: config.title
|
|
189
207
|
})
|
|
190
|
-
._withLegendConfig("gradient",
|
|
208
|
+
._withLegendConfig("gradient", currentConfig)
|
|
191
209
|
.editGraphics({
|
|
192
210
|
target: "colorGradientStrips",
|
|
193
211
|
property: "length",
|
|
@@ -283,86 +301,99 @@ export const rematerializeGradientLegend = action(
|
|
|
283
301
|
}
|
|
284
302
|
);
|
|
285
303
|
|
|
304
|
+
|
|
305
|
+
export function resolveGradientLegendCreation(program, args = {}) {
|
|
306
|
+
const config = normalizeContinuousLegend(args, "gradient");
|
|
307
|
+
if (args.channels !== undefined && (
|
|
308
|
+
!Array.isArray(args.channels) ||
|
|
309
|
+
args.channels.length !== 1 ||
|
|
310
|
+
args.channels[0] !== "color"
|
|
311
|
+
)) {
|
|
312
|
+
throw new Error('Gradient legend requires channels: ["color"].');
|
|
313
|
+
}
|
|
314
|
+
if (args.gradient !== undefined && !isPlainObject(args.gradient)) {
|
|
315
|
+
throw new TypeError("createLegend.gradient must be a plain object.");
|
|
316
|
+
}
|
|
317
|
+
validateKeys(
|
|
318
|
+
args.gradient ?? {},
|
|
319
|
+
GRADIENT_OPTIONS,
|
|
320
|
+
"createLegend.gradient"
|
|
321
|
+
);
|
|
322
|
+
config.gradient = {
|
|
323
|
+
length: args.gradient?.length ?? DEFAULT_GRADIENT_SIZE.length,
|
|
324
|
+
thickness: args.gradient?.thickness ?? DEFAULT_GRADIENT_SIZE.thickness
|
|
325
|
+
};
|
|
326
|
+
config.titleVisible = true;
|
|
327
|
+
validatePositive(config.gradient.length, "Gradient length");
|
|
328
|
+
validatePositive(config.gradient.thickness, "Gradient thickness");
|
|
329
|
+
const resolved = resolveGradientConfig(program, config);
|
|
330
|
+
return resolved;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
export function createGradientLegendFromConfig(program, config) {
|
|
334
|
+
const resolved = resolveGradientConfig(program, config);
|
|
335
|
+
resolveGradientLayout(program, resolved.config, resolved.scale);
|
|
336
|
+
let next = program
|
|
337
|
+
.editSemantic({
|
|
338
|
+
property: "guide.legend.color.scale",
|
|
339
|
+
value: resolved.encoding.scale
|
|
340
|
+
})
|
|
341
|
+
.editSemantic({
|
|
342
|
+
property: "guide.legend.color.title",
|
|
343
|
+
value: resolved.config.title
|
|
344
|
+
})
|
|
345
|
+
._withLegendConfig("gradient", resolved.config);
|
|
346
|
+
if (resolved.config.border !== false) {
|
|
347
|
+
next = next.createGraphics({
|
|
348
|
+
id: "colorGradientBackground",
|
|
349
|
+
type: "rect",
|
|
350
|
+
...resolveLegendGraphicPlacement(next)
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
next = next
|
|
354
|
+
.createGraphics({
|
|
355
|
+
id: "colorGradientStrips",
|
|
356
|
+
type: "rect",
|
|
357
|
+
length: 0,
|
|
358
|
+
...resolveLegendGraphicPlacement(next, resolved.config.border === false
|
|
359
|
+
? {}
|
|
360
|
+
: { after: "colorGradientBackground" })
|
|
361
|
+
})
|
|
362
|
+
.createGraphics({
|
|
363
|
+
id: "colorGradientTicks",
|
|
364
|
+
type: "line",
|
|
365
|
+
length: 0,
|
|
366
|
+
...resolveLegendGraphicPlacement(next)
|
|
367
|
+
})
|
|
368
|
+
.createGraphics({
|
|
369
|
+
id: "colorGradientLabels",
|
|
370
|
+
type: "text",
|
|
371
|
+
length: 0,
|
|
372
|
+
...resolveLegendGraphicPlacement(next)
|
|
373
|
+
});
|
|
374
|
+
if (resolved.config.titleVisible !== false) {
|
|
375
|
+
next = next.createGraphics({
|
|
376
|
+
id: "colorGradientTitle",
|
|
377
|
+
type: "text",
|
|
378
|
+
...resolveLegendGraphicPlacement(next)
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
return next.rematerializeGradientLegend();
|
|
382
|
+
}
|
|
383
|
+
|
|
286
384
|
export const createGradientLegend = action(
|
|
287
385
|
{
|
|
288
386
|
op: "createGradientLegend",
|
|
289
387
|
description: "Create a continuous color gradient legend."
|
|
290
388
|
},
|
|
291
389
|
function (args = {}) {
|
|
292
|
-
const
|
|
293
|
-
if (args.channels !== undefined && (
|
|
294
|
-
!Array.isArray(args.channels) ||
|
|
295
|
-
args.channels.length !== 1 ||
|
|
296
|
-
args.channels[0] !== "color"
|
|
297
|
-
)) {
|
|
298
|
-
throw new Error('Gradient legend requires channels: ["color"].');
|
|
299
|
-
}
|
|
300
|
-
if (args.gradient !== undefined && !isPlainObject(args.gradient)) {
|
|
301
|
-
throw new TypeError("createLegend.gradient must be a plain object.");
|
|
302
|
-
}
|
|
303
|
-
validateKeys(
|
|
304
|
-
args.gradient ?? {},
|
|
305
|
-
GRADIENT_OPTIONS,
|
|
306
|
-
"createLegend.gradient"
|
|
307
|
-
);
|
|
308
|
-
config.gradient = {
|
|
309
|
-
length: args.gradient?.length ?? 120,
|
|
310
|
-
thickness: args.gradient?.thickness ?? 12
|
|
311
|
-
};
|
|
312
|
-
config.titleVisible = true;
|
|
313
|
-
validatePositive(config.gradient.length, "Gradient length");
|
|
314
|
-
validatePositive(config.gradient.thickness, "Gradient thickness");
|
|
315
|
-
const resolved = resolveGradientConfig(this, config);
|
|
390
|
+
const resolved = resolveGradientLegendCreation(this, args);
|
|
316
391
|
resolveGradientLayout(this, resolved.config, resolved.scale);
|
|
317
392
|
if (this.graphicSpec.objects.colorGradientStrips !== undefined) {
|
|
318
393
|
throw new Error(
|
|
319
394
|
"createGradientLegend requires a missing gradient legend."
|
|
320
395
|
);
|
|
321
396
|
}
|
|
322
|
-
|
|
323
|
-
.editSemantic({
|
|
324
|
-
property: "guide.legend.color.scale",
|
|
325
|
-
value: resolved.encoding.scale
|
|
326
|
-
})
|
|
327
|
-
.editSemantic({
|
|
328
|
-
property: "guide.legend.color.title",
|
|
329
|
-
value: resolved.config.title
|
|
330
|
-
})
|
|
331
|
-
._withLegendConfig("gradient", resolved.config);
|
|
332
|
-
if (resolved.config.border !== false) {
|
|
333
|
-
next = next.createGraphics({
|
|
334
|
-
id: "colorGradientBackground",
|
|
335
|
-
type: "rect",
|
|
336
|
-
...resolveLegendGraphicPlacement(next)
|
|
337
|
-
});
|
|
338
|
-
}
|
|
339
|
-
return next
|
|
340
|
-
.createGraphics({
|
|
341
|
-
id: "colorGradientStrips",
|
|
342
|
-
type: "rect",
|
|
343
|
-
length: 0,
|
|
344
|
-
...resolveLegendGraphicPlacement(next),
|
|
345
|
-
...(resolved.config.border === false
|
|
346
|
-
? {}
|
|
347
|
-
: { after: "colorGradientBackground" })
|
|
348
|
-
})
|
|
349
|
-
.createGraphics({
|
|
350
|
-
id: "colorGradientTicks",
|
|
351
|
-
type: "line",
|
|
352
|
-
length: 0,
|
|
353
|
-
...resolveLegendGraphicPlacement(next)
|
|
354
|
-
})
|
|
355
|
-
.createGraphics({
|
|
356
|
-
id: "colorGradientLabels",
|
|
357
|
-
type: "text",
|
|
358
|
-
length: 0,
|
|
359
|
-
...resolveLegendGraphicPlacement(next)
|
|
360
|
-
})
|
|
361
|
-
.createGraphics({
|
|
362
|
-
id: "colorGradientTitle",
|
|
363
|
-
type: "text",
|
|
364
|
-
...resolveLegendGraphicPlacement(next)
|
|
365
|
-
})
|
|
366
|
-
.rematerializeGradientLegend();
|
|
397
|
+
return createGradientLegendFromConfig(this, resolved.config);
|
|
367
398
|
}
|
|
368
399
|
);
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
removeOpacityLegend
|
|
6
6
|
} from "./opacity.js";
|
|
7
7
|
import { createIntervalLegend, rematerializeIntervalLegend } from "./interval.js";
|
|
8
|
+
import { registerStrokeColorLegendActions } from "./stroke.js";
|
|
8
9
|
|
|
9
10
|
export { createGradientLegend, rematerializeGradientLegend } from "./gradient.js";
|
|
10
11
|
export {
|
|
@@ -19,11 +20,16 @@ export function registerContinuousLegendActions(ProgramClass) {
|
|
|
19
20
|
ProgramClass.prototype.createOpacityLegend = createOpacityLegend;
|
|
20
21
|
ProgramClass.prototype.rematerializeOpacityLegend = rematerializeOpacityLegend;
|
|
21
22
|
ProgramClass.prototype.removeOpacityLegend = removeOpacityLegend;
|
|
22
|
-
ProgramClass
|
|
23
|
-
ProgramClass
|
|
23
|
+
registerIntervalLegendActions(ProgramClass);
|
|
24
|
+
registerStrokeColorLegendActions(ProgramClass);
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
export function registerGradientLegendActions(ProgramClass) {
|
|
27
28
|
ProgramClass.prototype.createGradientLegend = createGradientLegend;
|
|
28
29
|
ProgramClass.prototype.rematerializeGradientLegend = rematerializeGradientLegend;
|
|
29
30
|
}
|
|
31
|
+
|
|
32
|
+
export function registerIntervalLegendActions(ProgramClass) {
|
|
33
|
+
ProgramClass.prototype.createIntervalLegend = createIntervalLegend;
|
|
34
|
+
ProgramClass.prototype.rematerializeIntervalLegend = rematerializeIntervalLegend;
|
|
35
|
+
}
|