ggaction 0.0.16 → 0.0.18
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 +66 -0
- package/README.md +3 -2
- package/knowledge/action-card.schema.json +1 -1
- package/knowledge/action-cards.json +1 -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 -4
- package/src/BasicChartProgram.js +1 -1
- package/src/accessibility.js +3 -2
- package/src/actions/boxPlots/create.js +14 -4
- package/src/actions/boxPlots/edit.js +39 -33
- package/src/actions/boxPlots/materialize.js +7 -3
- package/src/actions/boxPlots/options.js +37 -11
- package/src/actions/canvas/actions.js +70 -95
- package/src/actions/canvas/fitting.js +16 -1
- package/src/actions/canvas/index.js +3 -0
- package/src/actions/canvas/plotBorder.js +28 -0
- package/src/actions/charts/area.js +2 -2
- package/src/actions/charts/bar.js +34 -7
- package/src/actions/charts/ecdf.js +4 -18
- package/src/actions/charts/endpoints.js +4 -18
- package/src/actions/charts/heatmap.js +4 -4
- package/src/actions/charts/index.js +2 -0
- package/src/actions/charts/interval-regression.js +2 -2
- package/src/actions/charts/line.js +1 -1
- package/src/actions/charts/pie.js +3 -47
- package/src/actions/charts/radial.js +34 -12
- package/src/actions/charts/scatter.js +16 -2
- package/src/actions/charts/shared.js +2 -2
- package/src/actions/charts/text.js +35 -0
- package/src/actions/composition/actions.js +6 -2
- package/src/actions/data/bin.js +1 -1
- package/src/actions/data/box.js +2 -1
- package/src/actions/data/create.js +18 -5
- package/src/actions/data/density.js +1 -1
- package/src/actions/data/derived.js +15 -1
- package/src/actions/data/edit.js +20 -7
- package/src/actions/data/filter.js +15 -4
- package/src/actions/data/index.js +3 -0
- package/src/actions/data/interval.js +2 -0
- package/src/actions/data/regression.js +6 -2
- package/src/actions/data/revise.js +37 -5
- package/src/actions/data/shared.js +33 -4
- package/src/actions/data/sort.js +21 -0
- package/src/actions/data/stack.js +3 -1
- package/src/actions/data/summary.js +1 -1
- package/src/actions/encodings/angle.js +4 -1
- package/src/actions/encodings/appearance.js +43 -31
- package/src/actions/encodings/barWidth.js +36 -11
- package/src/actions/encodings/channels.js +0 -1
- package/src/actions/encodings/color/continuous.js +5 -2
- package/src/actions/encodings/color/index.js +5 -2
- package/src/actions/encodings/color/policy.js +10 -3
- package/src/actions/encodings/density/resolve.js +1 -6
- package/src/actions/encodings/offset.js +4 -1
- package/src/actions/encodings/position/policies/arc.js +12 -10
- package/src/actions/encodings/position/policies/area.js +3 -3
- package/src/actions/encodings/position/policies/bar.js +15 -6
- package/src/actions/encodings/position/policies/line.js +17 -6
- package/src/actions/encodings/position/resolve.js +9 -3
- package/src/actions/encodings/ranged.js +23 -1
- package/src/actions/encodings/remove.js +3 -2
- package/src/actions/encodings/ruleAppearance.js +4 -1
- package/src/actions/encodings/seriesLayout.js +1 -0
- package/src/actions/encodings/stroke.js +4 -1
- package/src/actions/encodings/strokeDash.js +6 -1
- package/src/actions/encodings/text.js +12 -4
- package/src/actions/errorBands/create.js +2 -2
- package/src/actions/errorBands/edit.js +2 -1
- package/src/actions/facets/actions.js +158 -240
- package/src/actions/gradientPlots/resolve.js +2 -17
- package/src/actions/guides/applicability.js +1 -1
- package/src/actions/guides/axes/axis.js +16 -4
- package/src/actions/guides/axes/edit.js +4 -4
- package/src/actions/guides/axes/labels.js +120 -46
- package/src/actions/guides/axes/lines.js +6 -5
- package/src/actions/guides/axes/parallel/lifecycle.js +1 -1
- package/src/actions/guides/axes/parallel/policy.js +8 -7
- package/src/actions/guides/axes/remove.js +3 -2
- package/src/actions/guides/axes/tickGroups.js +5 -2
- package/src/actions/guides/axes/ticks.js +6 -3
- package/src/actions/guides/axes/titles.js +71 -26
- package/src/actions/guides/basic.js +2 -0
- package/src/actions/guides/facadeLegend.js +10 -2
- package/src/actions/guides/grids/grid.js +7 -1
- package/src/actions/guides/grids/resolve.js +59 -12
- package/src/actions/guides/legends/blocks.js +3 -1
- package/src/actions/guides/legends/categorical/actions.js +70 -17
- package/src/actions/guides/legends/categorical/components.js +12 -8
- package/src/actions/guides/legends/categorical/index.js +6 -0
- package/src/actions/guides/legends/categorical/layout.js +37 -8
- package/src/actions/guides/legends/categorical/options.js +31 -20
- package/src/actions/guides/legends/categorical/resolve.js +2 -2
- package/src/actions/guides/legends/categorical/symbols.js +18 -12
- package/src/actions/guides/legends/categorical/validation.js +1 -6
- package/src/actions/guides/legends/continuous/colorSymbols.js +1 -0
- package/src/actions/guides/legends/continuous/common.js +1 -447
- package/src/actions/guides/legends/continuous/gradient.js +47 -34
- package/src/actions/guides/legends/continuous/interval.js +4 -4
- package/src/actions/guides/legends/continuous/opacity.js +19 -12
- package/src/actions/guides/legends/edit.js +1 -604
- package/src/actions/guides/legends/focused.js +2 -2
- package/src/actions/guides/legends/group.js +1 -0
- package/src/actions/guides/legends/lane.js +1 -516
- package/src/actions/guides/legends/remove.js +3 -0
- package/src/actions/guides/legends/sampling.js +1 -1
- package/src/actions/guides/legends/size.js +51 -43
- package/src/actions/guides/legends/strokeWidth.js +11 -29
- package/src/actions/guides/legends/target.js +3 -3
- package/src/actions/guides/legends/transition.js +21 -7
- package/src/actions/guides/polar/axes/labels.js +4 -2
- package/src/actions/guides/polar/axes/titles.js +7 -5
- package/src/actions/guides/polar/resolve.js +6 -14
- package/src/actions/index.js +16 -16
- package/src/actions/marks/arc/actions.js +3 -10
- package/src/actions/marks/area/actions.js +8 -5
- package/src/actions/marks/area/materialize.js +8 -9
- package/src/actions/marks/bar/create.js +17 -4
- package/src/actions/marks/bar/edit.js +18 -7
- package/src/actions/marks/bar/materialize.js +9 -6
- package/src/actions/marks/line/actions.js +11 -4
- package/src/actions/marks/line/materialize.js +9 -5
- package/src/actions/marks/point/create.js +8 -1
- package/src/actions/marks/point/edit.js +13 -6
- package/src/actions/marks/point/materialize.js +4 -2
- package/src/actions/marks/rect/actions.js +26 -11
- package/src/actions/marks/remove.js +2 -0
- package/src/actions/marks/rule/actions.js +31 -12
- package/src/actions/marks/text/actions.js +42 -11
- package/src/actions/marks/text/layout.js +3 -0
- package/src/actions/marks/tick/actions.js +23 -8
- package/src/actions/primitives/semanticAction.js +37 -3
- package/src/actions/primitives/semanticValidation/dataset.js +13 -1
- package/src/actions/primitives/semanticValidation/layer.js +26 -2
- package/src/actions/primitives/semanticValidation/scale.js +6 -0
- package/src/actions/regression/create.js +26 -5
- package/src/actions/regression/edit.js +50 -15
- package/src/actions/regression/follow.js +22 -0
- package/src/actions/regression/resolve.js +2 -6
- package/src/actions/scales/channels.js +20 -15
- package/src/actions/scales/consumers/common.js +2 -1
- package/src/actions/scales/consumers/families.js +16 -5
- package/src/actions/scales/create.js +10 -4
- package/src/actions/scales/definitions.js +1 -425
- package/src/actions/scales/edit.js +3 -3
- package/src/actions/scales/editPolicy.js +9 -3
- package/src/actions/scales/materialize.js +1 -1
- package/src/actions/scales/parallel.js +1 -1
- package/src/actions/scales/patch.js +1 -1
- package/src/actions/scales/preview.js +49 -2
- package/src/actions/scales/quantitativeColor.js +3 -3
- package/src/actions/selection/actions.js +1 -13
- package/src/actions/theme/reconcile.js +83 -27
- package/src/actions/theme/state.js +12 -0
- package/src/actions/titles/actions.js +5 -18
- package/src/actions/titles/resolve.js +41 -19
- package/src/actions/violinPlots/edit.js +3 -20
- package/src/core/action.js +3 -2
- package/src/core/compositionState.js +4 -1
- package/src/core/font.js +9 -2
- package/src/core/optionRequirements.js +1 -0
- package/src/core/programState.js +5 -1
- package/src/core/resourceReferences.js +3 -0
- package/src/core/textMetricProfile.js +3 -2
- package/src/core/textMetrics.js +41 -4
- package/src/core/validation.js +26 -0
- package/src/core/vocabulary.js +4 -0
- package/src/grammar/aggregate.js +4 -18
- package/src/grammar/arcs.js +1 -386
- package/src/grammar/areaPaint.js +10 -0
- package/src/grammar/areaSeries.js +18 -10
- package/src/grammar/bars/geometry.js +30 -3
- package/src/grammar/bars/policy.js +12 -1
- package/src/grammar/bin.js +41 -22
- package/src/grammar/bin2d.js +1 -6
- package/src/grammar/boxPlot.js +56 -9
- package/src/grammar/categoryOrder.js +1 -6
- package/src/grammar/complete.js +15 -19
- package/src/grammar/computed.js +92 -25
- package/src/grammar/curveCommands.js +29 -16
- package/src/grammar/datasetSchema.js +382 -0
- package/src/grammar/density.js +53 -5
- package/src/grammar/displayLabels.js +28 -0
- package/src/grammar/ecdf.js +2 -9
- package/src/grammar/facets/dependencies.js +6 -2
- package/src/grammar/facets/guides.js +5 -0
- package/src/grammar/facets/index.js +2 -5
- package/src/grammar/filter.js +133 -39
- package/src/grammar/fold.js +2 -12
- package/src/grammar/gradientProfile.js +1 -6
- package/src/grammar/impute.js +21 -48
- package/src/grammar/interval.js +34 -10
- package/src/grammar/itemMissing.js +59 -0
- package/src/grammar/legendGradient.js +18 -0
- package/src/grammar/legendGroups.js +1 -0
- package/src/grammar/legendTitleStyle.js +22 -0
- package/src/grammar/lineSeries.js +32 -17
- package/src/grammar/markLabels.js +5 -2
- package/src/grammar/normalize.js +4 -54
- package/src/grammar/numeric.js +1 -290
- package/src/grammar/parallelCoordinates.js +1 -6
- package/src/grammar/parseTime.js +41 -0
- package/src/grammar/pathCommands.js +11 -4
- package/src/grammar/polar.js +1 -184
- package/src/grammar/positionCompatibility.js +3 -3
- package/src/grammar/regression/derive.js +63 -16
- package/src/grammar/regression/index.js +1 -0
- package/src/grammar/regression/models.js +116 -59
- package/src/grammar/regression/parameters.js +69 -6
- package/src/grammar/roundedRect.js +42 -54
- package/src/grammar/scales/appearance.js +1 -206
- package/src/grammar/scales/color.js +1 -382
- package/src/grammar/scales/colorConsumers.js +5 -2
- package/src/grammar/scales/definition.js +1 -190
- package/src/grammar/scales/discretized.js +1 -195
- package/src/grammar/scales/size.js +1 -456
- package/src/grammar/scales/temporal.js +2 -13
- package/src/grammar/scales/transformed.js +1 -383
- package/src/grammar/scales/types.js +1 -1
- package/src/grammar/scales/validation.js +1 -66
- package/src/grammar/schemas/concreteGraphic.js +11 -0
- package/src/grammar/schemas/graphic.js +3 -0
- package/src/grammar/schemas/semanticPath.js +5 -3
- package/src/grammar/sort.js +88 -0
- package/src/grammar/stack.js +2 -10
- package/src/grammar/statisticalReference.js +1 -6
- package/src/grammar/summary.js +120 -45
- package/src/grammar/text.js +19 -0
- package/src/grammar/timeUnit.js +16 -7
- package/src/grammar/transformKeys.js +47 -0
- package/src/grammar/transformTopology.js +1 -0
- package/src/grammar/transforms.js +25 -12
- package/src/grammar/valueFormat.js +17 -5
- package/src/grammar/weightedStatistics.js +1 -6
- package/src/grammar/window.js +4 -58
- package/src/inspection.js +495 -0
- package/src/inspectionDescriptors.js +3 -0
- package/src/layout/canvas.js +1 -110
- package/src/layout/facets.js +1 -244
- package/src/layout/labels.js +1 -0
- package/src/layout/legend.js +13 -0
- package/src/layout/legendGroup.js +1 -0
- package/src/layout/legendItems.js +59 -15
- package/src/layout/legendLane.js +1 -441
- package/src/layout/legendText.js +1 -0
- package/src/layout/title.js +35 -13
- package/src/materialization/bars/aggregate.js +11 -5
- package/src/materialization/bars/histogram.js +4 -1
- package/src/materialization/bars/ranged.js +19 -6
- package/src/materialization/bars/resolve.js +26 -3
- package/src/materialization/coordinateBounds.js +8 -0
- package/src/materialization/dependencies.js +4 -44
- package/src/materialization/facetGuides/layout.js +1 -0
- package/src/materialization/facetGuides/placement.js +19 -8
- package/src/materialization/facetGuides/preparation.js +15 -7
- package/src/materialization/facetHeaders.js +1 -347
- package/src/materialization/facets.js +1 -185
- package/src/materialization/graphicHierarchy.js +1 -1
- package/src/materialization/guides/layout.js +1 -57
- package/src/materialization/guides/resources.js +1 -123
- package/src/materialization/layout.js +53 -0
- package/src/materialization/legendGroups.js +1 -0
- package/src/materialization/legendText.js +1 -0
- package/src/materialization/legends.js +1 -1
- package/src/materialization/marks/capabilities.js +5 -6
- package/src/materialization/rect.js +1 -1
- package/src/materialization/revisionIdentity.js +19 -0
- package/src/materialization/scaleGuideDependencies.js +1 -1
- package/src/materialization/scales/map.js +3 -0
- package/src/materialization/scales/policies/arc.js +8 -7
- package/src/materialization/scales/policies/bar.js +19 -7
- package/src/materialization/scales/policies/series.js +2 -1
- package/src/materialization/scales/resolve.js +72 -7
- package/src/materialization/selection/items/arc.js +1 -1
- package/src/materialization/selection/items/bar.js +7 -6
- package/src/materialization/selection/items/path.js +7 -6
- package/src/materialization/selection/items/rect.js +2 -2
- package/src/materialization/text.js +17 -6
- package/src/materialization/textLines.js +15 -0
- package/src/persistence/codec.js +37 -6
- package/src/persistence/validation.js +47 -0
- package/src/persistence.js +94 -8
- package/src/renderers/canvas/circle.js +6 -3
- package/src/renderers/canvas/fill.js +7 -1
- package/src/renderers/canvas/index.js +2 -1
- package/src/renderers/canvas/line.js +1 -0
- package/src/renderers/canvas/path.js +1 -1
- package/src/renderers/canvas/rect.js +11 -8
- package/src/renderers/canvas/stroke.js +2 -1
- package/src/renderers/canvas/text.js +5 -3
- package/src/renderers/svg.js +4 -1
- package/src/selectors/layers.js +19 -0
- package/src/theme/defaults.js +8 -2
- package/src/version.js +1 -1
- package/types/basic.d.ts +1 -0
- package/types/index.d.ts +7 -0
- package/types/inspection.d.ts +17 -0
- package/types/program.d.ts +494 -112
|
@@ -26,7 +26,15 @@ function resolveBarChannelPolicy({
|
|
|
26
26
|
let stack;
|
|
27
27
|
const xEncoding = layer.encoding?.x;
|
|
28
28
|
const opposite = layer.encoding?.[channel === "x" ? "y" : "x"];
|
|
29
|
-
const
|
|
29
|
+
const pendingRange = program.markConfigs[layer.id]?.boxPlot !== undefined ||
|
|
30
|
+
layer.encoding?.[`${channel}2`] !== undefined;
|
|
31
|
+
|
|
32
|
+
if (layer.mark?.orientation !== undefined) {
|
|
33
|
+
if (fieldType !== "quantitative" || ["bin", "aggregate", "stack", "weight"].some(key => args[key] !== undefined)) {
|
|
34
|
+
throw new Error("Oriented numeric bars require raw quantitative positions without bin, aggregate, stack, or weight.");
|
|
35
|
+
}
|
|
36
|
+
return { bin, aggregate, stack };
|
|
37
|
+
}
|
|
30
38
|
|
|
31
39
|
if (["nominal", "ordinal", "temporal"].includes(fieldType)) {
|
|
32
40
|
if (args.aggregate !== undefined || args.bin !== undefined || args.stack !== undefined) {
|
|
@@ -73,11 +81,11 @@ function resolveBarChannelPolicy({
|
|
|
73
81
|
}
|
|
74
82
|
aggregate = args.aggregate ?? (
|
|
75
83
|
["nominal", "ordinal", "temporal"].includes(opposite?.fieldType) &&
|
|
76
|
-
!
|
|
84
|
+
!pendingRange
|
|
77
85
|
? "mean"
|
|
78
86
|
: undefined
|
|
79
87
|
);
|
|
80
|
-
if (
|
|
88
|
+
if (pendingRange && args.aggregate === undefined) {
|
|
81
89
|
return { bin, aggregate, stack };
|
|
82
90
|
}
|
|
83
91
|
stack = Object.hasOwn(args, "stack") ? args.stack : opposite === undefined ? undefined : null;
|
|
@@ -106,7 +114,8 @@ export function resolveBarPositionPolicy(context) {
|
|
|
106
114
|
const policy = resolveBarChannelPolicy(context);
|
|
107
115
|
const oppositeChannel = channel === "x" ? "y" : "x";
|
|
108
116
|
const opposite = layer.encoding?.[oppositeChannel];
|
|
109
|
-
const
|
|
117
|
+
const pendingRange = program.markConfigs[layer.id]?.boxPlot !== undefined ||
|
|
118
|
+
layer.encoding?.[`${channel}2`] !== undefined;
|
|
110
119
|
const candidate = {
|
|
111
120
|
...layer,
|
|
112
121
|
encoding: {
|
|
@@ -118,7 +127,7 @@ export function resolveBarPositionPolicy(context) {
|
|
|
118
127
|
// Absence of an aggregate/stack is sufficient to represent an unresolved role.
|
|
119
128
|
let companion;
|
|
120
129
|
if (
|
|
121
|
-
!
|
|
130
|
+
!pendingRange && resolveBarGrain(layer) === undefined &&
|
|
122
131
|
opposite?.fieldType === "quantitative" && opposite.bin === undefined
|
|
123
132
|
) {
|
|
124
133
|
const resolved = resolveBarChannelPolicy({
|
|
@@ -138,7 +147,7 @@ export function resolveBarPositionPolicy(context) {
|
|
|
138
147
|
}
|
|
139
148
|
}
|
|
140
149
|
const orientation = resolveBarOrientation(candidate);
|
|
141
|
-
if (opposite !== undefined && orientation === undefined && !
|
|
150
|
+
if (opposite !== undefined && orientation === undefined && !pendingRange) {
|
|
142
151
|
throw new Error(
|
|
143
152
|
`Bar ${channel} encoding requires a quantitative field opposite a categorical position.`
|
|
144
153
|
);
|
|
@@ -68,6 +68,13 @@ export function resolveLinePositionPolicy({
|
|
|
68
68
|
stack: undefined
|
|
69
69
|
};
|
|
70
70
|
}
|
|
71
|
+
if (["nominal", "ordinal"].includes(fieldType)) {
|
|
72
|
+
if (layer.encoding?.y !== undefined && layer.encoding.y.aggregate === undefined &&
|
|
73
|
+
layer.encoding.y.fieldType !== "quantitative") {
|
|
74
|
+
throw new Error("Categorical line x requires a quantitative value or aggregate y encoding.");
|
|
75
|
+
}
|
|
76
|
+
return emptyPositionPolicy();
|
|
77
|
+
}
|
|
71
78
|
const directPair =
|
|
72
79
|
layer.encoding?.y?.aggregate === undefined &&
|
|
73
80
|
["quantitative", "temporal"].includes(layer.encoding?.y?.fieldType) &&
|
|
@@ -82,7 +89,7 @@ export function resolveLinePositionPolicy({
|
|
|
82
89
|
)
|
|
83
90
|
) {
|
|
84
91
|
throw new Error(
|
|
85
|
-
"Line x encoding requires a temporal field or a compatible
|
|
92
|
+
"Line x encoding requires a categorical or temporal field, or a compatible quantitative field."
|
|
86
93
|
);
|
|
87
94
|
}
|
|
88
95
|
return emptyPositionPolicy();
|
|
@@ -93,15 +100,19 @@ export function resolveLinePositionPolicy({
|
|
|
93
100
|
if (layer.encoding?.x?.bin !== undefined && args.aggregate === undefined) {
|
|
94
101
|
throw new Error("Binned line x encoding requires an aggregate y encoding.");
|
|
95
102
|
}
|
|
96
|
-
|
|
103
|
+
if (["nominal", "ordinal"].includes(layer.encoding?.x?.fieldType) &&
|
|
104
|
+
args.aggregate === undefined && fieldType !== "quantitative") {
|
|
105
|
+
throw new Error("Categorical line x requires a quantitative value or aggregate y encoding.");
|
|
106
|
+
}
|
|
107
|
+
const continuousPair =
|
|
97
108
|
fieldType === "quantitative" &&
|
|
98
|
-
layer.encoding?.x?.fieldType
|
|
109
|
+
["quantitative", "temporal", "nominal", "ordinal"].includes(layer.encoding?.x?.fieldType) &&
|
|
99
110
|
layer.encoding.x.bin === undefined;
|
|
100
111
|
const prospectiveDirect =
|
|
101
112
|
args.aggregate === undefined &&
|
|
102
113
|
(fieldType === "temporal" ||
|
|
103
114
|
(fieldType === "quantitative" &&
|
|
104
|
-
(layer.encoding?.x === undefined ||
|
|
115
|
+
(layer.encoding?.x === undefined || continuousPair)));
|
|
105
116
|
if (interval || (windowOutput && args.aggregate === undefined) || prospectiveDirect) {
|
|
106
117
|
if (!["quantitative", "temporal"].includes(fieldType)) {
|
|
107
118
|
throw new Error(
|
|
@@ -125,12 +136,12 @@ export function resolveLinePositionPolicy({
|
|
|
125
136
|
|
|
126
137
|
if (layer.encoding?.x !== undefined) {
|
|
127
138
|
const compatibleAggregateX =
|
|
128
|
-
layer.encoding.x.fieldType
|
|
139
|
+
["temporal", "nominal", "ordinal"].includes(layer.encoding.x.fieldType) ||
|
|
129
140
|
(layer.encoding.x.fieldType === "quantitative" &&
|
|
130
141
|
layer.encoding.x.bin !== undefined);
|
|
131
142
|
if (!compatibleAggregateX) {
|
|
132
143
|
throw new Error(
|
|
133
|
-
"Aggregate line y encoding requires a temporal or binned quantitative x encoding."
|
|
144
|
+
"Aggregate line y encoding requires a temporal, categorical, or binned quantitative x encoding."
|
|
134
145
|
);
|
|
135
146
|
}
|
|
136
147
|
}
|
|
@@ -13,6 +13,8 @@ import {
|
|
|
13
13
|
validateFieldType,
|
|
14
14
|
validatePositionChannel
|
|
15
15
|
} from "../../../grammar/scales/index.js";
|
|
16
|
+
import { applyRequestedItemMissingPolicy } from
|
|
17
|
+
"../../../grammar/itemMissing.js";
|
|
16
18
|
import { resolvePositionScaleDefinition } from "../../scales/definitions.js";
|
|
17
19
|
import { findCoordinate } from "../../../selectors/coordinates.js";
|
|
18
20
|
import {
|
|
@@ -92,7 +94,7 @@ function resolveCoordinate(program, channel, layer, requestedId) {
|
|
|
92
94
|
export function resolvePositionEncoding(program, channel, args, operation) {
|
|
93
95
|
validateOptions(args, POSITION_ENCODING_OPTIONS, operation);
|
|
94
96
|
validatePositionChannel(channel);
|
|
95
|
-
const { id: target, dataset, layer } = resolveTarget(
|
|
97
|
+
const { id: target, dataset: sourceDataset, layer } = resolveTarget(
|
|
96
98
|
program,
|
|
97
99
|
args.target,
|
|
98
100
|
getPositionChannelDefinition(channel).markTypes
|
|
@@ -140,7 +142,8 @@ export function resolvePositionEncoding(program, channel, args, operation) {
|
|
|
140
142
|
? undefined
|
|
141
143
|
: args.mapping ?? findSemanticScale(program, previous?.scale)?.radialMapping
|
|
142
144
|
: undefined;
|
|
143
|
-
const countRadius =
|
|
145
|
+
const countRadius = channel === "radius" && layer.mark.type === "arc" &&
|
|
146
|
+
!clearRadialMapping && (args.aggregate ?? previous?.aggregate) === "count";
|
|
144
147
|
const requestedFieldType = args.fieldType ?? previous?.fieldType ?? (
|
|
145
148
|
["rule", "rect", "text", "point", "tick"].includes(layer.mark.type) && hasDatum
|
|
146
149
|
? inferDatumFieldType(args.datum, operation)
|
|
@@ -157,6 +160,9 @@ export function resolvePositionEncoding(program, channel, args, operation) {
|
|
|
157
160
|
xEncoding?.bin !== undefined && args.field === undefined
|
|
158
161
|
? xEncoding.field
|
|
159
162
|
: args.field;
|
|
163
|
+
const dataset = hasField
|
|
164
|
+
? applyRequestedItemMissingPolicy(layer, sourceDataset, field)
|
|
165
|
+
: applyRequestedItemMissingPolicy(layer, sourceDataset);
|
|
160
166
|
const datum = args.datum;
|
|
161
167
|
const temporalUnit = resolveTemporalUnit({ ...args, ...(hasDatum ? {} : { field }) }, fieldType, previous);
|
|
162
168
|
const usesField = !countRadius && (!["rule", "area", "rect", "text", "point", "tick"].includes(layer.mark.type) || hasField);
|
|
@@ -174,7 +180,7 @@ export function resolvePositionEncoding(program, channel, args, operation) {
|
|
|
174
180
|
} else readScaleField(dataset.values, field, fieldType, { temporalUnit });
|
|
175
181
|
}
|
|
176
182
|
const effectiveArgs = { ...args };
|
|
177
|
-
if (clearRadialMapping) delete effectiveArgs.mapping;
|
|
183
|
+
if (clearRadialMapping && args.aggregate === undefined) delete effectiveArgs.mapping;
|
|
178
184
|
else if (mapping !== undefined) effectiveArgs.mapping = mapping;
|
|
179
185
|
const directQuantitativeArcTheta =
|
|
180
186
|
layer.mark.type === "arc" &&
|
|
@@ -14,6 +14,8 @@ import { deriveAreaSeries } from "../../grammar/areaSeries.js";
|
|
|
14
14
|
import { resolvePositionEncoding } from "./position/resolve.js";
|
|
15
15
|
import { resolveScalePreview } from "../scales/preview.js";
|
|
16
16
|
import { findUpstreamTransform } from "../../materialization/dataProvenance.js";
|
|
17
|
+
import { applyRequestedItemMissingPolicy } from
|
|
18
|
+
"../../grammar/itemMissing.js";
|
|
17
19
|
|
|
18
20
|
const SECONDARY_OPTIONS = Object.freeze([
|
|
19
21
|
"field", "datum", "target", "fieldType", "scale", "coordinate", "temporalUnit"
|
|
@@ -46,7 +48,7 @@ function validateSecondaryField(dataset, field, fieldType, temporalUnit) {
|
|
|
46
48
|
|
|
47
49
|
function encodeSecondaryPosition(program, channel, args, operation, types) {
|
|
48
50
|
validateOptions(args, SECONDARY_OPTIONS, operation);
|
|
49
|
-
const { id: target, dataset, layer } = resolveTarget(
|
|
51
|
+
const { id: target, dataset: sourceDataset, layer } = resolveTarget(
|
|
50
52
|
program,
|
|
51
53
|
args.target,
|
|
52
54
|
types,
|
|
@@ -72,6 +74,9 @@ function encodeSecondaryPosition(program, channel, args, operation, types) {
|
|
|
72
74
|
const rect = layer.mark.type === "rect";
|
|
73
75
|
const hasField = Object.hasOwn(args, "field");
|
|
74
76
|
const hasDatum = Object.hasOwn(args, "datum");
|
|
77
|
+
const dataset = hasField
|
|
78
|
+
? applyRequestedItemMissingPolicy(layer, sourceDataset, args.field)
|
|
79
|
+
: applyRequestedItemMissingPolicy(layer, sourceDataset);
|
|
75
80
|
if ((rule || area || rect) && hasField === hasDatum) {
|
|
76
81
|
throw new Error(`${operation} requires exactly one of field or datum for a ${layer.mark.type} mark.`);
|
|
77
82
|
}
|
|
@@ -122,6 +127,9 @@ function encodeSecondaryPosition(program, channel, args, operation, types) {
|
|
|
122
127
|
|
|
123
128
|
let next = program;
|
|
124
129
|
if (layer.mark.type === "bar") {
|
|
130
|
+
if (layer.mark.orientation !== undefined && primaryChannel !== (layer.mark.orientation === "vertical" ? "y" : "x")) {
|
|
131
|
+
throw new Error("Numeric bar endpoints must use the measure channel.");
|
|
132
|
+
}
|
|
125
133
|
if (layer.layout?.mode !== undefined && layer.layout.mode !== "overlay") throw new Error("Ranged bars support only overlay layout; clear series layout before assigning endpoints.");
|
|
126
134
|
for (const property of ["aggregate", "stack", "bin"]) {
|
|
127
135
|
if (layer.encoding?.[primaryChannel]?.[property] !== undefined) {
|
|
@@ -205,6 +213,20 @@ function rangeAction(channel) {
|
|
|
205
213
|
const { layer, dataset } = resolveTarget(this, args.target, ["area", "bar", "rect", "rule"], "ranged mark");
|
|
206
214
|
let next = this;
|
|
207
215
|
let lower = { field: args.lower }, upper = { field: args.upper };
|
|
216
|
+
if (layer.mark.type === "bar") {
|
|
217
|
+
if (layer.mark.orientation !== undefined && channel !== (layer.mark.orientation === "vertical" ? "y" : "x")) {
|
|
218
|
+
throw new Error("Numeric bar endpoints must use the measure channel.");
|
|
219
|
+
}
|
|
220
|
+
common.target = layer.id;
|
|
221
|
+
const resolved = resolvePositionEncoding(this, channel, {
|
|
222
|
+
...common, ...lower,
|
|
223
|
+
...(args.coordinate === undefined ? {} : { coordinate: args.coordinate }),
|
|
224
|
+
...(args.scale === undefined ? {} : { scale: args.scale })
|
|
225
|
+
}, op);
|
|
226
|
+
next = setEncodingProperties(next, layer.id, `${channel}2`, {
|
|
227
|
+
...upper, fieldType: common.fieldType, scale: resolved.scale.id
|
|
228
|
+
});
|
|
229
|
+
}
|
|
208
230
|
if (layer.mark.type === "area") {
|
|
209
231
|
common.target = layer.id;
|
|
210
232
|
const prepared = prepareAreaRange(this, layer, dataset, channel, args, common);
|
|
@@ -26,6 +26,7 @@ import { findDataset } from "../../selectors/datasets.js";
|
|
|
26
26
|
import { assertPathGroupCompatible, validatePathGroupAppearance } from "../../materialization/marks/grouping.js";
|
|
27
27
|
import { assertEncodingSelectionCompatibility } from "../../materialization/selection/compatibility.js";
|
|
28
28
|
import { planLegendBlockTransitions } from "../guides/legends/transition.js";
|
|
29
|
+
import { withGuideLayoutValidation } from "../../materialization/guides/layout.js";
|
|
29
30
|
|
|
30
31
|
const OPTIONS = Object.freeze(["target", "channel"]);
|
|
31
32
|
const REMOVABLE_CHANNELS = Object.freeze([
|
|
@@ -208,7 +209,7 @@ export const removeEncoding = /* @__PURE__ */ closedAction(
|
|
|
208
209
|
op: "removeEncoding",
|
|
209
210
|
description: "Remove one semantic encoding and its owned materialized state."
|
|
210
211
|
}, OPTIONS,
|
|
211
|
-
function (args = {}) {
|
|
212
|
+
withGuideLayoutValidation(function (args = {}) {
|
|
212
213
|
if (!REMOVABLE_CHANNELS.includes(args.channel)) {
|
|
213
214
|
throw new Error(
|
|
214
215
|
`Unsupported removable encoding channel "${args.channel}".`
|
|
@@ -259,7 +260,7 @@ export const removeEncoding = /* @__PURE__ */ closedAction(
|
|
|
259
260
|
next = cleanupLegends(next, layer.id, channels);
|
|
260
261
|
next = cleanupPositionGuides(next, removedPositions);
|
|
261
262
|
return applyDetachedScaleRematerialization(rematerializeTarget(next, layer.id), [layer]);
|
|
262
|
-
}
|
|
263
|
+
})
|
|
263
264
|
);
|
|
264
265
|
|
|
265
266
|
export function registerEncodingRemovalAction(ProgramClass) {
|
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
import { findLayer } from "../../selectors/layers.js";
|
|
15
15
|
import { validatePathSeriesAppearance } from "../../grammar/pathSeries.js";
|
|
16
16
|
import { assertEncodingSelectionCompatibility } from "../../materialization/selection/compatibility.js";
|
|
17
|
+
import { applyRequestedItemMissingPolicy } from
|
|
18
|
+
"../../grammar/itemMissing.js";
|
|
17
19
|
|
|
18
20
|
const WIDTH_OPTIONS = Object.freeze([
|
|
19
21
|
"target", "value", "field", "fieldType", "scale"
|
|
@@ -57,12 +59,13 @@ const encodeStrokeWidth = /* @__PURE__ */ action(
|
|
|
57
59
|
next = next.rematerializeRuleMark({ id });
|
|
58
60
|
return applyDetachedScaleRematerialization(next, [layer]);
|
|
59
61
|
}
|
|
60
|
-
const { id, dataset, layer } = resolveTarget(
|
|
62
|
+
const { id, dataset: sourceDataset, layer } = resolveTarget(
|
|
61
63
|
this,
|
|
62
64
|
args.target,
|
|
63
65
|
["rule", "line"],
|
|
64
66
|
"rule or line mark"
|
|
65
67
|
);
|
|
68
|
+
const dataset = applyRequestedItemMissingPolicy(layer, sourceDataset, args.field);
|
|
66
69
|
const fieldType = args.fieldType ?? "quantitative";
|
|
67
70
|
if (fieldType !== "quantitative") {
|
|
68
71
|
throw new Error("encodeStrokeWidth requires a quantitative field.");
|
|
@@ -48,6 +48,7 @@ export const layoutSeries = /* @__PURE__ */ action({ op: "layoutSeries", descrip
|
|
|
48
48
|
if (grain === undefined || encoding.x?.scale === undefined || encoding.y?.scale === undefined) throw new Error("Series layout requires a complete bar.");
|
|
49
49
|
if (mode === "center") throw new Error("Centered bars are not supported.");
|
|
50
50
|
if (grain === BAR_GRAINS.ranged && mode !== "overlay") throw new Error("Ranged bars support only overlay layout.");
|
|
51
|
+
if (grain === BAR_GRAINS.centered && mode !== "overlay") throw new Error("Numeric-center bars support only overlay layout.");
|
|
51
52
|
if (grain === BAR_GRAINS.aggregate) deriveBarAggregates(dataset.values, updated);
|
|
52
53
|
if (mode === "group" && grain === BAR_GRAINS.aggregate) {
|
|
53
54
|
if (encoding.group === undefined) throw new Error("Grouped bars require a group encoding.");
|
|
@@ -26,6 +26,8 @@ import {
|
|
|
26
26
|
setEncodingProperties,
|
|
27
27
|
validateOptions
|
|
28
28
|
} from "./shared.js";
|
|
29
|
+
import { applyRequestedItemMissingPolicy } from
|
|
30
|
+
"../../grammar/itemMissing.js";
|
|
29
31
|
|
|
30
32
|
const OPTIONS = Object.freeze([
|
|
31
33
|
"target", "value", "field", "fieldType", "temporalUnit", "scale"
|
|
@@ -122,7 +124,7 @@ export const encodeStroke = /* @__PURE__ */ action(
|
|
|
122
124
|
"Constant stroke does not accept fieldType, temporalUnit, or scale."
|
|
123
125
|
);
|
|
124
126
|
}
|
|
125
|
-
const { id: target, dataset, layer } = resolveTarget(
|
|
127
|
+
const { id: target, dataset: sourceDataset, layer } = resolveTarget(
|
|
126
128
|
this,
|
|
127
129
|
args.target,
|
|
128
130
|
MARK_TYPES,
|
|
@@ -155,6 +157,7 @@ export const encodeStroke = /* @__PURE__ */ action(
|
|
|
155
157
|
}
|
|
156
158
|
|
|
157
159
|
const fieldType = args.fieldType ?? "nominal";
|
|
160
|
+
const dataset = applyRequestedItemMissingPolicy(layer, sourceDataset, args.field);
|
|
158
161
|
const previous = layer.encoding?.stroke;
|
|
159
162
|
const temporalUnit = resolveTemporalUnit(args, fieldType, previous);
|
|
160
163
|
const requestedScale = resolveReassignmentScaleOptions(
|
|
@@ -15,6 +15,8 @@ import {
|
|
|
15
15
|
} from "./shared.js";
|
|
16
16
|
import { findLayer } from "../../selectors/layers.js";
|
|
17
17
|
import { validatePathSeriesAppearance } from "../../grammar/pathSeries.js";
|
|
18
|
+
import { applyRequestedItemMissingPolicy } from
|
|
19
|
+
"../../grammar/itemMissing.js";
|
|
18
20
|
|
|
19
21
|
const STROKE_DASH_ENCODING_OPTIONS = Object.freeze([
|
|
20
22
|
"field", "value", "target", "fieldType", "scale"
|
|
@@ -73,12 +75,15 @@ const encodeStrokeDash = /* @__PURE__ */ action(
|
|
|
73
75
|
if (hasValue && (args.fieldType !== undefined || args.scale !== undefined)) {
|
|
74
76
|
throw new Error("Constant stroke dash does not accept fieldType or scale.");
|
|
75
77
|
}
|
|
76
|
-
const { id: target, dataset, layer } = resolveTarget(
|
|
78
|
+
const { id: target, dataset: sourceDataset, layer } = resolveTarget(
|
|
77
79
|
this,
|
|
78
80
|
args.target,
|
|
79
81
|
["line", "rule"],
|
|
80
82
|
"line or rule mark"
|
|
81
83
|
);
|
|
84
|
+
const dataset = hasField
|
|
85
|
+
? applyRequestedItemMissingPolicy(layer, sourceDataset, args.field)
|
|
86
|
+
: sourceDataset;
|
|
82
87
|
if (hasValue) {
|
|
83
88
|
normalizeStrokeDashPattern(args.value);
|
|
84
89
|
validatePathSeriesAppearance(dataset.values, {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { action } from "../../core/action.js";
|
|
2
|
+
import { schemaField } from "../../grammar/datasetSchema.js";
|
|
2
3
|
import { formatTextValue, validateTextFormat } from "../../grammar/text.js";
|
|
3
4
|
import { normalizeMarkLabelContent, resolveMarkLabelValues } from "../../grammar/markLabels.js";
|
|
4
5
|
import { canMaterializeText } from "../../materialization/marks/index.js";
|
|
@@ -9,14 +10,18 @@ import {
|
|
|
9
10
|
setEncodingProperties,
|
|
10
11
|
validateOptions
|
|
11
12
|
} from "./shared.js";
|
|
13
|
+
import { applyRequestedItemMissingPolicy } from
|
|
14
|
+
"../../grammar/itemMissing.js";
|
|
12
15
|
|
|
13
16
|
const OPTIONS = Object.freeze(["target", "field", "value", "content", "normalizeBy", "format"]);
|
|
14
17
|
|
|
15
|
-
function validateTextField(
|
|
18
|
+
function validateTextField(dataset, field, format) {
|
|
19
|
+
const rows = dataset.values;
|
|
16
20
|
if (typeof field !== "string" || field.length === 0) {
|
|
17
21
|
throw new TypeError("encodeText field must be a non-empty string.");
|
|
18
22
|
}
|
|
19
|
-
let found =
|
|
23
|
+
let found = rows.length === 0 && dataset.schema !== undefined &&
|
|
24
|
+
schemaField(dataset.schema, field) !== undefined;
|
|
20
25
|
for (const row of rows) {
|
|
21
26
|
if (!Object.hasOwn(row, field)) continue;
|
|
22
27
|
found = true;
|
|
@@ -41,18 +46,21 @@ export const encodeText = /* @__PURE__ */ action(
|
|
|
41
46
|
if (Object.hasOwn(args, "normalizeBy") && (!hasContent || args.content !== "share")) {
|
|
42
47
|
throw new Error("Text normalizeBy is only supported with share content.");
|
|
43
48
|
}
|
|
44
|
-
const { id: target, dataset, layer } = resolveTarget(
|
|
49
|
+
const { id: target, dataset: sourceDataset, layer } = resolveTarget(
|
|
45
50
|
this,
|
|
46
51
|
args.target,
|
|
47
52
|
["text"],
|
|
48
53
|
"text mark"
|
|
49
54
|
);
|
|
55
|
+
const dataset = hasField
|
|
56
|
+
? applyRequestedItemMissingPolicy(layer, sourceDataset, args.field)
|
|
57
|
+
: applyRequestedItemMissingPolicy(layer, sourceDataset);
|
|
50
58
|
const previous = layer.encoding?.text;
|
|
51
59
|
const format = validateTextFormat(args.format ?? previous?.format ?? "auto");
|
|
52
60
|
const content = hasContent
|
|
53
61
|
? normalizeMarkLabelContent(findLayer(this, layer.source), args)
|
|
54
62
|
: undefined;
|
|
55
|
-
if (hasField) validateTextField(dataset
|
|
63
|
+
if (hasField) validateTextField(dataset, args.field, format);
|
|
56
64
|
else if (hasValue && formatTextValue(args.value, format) === undefined) {
|
|
57
65
|
throw new Error("encodeText value must produce non-empty text.");
|
|
58
66
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { validateFillPaint } from "../../grammar/paint.js";
|
|
1
2
|
import { editGraphicProperties } from "../primitives/graphicProperties.js";
|
|
2
3
|
import { action, closedAction } from "../../core/action.js";
|
|
3
4
|
import { isPlainObject } from "../../core/immutable.js";
|
|
4
|
-
import { validateNonEmptyString } from "../../core/validation.js";
|
|
5
5
|
import { DEFAULT_COLORS } from "../../theme/defaults.js";
|
|
6
6
|
import { validateCurveInterpolation } from "../../grammar/curveCommands.js";
|
|
7
7
|
import {
|
|
@@ -176,7 +176,7 @@ export const createErrorBand = /* @__PURE__ */ closedAction(
|
|
|
176
176
|
const resolved = resolveErrorBand(this, args);
|
|
177
177
|
const curve = validateCurveInterpolation(args.curve ?? "linear");
|
|
178
178
|
const boundaries = resolveBoundaries(args.boundaries, curve);
|
|
179
|
-
if (args.fill !== undefined)
|
|
179
|
+
if (args.fill !== undefined) validateFillPaint(args.fill, "Error-band fill");
|
|
180
180
|
let next = createResolvedIntervalData(this, resolved);
|
|
181
181
|
next = next.createAreaMark({
|
|
182
182
|
id: resolved.id,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { validateFillPaint } from "../../grammar/paint.js";
|
|
1
2
|
import { currentIntervalRoleArgs, resolveIntervalRoleScales, applyIntervalStatistics } from "../intervals/revision.js";
|
|
2
3
|
import { closedAction } from "../../core/action.js";
|
|
3
4
|
import { isPlainObject } from "../../core/immutable.js";
|
|
@@ -333,7 +334,7 @@ export const editErrorBand = /* @__PURE__ */ closedAction(
|
|
|
333
334
|
if (owner.encoding?.color !== undefined) {
|
|
334
335
|
throw new Error("editErrorBand fill conflicts with a color encoding; remove the color encoding first.");
|
|
335
336
|
}
|
|
336
|
-
errorBand.fill =
|
|
337
|
+
errorBand.fill = validateFillPaint(args.fill, "Error-band fill");
|
|
337
338
|
config.fill = errorBand.fill;
|
|
338
339
|
}
|
|
339
340
|
}
|