ggaction 0.0.21 → 0.0.22
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 +73 -0
- package/README.md +3 -2
- package/knowledge/action-cards.json +1 -1
- package/knowledge/intent-taxonomy.json +1 -1
- package/knowledge/mcp-resources.json +1 -1
- package/package.json +6 -2
- package/src/BasicChartProgram.js +1 -1
- package/src/accessibility.js +1 -0
- package/src/actions/boxPlots/components.js +1 -96
- package/src/actions/boxPlots/create.js +6 -1
- package/src/actions/boxPlots/edit.js +8 -0
- package/src/actions/boxPlots/materialize.js +3 -0
- package/src/actions/boxPlots/options.js +1 -1
- package/src/actions/canvas/actions.js +2 -1
- package/src/actions/canvas/index.js +3 -0
- package/src/actions/canvas/plotBorder.js +2 -1
- package/src/actions/canvas/plotOrder.js +14 -0
- package/src/actions/charts/area.js +1 -1
- package/src/actions/charts/bar.js +4 -4
- package/src/actions/charts/density.js +1 -1
- package/src/actions/charts/ecdf.js +1 -1
- package/src/actions/charts/endpoints.js +54 -5
- package/src/actions/charts/heatmap.js +1 -1
- package/src/actions/charts/histogram.js +1 -1
- package/src/actions/charts/horizon.js +1 -1
- package/src/actions/charts/index.js +2 -1
- package/src/actions/charts/interval-regression.js +1 -1
- package/src/actions/charts/line.js +1 -1
- package/src/actions/charts/radar.js +63 -3
- package/src/actions/charts/raincloud.js +37 -6
- package/src/actions/charts/rug-strip.js +2 -2
- package/src/actions/charts/scatter.js +3 -3
- package/src/actions/charts/text.js +1 -1
- package/src/actions/composition/actions.js +10 -3
- package/src/actions/coordinates/actions.js +2 -0
- package/src/actions/data/basic.js +6 -0
- package/src/actions/data/derived.js +6 -2
- package/src/actions/data/edit.js +21 -35
- package/src/actions/data/filter.js +1 -376
- package/src/actions/data/flatten.js +19 -0
- package/src/actions/data/index.js +6 -0
- package/src/actions/data/lookup.js +50 -0
- package/src/actions/data/raincloudFilter.js +56 -0
- package/src/actions/data/shared.js +10 -0
- package/src/actions/distributions/revision.js +3 -1
- package/src/actions/encodings/angle.js +2 -1
- package/src/actions/encodings/appearance.js +38 -13
- package/src/actions/encodings/channels.js +14 -3
- package/src/actions/encodings/color/continuous.js +26 -10
- package/src/actions/encodings/color/index.js +50 -20
- package/src/actions/encodings/color/policy.js +57 -4
- package/src/actions/encodings/parallel.js +4 -0
- package/src/actions/encodings/position/policies/bar.js +10 -6
- package/src/actions/encodings/position/policies/line.js +1 -152
- package/src/actions/encodings/position/resolve.js +4 -1
- package/src/actions/encodings/ranged.js +46 -0
- package/src/actions/encodings/remove.js +1 -1
- package/src/actions/encodings/ruleAppearance.js +7 -6
- package/src/actions/encodings/shared.js +14 -2
- package/src/actions/encodings/stroke.js +20 -14
- package/src/actions/encodings/strokeDash.js +9 -11
- package/src/actions/encodings/text.js +30 -2
- package/src/actions/errorBars/edit.js +24 -4
- package/src/actions/facets/actions.js +1 -793
- package/src/actions/facets/derive.js +34 -5
- package/src/actions/facets/replay.js +1 -38
- package/src/actions/gradientPlots/components.js +21 -9
- package/src/actions/gradientPlots/create.js +4 -2
- package/src/actions/gradientPlots/edit.js +18 -1
- package/src/actions/gradientPlots/materialize.js +35 -36
- package/src/actions/gradientPlots/options.js +1 -1
- package/src/actions/gradientPlots/paint.js +13 -7
- package/src/actions/guides/applicability.js +2 -0
- package/src/actions/guides/axes/axes.js +39 -0
- package/src/actions/guides/axes/axis.js +9 -5
- package/src/actions/guides/axes/edit.js +11 -8
- package/src/actions/guides/axes/labels.js +24 -8
- package/src/actions/guides/axes/parallel/policy.js +3 -5
- package/src/actions/guides/axes/parallel/resolve.js +4 -3
- package/src/actions/guides/axes/policy.js +22 -0
- package/src/actions/guides/axes/tickGroups.js +4 -2
- package/src/actions/guides/axes/ticks.js +22 -4
- package/src/actions/guides/axes/titles.js +19 -6
- package/src/actions/guides/facadeAxes.js +3 -0
- package/src/actions/guides/grids/grid.js +3 -1
- package/src/actions/guides/grids/resolve.js +7 -11
- package/src/actions/guides/guides.js +21 -1
- package/src/actions/guides/legends/categorical/actions.js +1 -515
- package/src/actions/guides/legends/categorical/layout.js +5 -2
- package/src/actions/guides/legends/categorical/options.js +4 -0
- package/src/actions/guides/legends/categorical/resolve.js +8 -3
- package/src/actions/guides/legends/categorical/symbols.js +3 -3
- package/src/actions/guides/legends/colorSize.js +2 -0
- package/src/actions/guides/legends/continuous/common.js +1 -1
- package/src/actions/guides/legends/continuous/gradient.js +1 -360
- package/src/actions/guides/legends/continuous/interval.js +1 -1
- package/src/actions/guides/legends/continuous/opacity.js +15 -1
- package/src/actions/guides/legends/edit.js +1 -1
- package/src/actions/guides/legends/focused.js +1 -1
- package/src/actions/guides/legends/lane.js +1 -1
- package/src/actions/guides/legends/sampling.js +1 -140
- package/src/actions/guides/legends/strokeWidth.js +3 -2
- package/src/actions/guides/legends/transition.js +19 -2
- package/src/actions/guides/polar/axes/facade.js +5 -4
- package/src/actions/guides/polar/axes/labels.js +5 -4
- package/src/actions/guides/polar/axes/titles.js +5 -4
- package/src/actions/guides/polar/resolve.js +2 -1
- package/src/actions/guides/tickValues.js +7 -7
- package/src/actions/marks/arc/actions.js +5 -7
- package/src/actions/marks/area/actions.js +9 -3
- package/src/actions/marks/bar/create.js +4 -2
- package/src/actions/marks/bar/edit.js +9 -6
- package/src/actions/marks/bar/materialize.js +5 -3
- package/src/actions/marks/dependentReferences.js +87 -0
- package/src/actions/marks/index.js +2 -0
- package/src/actions/marks/lifecycle.js +2 -1
- package/src/actions/marks/line/actions.js +24 -6
- package/src/actions/marks/line/materialize.js +7 -3
- package/src/actions/marks/point/create.js +5 -4
- package/src/actions/marks/point/edit.js +6 -3
- package/src/actions/marks/point/jitter.js +6 -6
- package/src/actions/marks/point/materialize.js +36 -39
- package/src/actions/marks/rect/actions.js +16 -4
- package/src/actions/marks/references.js +183 -35
- package/src/actions/marks/rule/actions.js +20 -9
- package/src/actions/marks/shared.js +2 -1
- package/src/actions/marks/text/actions.js +10 -3
- package/src/actions/marks/tick/actions.js +11 -2
- package/src/actions/primitives/editGraphics.js +28 -3
- package/src/actions/primitives/index.js +9 -0
- package/src/actions/primitives/semanticAction.js +13 -7
- package/src/actions/primitives/semanticValidation/layer.js +20 -0
- package/src/actions/primitives/semanticValidation/scale.js +8 -1
- package/src/actions/scales/channels.js +1 -1
- package/src/actions/scales/consumers/common.js +19 -1
- package/src/actions/scales/consumers/families.js +2 -2
- package/src/actions/scales/consumers/seriesLayout.js +4 -3
- package/src/actions/scales/edit.js +11 -0
- package/src/actions/scales/editPolicy.js +5 -3
- package/src/actions/scales/preview.js +2 -1
- package/src/actions/scales/quantitativeColor.js +2 -4
- package/src/actions/selection/actions.js +10 -1
- package/src/actions/theme/reconcile.js +37 -9
- package/src/core/compositionState.js +21 -4
- package/src/core/datasetReferences.js +17 -0
- package/src/core/immutable.js +1 -0
- package/src/core/resourceReferences.js +5 -3
- package/src/core/validation.js +4 -0
- package/src/core/vocabulary.js +24 -2
- package/src/grammar/aggregate.js +1 -0
- package/src/grammar/arcs.js +1 -1
- package/src/grammar/areaSeries.js +1 -1
- package/src/grammar/axisTextPlacement.js +1 -1
- package/src/grammar/bars/policy.js +2 -2
- package/src/grammar/datasetDependencies.js +97 -0
- package/src/grammar/datasetSchema.js +31 -1
- package/src/grammar/facetHeaders.js +1 -0
- package/src/grammar/facets/dependencies.js +6 -2
- package/src/grammar/facets/legendIntent.js +6 -2
- package/src/grammar/facets/scales.js +27 -7
- package/src/grammar/flatten.js +85 -0
- package/src/grammar/histogram.js +13 -1
- package/src/grammar/horizon.js +1 -494
- package/src/grammar/jitter.js +2 -2
- package/src/grammar/legendSpacing.js +45 -0
- package/src/grammar/lineMissing.js +90 -0
- package/src/grammar/lineSeries.js +1 -376
- package/src/grammar/lookup.js +97 -0
- package/src/grammar/markFilter.js +4 -2
- package/src/grammar/markLabels.js +8 -3
- package/src/grammar/numeric.js +1 -1
- package/src/grammar/pathCommands.js +1 -1
- package/src/grammar/pathSeries.js +1 -109
- package/src/grammar/pointPolygon.js +81 -0
- package/src/grammar/pointShapes.js +15 -15
- package/src/grammar/rects.js +1 -1
- package/src/grammar/regression/derive.js +1 -195
- package/src/grammar/scales/appearance.js +1 -1
- package/src/grammar/scales/color.js +1 -1
- package/src/grammar/scales/colorConsumers.js +1 -63
- package/src/grammar/scales/continuous.js +58 -1
- package/src/grammar/scales/definition.js +1 -1
- package/src/grammar/scales/ordinal.js +5 -2
- package/src/grammar/scales/policies.js +3 -3
- package/src/grammar/schemas/graphicBounds.js +40 -12
- package/src/grammar/schemas/graphicClip.js +23 -0
- package/src/grammar/schemas/graphicTree.js +16 -0
- package/src/grammar/statisticalReference.js +22 -0
- package/src/grammar/text.js +65 -1
- package/src/grammar/ticks.js +92 -1
- package/src/grammar/transformTopology.js +3 -1
- package/src/grammar/transforms.js +24 -0
- package/src/inspection.js +1 -495
- package/src/inspectionDescriptors.js +1 -1
- package/src/layout/canvas.js +1 -1
- package/src/layout/facetContentFlow.js +101 -0
- package/src/layout/facetHeaderTitles.js +1 -0
- package/src/layout/facetTopLane.js +29 -0
- package/src/layout/facets.js +1 -1
- package/src/layout/legendGroup.js +1 -1
- package/src/layout/legendItems.js +26 -10
- package/src/layout/legendSpacing.js +66 -0
- package/src/layout/plotDrawOrder.js +17 -0
- package/src/materialization/bars/histogram.js +8 -2
- package/src/materialization/bars/resolve.js +1 -1
- package/src/materialization/composition.js +1 -0
- package/src/materialization/compositionSnapshot.js +22 -2
- package/src/materialization/dataProvenance.js +1 -1
- package/src/materialization/facetGuides/layout.js +1 -1
- package/src/materialization/facetGuides/placement.js +23 -11
- package/src/materialization/facetGuides/preparation.js +43 -24
- package/src/materialization/facetHeaders.js +1 -1
- package/src/materialization/facetTopLane.js +25 -0
- package/src/materialization/facets.js +1 -1
- package/src/materialization/graphicHierarchy.js +5 -0
- package/src/materialization/graphicTranslation.js +2 -0
- package/src/materialization/guides/layout.js +1 -1
- package/src/materialization/guides/resources.js +1 -1
- package/src/materialization/legends.js +1 -1
- package/src/materialization/markClip.js +26 -0
- package/src/materialization/marks/capabilities.js +3 -2
- package/src/materialization/marks/grouping.js +7 -5
- package/src/materialization/marks/index.js +4 -2
- package/src/materialization/marks/policies.js +4 -0
- package/src/materialization/plotDrawOrder.js +44 -0
- package/src/materialization/rect.js +11 -3
- package/src/materialization/scaleGuideDependencies.js +13 -5
- package/src/materialization/selection/items/common.js +1 -1
- package/src/materialization/selection/items/path.js +1 -0
- package/src/materialization/selection/policies/index.js +2 -1
- package/src/materialization/text.js +10 -5
- package/src/mcp/cli.js +0 -0
- package/src/persistence/validation.js +108 -18
- package/src/persistence.js +4 -2
- package/src/renderers/canvas/index.js +16 -0
- package/src/renderers/canvas/isolated.js +11 -1
- package/src/renderers/canvas/native.js +7 -0
- package/src/renderers/canvas/scene.js +66 -0
- package/src/renderers/canvas/text.js +3 -2
- package/src/renderers/svg.js +1 -617
- package/src/renderers/svgSerialization.js +643 -0
- package/src/renderers/transitionScene.js +67 -0
- package/src/scene.js +89 -0
- package/src/selectors/axes.js +1 -1
- package/src/selectors/datasets.js +1 -0
- package/src/selectors/grids.js +9 -0
- package/src/selectors/markClip.js +28 -0
- package/src/selectors/raincloud.js +7 -0
- package/src/version.js +1 -1
- package/types/basic.d.ts +7 -0
- package/types/index.d.ts +8 -0
- package/types/program.d.ts +270 -60
- package/types/scene.d.ts +35 -0
|
@@ -1,152 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
validateAggregate,
|
|
3
|
-
validateAggregateFieldType
|
|
4
|
-
} from "../../../../grammar/aggregate.js";
|
|
5
|
-
import { emptyPositionPolicy, resolveBin } from "./common.js";
|
|
6
|
-
|
|
7
|
-
function rejectStack(args, channel) {
|
|
8
|
-
if (args.stack !== undefined) {
|
|
9
|
-
throw new Error(`Line ${channel} encoding does not support stack.`);
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function rejectBin(args, channel) {
|
|
14
|
-
if (args.bin !== undefined) {
|
|
15
|
-
throw new Error(`Line ${channel} encoding does not support bin.`);
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function resolveLinePositionPolicy({
|
|
20
|
-
program,
|
|
21
|
-
layer,
|
|
22
|
-
dataset,
|
|
23
|
-
channel,
|
|
24
|
-
args,
|
|
25
|
-
fieldType
|
|
26
|
-
}) {
|
|
27
|
-
const polar = ["theta", "radius"].includes(channel);
|
|
28
|
-
const config = program.markConfigs[layer.id] ?? {};
|
|
29
|
-
if (polar) {
|
|
30
|
-
if (config.curve !== undefined && config.curve !== "linear") {
|
|
31
|
-
throw new Error("Polar line position currently requires curve \"linear\".");
|
|
32
|
-
}
|
|
33
|
-
rejectBin(args, channel);
|
|
34
|
-
rejectStack(args, channel);
|
|
35
|
-
if (args.aggregate !== undefined) {
|
|
36
|
-
throw new Error(`Line ${channel} encoding does not support aggregate.`);
|
|
37
|
-
}
|
|
38
|
-
return emptyPositionPolicy();
|
|
39
|
-
}
|
|
40
|
-
if (config.closed === true) {
|
|
41
|
-
throw new Error("Line closed requires theta/radius Polar position encodings.");
|
|
42
|
-
}
|
|
43
|
-
const regression = dataset.transform?.some(item => item.type === "regression");
|
|
44
|
-
const interval = dataset.transform?.some(item => item.type === "interval");
|
|
45
|
-
const windowOutput = dataset.transform?.some(item =>
|
|
46
|
-
item.type === "window" &&
|
|
47
|
-
item.operations?.some(operation => operation.as === args.field)
|
|
48
|
-
);
|
|
49
|
-
|
|
50
|
-
if (channel === "x") {
|
|
51
|
-
rejectStack(args, "x");
|
|
52
|
-
if (args.aggregate !== undefined) {
|
|
53
|
-
throw new Error("Line x encoding does not support aggregate.");
|
|
54
|
-
}
|
|
55
|
-
if (args.bin !== undefined) {
|
|
56
|
-
if (fieldType !== "quantitative") {
|
|
57
|
-
throw new Error("Binned line x encoding requires a quantitative field.");
|
|
58
|
-
}
|
|
59
|
-
if (
|
|
60
|
-
layer.encoding?.y !== undefined &&
|
|
61
|
-
layer.encoding.y.aggregate === undefined
|
|
62
|
-
) {
|
|
63
|
-
throw new Error("Binned line x encoding requires an aggregate y encoding.");
|
|
64
|
-
}
|
|
65
|
-
return {
|
|
66
|
-
bin: resolveBin(args.bin),
|
|
67
|
-
aggregate: undefined,
|
|
68
|
-
stack: undefined
|
|
69
|
-
};
|
|
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
|
-
}
|
|
78
|
-
const directPair =
|
|
79
|
-
layer.encoding?.y?.aggregate === undefined &&
|
|
80
|
-
["quantitative", "temporal"].includes(layer.encoding?.y?.fieldType) &&
|
|
81
|
-
fieldType === "quantitative";
|
|
82
|
-
const pendingQuantitativePair =
|
|
83
|
-
layer.encoding?.y === undefined && fieldType === "quantitative";
|
|
84
|
-
if (
|
|
85
|
-
fieldType !== "temporal" &&
|
|
86
|
-
!(
|
|
87
|
-
(regression || interval || directPair || pendingQuantitativePair) &&
|
|
88
|
-
fieldType === "quantitative"
|
|
89
|
-
)
|
|
90
|
-
) {
|
|
91
|
-
throw new Error(
|
|
92
|
-
"Line x encoding requires a categorical or temporal field, or a compatible quantitative field."
|
|
93
|
-
);
|
|
94
|
-
}
|
|
95
|
-
return emptyPositionPolicy();
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
rejectBin(args, "y");
|
|
99
|
-
rejectStack(args, "y");
|
|
100
|
-
if (layer.encoding?.x?.bin !== undefined && args.aggregate === undefined) {
|
|
101
|
-
throw new Error("Binned line x encoding requires an aggregate y encoding.");
|
|
102
|
-
}
|
|
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 =
|
|
108
|
-
fieldType === "quantitative" &&
|
|
109
|
-
["quantitative", "temporal", "nominal", "ordinal"].includes(layer.encoding?.x?.fieldType) &&
|
|
110
|
-
layer.encoding.x.bin === undefined;
|
|
111
|
-
const prospectiveDirect =
|
|
112
|
-
args.aggregate === undefined &&
|
|
113
|
-
(fieldType === "temporal" ||
|
|
114
|
-
(fieldType === "quantitative" &&
|
|
115
|
-
(layer.encoding?.x === undefined || continuousPair)));
|
|
116
|
-
if (interval || (windowOutput && args.aggregate === undefined) || prospectiveDirect) {
|
|
117
|
-
if (!["quantitative", "temporal"].includes(fieldType)) {
|
|
118
|
-
throw new Error(
|
|
119
|
-
"Direct line y encoding requires a quantitative or temporal field."
|
|
120
|
-
);
|
|
121
|
-
}
|
|
122
|
-
if (args.aggregate !== undefined) {
|
|
123
|
-
throw new Error("Direct line y encoding does not support aggregate.");
|
|
124
|
-
}
|
|
125
|
-
return emptyPositionPolicy();
|
|
126
|
-
}
|
|
127
|
-
if (regression) {
|
|
128
|
-
if (fieldType !== "quantitative") {
|
|
129
|
-
throw new Error("Regression line y encoding requires a quantitative field.");
|
|
130
|
-
}
|
|
131
|
-
if (args.aggregate !== undefined) {
|
|
132
|
-
throw new Error("Regression line y encoding does not support aggregate.");
|
|
133
|
-
}
|
|
134
|
-
return emptyPositionPolicy();
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (layer.encoding?.x !== undefined) {
|
|
138
|
-
const compatibleAggregateX =
|
|
139
|
-
["temporal", "nominal", "ordinal"].includes(layer.encoding.x.fieldType) ||
|
|
140
|
-
(layer.encoding.x.fieldType === "quantitative" &&
|
|
141
|
-
layer.encoding.x.bin !== undefined);
|
|
142
|
-
if (!compatibleAggregateX) {
|
|
143
|
-
throw new Error(
|
|
144
|
-
"Aggregate line y encoding requires a temporal, categorical, or binned quantitative x encoding."
|
|
145
|
-
);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const aggregate = validateAggregate(args.aggregate);
|
|
150
|
-
validateAggregateFieldType(aggregate, fieldType);
|
|
151
|
-
return { bin: undefined, aggregate, stack: undefined };
|
|
152
|
-
}
|
|
1
|
+
import{validateAggregate as E,validateAggregateFieldType as b}from"../../../../grammar/aggregate.js";import{emptyPositionPolicy as a,resolveBin as h}from"./common.js";import{findSemanticScale as P}from"../../../../selectors/scales.js";import{validateCategoricalColorConsumer as L}from"../../../../grammar/scales/colorConsumers.js";import{validateLineMissingScope as k}from"../../../../grammar/lineMissing.js";function u(d,e){if(d.stack!==void 0)throw new Error(`Line ${e} encoding does not support stack.`)}function p(d,e){if(d.bin!==void 0)throw new Error(`Line ${e} encoding does not support bin.`)}function $({program:d,layer:e,dataset:g,channel:o,args:n,fieldType:i}){k({...e,encoding:{...e.encoding,[o]:{...n,fieldType:i}}});for(const t of["color","strokeDash"]){const r=e.encoding?.[t],l=r===void 0?void 0:P(d,r.scale);l!==void 0&&L({...e,encoding:{...e.encoding,[o]:{...e.encoding[o],...n,fieldType:i}}},r,l,t)}const m=["theta","radius"].includes(o),c=d.markConfigs[e.id]??{};if(m){if(c.curve!==void 0&&c.curve!=="linear")throw new Error('Polar line position currently requires curve "linear".');if(p(n,o),u(n,o),n.aggregate!==void 0)throw new Error(`Line ${o} encoding does not support aggregate.`);return a()}if(c.closed===!0)throw new Error("Line closed requires theta/radius Polar position encodings.");const f=g.transform?.some(t=>t.type==="regression"),s=g.transform?.some(t=>t.type==="interval"),v=g.transform?.some(t=>t.type==="window"&&t.operations?.some(r=>r.as===n.field));if(o==="x"){if(u(n,"x"),n.aggregate!==void 0)throw new Error("Line x encoding does not support aggregate.");if(n.bin!==void 0){if(i!=="quantitative")throw new Error("Binned line x encoding requires a quantitative field.");if(e.encoding?.y!==void 0&&e.encoding.y.aggregate===void 0)throw new Error("Binned line x encoding requires an aggregate y encoding.");return{bin:h(n.bin),aggregate:void 0,stack:void 0}}if(["nominal","ordinal"].includes(i)){if(e.encoding?.y!==void 0&&e.encoding.y.aggregate===void 0&&e.encoding.y.fieldType!=="quantitative")throw new Error("Categorical line x requires a quantitative value or aggregate y encoding.");return a()}const t=e.encoding?.y?.aggregate===void 0&&["quantitative","temporal"].includes(e.encoding?.y?.fieldType)&&i==="quantitative",r=e.encoding?.y===void 0&&i==="quantitative";if(i!=="temporal"&&!((f||s||t||r)&&i==="quantitative"))throw new Error("Line x encoding requires a categorical or temporal field, or a compatible quantitative field.");return a()}if(p(n,"y"),u(n,"y"),e.encoding?.x?.bin!==void 0&&n.aggregate===void 0)throw new Error("Binned line x encoding requires an aggregate y encoding.");if(["nominal","ordinal"].includes(e.encoding?.x?.fieldType)&&n.aggregate===void 0&&i!=="quantitative")throw new Error("Categorical line x requires a quantitative value or aggregate y encoding.");const q=i==="quantitative"&&["quantitative","temporal","nominal","ordinal"].includes(e.encoding?.x?.fieldType)&&e.encoding.x.bin===void 0,x=n.aggregate===void 0&&(i==="temporal"||i==="quantitative"&&(e.encoding?.x===void 0||q));if(s||v&&n.aggregate===void 0||x){if(!["quantitative","temporal"].includes(i))throw new Error("Direct line y encoding requires a quantitative or temporal field.");if(n.aggregate!==void 0)throw new Error("Direct line y encoding does not support aggregate.");return a()}if(f){if(i!=="quantitative")throw new Error("Regression line y encoding requires a quantitative field.");if(n.aggregate!==void 0)throw new Error("Regression line y encoding does not support aggregate.");return a()}if(e.encoding?.x!==void 0&&!(["temporal","nominal","ordinal"].includes(e.encoding.x.fieldType)||e.encoding.x.fieldType==="quantitative"&&e.encoding.x.bin!==void 0))throw new Error("Aggregate line y encoding requires a temporal, categorical, or binned quantitative x encoding.");const w=E(n.aggregate);return b(w,i),{bin:void 0,aggregate:w,stack:void 0}}export{$ as resolveLinePositionPolicy};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { isSourceOwnedText } from "../../../grammar/text.js";
|
|
2
2
|
import { findSemanticScale } from "../../../selectors/scales.js";
|
|
3
3
|
import { readAreaEndpoint } from "../../../grammar/areaEndpoints.js";
|
|
4
|
+
import { readLinePosition } from "../../../grammar/lineMissing.js";
|
|
4
5
|
import { validateUserId } from "../../../core/identifiers.js";
|
|
5
6
|
import { getPositionCoordinateDefaults } from "../../../grammar/coordinates.js";
|
|
6
7
|
import {
|
|
@@ -272,7 +273,9 @@ export function resolvePositionEncoding(program, channel, args, operation) {
|
|
|
272
273
|
"Position scale unknown currently requires a row-owned point mark."
|
|
273
274
|
);
|
|
274
275
|
}
|
|
275
|
-
if (layer.mark.type === "
|
|
276
|
+
if (layer.mark.type === "line" && layer.mark.missing === "break") {
|
|
277
|
+
readLinePosition(dataset.values, { field, fieldType, temporalUnit });
|
|
278
|
+
} else if (layer.mark.type === "area" && fieldType === "quantitative") {
|
|
276
279
|
readAreaEndpoint(dataset.values, { ...(hasDatum ? { datum } : { field }), fieldType }, layer.mark.missing);
|
|
277
280
|
} else if (["rule", "rect", "text", "point", "tick"].includes(layer.mark.type) && hasDatum) {
|
|
278
281
|
const mark = layer.mark.type[0].toUpperCase() + layer.mark.type.slice(1);
|
|
@@ -199,6 +199,39 @@ const encodeY2 = /* @__PURE__ */ action(
|
|
|
199
199
|
}
|
|
200
200
|
);
|
|
201
201
|
|
|
202
|
+
function normalizeDatumRangeBound(value, fieldType, operation) {
|
|
203
|
+
if (typeof value === "string") return { field: value };
|
|
204
|
+
validateOptions(value, ["datum"], `${operation} endpoint`);
|
|
205
|
+
if (!Object.hasOwn(value, "datum") || fieldType !== "quantitative") {
|
|
206
|
+
throw new TypeError(`${operation} datum endpoints require a quantitative datum object.`);
|
|
207
|
+
}
|
|
208
|
+
return { datum: normalizePositionDatum(value.datum, fieldType, operation, undefined, "Range") };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function prepareDatumRange(program, layer, dataset, channel, args, common, lower, upper) {
|
|
212
|
+
if (Object.hasOwn(upper, "field")) {
|
|
213
|
+
validateSecondaryField(applyRequestedItemMissingPolicy(layer, dataset, upper.field),
|
|
214
|
+
upper.field, common.fieldType, undefined);
|
|
215
|
+
}
|
|
216
|
+
const resolved = resolvePositionEncoding(program, channel, {
|
|
217
|
+
...common, ...lower,
|
|
218
|
+
...(args.coordinate === undefined ? {} : { coordinate: args.coordinate }),
|
|
219
|
+
...(args.scale === undefined ? {} : { scale: args.scale })
|
|
220
|
+
}, channel === "x" ? "encodeXRange" : "encodeYRange");
|
|
221
|
+
const primary = { ...lower, fieldType: common.fieldType, scale: resolved.scale.id };
|
|
222
|
+
const secondary = { ...upper, fieldType: common.fieldType, scale: resolved.scale.id };
|
|
223
|
+
const updated = { ...layer, coordinate: resolved.coordinate.id,
|
|
224
|
+
encoding: { ...layer.encoding, [channel]: primary, [`${channel}2`]: secondary } };
|
|
225
|
+
const coordinates = program.semanticSpec.coordinates;
|
|
226
|
+
resolveScalePreview({ ...program, semanticSpec: { ...program.semanticSpec,
|
|
227
|
+
layers: program.semanticSpec.layers.map(item => item.id === layer.id ? updated : item),
|
|
228
|
+
coordinates: coordinates.some(item => item.id === resolved.coordinate.id)
|
|
229
|
+
? coordinates : [...coordinates, resolved.coordinate],
|
|
230
|
+
scales: [...program.semanticSpec.scales.filter(item => item.id !== resolved.scale.id), resolved.scale]
|
|
231
|
+
} }, resolved.scale.id);
|
|
232
|
+
return secondary;
|
|
233
|
+
}
|
|
234
|
+
|
|
202
235
|
function rangeAction(channel) {
|
|
203
236
|
const primary = channel === "x" ? "encodeX" : "encodeY";
|
|
204
237
|
const secondary = channel === "x" ? "encodeX2" : "encodeY2";
|
|
@@ -213,6 +246,19 @@ function rangeAction(channel) {
|
|
|
213
246
|
const { layer, dataset } = resolveTarget(this, args.target, ["area", "bar", "rect", "rule"], "ranged mark");
|
|
214
247
|
let next = this;
|
|
215
248
|
let lower = { field: args.lower }, upper = { field: args.upper };
|
|
249
|
+
if (layer.mark.type === "rect" || layer.mark.type === "rule") {
|
|
250
|
+
lower = normalizeDatumRangeBound(args.lower, common.fieldType, op);
|
|
251
|
+
upper = normalizeDatumRangeBound(args.upper, common.fieldType, op);
|
|
252
|
+
if (Object.hasOwn(lower, "datum") || Object.hasOwn(upper, "datum")) {
|
|
253
|
+
common.target = layer.id;
|
|
254
|
+
const prepared = prepareDatumRange(this, layer, dataset, channel, args, common, lower, upper);
|
|
255
|
+
const secondaryChannel = `${channel}2`;
|
|
256
|
+
if (layer.encoding?.[secondaryChannel] !== undefined) {
|
|
257
|
+
next = next.editSemantic({ property: `layer[${layer.id}].encoding.${secondaryChannel}`, remove: true });
|
|
258
|
+
}
|
|
259
|
+
next = setEncodingProperties(next, layer.id, secondaryChannel, prepared);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
216
262
|
if (layer.mark.type === "bar") {
|
|
217
263
|
if (layer.mark.orientation !== undefined && channel !== (layer.mark.orientation === "vertical" ? "y" : "x")) {
|
|
218
264
|
throw new Error("Numeric bar endpoints must use the measure channel.");
|
|
@@ -33,7 +33,7 @@ const OPTIONS = Object.freeze(["target", "channel"]);
|
|
|
33
33
|
const REMOVABLE_CHANNELS = Object.freeze([
|
|
34
34
|
"x", "y", "x2", "y2", "xOffset", "yOffset", "theta", "radius",
|
|
35
35
|
"color", "stroke", "strokeDash", "strokeWidth", "size", "shape", "group",
|
|
36
|
-
"angle", "opacity", "text"
|
|
36
|
+
"angle", "opacity", "text", "dx", "dy"
|
|
37
37
|
]);
|
|
38
38
|
const REMOVE_AXIS = Object.freeze({
|
|
39
39
|
x: "removeXAxis",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { APPEARANCE_MARK_TYPES } from "../../core/vocabulary.js";
|
|
1
2
|
import { action } from "../../core/action.js";
|
|
2
3
|
import { validateRuleStrokeWidth } from "../../grammar/ruleAppearance.js";
|
|
3
4
|
import { readQuantitativeField } from "../../grammar/scales/index.js";
|
|
@@ -24,7 +25,7 @@ const WIDTH_OPTIONS = Object.freeze([
|
|
|
24
25
|
const encodeStrokeWidth = /* @__PURE__ */ action(
|
|
25
26
|
{
|
|
26
27
|
op: "encodeStrokeWidth",
|
|
27
|
-
description: "Assign constant or field-driven
|
|
28
|
+
description: "Assign constant or field-driven mark stroke width."
|
|
28
29
|
},
|
|
29
30
|
function (args = {}) {
|
|
30
31
|
validateOptions(args, WIDTH_OPTIONS, "encodeStrokeWidth");
|
|
@@ -38,7 +39,7 @@ const encodeStrokeWidth = /* @__PURE__ */ action(
|
|
|
38
39
|
throw new Error("Constant stroke width does not accept fieldType or scale.");
|
|
39
40
|
}
|
|
40
41
|
validateRuleStrokeWidth(args.value);
|
|
41
|
-
const { id, layer } = resolveTarget(this, args.target,
|
|
42
|
+
const { id, layer } = resolveTarget(this, args.target, APPEARANCE_MARK_TYPES.encodeStrokeWidth, "rule, line or rect mark");
|
|
42
43
|
assertEncodingSelectionCompatibility(this, id, ["strokeWidth"]);
|
|
43
44
|
let next = this.guideConfigs.legend?.strokeWidth?.target === id
|
|
44
45
|
? this.removeLegend({ target: id, channels: ["strokeWidth"] })
|
|
@@ -53,7 +54,7 @@ const encodeStrokeWidth = /* @__PURE__ */ action(
|
|
|
53
54
|
...next.markConfigs[id],
|
|
54
55
|
strokeWidth: args.value
|
|
55
56
|
});
|
|
56
|
-
if (layer.mark.type
|
|
57
|
+
if (["line", "rect"].includes(layer.mark.type)) {
|
|
57
58
|
return rematerializeEncoding(next, id, "strokeWidth", undefined, layer);
|
|
58
59
|
}
|
|
59
60
|
next = next.rematerializeRuleMark({ id });
|
|
@@ -62,8 +63,8 @@ const encodeStrokeWidth = /* @__PURE__ */ action(
|
|
|
62
63
|
const { id, dataset: sourceDataset, layer } = resolveTarget(
|
|
63
64
|
this,
|
|
64
65
|
args.target,
|
|
65
|
-
|
|
66
|
-
"rule or
|
|
66
|
+
APPEARANCE_MARK_TYPES.encodeStrokeWidth,
|
|
67
|
+
"rule, line or rect mark"
|
|
67
68
|
);
|
|
68
69
|
const dataset = applyRequestedItemMissingPolicy(layer, sourceDataset, args.field);
|
|
69
70
|
const fieldType = args.fieldType ?? "quantitative";
|
|
@@ -78,7 +79,7 @@ const encodeStrokeWidth = /* @__PURE__ */ action(
|
|
|
78
79
|
}
|
|
79
80
|
validatePathSeriesAppearance(dataset.values, {
|
|
80
81
|
...layer, encoding: { ...layer.encoding, strokeWidth: { field: args.field, fieldType } }
|
|
81
|
-
});
|
|
82
|
+
}, this.semanticSpec.scales);
|
|
82
83
|
const previous = layer.encoding?.strokeWidth;
|
|
83
84
|
const requestedScale = resolveReassignmentScaleOptions(
|
|
84
85
|
previous,
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { editScaleForEncoding } from "../scales/edit.js";
|
|
2
|
+
import { collectResourceReferences } from "../../core/resourceReferences.js";
|
|
3
|
+
import { removeScale } from "../resources/remove.js";
|
|
1
4
|
import { cartesianAxisOwners } from "../../selectors/axes.js";
|
|
2
5
|
import { isSourceOwnedText } from "../../grammar/text.js";
|
|
3
6
|
import { validateUserId } from "../../core/identifiers.js";
|
|
@@ -60,6 +63,15 @@ export function resolveReassignmentScaleOptions(encoding, options) {
|
|
|
60
63
|
return { ...options, id: encoding.scale };
|
|
61
64
|
}
|
|
62
65
|
|
|
66
|
+
// A fixed-to-field transition may reuse a detached named channel scale with
|
|
67
|
+
// changed options. Recreate it through the existing resource owner, preserving
|
|
68
|
+
// the resolved definition's omitted defaults and never touching live consumers.
|
|
69
|
+
export function releaseDetachedEncodingScale(program, id) {
|
|
70
|
+
if (findSemanticScale(program, id) === undefined ||
|
|
71
|
+
collectResourceReferences(program, { kind: "scale", id }).some(ref => ref.strength === "live")) return program;
|
|
72
|
+
return removeScale.call(program, { id });
|
|
73
|
+
}
|
|
74
|
+
|
|
63
75
|
export function applyEncodingScale(
|
|
64
76
|
program,
|
|
65
77
|
definition,
|
|
@@ -71,7 +83,7 @@ export function applyEncodingScale(
|
|
|
71
83
|
return program.createScale(definition);
|
|
72
84
|
}
|
|
73
85
|
if (existing.type !== definition.type) {
|
|
74
|
-
if (reassignment && allowTypeChange) return program
|
|
86
|
+
if (reassignment && allowTypeChange) return editScaleForEncoding(program, { ...definition });
|
|
75
87
|
throw new Error(
|
|
76
88
|
`Scale "${definition.id}" cannot change type from "${existing.type}" to "${definition.type}".`
|
|
77
89
|
);
|
|
@@ -89,7 +101,7 @@ export function applyEncodingScale(
|
|
|
89
101
|
|
|
90
102
|
return Object.keys(patch).length === 1 || !reassignment
|
|
91
103
|
? program.createScale(definition)
|
|
92
|
-
: program
|
|
104
|
+
: editScaleForEncoding(program, patch);
|
|
93
105
|
}
|
|
94
106
|
|
|
95
107
|
export function rebindPositionGuides(
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { APPEARANCE_MARK_TYPES } from "../../core/vocabulary.js";
|
|
1
2
|
import { action } from "../../core/action.js";
|
|
2
3
|
import {
|
|
3
4
|
readNominalField,
|
|
@@ -7,9 +8,10 @@ import {
|
|
|
7
8
|
resolveTemporalUnit,
|
|
8
9
|
validateCategoricalFieldType
|
|
9
10
|
} from "../../grammar/scales/index.js";
|
|
10
|
-
import { validateContinuousColorConsumer } from
|
|
11
|
+
import { validateContinuousColorConsumer, validateTickColorChannels } from
|
|
11
12
|
"../../grammar/scales/colorConsumers.js";
|
|
12
|
-
import {
|
|
13
|
+
import { validatePathGroupAppearance } from "../../materialization/marks/grouping.js";
|
|
14
|
+
import { resolveColorReassignmentScaleOptions } from "./color/policy.js";
|
|
13
15
|
import { validateRuleStroke } from "../../grammar/ruleAppearance.js";
|
|
14
16
|
import { assertEncodingSelectionCompatibility } from
|
|
15
17
|
"../../materialization/selection/compatibility.js";
|
|
@@ -20,8 +22,8 @@ import {
|
|
|
20
22
|
import { applyTemporalUnit } from "./temporal.js";
|
|
21
23
|
import {
|
|
22
24
|
applyEncodingScale,
|
|
25
|
+
releaseDetachedEncodingScale,
|
|
23
26
|
rematerializeEncoding,
|
|
24
|
-
resolveReassignmentScaleOptions,
|
|
25
27
|
resolveTarget,
|
|
26
28
|
setEncodingProperties,
|
|
27
29
|
validateOptions
|
|
@@ -32,9 +34,7 @@ import { applyRequestedItemMissingPolicy } from
|
|
|
32
34
|
const OPTIONS = Object.freeze([
|
|
33
35
|
"target", "value", "field", "fieldType", "temporalUnit", "scale"
|
|
34
36
|
]);
|
|
35
|
-
const MARK_TYPES =
|
|
36
|
-
"point", "line", "area", "bar", "rect", "arc", "rule", "tick"
|
|
37
|
-
]);
|
|
37
|
+
const MARK_TYPES = APPEARANCE_MARK_TYPES.encodeStroke;
|
|
38
38
|
|
|
39
39
|
function withoutConstantStroke(program, layer) {
|
|
40
40
|
const config = { ...program.markConfigs[layer.id] };
|
|
@@ -79,17 +79,17 @@ function hasStrokeLegend(program, target) {
|
|
|
79
79
|
);
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
function validateStrokeField(dataset, layer, args, scale, temporalUnit) {
|
|
82
|
+
function validateStrokeField(program, dataset, layer, args, scale, temporalUnit) {
|
|
83
83
|
const { field, fieldType } = args;
|
|
84
84
|
validateContinuousColorConsumer(layer, args, scale, { channel: "stroke" });
|
|
85
85
|
if (["line", "area"].includes(layer.mark.type)) {
|
|
86
|
-
|
|
86
|
+
validatePathGroupAppearance(program, {
|
|
87
87
|
...layer,
|
|
88
88
|
encoding: {
|
|
89
89
|
...layer.encoding,
|
|
90
90
|
stroke: { field, fieldType, temporalUnit, scale: scale.id }
|
|
91
91
|
}
|
|
92
|
-
});
|
|
92
|
+
}, dataset);
|
|
93
93
|
}
|
|
94
94
|
if (Object.hasOwn(scale, "unknown")) {
|
|
95
95
|
readScaleField(dataset.values, field, fieldType, {
|
|
@@ -130,6 +130,7 @@ export const encodeStroke = /* @__PURE__ */ action(
|
|
|
130
130
|
MARK_TYPES,
|
|
131
131
|
"stroke-capable mark"
|
|
132
132
|
);
|
|
133
|
+
validateTickColorChannels(layer, "stroke");
|
|
133
134
|
|
|
134
135
|
if (hasValue) {
|
|
135
136
|
const value = validateRuleStroke(args.value);
|
|
@@ -160,8 +161,8 @@ export const encodeStroke = /* @__PURE__ */ action(
|
|
|
160
161
|
const dataset = applyRequestedItemMissingPolicy(layer, sourceDataset, args.field);
|
|
161
162
|
const previous = layer.encoding?.stroke;
|
|
162
163
|
const temporalUnit = resolveTemporalUnit(args, fieldType, previous);
|
|
163
|
-
const requestedScale =
|
|
164
|
-
|
|
164
|
+
const requestedScale = resolveColorReassignmentScaleOptions(
|
|
165
|
+
this, layer, "stroke", fieldType,
|
|
165
166
|
args.scale ?? {}
|
|
166
167
|
);
|
|
167
168
|
let scale;
|
|
@@ -179,7 +180,7 @@ export const encodeStroke = /* @__PURE__ */ action(
|
|
|
179
180
|
readNominalField(dataset.values, args.field);
|
|
180
181
|
}
|
|
181
182
|
if (["line", "area"].includes(layer.mark.type)) {
|
|
182
|
-
|
|
183
|
+
validatePathGroupAppearance(this, {
|
|
183
184
|
...layer,
|
|
184
185
|
encoding: {
|
|
185
186
|
...layer.encoding,
|
|
@@ -189,7 +190,7 @@ export const encodeStroke = /* @__PURE__ */ action(
|
|
|
189
190
|
scale: scale.id
|
|
190
191
|
}
|
|
191
192
|
}
|
|
192
|
-
});
|
|
193
|
+
}, dataset);
|
|
193
194
|
}
|
|
194
195
|
} else if (["quantitative", "temporal"].includes(fieldType)) {
|
|
195
196
|
scale = resolveQuantitativeColorScaleDefinition(
|
|
@@ -199,17 +200,22 @@ export const encodeStroke = /* @__PURE__ */ action(
|
|
|
199
200
|
"stroke"
|
|
200
201
|
);
|
|
201
202
|
validateStrokeField(
|
|
203
|
+
this,
|
|
202
204
|
dataset,
|
|
203
205
|
layer,
|
|
204
206
|
{ ...args, fieldType },
|
|
205
207
|
scale,
|
|
206
|
-
temporalUnit
|
|
208
|
+
temporalUnit,
|
|
209
|
+
this.semanticSpec.scales
|
|
207
210
|
);
|
|
208
211
|
} else {
|
|
209
212
|
throw new Error(`Unsupported stroke field type "${fieldType}".`);
|
|
210
213
|
}
|
|
211
214
|
|
|
212
215
|
let next = withoutConstantStroke(this, layer);
|
|
216
|
+
if (this.markConfigs[target]?.stroke !== undefined || this.markConfigs[target]?.barAppearance?.stroke !== undefined) {
|
|
217
|
+
next = releaseDetachedEncodingScale(next, scale.id);
|
|
218
|
+
}
|
|
213
219
|
next = applyTemporalUnit(next, target, "stroke", temporalUnit, previous);
|
|
214
220
|
next = setEncodingProperties(next, target, "stroke", {
|
|
215
221
|
field: args.field,
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { APPEARANCE_MARK_TYPES } from "../../core/vocabulary.js";
|
|
1
2
|
import { action } from "../../core/action.js";
|
|
2
3
|
import {
|
|
3
4
|
normalizeStrokeDashPattern,
|
|
4
5
|
readNominalField,
|
|
5
|
-
|
|
6
|
+
validateCategoricalFieldType
|
|
6
7
|
} from "../../grammar/scales/index.js";
|
|
7
8
|
import { resolveStrokeDashScaleDefinition } from "../scales/definitions.js";
|
|
8
9
|
import {
|
|
@@ -14,6 +15,7 @@ import {
|
|
|
14
15
|
validateOptions
|
|
15
16
|
} from "./shared.js";
|
|
16
17
|
import { findLayer } from "../../selectors/layers.js";
|
|
18
|
+
import { validateCategoricalColorConsumer } from "../../grammar/scales/colorConsumers.js";
|
|
17
19
|
import { validatePathSeriesAppearance } from "../../grammar/pathSeries.js";
|
|
18
20
|
import { applyRequestedItemMissingPolicy } from
|
|
19
21
|
"../../grammar/itemMissing.js";
|
|
@@ -63,7 +65,7 @@ function reconcileLegendAfterDashRemoval(program, target) {
|
|
|
63
65
|
const encodeStrokeDash = /* @__PURE__ */ action(
|
|
64
66
|
{
|
|
65
67
|
op: "encodeStrokeDash",
|
|
66
|
-
description: "Encode a
|
|
68
|
+
description: "Encode a categorical field as line stroke dash."
|
|
67
69
|
},
|
|
68
70
|
function (args = {}) {
|
|
69
71
|
validateOptions(args, STROKE_DASH_ENCODING_OPTIONS, "encodeStrokeDash");
|
|
@@ -78,7 +80,7 @@ const encodeStrokeDash = /* @__PURE__ */ action(
|
|
|
78
80
|
const { id: target, dataset: sourceDataset, layer } = resolveTarget(
|
|
79
81
|
this,
|
|
80
82
|
args.target,
|
|
81
|
-
|
|
83
|
+
APPEARANCE_MARK_TYPES.encodeStrokeDash,
|
|
82
84
|
"line or rule mark"
|
|
83
85
|
);
|
|
84
86
|
const dataset = hasField
|
|
@@ -88,7 +90,7 @@ const encodeStrokeDash = /* @__PURE__ */ action(
|
|
|
88
90
|
normalizeStrokeDashPattern(args.value);
|
|
89
91
|
validatePathSeriesAppearance(dataset.values, {
|
|
90
92
|
...layer, encoding: { ...layer.encoding, strokeDash: { datum: args.value } }
|
|
91
|
-
});
|
|
93
|
+
}, this.semanticSpec.scales);
|
|
92
94
|
let next = layer.encoding?.strokeDash === undefined
|
|
93
95
|
? this
|
|
94
96
|
: this.clearStrokeDashEncoding({ target });
|
|
@@ -100,20 +102,16 @@ const encodeStrokeDash = /* @__PURE__ */ action(
|
|
|
100
102
|
return rematerializeEncoding(next, target, "strokeDash", undefined, layer);
|
|
101
103
|
}
|
|
102
104
|
|
|
103
|
-
const fieldType =
|
|
105
|
+
const fieldType = validateCategoricalFieldType(args.fieldType ?? "nominal");
|
|
104
106
|
validatePathSeriesAppearance(dataset.values, {
|
|
105
107
|
...layer, encoding: { ...layer.encoding, strokeDash: { field: args.field, fieldType } }
|
|
106
|
-
});
|
|
108
|
+
}, this.semanticSpec.scales);
|
|
107
109
|
const previous = layer.encoding?.strokeDash;
|
|
108
110
|
const requestedScale = previous?.field === args.field
|
|
109
111
|
? resolveReassignmentScaleOptions(previous, args.scale ?? {})
|
|
110
112
|
: args.scale ?? {};
|
|
111
113
|
const scale = resolveStrokeDashScaleDefinition(this, requestedScale);
|
|
112
|
-
|
|
113
|
-
throw new Error(
|
|
114
|
-
"StrokeDash scale unknown is not supported for grouped line or rule paths."
|
|
115
|
-
);
|
|
116
|
-
}
|
|
114
|
+
validateCategoricalColorConsumer(layer, { field: args.field, fieldType }, scale, "strokeDash");
|
|
117
115
|
readNominalField(dataset.values, args.field);
|
|
118
116
|
|
|
119
117
|
let next = previous === undefined
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { action } from "../../core/action.js";
|
|
2
2
|
import { schemaField } from "../../grammar/datasetSchema.js";
|
|
3
|
-
import { formatTextValue, validateTextFormat } from "../../grammar/text.js";
|
|
3
|
+
import { formatTextValue, validateTextFormat, isIndependentText, resolveTextOffsetDataset, normalizeTextOffsetAssignment } from "../../grammar/text.js";
|
|
4
|
+
import { validateUserId } from "../../core/identifiers.js";
|
|
4
5
|
import { normalizeMarkLabelContent, resolveMarkLabelValues } from "../../grammar/markLabels.js";
|
|
5
6
|
import { canMaterializeText } from "../../materialization/marks/index.js";
|
|
6
7
|
import { resolveMarkItems } from "../../materialization/selection/policies/index.js";
|
|
7
|
-
import { findLayer } from "../../selectors/layers.js";
|
|
8
|
+
import { findLayer, resolveEligibleLayer } from "../../selectors/layers.js";
|
|
8
9
|
import {
|
|
9
10
|
resolveTarget,
|
|
10
11
|
setEncodingProperties,
|
|
@@ -90,6 +91,33 @@ export const encodeText = /* @__PURE__ */ action(
|
|
|
90
91
|
}
|
|
91
92
|
);
|
|
92
93
|
|
|
94
|
+
function createTextOffsetEncoding(channel) {
|
|
95
|
+
const operation = channel === "dx" ? "encodeDx" : "encodeDy";
|
|
96
|
+
return action({ op: operation, description: `Assign direct numeric ${channel} pixel offsets to independent Text.` }, function (args = {}) {
|
|
97
|
+
const properties = normalizeTextOffsetAssignment(args, channel);
|
|
98
|
+
const selected = resolveEligibleLayer(this, {
|
|
99
|
+
target: args.target === undefined ? undefined : validateUserId(args.target, "Text offset target"),
|
|
100
|
+
predicate: isIndependentText,
|
|
101
|
+
label: "Independent Text offset"
|
|
102
|
+
});
|
|
103
|
+
const { id: target, layer, dataset } = resolveTarget(this, selected.id, ["text"], "independent Text");
|
|
104
|
+
resolveTextOffsetDataset({ ...layer, encoding: { ...layer.encoding, [channel]: properties } }, dataset);
|
|
105
|
+
let next = this;
|
|
106
|
+
for (const property of ["field", "fieldType", "datum"]) {
|
|
107
|
+
if (Object.hasOwn(layer.encoding?.[channel] ?? {}, property)) {
|
|
108
|
+
next = next.editSemantic({ property: `layer[${target}].encoding.${channel}.${property}`, remove: true });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
next = setEncodingProperties(next, target, channel, properties);
|
|
112
|
+
return canMaterializeText(next, findLayer(next, target)) ? next.rematerializeTextMark({ id: target }) : next;
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export const encodeDx = /* @__PURE__ */ createTextOffsetEncoding("dx");
|
|
117
|
+
export const encodeDy = /* @__PURE__ */ createTextOffsetEncoding("dy");
|
|
118
|
+
|
|
93
119
|
export function registerTextEncodingAction(ProgramClass) {
|
|
94
120
|
ProgramClass.prototype.encodeText = encodeText;
|
|
121
|
+
ProgramClass.prototype.encodeDx = encodeDx;
|
|
122
|
+
ProgramClass.prototype.encodeDy = encodeDy;
|
|
95
123
|
}
|
|
@@ -266,8 +266,8 @@ function updateErrorBarOffsets(program, ids, current, candidate) {
|
|
|
266
266
|
return next;
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
-
function updateErrorBarRoles(program, owner, current, candidate, dataId, capIds) {
|
|
270
|
-
const owned = [owner.id, ...capIds];
|
|
269
|
+
function updateErrorBarRoles(program, owner, current, candidate, dataId, capIds, centerIds) {
|
|
270
|
+
const owned = [owner.id, ...capIds, ...centerIds];
|
|
271
271
|
let next = updateErrorBarOffsets(
|
|
272
272
|
program, owned, current, { ...candidate, offset: undefined }
|
|
273
273
|
);
|
|
@@ -301,6 +301,18 @@ function updateErrorBarRoles(program, owner, current, candidate, dataId, capIds)
|
|
|
301
301
|
}
|
|
302
302
|
});
|
|
303
303
|
}
|
|
304
|
+
for (const id of centerIds) {
|
|
305
|
+
updated = setCartesianPosition(updated, id, categoryChannel, {
|
|
306
|
+
field: candidate.position.field,
|
|
307
|
+
fieldType: candidate.position.fieldType,
|
|
308
|
+
scale: category.scale
|
|
309
|
+
});
|
|
310
|
+
updated = setCartesianPosition(updated, id, measureChannel, {
|
|
311
|
+
field: candidate.fields.center,
|
|
312
|
+
fieldType: "quantitative",
|
|
313
|
+
scale: measure.scale
|
|
314
|
+
});
|
|
315
|
+
}
|
|
304
316
|
return updated;
|
|
305
317
|
}
|
|
306
318
|
});
|
|
@@ -411,7 +423,12 @@ export const editErrorBar = /* @__PURE__ */ closedAction(
|
|
|
411
423
|
const capIds = errorBarCaps(current)
|
|
412
424
|
.map(([id]) => id)
|
|
413
425
|
.filter(id => findLayer(this, id) !== undefined);
|
|
414
|
-
|
|
426
|
+
// createIntervalPlot records the center's ownership explicitly. It must
|
|
427
|
+
// follow the interval revision just like caps and source-owned labels.
|
|
428
|
+
const centerIds = this.semanticSpec.layers.filter(layer =>
|
|
429
|
+
this.markConfigs[layer.id]?.intervalPlot?.intervalId === owner.id
|
|
430
|
+
).map(layer => layer.id);
|
|
431
|
+
const consumers = collectIntervalConsumers(this, [owner.id, ...capIds, ...centerIds]);
|
|
415
432
|
const interval = roleRequested
|
|
416
433
|
? planIntervalRoleData(this, {
|
|
417
434
|
owner: owner.id,
|
|
@@ -442,9 +459,12 @@ export const editErrorBar = /* @__PURE__ */ closedAction(
|
|
|
442
459
|
});
|
|
443
460
|
next = roleRequested
|
|
444
461
|
? updateErrorBarRoles(
|
|
445
|
-
next, owner, current, candidate, interval.dataId, capIds
|
|
462
|
+
next, owner, current, candidate, interval.dataId, capIds, centerIds
|
|
446
463
|
)
|
|
447
464
|
: next.rematerializeErrorBar({ id: owner.id });
|
|
465
|
+
if (interval.changed || roleRequested) {
|
|
466
|
+
for (const id of centerIds) next = next.rematerializePointMark({ id });
|
|
467
|
+
}
|
|
448
468
|
return releaseIntervalRevision(next, interval);
|
|
449
469
|
}
|
|
450
470
|
);
|