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 { validateContinuousColorConsumer } from "../../grammar/scales/colorConsumers.js";
|
|
1
2
|
import {
|
|
2
3
|
hasOrdinalDomain,
|
|
3
4
|
isDiscretizedColorScaleType,
|
|
4
5
|
isDiscretePositionScaleType,
|
|
5
6
|
isTransformedScaleType,
|
|
7
|
+
normalizeSizeScaleDefinition,
|
|
6
8
|
normalizeScaleDefinition,
|
|
7
9
|
SCALE_ROLES,
|
|
8
10
|
validateColorRange,
|
|
@@ -14,6 +16,7 @@ import {
|
|
|
14
16
|
validateScaleType,
|
|
15
17
|
validateScaleTypeForRole,
|
|
16
18
|
validateScaleUnknown,
|
|
19
|
+
validateSequentialMidpoint,
|
|
17
20
|
validateSequentialColorRange,
|
|
18
21
|
validateShapeRange,
|
|
19
22
|
validateSizeRange,
|
|
@@ -24,11 +27,20 @@ import {
|
|
|
24
27
|
validateRadialRange,
|
|
25
28
|
validateThetaRange
|
|
26
29
|
} from "../../grammar/polar.js";
|
|
27
|
-
import {
|
|
30
|
+
import {
|
|
31
|
+
normalizePositionScaleChannel,
|
|
32
|
+
OFFSET_POSITION_CHANNELS
|
|
33
|
+
} from "../../core/vocabulary.js";
|
|
34
|
+
import { normalizeOffsetScalePolicy } from "../../grammar/bars/geometry.js";
|
|
35
|
+
import { resolveRequestedOffsetPolicy } from
|
|
36
|
+
"../../materialization/scales/policies/offset.js";
|
|
28
37
|
|
|
29
38
|
export function resolveScaleConsumerChannel(consumers, id) {
|
|
30
39
|
const channels = new Set(
|
|
31
|
-
consumers.map(consumer =>
|
|
40
|
+
consumers.map(consumer => {
|
|
41
|
+
const channel = normalizePositionScaleChannel(consumer.channel);
|
|
42
|
+
return channel === "stroke" ? "color" : channel;
|
|
43
|
+
})
|
|
32
44
|
);
|
|
33
45
|
if (channels.size > 1) {
|
|
34
46
|
throw new Error(`Scale "${id}" cannot be shared across channels.`);
|
|
@@ -38,6 +50,7 @@ export function resolveScaleConsumerChannel(consumers, id) {
|
|
|
38
50
|
|
|
39
51
|
function validateRangeForChannel(scale, channel, value) {
|
|
40
52
|
if (value === "auto") return value;
|
|
53
|
+
if (channel === "size") return validateSizeRange(value);
|
|
41
54
|
if (channel === "theta") return validateThetaRange(value);
|
|
42
55
|
if (channel === "radius") return validateRadialRange(value);
|
|
43
56
|
if (scale.type === "sequential") {
|
|
@@ -47,7 +60,7 @@ function validateRangeForChannel(scale, channel, value) {
|
|
|
47
60
|
return validateDiscretizedColorRange(value);
|
|
48
61
|
}
|
|
49
62
|
if (scale.type !== "ordinal") return validateScaleRange(value);
|
|
50
|
-
if (
|
|
63
|
+
if (["color", "stroke"].includes(channel)) return validateColorRange(value);
|
|
51
64
|
if (channel === "shape") return validateShapeRange(value);
|
|
52
65
|
if (channel === "strokeDash") return validateStrokeDashRange(value);
|
|
53
66
|
if (channel === "size") return validateSizeRange(value);
|
|
@@ -60,17 +73,26 @@ function validateTypeTransition(scale, nextType, channel, consumers) {
|
|
|
60
73
|
validateScaleType(nextType);
|
|
61
74
|
if (consumers.length === 0) return;
|
|
62
75
|
if (nextType === "sequential" || isDiscretizedColorScaleType(nextType)) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
consumer.layer
|
|
69
|
-
|
|
70
|
-
|
|
76
|
+
if (channel !== "color") {
|
|
77
|
+
throw new Error(`Scale "${scale.id}" has a consumer incompatible with type "${nextType}".`);
|
|
78
|
+
}
|
|
79
|
+
for (const consumer of consumers) {
|
|
80
|
+
validateContinuousColorConsumer(
|
|
81
|
+
consumer.layer,
|
|
82
|
+
consumer.encoding,
|
|
83
|
+
{ type: nextType },
|
|
84
|
+
{ channel: consumer.channel }
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (nextType === "ordinal" && channel === "color") {
|
|
90
|
+
if (consumers.some(consumer =>
|
|
91
|
+
!["color", "stroke"].includes(consumer.channel) ||
|
|
92
|
+
!["nominal", "ordinal"].includes(consumer.encoding.fieldType)
|
|
71
93
|
)) {
|
|
72
94
|
throw new Error(
|
|
73
|
-
`Scale "${scale.id}" has a consumer incompatible with type "
|
|
95
|
+
`Scale "${scale.id}" has a consumer incompatible with type "ordinal".`
|
|
74
96
|
);
|
|
75
97
|
}
|
|
76
98
|
return;
|
|
@@ -132,33 +154,65 @@ function validateTypeTransition(scale, nextType, channel, consumers) {
|
|
|
132
154
|
}
|
|
133
155
|
}
|
|
134
156
|
|
|
135
|
-
function
|
|
136
|
-
if (
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
throw new Error(
|
|
140
|
-
`Scale "${scale.id}" cannot change type while its gradient legend is active.`
|
|
141
|
-
);
|
|
157
|
+
function normalizeDefinition(program, scale, channel, consumers, patch) {
|
|
158
|
+
if (scale.radialMapping !== undefined && Object.hasOwn(patch, "radialMapping") &&
|
|
159
|
+
patch.radialMapping === undefined && consumers.some(consumer => consumer.encoding.aggregate !== undefined)) {
|
|
160
|
+
throw new Error("Remove measured radius encodings before clearing their scale mapping.");
|
|
142
161
|
}
|
|
143
|
-
if (
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
162
|
+
if (channel === "size") {
|
|
163
|
+
if (consumers.some(consumer =>
|
|
164
|
+
consumer.layer.mark?.type !== "point" ||
|
|
165
|
+
consumer.encoding.fieldType !== "quantitative"
|
|
166
|
+
)) {
|
|
167
|
+
throw new Error(
|
|
168
|
+
`Scale "${scale.id}" has a consumer incompatible with size mapping.`
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
return normalizeSizeScaleDefinition({ previous: scale, patch });
|
|
150
172
|
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function normalizeDefinition(scale, channel, consumers, patch) {
|
|
154
173
|
const type = patch.type ?? scale.type;
|
|
155
174
|
validateTypeTransition(scale, type, channel, consumers);
|
|
175
|
+
const offset = OFFSET_POSITION_CHANNELS.includes(channel);
|
|
176
|
+
if (offset && Object.hasOwn(patch, "range")) {
|
|
177
|
+
throw new Error(
|
|
178
|
+
`${channel} scale range is derived from its parent categorical slot.`
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
if (offset && Object.hasOwn(patch, "unknown")) {
|
|
182
|
+
throw new Error(`${channel} scale unknown is not supported for offset positions.`);
|
|
183
|
+
}
|
|
184
|
+
if (type !== scale.type && scale.domain !== "auto" && !Object.hasOwn(patch, "domain") &&
|
|
185
|
+
(["quantile", "threshold"].includes(type) || ["quantile", "threshold"].includes(scale.type))) {
|
|
186
|
+
throw new Error("Color scale type transition requires an explicit domain when its meaning changes.");
|
|
187
|
+
}
|
|
188
|
+
const offsetPolicy = offset
|
|
189
|
+
? normalizeOffsetScalePolicy(
|
|
190
|
+
patch,
|
|
191
|
+
resolveRequestedOffsetPolicy({
|
|
192
|
+
scale,
|
|
193
|
+
consumers,
|
|
194
|
+
markConfigs: program.markConfigs,
|
|
195
|
+
id: scale.id,
|
|
196
|
+
channel
|
|
197
|
+
}),
|
|
198
|
+
channel
|
|
199
|
+
)
|
|
200
|
+
: undefined;
|
|
201
|
+
const normalizedPatch = offset
|
|
202
|
+
? {
|
|
203
|
+
...Object.fromEntries(Object.entries(patch).filter(
|
|
204
|
+
([property]) => property !== "padding"
|
|
205
|
+
)),
|
|
206
|
+
...offsetPolicy
|
|
207
|
+
}
|
|
208
|
+
: patch;
|
|
156
209
|
const definition = normalizeScaleDefinition({
|
|
157
210
|
type,
|
|
158
211
|
previous: scale,
|
|
159
|
-
patch,
|
|
212
|
+
patch: normalizedPatch,
|
|
160
213
|
retainCoreOnTypeChange: true,
|
|
161
214
|
retainCompatibleOnTypeChange: true,
|
|
215
|
+
allowOrdinalBandParameters: offset,
|
|
162
216
|
validateDomain: (scaleType, value) =>
|
|
163
217
|
isDiscretizedColorScaleType(scaleType)
|
|
164
218
|
? validateDiscretizedColorDomain(scaleType, value)
|
|
@@ -175,9 +229,16 @@ function normalizeDefinition(scale, channel, consumers, patch) {
|
|
|
175
229
|
) {
|
|
176
230
|
throw new RangeError("StrokeWidth scale domain cannot contain negative values.");
|
|
177
231
|
}
|
|
232
|
+
validateSequentialMidpoint(definition.midpoint, type, definition.domain);
|
|
233
|
+
if (definition.midpoint !== undefined && consumers.some(
|
|
234
|
+
consumer => !["color", "stroke"].includes(consumer.channel) ||
|
|
235
|
+
consumer.encoding.fieldType !== "quantitative"
|
|
236
|
+
)) {
|
|
237
|
+
throw new Error("Scale midpoint requires quantitative color consumers.");
|
|
238
|
+
}
|
|
178
239
|
const typeChanged = type !== scale.type;
|
|
179
|
-
const unknown = Object.hasOwn(
|
|
180
|
-
?
|
|
240
|
+
const unknown = Object.hasOwn(normalizedPatch, "unknown")
|
|
241
|
+
? normalizedPatch.unknown
|
|
181
242
|
: typeChanged ? undefined : scale.unknown;
|
|
182
243
|
if (unknown !== undefined) {
|
|
183
244
|
if (consumers.some(consumer => consumer.layer.mark?.type !== "point")) {
|
|
@@ -197,7 +258,7 @@ export function prepareScaleEdit(program, scale, channel, consumers, args) {
|
|
|
197
258
|
}
|
|
198
259
|
if (
|
|
199
260
|
hasPalette &&
|
|
200
|
-
|
|
261
|
+
!["color", "stroke"].includes(channel) &&
|
|
201
262
|
scale.type !== "sequential" &&
|
|
202
263
|
!isDiscretizedColorScaleType(scale.type)
|
|
203
264
|
) {
|
|
@@ -209,6 +270,5 @@ export function prepareScaleEdit(program, scale, channel, consumers, args) {
|
|
|
209
270
|
...Object.entries(args).filter(([key]) => key !== "palette"),
|
|
210
271
|
["range", { palette: args.palette }]
|
|
211
272
|
]);
|
|
212
|
-
|
|
213
|
-
return normalizeDefinition(scale, channel, consumers, patch);
|
|
273
|
+
return normalizeDefinition(program, scale, channel, consumers, patch);
|
|
214
274
|
}
|
|
@@ -2,9 +2,15 @@ import { createScale } from "./create.js";
|
|
|
2
2
|
import { editScale } from "./edit.js";
|
|
3
3
|
import { rematerializeScale } from "./materialize.js";
|
|
4
4
|
import { setQuantitativeColorScale } from "./quantitativeColor.js";
|
|
5
|
+
import { registerChannelScaleActions } from "./channels.js";
|
|
6
|
+
import { editParallelScale } from "./parallel.js";
|
|
7
|
+
import { registerOffsetScaleActions } from "./offset.js";
|
|
5
8
|
|
|
6
9
|
export function registerScaleActions(ProgramClass) {
|
|
7
10
|
ProgramClass.prototype.editScale = editScale;
|
|
11
|
+
ProgramClass.prototype.editParallelScale = editParallelScale;
|
|
12
|
+
registerOffsetScaleActions(ProgramClass);
|
|
13
|
+
registerChannelScaleActions(ProgramClass);
|
|
8
14
|
registerBasicScaleActions(ProgramClass);
|
|
9
15
|
}
|
|
10
16
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { isPendingMeasuredRadiusConsumer } from "../../materialization/scales/policies/arc.js";
|
|
2
|
+
import { findScale, findScaleConsumers } from "./consumers/index.js";
|
|
3
|
+
import { resolveScalePreview, validatePendingMeasuredScale } from "./preview.js";
|
|
1
4
|
import { action } from "../../core/action.js";
|
|
2
5
|
import { validateUserId } from "../../core/identifiers.js";
|
|
3
|
-
import { normalizePositionScaleChannel } from "../../core/vocabulary.js";
|
|
4
6
|
import { validateKeys } from "../../core/validation.js";
|
|
5
|
-
import { resolveGraphicBounds } from "../../layout/canvas.js";
|
|
6
7
|
import {
|
|
7
8
|
applyMaterializationPlan,
|
|
8
9
|
planScaleGuideRematerialization
|
|
@@ -14,16 +15,6 @@ import {
|
|
|
14
15
|
} from "../../materialization/marks/index.js";
|
|
15
16
|
import { mapScaleConsumerValues } from
|
|
16
17
|
"../../materialization/scales/map.js";
|
|
17
|
-
import { resolveScaleMaterialization } from
|
|
18
|
-
"../../materialization/scales/resolve.js";
|
|
19
|
-
import {
|
|
20
|
-
findScale,
|
|
21
|
-
findScaleConsumers,
|
|
22
|
-
resolveConsumerCategoryOrder,
|
|
23
|
-
resolveConsumerValues,
|
|
24
|
-
resolveSeriesLayoutScaleValues
|
|
25
|
-
} from "./consumers/index.js";
|
|
26
|
-
|
|
27
18
|
const OPTIONS = Object.freeze(["id", "guides", "marks"]);
|
|
28
19
|
|
|
29
20
|
function validateOptions(args) {
|
|
@@ -36,16 +27,6 @@ function validateOptions(args) {
|
|
|
36
27
|
}
|
|
37
28
|
}
|
|
38
29
|
|
|
39
|
-
function resolveScaleChannel(id, consumers) {
|
|
40
|
-
const channels = new Set(
|
|
41
|
-
consumers.map(consumer => normalizePositionScaleChannel(consumer.channel))
|
|
42
|
-
);
|
|
43
|
-
if (channels.size !== 1) {
|
|
44
|
-
throw new Error(`Scale "${id}" cannot be shared across channels.`);
|
|
45
|
-
}
|
|
46
|
-
return normalizePositionScaleChannel(consumers[0].channel);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
30
|
export const rematerializeScale = action(
|
|
50
31
|
{
|
|
51
32
|
op: "rematerializeScale",
|
|
@@ -54,31 +35,14 @@ export const rematerializeScale = action(
|
|
|
54
35
|
function (args = {}) {
|
|
55
36
|
validateOptions(args);
|
|
56
37
|
const id = validateUserId(args.id, "Scale id");
|
|
57
|
-
const
|
|
38
|
+
const semanticScale = findScale(this, id);
|
|
58
39
|
const consumers = findScaleConsumers(this, id);
|
|
59
|
-
if (consumers.length
|
|
60
|
-
|
|
40
|
+
if (semanticScale.radialMapping !== undefined && consumers.length > 0 &&
|
|
41
|
+
consumers.every(isPendingMeasuredRadiusConsumer)) {
|
|
42
|
+
validatePendingMeasuredScale(this, semanticScale, consumers);
|
|
43
|
+
return this._withoutResolvedScale(id);
|
|
61
44
|
}
|
|
62
|
-
const channel =
|
|
63
|
-
const valuesByConsumer = consumers.map(consumer => ({
|
|
64
|
-
consumer,
|
|
65
|
-
values: resolveConsumerValues(this, consumer),
|
|
66
|
-
categoryOrder: resolveConsumerCategoryOrder(this, consumer),
|
|
67
|
-
seriesLayout: resolveSeriesLayoutScaleValues(this, consumer)
|
|
68
|
-
}));
|
|
69
|
-
const resolvedScale = resolveScaleMaterialization({
|
|
70
|
-
id,
|
|
71
|
-
scale,
|
|
72
|
-
channel,
|
|
73
|
-
consumers,
|
|
74
|
-
valuesByConsumer,
|
|
75
|
-
bounds: ["color", "strokeDash", "strokeWidth", "shape", "size", "opacity", "xOffset", "yOffset"]
|
|
76
|
-
.includes(channel)
|
|
77
|
-
? undefined
|
|
78
|
-
: resolveGraphicBounds(this),
|
|
79
|
-
resolvedScales: this.resolvedScales,
|
|
80
|
-
markConfigs: this.markConfigs
|
|
81
|
-
});
|
|
45
|
+
const { channel, valuesByConsumer, resolvedScale } = resolveScalePreview(this, id);
|
|
82
46
|
let next = this._withResolvedScale(id, resolvedScale);
|
|
83
47
|
|
|
84
48
|
for (const { consumer, values } of valuesByConsumer) {
|
|
@@ -88,13 +52,14 @@ export const rematerializeScale = action(
|
|
|
88
52
|
) {
|
|
89
53
|
continue;
|
|
90
54
|
}
|
|
55
|
+
const consumerChannel = consumer.channel;
|
|
91
56
|
const materializationMode = getScaleConsumerMaterializationMode(
|
|
92
57
|
consumer.layer,
|
|
93
|
-
|
|
58
|
+
consumerChannel
|
|
94
59
|
);
|
|
95
60
|
if (materializationMode === "rematerialize") {
|
|
96
61
|
if (args.marks !== false) {
|
|
97
|
-
const step = getMarkRematerializationStep(consumer.layer);
|
|
62
|
+
const step = getMarkRematerializationStep(this, consumer.layer);
|
|
98
63
|
if (step !== undefined) next = next[step.op](step.args);
|
|
99
64
|
}
|
|
100
65
|
continue;
|
|
@@ -107,8 +72,8 @@ export const rematerializeScale = action(
|
|
|
107
72
|
}
|
|
108
73
|
next = next.editGraphics({
|
|
109
74
|
target: consumer.layer.id,
|
|
110
|
-
property:
|
|
111
|
-
value: mapScaleConsumerValues(values, resolvedScale,
|
|
75
|
+
property: consumerChannel === "color" ? "fill" : consumerChannel,
|
|
76
|
+
value: mapScaleConsumerValues(values, resolvedScale, consumerChannel)
|
|
112
77
|
});
|
|
113
78
|
}
|
|
114
79
|
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { action } from "../../core/action.js";
|
|
2
|
+
import { validateUserId } from "../../core/identifiers.js";
|
|
3
|
+
import { validateOptionObject } from "../../core/validation.js";
|
|
4
|
+
import { requireLayer } from "../../selectors/layers.js";
|
|
5
|
+
import { requireSemanticScale } from "../../selectors/scales.js";
|
|
6
|
+
|
|
7
|
+
const EDITABLE = Object.freeze([
|
|
8
|
+
"domain", "reverse", "padding", "paddingInner", "paddingOuter", "align"
|
|
9
|
+
]);
|
|
10
|
+
|
|
11
|
+
function createOffsetScaleEditor(channel) {
|
|
12
|
+
const operation = channel === "xOffset"
|
|
13
|
+
? "editXOffsetScale"
|
|
14
|
+
: "editYOffsetScale";
|
|
15
|
+
return action(
|
|
16
|
+
{
|
|
17
|
+
op: operation,
|
|
18
|
+
description: `Edit the nested ${channel} scale for one mark.`
|
|
19
|
+
},
|
|
20
|
+
function (args = {}) {
|
|
21
|
+
validateOptionObject(args, ["target", ...EDITABLE], operation);
|
|
22
|
+
const target = validateUserId(args.target, `${operation} target`);
|
|
23
|
+
if (!EDITABLE.some(property => Object.hasOwn(args, property))) {
|
|
24
|
+
throw new Error(`${operation} requires at least one editable property.`);
|
|
25
|
+
}
|
|
26
|
+
const layer = requireLayer(this, target, `${operation} target "${target}"`);
|
|
27
|
+
const scaleId = layer.encoding?.[channel]?.scale;
|
|
28
|
+
if (typeof scaleId !== "string") {
|
|
29
|
+
throw new Error(
|
|
30
|
+
`${operation} target "${target}" has no ${channel} scale.`
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
requireSemanticScale(this, scaleId);
|
|
34
|
+
const patch = Object.fromEntries(Object.entries(args).filter(
|
|
35
|
+
([property]) => property !== "target"
|
|
36
|
+
));
|
|
37
|
+
return this.editScale({ id: scaleId, ...patch });
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const editXOffsetScale = createOffsetScaleEditor("xOffset");
|
|
43
|
+
export const editYOffsetScale = createOffsetScaleEditor("yOffset");
|
|
44
|
+
|
|
45
|
+
export function registerOffsetScaleActions(ProgramClass) {
|
|
46
|
+
ProgramClass.prototype.editXOffsetScale = editXOffsetScale;
|
|
47
|
+
ProgramClass.prototype.editYOffsetScale = editYOffsetScale;
|
|
48
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { action } from "../../core/action.js";
|
|
2
|
+
import { validateUserId } from "../../core/identifiers.js";
|
|
3
|
+
import { validateOptionObject } from "../../core/validation.js";
|
|
4
|
+
import { findCoordinate } from "../../selectors/coordinates.js";
|
|
5
|
+
import { requireLayer } from "../../selectors/layers.js";
|
|
6
|
+
import { requireSemanticScale } from "../../selectors/scales.js";
|
|
7
|
+
|
|
8
|
+
const QUANTITATIVE_OPTIONS = Object.freeze([
|
|
9
|
+
"type", "domain", "range", "nice", "zero", "clamp", "reverse",
|
|
10
|
+
"base", "exponent", "constant", "unknown"
|
|
11
|
+
]);
|
|
12
|
+
const ORDINAL_OPTIONS = Object.freeze([
|
|
13
|
+
"type", "domain", "range", "reverse", "padding", "align", "unknown"
|
|
14
|
+
]);
|
|
15
|
+
const ALL_OPTIONS = Object.freeze([
|
|
16
|
+
"target", "dimension",
|
|
17
|
+
...new Set([...QUANTITATIVE_OPTIONS, ...ORDINAL_OPTIONS])
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
function resolveParallelDimension(program, args) {
|
|
21
|
+
validateOptionObject(args, ALL_OPTIONS, "editParallelScale");
|
|
22
|
+
const target = validateUserId(args.target, "Parallel scale target");
|
|
23
|
+
if (typeof args.dimension !== "string" || args.dimension.length === 0) {
|
|
24
|
+
throw new TypeError("Parallel scale dimension must be a non-empty field string.");
|
|
25
|
+
}
|
|
26
|
+
const layer = requireLayer(program, target, `Parallel scale target "${target}"`);
|
|
27
|
+
if (
|
|
28
|
+
findCoordinate(program, layer.coordinate)?.type !== "parallel" ||
|
|
29
|
+
!Array.isArray(layer.encoding?.parallel?.dimensions)
|
|
30
|
+
) {
|
|
31
|
+
throw new Error(`Parallel scale target "${target}" is not a Parallel layer.`);
|
|
32
|
+
}
|
|
33
|
+
const matches = layer.encoding.parallel.dimensions.filter(
|
|
34
|
+
dimension => dimension.field === args.dimension
|
|
35
|
+
);
|
|
36
|
+
if (matches.length !== 1) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
matches.length === 0
|
|
39
|
+
? `Parallel scale target "${target}" has no dimension field "${args.dimension}".`
|
|
40
|
+
: `Parallel scale dimension field "${args.dimension}" is ambiguous on target "${target}".`
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
const dimension = matches[0];
|
|
44
|
+
requireSemanticScale(program, dimension.scale);
|
|
45
|
+
return dimension;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const editParallelScale = action(
|
|
49
|
+
{
|
|
50
|
+
op: "editParallelScale",
|
|
51
|
+
description: "Edit a Parallel-coordinate dimension scale by field identity."
|
|
52
|
+
},
|
|
53
|
+
function (args = {}) {
|
|
54
|
+
const dimension = resolveParallelDimension(this, args);
|
|
55
|
+
const editable = dimension.fieldType === "quantitative"
|
|
56
|
+
? QUANTITATIVE_OPTIONS
|
|
57
|
+
: ORDINAL_OPTIONS;
|
|
58
|
+
validateOptionObject(
|
|
59
|
+
args,
|
|
60
|
+
["target", "dimension", ...editable],
|
|
61
|
+
"editParallelScale"
|
|
62
|
+
);
|
|
63
|
+
if (!editable.some(property => Object.hasOwn(args, property))) {
|
|
64
|
+
throw new Error("editParallelScale requires at least one editable property.");
|
|
65
|
+
}
|
|
66
|
+
const patch = Object.fromEntries(Object.entries(args).filter(
|
|
67
|
+
([property]) => !["target", "dimension"].includes(property)
|
|
68
|
+
));
|
|
69
|
+
return this.editScale({ id: dimension.scale, ...patch });
|
|
70
|
+
}
|
|
71
|
+
);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const SCALE_EDIT_PROPERTIES = Object.freeze([
|
|
2
2
|
"type", "domain", "range", "nice", "zero", "clamp", "reverse",
|
|
3
3
|
"base", "exponent", "constant", "paddingInner", "paddingOuter",
|
|
4
|
-
"padding", "align", "interpolate", "unknown"
|
|
4
|
+
"padding", "align", "interpolate", "midpoint", "unknown"
|
|
5
5
|
]);
|
|
6
6
|
|
|
7
7
|
function sameValue(left, right) {
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { resolveScaleRange } from "../../grammar/scales/index.js";
|
|
2
|
+
import { resolvePolarFrame } from "../../grammar/polar.js";
|
|
3
|
+
import { resolveArcAutoPositionRange, validateMeasuredRadiusConsumers } from "../../materialization/scales/policies/arc.js";
|
|
4
|
+
import { findSemanticScale } from "../../selectors/scales.js";
|
|
5
|
+
import { normalizePositionScaleChannel } from "../../core/vocabulary.js";
|
|
6
|
+
import { resolveGraphicBounds } from "../../layout/canvas.js";
|
|
7
|
+
import {
|
|
8
|
+
resolveDataAspectScalePair,
|
|
9
|
+
resolveScaleConsumerBounds
|
|
10
|
+
} from "../../materialization/coordinateBounds.js";
|
|
11
|
+
import { requireCoordinate } from "../../selectors/coordinates.js";
|
|
12
|
+
import { resolveScaleMaterialization } from "../../materialization/scales/resolve.js";
|
|
13
|
+
import { findScale, findScaleConsumers, resolveConsumerCategoryOrder,
|
|
14
|
+
resolveConsumerValues, resolveSeriesLayoutScaleValues } from "./consumers/index.js";
|
|
15
|
+
|
|
16
|
+
function resolveScalePreviewAtBounds(program, id, bounds) {
|
|
17
|
+
const scale = findScale(program, id);
|
|
18
|
+
const consumers = findScaleConsumers(program, id);
|
|
19
|
+
if (consumers.length === 0) throw new Error(`Scale "${id}" has no supported consumers.`);
|
|
20
|
+
const consumerChannels = new Set(consumers.map(
|
|
21
|
+
consumer => normalizePositionScaleChannel(consumer.channel)
|
|
22
|
+
));
|
|
23
|
+
const families = new Set([...consumerChannels].map(
|
|
24
|
+
channel => channel === "stroke" ? "color" : channel
|
|
25
|
+
));
|
|
26
|
+
if (families.size !== 1) {
|
|
27
|
+
throw new Error(`Scale "${id}" cannot be shared across channels.`);
|
|
28
|
+
}
|
|
29
|
+
const channel = consumerChannels.size === 1
|
|
30
|
+
? consumerChannels.values().next().value
|
|
31
|
+
: families.values().next().value;
|
|
32
|
+
const valuesByConsumer = consumers.map(consumer => {
|
|
33
|
+
if (
|
|
34
|
+
program.markConfigs?.[consumer.layer.id]?.markFilter?.empty === true ||
|
|
35
|
+
program.markConfigs?.[consumer.layer.id]?.statisticalReference !== undefined
|
|
36
|
+
) {
|
|
37
|
+
return {
|
|
38
|
+
consumer,
|
|
39
|
+
values: [],
|
|
40
|
+
categoryOrder: undefined,
|
|
41
|
+
seriesLayout: undefined,
|
|
42
|
+
...(program.markConfigs?.[consumer.layer.id]?.statisticalReference === undefined
|
|
43
|
+
? {}
|
|
44
|
+
: { scalePolicy: false })
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return { consumer,
|
|
48
|
+
values: resolveConsumerValues(program, consumer),
|
|
49
|
+
categoryOrder: resolveConsumerCategoryOrder(program, consumer),
|
|
50
|
+
seriesLayout: resolveSeriesLayoutScaleValues(program, consumer) };
|
|
51
|
+
});
|
|
52
|
+
const polarFrame = channel === "radius"
|
|
53
|
+
? resolveSharedRadiusFrame(program, consumers, bounds)
|
|
54
|
+
: undefined;
|
|
55
|
+
const resolvedScale = resolveScaleMaterialization({ id, scale, channel, consumers, valuesByConsumer,
|
|
56
|
+
bounds: ["color", "stroke", "strokeDash", "strokeWidth", "shape", "size", "opacity", "xOffset", "yOffset"].includes(channel)
|
|
57
|
+
? undefined : bounds,
|
|
58
|
+
resolvedScales: program.resolvedScales, markConfigs: program.markConfigs, polarFrame,
|
|
59
|
+
thetaScales: scale.radialMapping === undefined ? undefined : Object.fromEntries(consumers.map(({ layer }) =>
|
|
60
|
+
[layer.id, findSemanticScale(program, layer.encoding?.theta?.scale)])) });
|
|
61
|
+
return { channel, consumers, valuesByConsumer, resolvedScale };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function resolveSharedRadiusFrame(program, consumers, bounds) {
|
|
65
|
+
const coordinateIds = [...new Set(consumers.map(({ layer }) => layer.coordinate))];
|
|
66
|
+
if (coordinateIds.some(id => id === undefined)) {
|
|
67
|
+
throw new Error("Radius scale consumers require stored Polar coordinates.");
|
|
68
|
+
}
|
|
69
|
+
const frames = coordinateIds.map(id => {
|
|
70
|
+
const coordinate = requireCoordinate(program, id);
|
|
71
|
+
if (coordinate.type !== "polar") {
|
|
72
|
+
throw new Error("Radius scale consumers require Polar coordinates.");
|
|
73
|
+
}
|
|
74
|
+
return resolvePolarFrame(bounds, coordinate.polarFrame);
|
|
75
|
+
});
|
|
76
|
+
if (frames.slice(1).some(
|
|
77
|
+
frame => Math.abs(frame.availableRadius - frames[0].availableRadius) > 1e-9
|
|
78
|
+
)) {
|
|
79
|
+
throw new Error(
|
|
80
|
+
"A radius scale cannot be shared across coordinates with different Polar frames."
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
return frames[0];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function dataAspectScaleIds(program) {
|
|
87
|
+
const ids = new Set();
|
|
88
|
+
for (const coordinate of program.semanticSpec.coordinates) {
|
|
89
|
+
if (coordinate.aspect?.mode !== "data") continue;
|
|
90
|
+
const pair = resolveDataAspectScalePair(program, coordinate);
|
|
91
|
+
ids.add(pair.x);
|
|
92
|
+
ids.add(pair.y);
|
|
93
|
+
}
|
|
94
|
+
return ids;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function resolveScalePreview(program, id) {
|
|
98
|
+
const requestedConsumers = findScaleConsumers(program, id);
|
|
99
|
+
const positionChannels = new Set([
|
|
100
|
+
"x", "y", "x2", "y2", "theta", "radius"
|
|
101
|
+
]);
|
|
102
|
+
const positional = requestedConsumers.some(consumer =>
|
|
103
|
+
consumer.role === "parallelDimension" ||
|
|
104
|
+
positionChannels.has(consumer.channel)
|
|
105
|
+
);
|
|
106
|
+
const allocated = positional ? resolveGraphicBounds(program) : undefined;
|
|
107
|
+
const provisional = resolveScalePreviewAtBounds(program, id, allocated);
|
|
108
|
+
if (!positional) return provisional;
|
|
109
|
+
const resolvedScales = {
|
|
110
|
+
...program.resolvedScales,
|
|
111
|
+
[id]: provisional.resolvedScale
|
|
112
|
+
};
|
|
113
|
+
for (const scaleId of dataAspectScaleIds(program)) {
|
|
114
|
+
if (scaleId === id) continue;
|
|
115
|
+
resolvedScales[scaleId] = resolveScalePreviewAtBounds(
|
|
116
|
+
program,
|
|
117
|
+
scaleId,
|
|
118
|
+
allocated
|
|
119
|
+
).resolvedScale;
|
|
120
|
+
}
|
|
121
|
+
const bounds = resolveScaleConsumerBounds(
|
|
122
|
+
program,
|
|
123
|
+
provisional.consumers,
|
|
124
|
+
{ resolvedScales }
|
|
125
|
+
);
|
|
126
|
+
return resolveScalePreviewAtBounds(program, id, bounds);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function validatePendingMeasuredScale(program, scale, consumers) {
|
|
130
|
+
const bounds = resolveScaleConsumerBounds(program, consumers);
|
|
131
|
+
const range = resolveArcAutoPositionRange({ consumers, scale, channel: "radius",
|
|
132
|
+
range: resolveScaleRange(
|
|
133
|
+
scale.range,
|
|
134
|
+
"radius",
|
|
135
|
+
bounds,
|
|
136
|
+
resolveSharedRadiusFrame(program, consumers, bounds)
|
|
137
|
+
), markConfigs: program.markConfigs });
|
|
138
|
+
validateMeasuredRadiusConsumers({ scale, domain: scale.domain, range, consumers,
|
|
139
|
+
markConfigs: program.markConfigs, thetaScales: {} });
|
|
140
|
+
}
|
|
@@ -7,7 +7,7 @@ const OPTIONS = Object.freeze([
|
|
|
7
7
|
"type",
|
|
8
8
|
"domain",
|
|
9
9
|
"range",
|
|
10
|
-
"interpolate",
|
|
10
|
+
"interpolate", "midpoint",
|
|
11
11
|
"clamp",
|
|
12
12
|
"reverse",
|
|
13
13
|
"unknown"
|
|
@@ -25,13 +25,17 @@ export const setQuantitativeColorScale = action(
|
|
|
25
25
|
}
|
|
26
26
|
const existing = findSemanticScale(this, args.id);
|
|
27
27
|
if (existing !== undefined && existing.type !== args.type) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
if (typeof this.editScale !== "function") {
|
|
29
|
+
throw new Error("Color scale type transitions require the Full ChartProgram; use a new scale id in Basic.");
|
|
30
|
+
}
|
|
31
|
+
return this.editScale(args);
|
|
31
32
|
}
|
|
32
33
|
let next = this;
|
|
34
|
+
if (existing?.midpoint !== undefined && args.midpoint === undefined) {
|
|
35
|
+
next = next.editSemantic({ property: `scale[${args.id}].midpoint`, remove: true });
|
|
36
|
+
}
|
|
33
37
|
for (const property of [
|
|
34
|
-
"type", "domain", "range", "interpolate", "clamp", "reverse", "unknown"
|
|
38
|
+
"type", "domain", "range", "interpolate", "midpoint", "clamp", "reverse", "unknown"
|
|
35
39
|
]) {
|
|
36
40
|
if (!Object.hasOwn(args, property)) continue;
|
|
37
41
|
if (existing?.[property] === args[property]) continue;
|