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,3 +1,6 @@
|
|
|
1
|
+
import { isSourceOwnedText } from "../../../grammar/text.js";
|
|
2
|
+
import { findSemanticScale } from "../../../selectors/scales.js";
|
|
3
|
+
import { readAreaEndpoint } from "../../../grammar/areaEndpoints.js";
|
|
1
4
|
import { validateUserId } from "../../../core/identifiers.js";
|
|
2
5
|
import { getPositionCoordinateDefaults } from "../../../grammar/coordinates.js";
|
|
3
6
|
import {
|
|
@@ -6,6 +9,7 @@ import {
|
|
|
6
9
|
readQuantitativeField,
|
|
7
10
|
readScaleField,
|
|
8
11
|
readTemporalField,
|
|
12
|
+
resolveTemporalUnit,
|
|
9
13
|
validateFieldType,
|
|
10
14
|
validatePositionChannel
|
|
11
15
|
} from "../../../grammar/scales/index.js";
|
|
@@ -21,7 +25,7 @@ import {
|
|
|
21
25
|
validateAggregateFieldType,
|
|
22
26
|
validateAggregateFieldValues,
|
|
23
27
|
} from "../../../grammar/aggregate.js";
|
|
24
|
-
import {
|
|
28
|
+
import { normalizePositionDatum } from "../../../grammar/positionDatum.js";
|
|
25
29
|
import {
|
|
26
30
|
readArcThetaValues,
|
|
27
31
|
readArcThetaWeights
|
|
@@ -34,10 +38,10 @@ import {
|
|
|
34
38
|
|
|
35
39
|
const POSITION_ENCODING_OPTIONS = Object.freeze([
|
|
36
40
|
"field", "datum", "target", "fieldType", "scale", "coordinate",
|
|
37
|
-
"aggregate", "bin", "stack", "weight"
|
|
41
|
+
"aggregate", "bin", "stack", "weight", "temporalUnit", "mapping"
|
|
38
42
|
]);
|
|
39
43
|
|
|
40
|
-
function
|
|
44
|
+
function inferDatumFieldType(datum, operation) {
|
|
41
45
|
if (Number.isFinite(datum)) return "quantitative";
|
|
42
46
|
if (isNominalValue(datum)) return "nominal";
|
|
43
47
|
throw new Error(`${operation} datum requires an explicit fieldType.`);
|
|
@@ -93,29 +97,53 @@ export function resolvePositionEncoding(program, channel, args, operation) {
|
|
|
93
97
|
args.target,
|
|
94
98
|
getPositionChannelDefinition(channel).markTypes
|
|
95
99
|
);
|
|
100
|
+
const previous = layer.encoding?.[channel];
|
|
101
|
+
if (isSourceOwnedText(layer)) {
|
|
102
|
+
throw new Error(`${operation} cannot replace source-owned Text positions; edit the source, use dx/dy, or create independent Text with data.`);
|
|
103
|
+
}
|
|
96
104
|
if (
|
|
97
105
|
Object.hasOwn(args, "weight") &&
|
|
98
|
-
!(layer.mark.type === "arc" && channel === "theta")
|
|
106
|
+
!(layer.mark.type === "arc" && channel === "theta") &&
|
|
107
|
+
!(layer.mark.type === "bar" && channel === "x" &&
|
|
108
|
+
(args.bin !== undefined || previous?.bin !== undefined))
|
|
99
109
|
) {
|
|
100
|
-
|
|
110
|
+
throw new Error(
|
|
111
|
+
`${operation} weight is supported only for arc theta encoding or histogram x encoding.`
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
if (Object.hasOwn(args, "mapping") && (layer.mark.type !== "arc" || channel !== "radius")) {
|
|
115
|
+
throw new Error(`${operation} mapping requires Arc radius.`);
|
|
101
116
|
}
|
|
102
117
|
validateCoordinateFamily(layer, channel, operation);
|
|
118
|
+
const clearRadialMapping =
|
|
119
|
+
layer.mark.type === "arc" && channel === "radius" && args.mapping === false;
|
|
103
120
|
const hasField = Object.hasOwn(args, "field");
|
|
104
121
|
const hasDatum = Object.hasOwn(args, "datum");
|
|
105
|
-
if (layer.mark.type
|
|
122
|
+
if (["rule", "rect", "text", "point", "tick"].includes(layer.mark.type)) {
|
|
106
123
|
if (hasField === hasDatum) {
|
|
107
|
-
throw new Error(`${operation} requires exactly one of field or datum for a
|
|
124
|
+
throw new Error(`${operation} requires exactly one of field or datum for a ${layer.mark.type} mark.`);
|
|
108
125
|
}
|
|
109
|
-
if (hasField && args.fieldType === undefined) {
|
|
126
|
+
if (layer.mark.type === "rule" && hasField && args.fieldType === undefined) {
|
|
110
127
|
throw new Error(`${operation} requires fieldType for a rule mark.`);
|
|
111
128
|
}
|
|
129
|
+
} else if (layer.mark.type === "area") {
|
|
130
|
+
if (hasField === hasDatum) throw new Error(`${operation} requires exactly one of field or datum for an area mark.`);
|
|
131
|
+
if (hasDatum && (!Number.isFinite(args.datum) || (args.fieldType ?? "quantitative") !== "quantitative" ||
|
|
132
|
+
["aggregate", "bin", "stack", "temporalUnit"].some(key => Object.hasOwn(args, key)))) {
|
|
133
|
+
throw new Error("Area datum requires a finite quantitative constant without aggregate, bin, stack, or temporalUnit.");
|
|
134
|
+
}
|
|
112
135
|
} else if (hasDatum) {
|
|
113
136
|
throw new Error(`${operation} does not support datum for a ${layer.mark.type} mark.`);
|
|
114
137
|
}
|
|
115
|
-
const
|
|
138
|
+
const mapping = channel === "radius" && layer.mark.type === "arc"
|
|
139
|
+
? clearRadialMapping
|
|
140
|
+
? undefined
|
|
141
|
+
: args.mapping ?? findSemanticScale(program, previous?.scale)?.radialMapping
|
|
142
|
+
: undefined;
|
|
143
|
+
const countRadius = mapping !== undefined && (args.aggregate ?? previous?.aggregate) === "count";
|
|
116
144
|
const requestedFieldType = args.fieldType ?? previous?.fieldType ?? (
|
|
117
|
-
layer.mark.type
|
|
118
|
-
?
|
|
145
|
+
["rule", "rect", "text", "point", "tick"].includes(layer.mark.type) && hasDatum
|
|
146
|
+
? inferDatumFieldType(args.datum, operation)
|
|
119
147
|
: layer.mark.type === "arc" && channel === "theta" &&
|
|
120
148
|
["count", "sum"].includes(args.aggregate)
|
|
121
149
|
? "nominal"
|
|
@@ -130,7 +158,24 @@ export function resolvePositionEncoding(program, channel, args, operation) {
|
|
|
130
158
|
? xEncoding.field
|
|
131
159
|
: args.field;
|
|
132
160
|
const datum = args.datum;
|
|
161
|
+
const temporalUnit = resolveTemporalUnit({ ...args, ...(hasDatum ? {} : { field }) }, fieldType, previous);
|
|
162
|
+
const usesField = !countRadius && (!["rule", "area", "rect", "text", "point", "tick"].includes(layer.mark.type) || hasField);
|
|
163
|
+
if (usesField && (typeof field !== "string" || field.length === 0)) {
|
|
164
|
+
throw new TypeError(`${operation} field must be a non-empty string.`);
|
|
165
|
+
}
|
|
166
|
+
// A missing field is invalid even before the Bar's measure/category role is known.
|
|
167
|
+
if (layer.mark.type === "bar" && program.markConfigs[target]?.boxPlot === undefined) {
|
|
168
|
+
if (fieldType === "quantitative" && dataset.values.length > 0 &&
|
|
169
|
+
!dataset.values.some(row => Object.hasOwn(row, field))) {
|
|
170
|
+
throw new Error(`${operation} field "${field}" does not exist in the dataset.`);
|
|
171
|
+
}
|
|
172
|
+
if (fieldType === "quantitative") {
|
|
173
|
+
validateAggregateFieldValues(dataset.values, field, fieldType);
|
|
174
|
+
} else readScaleField(dataset.values, field, fieldType, { temporalUnit });
|
|
175
|
+
}
|
|
133
176
|
const effectiveArgs = { ...args };
|
|
177
|
+
if (clearRadialMapping) delete effectiveArgs.mapping;
|
|
178
|
+
else if (mapping !== undefined) effectiveArgs.mapping = mapping;
|
|
134
179
|
const directQuantitativeArcTheta =
|
|
135
180
|
layer.mark.type === "arc" &&
|
|
136
181
|
channel === "theta" &&
|
|
@@ -138,7 +183,8 @@ export function resolvePositionEncoding(program, channel, args, operation) {
|
|
|
138
183
|
for (const property of ["aggregate", "bin", "stack"]) {
|
|
139
184
|
if (!Object.hasOwn(effectiveArgs, property) && previous !== undefined &&
|
|
140
185
|
Object.hasOwn(previous, property) &&
|
|
141
|
-
!(directQuantitativeArcTheta && property === "aggregate")
|
|
186
|
+
!(directQuantitativeArcTheta && property === "aggregate") &&
|
|
187
|
+
!(clearRadialMapping && property === "aggregate")) {
|
|
142
188
|
effectiveArgs[property] = previous[property];
|
|
143
189
|
}
|
|
144
190
|
}
|
|
@@ -159,11 +205,6 @@ export function resolvePositionEncoding(program, channel, args, operation) {
|
|
|
159
205
|
fieldType,
|
|
160
206
|
field
|
|
161
207
|
});
|
|
162
|
-
const usesField = layer.mark.type !== "rule" || hasField;
|
|
163
|
-
if (usesField && (typeof field !== "string" || field.length === 0)) {
|
|
164
|
-
throw new TypeError(`${operation} field must be a non-empty string.`);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
208
|
const aggregateOutput = isAggregate(policy.aggregate) && !(
|
|
168
209
|
layer.mark.type === "arc" && channel === "theta"
|
|
169
210
|
);
|
|
@@ -171,16 +212,19 @@ export function resolvePositionEncoding(program, channel, args, operation) {
|
|
|
171
212
|
previous,
|
|
172
213
|
Object.hasOwn(args, "scale") ? args.scale : {}
|
|
173
214
|
);
|
|
174
|
-
const
|
|
215
|
+
const resolvedScale = resolvePositionScaleDefinition(
|
|
175
216
|
program,
|
|
176
217
|
channel,
|
|
177
218
|
aggregateOutput ? "quantitative" : fieldType,
|
|
178
219
|
requestedScale,
|
|
179
|
-
|
|
220
|
+
mapping !== undefined ? { radialMapping: mapping, zero: true, nice: false }
|
|
221
|
+
: layer.mark.type === "bar"
|
|
180
222
|
? program.markConfigs[target]?.boxPlot !== undefined && fieldType === "quantitative"
|
|
181
223
|
? { nice: true, zero: false }
|
|
182
224
|
: fieldType === "quantitative"
|
|
183
|
-
? policy.bin
|
|
225
|
+
? policy.bin === undefined && policy.stack === undefined
|
|
226
|
+
? { nice: true }
|
|
227
|
+
: policy.bin !== undefined || policy.stack === null
|
|
184
228
|
? { nice: true, zero: false }
|
|
185
229
|
: { nice: true, zero: true }
|
|
186
230
|
: fieldType === "temporal"
|
|
@@ -196,6 +240,11 @@ export function resolvePositionEncoding(program, channel, args, operation) {
|
|
|
196
240
|
}
|
|
197
241
|
: {}
|
|
198
242
|
);
|
|
243
|
+
const scale = clearRadialMapping
|
|
244
|
+
? Object.fromEntries(
|
|
245
|
+
Object.entries(resolvedScale).filter(([property]) => property !== "radialMapping")
|
|
246
|
+
)
|
|
247
|
+
: resolvedScale;
|
|
199
248
|
if (
|
|
200
249
|
layer.mark.type === "bar" &&
|
|
201
250
|
program.markConfigs[target]?.boxPlot === undefined &&
|
|
@@ -217,8 +266,13 @@ export function resolvePositionEncoding(program, channel, args, operation) {
|
|
|
217
266
|
"Position scale unknown currently requires a row-owned point mark."
|
|
218
267
|
);
|
|
219
268
|
}
|
|
220
|
-
if (layer.mark.type === "
|
|
221
|
-
|
|
269
|
+
if (layer.mark.type === "area" && fieldType === "quantitative") {
|
|
270
|
+
readAreaEndpoint(dataset.values, { ...(hasDatum ? { datum } : { field }), fieldType }, layer.mark.missing);
|
|
271
|
+
} else if (["rule", "rect", "text", "point", "tick"].includes(layer.mark.type) && hasDatum) {
|
|
272
|
+
const mark = layer.mark.type[0].toUpperCase() + layer.mark.type.slice(1);
|
|
273
|
+
normalizePositionDatum(datum, fieldType, channel, temporalUnit, mark);
|
|
274
|
+
} else if (countRadius) {
|
|
275
|
+
// Count has no source measure field. Category-grain validation runs once theta exists.
|
|
222
276
|
} else if (aggregateOutput) {
|
|
223
277
|
validateAggregateFieldType(policy.aggregate, fieldType);
|
|
224
278
|
validateAggregateFieldValues(dataset.values, field, fieldType);
|
|
@@ -239,14 +293,20 @@ export function resolvePositionEncoding(program, channel, args, operation) {
|
|
|
239
293
|
}
|
|
240
294
|
}
|
|
241
295
|
} else if (layer.mark.type === "rect") {
|
|
242
|
-
readScaleField(dataset.values, field, fieldType, { allowUnknown: true });
|
|
296
|
+
readScaleField(dataset.values, field, fieldType, { allowUnknown: true, temporalUnit });
|
|
243
297
|
} else if (Object.hasOwn(scale, "unknown")) {
|
|
244
|
-
readScaleField(dataset.values, field, fieldType, { allowUnknown: true });
|
|
245
|
-
} else if (fieldType === "temporal") readTemporalField(dataset.values, field);
|
|
298
|
+
readScaleField(dataset.values, field, fieldType, { allowUnknown: true, temporalUnit });
|
|
299
|
+
} else if (fieldType === "temporal") readTemporalField(dataset.values, field, temporalUnit);
|
|
246
300
|
else if (["ordinal", "nominal"].includes(fieldType)) {
|
|
247
301
|
readNominalField(dataset.values, field);
|
|
248
302
|
} else readQuantitativeField(dataset.values, field);
|
|
249
303
|
|
|
304
|
+
if (mapping !== undefined) {
|
|
305
|
+
const values = countRadius ? dataset.values.map(() => 1) : readQuantitativeField(dataset.values, field);
|
|
306
|
+
if (values.length === 0 || values.some(value => value < 0) || !values.some(value => value > 0)) {
|
|
307
|
+
throw new Error("Measured radius requires non-negative values and a positive aggregate.");
|
|
308
|
+
}
|
|
309
|
+
}
|
|
250
310
|
if (
|
|
251
311
|
["bar", "rect"].includes(layer.mark.type) &&
|
|
252
312
|
["ordinal", "nominal"].includes(fieldType) &&
|
|
@@ -258,13 +318,15 @@ export function resolvePositionEncoding(program, channel, args, operation) {
|
|
|
258
318
|
target,
|
|
259
319
|
layer,
|
|
260
320
|
previous,
|
|
261
|
-
requestedScale,
|
|
321
|
+
requestedScale: mapping === undefined ? requestedScale : { ...requestedScale, radialMapping: mapping },
|
|
262
322
|
field,
|
|
263
323
|
datum,
|
|
264
324
|
hasField: usesField,
|
|
265
325
|
fieldType,
|
|
326
|
+
temporalUnit,
|
|
266
327
|
scale,
|
|
267
328
|
coordinate: resolveCoordinate(program, channel, layer, args.coordinate),
|
|
329
|
+
clearRadialMapping,
|
|
268
330
|
...policy
|
|
269
331
|
};
|
|
270
332
|
}
|
|
@@ -1,34 +1,27 @@
|
|
|
1
|
+
import { readAreaEndpoint, validateAreaEndpointPair, normalizeAreaBound } from "../../grammar/areaEndpoints.js";
|
|
1
2
|
import { action } from "../../core/action.js";
|
|
2
3
|
import { validateUserId } from "../../core/identifiers.js";
|
|
3
4
|
import { isPlainObject } from "../../core/immutable.js";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
validateSemanticFieldType
|
|
10
|
-
} from "../../grammar/scales/index.js";
|
|
11
|
-
import { normalizeRuleDatum } from "../../grammar/rules.js";
|
|
12
|
-
import {
|
|
13
|
-
canMaterializeArea,
|
|
14
|
-
canMaterializeLine,
|
|
15
|
-
getPositionEncodingMaterializationSteps
|
|
16
|
-
} from "../../materialization/marks/index.js";
|
|
5
|
+
import { readNominalField, readQuantitativeField, readScaleField, readTemporalField, resolveTemporalUnit, validateSemanticFieldType } from "../../grammar/scales/index.js";
|
|
6
|
+
import { normalizePositionDatum } from "../../grammar/positionDatum.js";
|
|
7
|
+
import { normalizeGroupFields } from "../../grammar/pathSeries.js";
|
|
8
|
+
import { assertPathGroupCompatible, validatePathGroupAppearance } from "../../materialization/marks/grouping.js";
|
|
9
|
+
import { canMaterializeArea, canMaterializeLine, getPositionEncodingMaterializationSteps } from "../../materialization/marks/index.js";
|
|
17
10
|
import { findLayer } from "../../selectors/layers.js";
|
|
18
|
-
import {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
} from "
|
|
11
|
+
import { resolveTarget, setEncodingProperties, validateOptions } from "./shared.js";
|
|
12
|
+
import { applyTemporalUnit } from "./temporal.js";
|
|
13
|
+
import { deriveAreaSeries } from "../../grammar/areaSeries.js";
|
|
14
|
+
import { resolvePositionEncoding } from "./position/resolve.js";
|
|
15
|
+
import { resolveScalePreview } from "../scales/preview.js";
|
|
16
|
+
import { findUpstreamTransform } from "../../materialization/dataProvenance.js";
|
|
24
17
|
|
|
25
18
|
const SECONDARY_OPTIONS = Object.freeze([
|
|
26
|
-
"field", "datum", "target", "fieldType", "scale", "coordinate"
|
|
19
|
+
"field", "datum", "target", "fieldType", "scale", "coordinate", "temporalUnit"
|
|
27
20
|
]);
|
|
28
21
|
const RANGE_OPTIONS = Object.freeze([
|
|
29
|
-
"lower", "upper", "target", "fieldType", "coordinate", "scale"
|
|
22
|
+
"lower", "upper", "target", "fieldType", "coordinate", "scale", "temporalUnit"
|
|
30
23
|
]);
|
|
31
|
-
const GROUP_OPTIONS = Object.freeze(["field", "target", "fieldType"]);
|
|
24
|
+
const GROUP_OPTIONS = Object.freeze(["field", "fields", "target", "fieldType"]);
|
|
32
25
|
|
|
33
26
|
function validateSecondaryScale(args, primaryScale, axisLabel) {
|
|
34
27
|
if (args.scale === undefined) return;
|
|
@@ -41,11 +34,11 @@ function validateSecondaryScale(args, primaryScale, axisLabel) {
|
|
|
41
34
|
}
|
|
42
35
|
}
|
|
43
36
|
|
|
44
|
-
function validateSecondaryField(dataset, field, fieldType) {
|
|
37
|
+
function validateSecondaryField(dataset, field, fieldType, temporalUnit) {
|
|
45
38
|
if (["nominal", "ordinal"].includes(fieldType)) {
|
|
46
39
|
readNominalField(dataset.values, field);
|
|
47
40
|
} else if (fieldType === "temporal") {
|
|
48
|
-
readTemporalField(dataset.values, field);
|
|
41
|
+
readTemporalField(dataset.values, field, temporalUnit);
|
|
49
42
|
} else {
|
|
50
43
|
readQuantitativeField(dataset.values, field);
|
|
51
44
|
}
|
|
@@ -75,12 +68,14 @@ function encodeSecondaryPosition(program, channel, args, operation, types) {
|
|
|
75
68
|
validateSecondaryScale(args, primary.scale, primaryChannel);
|
|
76
69
|
|
|
77
70
|
const rule = layer.mark.type === "rule";
|
|
71
|
+
const area = layer.mark.type === "area";
|
|
72
|
+
const rect = layer.mark.type === "rect";
|
|
78
73
|
const hasField = Object.hasOwn(args, "field");
|
|
79
74
|
const hasDatum = Object.hasOwn(args, "datum");
|
|
80
|
-
if (rule && hasField === hasDatum) {
|
|
81
|
-
throw new Error(`${operation} requires exactly one of field or datum for a
|
|
75
|
+
if ((rule || area || rect) && hasField === hasDatum) {
|
|
76
|
+
throw new Error(`${operation} requires exactly one of field or datum for a ${layer.mark.type} mark.`);
|
|
82
77
|
}
|
|
83
|
-
if (!rule && (!hasField || hasDatum)) {
|
|
78
|
+
if (!rule && !area && !rect && (!hasField || hasDatum)) {
|
|
84
79
|
throw new Error(`${operation} requires a field for a ranged mark.`);
|
|
85
80
|
}
|
|
86
81
|
if (rule && args.fieldType === undefined) {
|
|
@@ -114,14 +109,20 @@ function encodeSecondaryPosition(program, channel, args, operation, types) {
|
|
|
114
109
|
`${operation} fieldType must match the primary ${primaryChannel} fieldType.`
|
|
115
110
|
);
|
|
116
111
|
}
|
|
117
|
-
if (hasField && layer.mark.type === "rect") {
|
|
118
|
-
readScaleField(dataset.values, args.field, fieldType, { allowUnknown: true });
|
|
119
|
-
} else if (hasField) validateSecondaryField(dataset, args.field, fieldType);
|
|
120
|
-
else normalizeRuleDatum(args.datum, fieldType, channel);
|
|
121
|
-
|
|
122
112
|
const previous = layer.encoding?.[channel];
|
|
113
|
+
const temporalUnit = resolveTemporalUnit(args, fieldType, previous);
|
|
114
|
+
if (area) {
|
|
115
|
+
const endpoint = { ...(hasField ? { field: args.field } : { datum: args.datum }), fieldType };
|
|
116
|
+
validateAreaEndpointPair(primary, endpoint);
|
|
117
|
+
readAreaEndpoint(dataset.values, endpoint, layer.mark.missing);
|
|
118
|
+
} else if (hasField && layer.mark.type === "rect") {
|
|
119
|
+
readScaleField(dataset.values, args.field, fieldType, { allowUnknown: true, temporalUnit });
|
|
120
|
+
} else if (hasField) validateSecondaryField(dataset, args.field, fieldType, temporalUnit);
|
|
121
|
+
else normalizePositionDatum(args.datum, fieldType, channel, temporalUnit, rule ? "Rule" : "Rect");
|
|
122
|
+
|
|
123
123
|
let next = program;
|
|
124
124
|
if (layer.mark.type === "bar") {
|
|
125
|
+
if (layer.layout?.mode !== undefined && layer.layout.mode !== "overlay") throw new Error("Ranged bars support only overlay layout; clear series layout before assigning endpoints.");
|
|
125
126
|
for (const property of ["aggregate", "stack", "bin"]) {
|
|
126
127
|
if (layer.encoding?.[primaryChannel]?.[property] !== undefined) {
|
|
127
128
|
next = next.editSemantic({
|
|
@@ -146,6 +147,7 @@ function encodeSecondaryPosition(program, channel, args, operation, types) {
|
|
|
146
147
|
scale: primary.scale
|
|
147
148
|
});
|
|
148
149
|
|
|
150
|
+
next = applyTemporalUnit(next, target, channel, temporalUnit, previous);
|
|
149
151
|
const updated = findLayer(next, target);
|
|
150
152
|
for (const step of getPositionEncodingMaterializationSteps(
|
|
151
153
|
next,
|
|
@@ -189,91 +191,91 @@ const encodeY2 = action(
|
|
|
189
191
|
}
|
|
190
192
|
);
|
|
191
193
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
},
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
field: args.lower,
|
|
202
|
-
...(target === undefined ? {} : { target }),
|
|
203
|
-
fieldType: args.fieldType ?? "quantitative",
|
|
204
|
-
...(args.coordinate === undefined ? {} : { coordinate: args.coordinate }),
|
|
205
|
-
...(args.scale === undefined ? {} : { scale: args.scale })
|
|
206
|
-
});
|
|
207
|
-
return lower.encodeY2({
|
|
208
|
-
field: args.upper,
|
|
209
|
-
...(target === undefined ? {} : { target }),
|
|
194
|
+
function rangeAction(channel) {
|
|
195
|
+
const primary = channel === "x" ? "encodeX" : "encodeY";
|
|
196
|
+
const secondary = channel === "x" ? "encodeX2" : "encodeY2";
|
|
197
|
+
const op = channel === "x" ? "encodeXRange" : "encodeYRange";
|
|
198
|
+
return action({ op, description: "Atomically encode lower and upper bounds." }, function (args = {}) {
|
|
199
|
+
validateOptions(args, RANGE_OPTIONS, op);
|
|
200
|
+
const common = {
|
|
201
|
+
...(args.target === undefined ? {} : { target: args.target }),
|
|
202
|
+
...(Object.hasOwn(args, "temporalUnit") ? { temporalUnit: args.temporalUnit } : {}),
|
|
210
203
|
fieldType: args.fieldType ?? "quantitative"
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
...
|
|
226
|
-
fieldType: args.fieldType ?? "quantitative",
|
|
204
|
+
};
|
|
205
|
+
const { layer, dataset } = resolveTarget(this, args.target, ["area", "bar", "rect", "rule"], "ranged mark");
|
|
206
|
+
let next = this;
|
|
207
|
+
let lower = { field: args.lower }, upper = { field: args.upper };
|
|
208
|
+
if (layer.mark.type === "area") {
|
|
209
|
+
common.target = layer.id;
|
|
210
|
+
const prepared = prepareAreaRange(this, layer, dataset, channel, args, common);
|
|
211
|
+
lower = prepared.lower; upper = prepared.upper;
|
|
212
|
+
const secondaryChannel = `${channel}2`;
|
|
213
|
+
if (layer.encoding?.[secondaryChannel] !== undefined) next = next.editSemantic({ property: `layer[${layer.id}].encoding.${secondaryChannel}`, remove: true });
|
|
214
|
+
next = setEncodingProperties(next, layer.id, secondaryChannel, prepared.secondary);
|
|
215
|
+
common.target = layer.id;
|
|
216
|
+
}
|
|
217
|
+
return next[primary]({
|
|
218
|
+
...common, ...lower,
|
|
227
219
|
...(args.coordinate === undefined ? {} : { coordinate: args.coordinate }),
|
|
228
220
|
...(args.scale === undefined ? {} : { scale: args.scale })
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
}
|
|
236
|
-
);
|
|
221
|
+
})[secondary]({ ...common, ...upper });
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const encodeYRange = rangeAction("y");
|
|
226
|
+
const encodeXRange = rangeAction("x");
|
|
237
227
|
|
|
238
228
|
const encodeGroup = action(
|
|
239
229
|
{
|
|
240
230
|
op: "encodeGroup",
|
|
241
|
-
description: "Split path geometry by
|
|
231
|
+
description: "Split path geometry by nominal identity fields without a scale."
|
|
242
232
|
},
|
|
243
233
|
function (args = {}) {
|
|
244
234
|
validateOptions(args, GROUP_OPTIONS, "encodeGroup");
|
|
245
235
|
const { id: target, dataset, layer } = resolveTarget(
|
|
246
236
|
this,
|
|
247
237
|
args.target,
|
|
248
|
-
["line", "area"],
|
|
249
|
-
"
|
|
238
|
+
["line", "area", "bar"],
|
|
239
|
+
"series mark"
|
|
250
240
|
);
|
|
251
241
|
if ((args.fieldType ?? "nominal") !== "nominal") {
|
|
252
242
|
throw new Error("encodeGroup requires a nominal field.");
|
|
253
243
|
}
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
: undefined;
|
|
258
|
-
if (densityTransform !== undefined && densityTransform.groupBy !== args.field) {
|
|
259
|
-
throw new Error(
|
|
260
|
-
densityTransform.groupBy === undefined
|
|
261
|
-
? `Ungrouped density area mark "${target}" cannot encode group.`
|
|
262
|
-
: `Density area mark "${target}" must group by "${densityTransform.groupBy}".`
|
|
263
|
-
);
|
|
244
|
+
const hasFields = Object.hasOwn(args, "fields");
|
|
245
|
+
if (hasFields === Object.hasOwn(args, "field")) {
|
|
246
|
+
throw new Error("encodeGroup requires exactly one of field or fields.");
|
|
264
247
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
248
|
+
if (hasFields ? !Array.isArray(args.fields) : typeof args.field !== "string") {
|
|
249
|
+
throw new TypeError("encodeGroup field must be a string and fields must be an array.");
|
|
250
|
+
}
|
|
251
|
+
const fields = normalizeGroupFields(hasFields ? args.fields : args.field);
|
|
252
|
+
const group = {
|
|
253
|
+
...(fields.length === 1 ? { field: fields[0] } : { fields }),
|
|
254
|
+
fieldType: "nominal"
|
|
255
|
+
};
|
|
256
|
+
assertPathGroupCompatible(this, layer, dataset, group);
|
|
257
|
+
for (const field of fields) readNominalField(dataset.values, field);
|
|
258
|
+
if (layer.mark.type !== "bar") validatePathGroupAppearance(this, {
|
|
259
|
+
...layer, encoding: { ...layer.encoding, group }
|
|
260
|
+
}, dataset);
|
|
261
|
+
let next = this;
|
|
262
|
+
const alternate = fields.length === 1 ? "fields" : "field";
|
|
263
|
+
if (layer.encoding?.group?.[alternate] !== undefined) {
|
|
264
|
+
next = next.editSemantic({
|
|
265
|
+
property: `layer[${target}].encoding.group.${alternate}`, remove: true
|
|
275
266
|
});
|
|
267
|
+
}
|
|
268
|
+
if (layer.encoding?.group?.inferredFrom !== undefined) next = next.editSemantic({ property: `layer[${target}].encoding.group.inferredFrom`, remove: true });
|
|
269
|
+
next = setEncodingProperties(next, target, "group", group);
|
|
270
|
+
if (layer.mark.type === "bar") {
|
|
271
|
+
const mode = layer.layout?.mode;
|
|
272
|
+
if (mode !== undefined) return next.layoutSeries({ target, mode });
|
|
273
|
+
if (layer.encoding?.x?.scale === undefined || layer.encoding?.y?.scale === undefined) return next;
|
|
274
|
+
for (const step of getPositionEncodingMaterializationSteps(next, findLayer(next, target), layer.encoding?.y?.scale)) next = next[step.op](step.args);
|
|
275
|
+
return next;
|
|
276
|
+
}
|
|
276
277
|
if (layer.mark.type === "area") {
|
|
278
|
+
if (layer.layout?.mode !== undefined) return next.layoutSeries({ target, mode: layer.layout.mode });
|
|
277
279
|
const updated = findLayer(next, target);
|
|
278
280
|
return canMaterializeArea(next, updated)
|
|
279
281
|
? next.rematerializeAreaMark({ id: target })
|
|
@@ -297,3 +299,27 @@ export function registerRangedEncodingActions(ProgramClass) {
|
|
|
297
299
|
ProgramClass.prototype.encodeXRange = encodeXRange;
|
|
298
300
|
ProgramClass.prototype.encodeYRange = encodeYRange;
|
|
299
301
|
}
|
|
302
|
+
|
|
303
|
+
export function prepareAreaRange(program, layer, dataset, channel, args, common) {
|
|
304
|
+
const lower = normalizeAreaBound(args.lower), upper = normalizeAreaBound(args.upper);
|
|
305
|
+
validateAreaEndpointPair(lower, upper);
|
|
306
|
+
if (common.fieldType !== "quantitative") throw new Error("Area range requires quantitative endpoints.");
|
|
307
|
+
readAreaEndpoint(dataset.values, { ...upper, fieldType: "quantitative" }, layer.mark.missing);
|
|
308
|
+
const resolved = resolvePositionEncoding(program, channel, { ...common, ...lower,
|
|
309
|
+
...(args.coordinate === undefined ? {} : { coordinate: args.coordinate }),
|
|
310
|
+
...(args.scale === undefined ? {} : { scale: args.scale }) }, `encode${channel.toUpperCase()}Range`);
|
|
311
|
+
const secondary = { ...upper, fieldType: "quantitative", scale: resolved.scale.id };
|
|
312
|
+
const primary = { ...lower, fieldType: "quantitative", scale: resolved.scale.id };
|
|
313
|
+
const updated = { ...layer, coordinate: resolved.coordinate.id,
|
|
314
|
+
encoding: { ...layer.encoding, [channel]: primary, [`${channel}2`]: secondary } };
|
|
315
|
+
const scales = program.semanticSpec.scales.filter(scale => scale.id !== resolved.scale.id);
|
|
316
|
+
const preview = { ...program, markConfigs: program.markConfigs, canvasConfig: program.canvasConfig,
|
|
317
|
+
semanticSpec: { ...program.semanticSpec,
|
|
318
|
+
layers: program.semanticSpec.layers.map(item => item.id === layer.id ? updated : item),
|
|
319
|
+
scales: [...scales, resolved.scale] } };
|
|
320
|
+
resolveScalePreview(preview, resolved.scale.id);
|
|
321
|
+
if (canMaterializeArea(preview, updated) && !findUpstreamTransform(program, dataset, "density")) {
|
|
322
|
+
deriveAreaSeries(dataset.values, updated);
|
|
323
|
+
}
|
|
324
|
+
return { lower, upper, secondary };
|
|
325
|
+
}
|