ggaction 0.0.21 → 0.0.23
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 +90 -0
- package/README.md +83 -90
- 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/package.json +11 -4
- 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 +3 -3
- 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 +22 -36
- 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/data/timeUnit.js +1 -1
- 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/components.js +1 -4
- 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/regression/create.js +6 -1
- package/src/actions/regression/edit.js +10 -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 +26 -7
- package/src/core/datasetReferences.js +17 -0
- package/src/core/facetAxisPolicy.js +30 -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/curveCommands.js +35 -2
- package/src/grammar/datasetDependencies.js +97 -0
- package/src/grammar/datasetSchema.js +32 -2
- package/src/grammar/facetHeaders.js +1 -0
- package/src/grammar/facets/dependencies.js +6 -2
- package/src/grammar/facets/guides.js +7 -5
- 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/regression/parameters.js +7 -0
- 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/timeUnit.js +9 -3
- 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/guideCollisions.js +9 -3
- 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/facetContentBounds.js +96 -0
- package/src/materialization/facetGuides/layout.js +1 -1
- package/src/materialization/facetGuides/placement.js +39 -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 +30 -1
- 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 +114 -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 +286 -70
- package/types/scene.d.ts +35 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { datasetInputReferences, rebindTransformDatasetInputs } from "../../core/datasetReferences.js";
|
|
1
2
|
import { action } from "../../core/action.js";
|
|
2
3
|
import { validateUserId } from "../../core/identifiers.js";
|
|
3
4
|
import { validateKeys } from "../../core/validation.js";
|
|
@@ -12,6 +13,15 @@ import {
|
|
|
12
13
|
validateRowsAgainstSchema
|
|
13
14
|
} from "../../grammar/datasetSchema.js";
|
|
14
15
|
|
|
16
|
+
export function resolveDatasetTransformInputs(program, transform) {
|
|
17
|
+
const replacements = new Map();
|
|
18
|
+
for (const reference of datasetInputReferences({ transform: [transform] })) {
|
|
19
|
+
const input = resolveDatasetReference(program, reference.id, "Lookup dataset");
|
|
20
|
+
if (input.id !== reference.id) replacements.set(reference.id, input.id);
|
|
21
|
+
}
|
|
22
|
+
return rebindTransformDatasetInputs(transform, replacements);
|
|
23
|
+
}
|
|
24
|
+
|
|
15
25
|
export const MATERIALIZE_OPTIONS = Object.freeze(["id"]);
|
|
16
26
|
|
|
17
27
|
export function requireDerivedDataset(program, id, type) {
|
|
@@ -6,7 +6,7 @@ import { derivedCreator, derivedMaterializer } from "./shared.js";
|
|
|
6
6
|
|
|
7
7
|
const OPTIONS = Object.freeze([
|
|
8
8
|
"id", "source", "field", "unit", "as", "temporalUnit", "timeZone",
|
|
9
|
-
"weekStartsOn", "weekRule"
|
|
9
|
+
"weekStartsOn", "weekRule", "missing"
|
|
10
10
|
]);
|
|
11
11
|
|
|
12
12
|
export const materializeTimeUnitData = /* @__PURE__ */ derivedMaterializer(
|
|
@@ -321,8 +321,10 @@ function rebindAxisOwner(program, channel, axisId, {
|
|
|
321
321
|
});
|
|
322
322
|
}
|
|
323
323
|
const scale = next.resolvedScales[nextScale];
|
|
324
|
+
const previousTicks = next.guideConfigs.axis?.[axisId]?.ticks;
|
|
324
325
|
const ticks = normalizeTickConfig(
|
|
325
|
-
|
|
326
|
+
previousTitle !== nextTitle && previousTicks?.mode === "values"
|
|
327
|
+
? { ...previousTicks, inferredValues: true } : previousTicks,
|
|
326
328
|
scale
|
|
327
329
|
);
|
|
328
330
|
if (ticks !== undefined) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { APPEARANCE_MARK_TYPES } from "../../core/vocabulary.js";
|
|
1
2
|
import { action } from "../../core/action.js";
|
|
2
3
|
import { readQuantitativeField } from "../../grammar/scales/index.js";
|
|
3
4
|
import {
|
|
@@ -34,7 +35,7 @@ export const encodeAngle = /* @__PURE__ */ action(
|
|
|
34
35
|
const { id: target, dataset: sourceDataset, layer } = resolveTarget(
|
|
35
36
|
this,
|
|
36
37
|
args.target,
|
|
37
|
-
|
|
38
|
+
APPEARANCE_MARK_TYPES.encodeAngle,
|
|
38
39
|
"point or Tick mark"
|
|
39
40
|
);
|
|
40
41
|
if (hasValue && !Number.isFinite(args.value)) {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { APPEARANCE_MARK_TYPES } from "../../core/vocabulary.js";
|
|
2
|
+
import { removeEncoding } from "./remove.js";
|
|
1
3
|
import { canMaterializeBar } from "../../materialization/marks/index.js";
|
|
2
4
|
import { action } from "../../core/action.js";
|
|
3
5
|
import { validatePathSeriesAppearance } from "../../grammar/pathSeries.js";
|
|
@@ -16,6 +18,7 @@ import {
|
|
|
16
18
|
} from "../../selectors/layers.js";
|
|
17
19
|
import {
|
|
18
20
|
applyEncodingScale,
|
|
21
|
+
releaseDetachedEncodingScale,
|
|
19
22
|
applyDetachedScaleRematerialization,
|
|
20
23
|
clearMarkGraphic,
|
|
21
24
|
rematerializeEncoding,
|
|
@@ -38,7 +41,7 @@ function encodeAppearanceField(program, channel, args, operation) {
|
|
|
38
41
|
const { id: target, dataset: sourceDataset, layer } = resolveTarget(
|
|
39
42
|
program,
|
|
40
43
|
args.target,
|
|
41
|
-
[
|
|
44
|
+
APPEARANCE_MARK_TYPES[operation],
|
|
42
45
|
"point mark"
|
|
43
46
|
);
|
|
44
47
|
const fieldTypes = channel === "shape" ? ["nominal"] : ["quantitative", "nominal", "ordinal"];
|
|
@@ -47,9 +50,7 @@ function encodeAppearanceField(program, channel, args, operation) {
|
|
|
47
50
|
if (!fieldTypes.includes(fieldType)) {
|
|
48
51
|
throw new Error(`${operation} requires a ${fieldTypes.join(" or ")} field.`);
|
|
49
52
|
}
|
|
50
|
-
|
|
51
|
-
throw new Error("encodeSize cannot be combined with a constant radius.");
|
|
52
|
-
}
|
|
53
|
+
|
|
53
54
|
const previous = layer.encoding?.[channel];
|
|
54
55
|
const requestedScale = resolveReassignmentScaleOptions(
|
|
55
56
|
previous,
|
|
@@ -65,7 +66,12 @@ function encodeAppearanceField(program, channel, args, operation) {
|
|
|
65
66
|
allowUnknown: Object.hasOwn(scale, "unknown")
|
|
66
67
|
});
|
|
67
68
|
|
|
68
|
-
|
|
69
|
+
const baseline = channel === "size"
|
|
70
|
+
? program._withoutMaterializationConfig(["marks", target, "radius"])
|
|
71
|
+
: program;
|
|
72
|
+
const prepared = channel === "size" && program.markConfigs[target]?.radius !== undefined
|
|
73
|
+
? releaseDetachedEncodingScale(baseline, scale.id) : baseline;
|
|
74
|
+
let next = setEncodingProperties(prepared, target, channel, {
|
|
69
75
|
field: args.field,
|
|
70
76
|
fieldType,
|
|
71
77
|
scale: scale.id
|
|
@@ -146,9 +152,26 @@ const removePointRadius = /* @__PURE__ */ action(
|
|
|
146
152
|
const encodeSize = /* @__PURE__ */ action(
|
|
147
153
|
{
|
|
148
154
|
op: "encodeSize",
|
|
149
|
-
description: "
|
|
155
|
+
description: "Assign constant or field-driven equal-area point size."
|
|
150
156
|
},
|
|
151
157
|
function (args = {}) {
|
|
158
|
+
validateOptions(args, [...FIELD_OPTIONS, "value"], "encodeSize");
|
|
159
|
+
const hasValue = Object.hasOwn(args, "value");
|
|
160
|
+
if (hasValue === Object.hasOwn(args, "field")) {
|
|
161
|
+
throw new Error("encodeSize requires exactly one of value or field.");
|
|
162
|
+
}
|
|
163
|
+
if (hasValue) {
|
|
164
|
+
if (args.fieldType !== undefined || args.scale !== undefined) {
|
|
165
|
+
throw new Error("Constant size does not accept fieldType or scale.");
|
|
166
|
+
}
|
|
167
|
+
if (!Number.isFinite(args.value) || args.value < 0) {
|
|
168
|
+
throw new RangeError("encodeSize requires a non-negative finite area.");
|
|
169
|
+
}
|
|
170
|
+
const { id: target, layer } = resolveTarget(this, args.target, APPEARANCE_MARK_TYPES.encodeSize, "point mark");
|
|
171
|
+
const next = layer.encoding?.size === undefined ? this
|
|
172
|
+
: removeEncoding.call(this, { target, channel: "size" });
|
|
173
|
+
return next.encodeRadius({ target, value: Math.sqrt(args.value / Math.PI) });
|
|
174
|
+
}
|
|
152
175
|
return encodeAppearanceField(this, "size", args, "encodeSize");
|
|
153
176
|
}
|
|
154
177
|
);
|
|
@@ -207,8 +230,8 @@ const encodeOpacity = /* @__PURE__ */ action(
|
|
|
207
230
|
const { id: target, dataset: sourceDataset, layer } = resolveTarget(
|
|
208
231
|
this,
|
|
209
232
|
args.target,
|
|
210
|
-
|
|
211
|
-
"point, rule, line or
|
|
233
|
+
APPEARANCE_MARK_TYPES.encodeOpacity,
|
|
234
|
+
"point, rule, line, bar or rect mark"
|
|
212
235
|
);
|
|
213
236
|
if (hasValue) {
|
|
214
237
|
validateOpacityValue(args.value, "encodeOpacity");
|
|
@@ -221,7 +244,7 @@ const encodeOpacity = /* @__PURE__ */ action(
|
|
|
221
244
|
.clearOpacityEncoding({ target })
|
|
222
245
|
._withoutMaterializationConfig(["marks", target, "opacity"])
|
|
223
246
|
._withMarkConfig(target, { ...config, opacity: args.value });
|
|
224
|
-
if (layer.mark.type
|
|
247
|
+
if (["line", "rect"].includes(layer.mark.type)) {
|
|
225
248
|
return rematerializeEncoding(next, target, "opacity", undefined, layer);
|
|
226
249
|
}
|
|
227
250
|
if (layer.mark.type === "bar") {
|
|
@@ -245,13 +268,13 @@ const encodeOpacity = /* @__PURE__ */ action(
|
|
|
245
268
|
args.scale ?? {}
|
|
246
269
|
);
|
|
247
270
|
const scale = resolveOpacityScaleDefinition(this, requestedScale);
|
|
248
|
-
if (layer.mark.type
|
|
271
|
+
if (["line", "rect"].includes(layer.mark.type)) {
|
|
249
272
|
if (Object.hasOwn(scale, "unknown")) {
|
|
250
|
-
throw new Error("Line opacity
|
|
273
|
+
throw new Error("Line and Rect opacity do not support unknown fallback.");
|
|
251
274
|
}
|
|
252
275
|
validatePathSeriesAppearance(dataset.values, {
|
|
253
276
|
...layer, encoding: { ...layer.encoding, opacity: { field: args.field, fieldType } }
|
|
254
|
-
});
|
|
277
|
+
}, this.semanticSpec.scales);
|
|
255
278
|
}
|
|
256
279
|
readScaleField(dataset.values, args.field, fieldType, {
|
|
257
280
|
allowUnknown: Object.hasOwn(scale, "unknown")
|
|
@@ -259,7 +282,9 @@ const encodeOpacity = /* @__PURE__ */ action(
|
|
|
259
282
|
const { opacity, ...config } = this.markConfigs[target] ?? {};
|
|
260
283
|
void opacity;
|
|
261
284
|
let next = setEncodingProperties(
|
|
262
|
-
|
|
285
|
+
this.markConfigs[target]?.opacity !== undefined || this.markConfigs[target]?.barAppearance?.opacity !== undefined
|
|
286
|
+
? releaseDetachedEncodingScale(withoutConstantOpacity(this, target), scale.id)
|
|
287
|
+
: withoutConstantOpacity(this, target),
|
|
263
288
|
target,
|
|
264
289
|
"opacity",
|
|
265
290
|
{ field: args.field, fieldType, scale: scale.id }
|
|
@@ -24,13 +24,14 @@ import {
|
|
|
24
24
|
import { buildMaterializationPlan } from "../../materialization/planner.js";
|
|
25
25
|
import { hasMaterializedLegend } from "../../materialization/legends.js";
|
|
26
26
|
import { rebindPositionGuides } from "./shared.js";
|
|
27
|
-
import { withoutPreviewLayerEncodings } from
|
|
27
|
+
import { withoutPreviewLayerEncodings, withPreviewLayerEncodings } from
|
|
28
28
|
"../primitives/semanticAction.js";
|
|
29
|
+
import { isIndependentText, normalizeTextOffsetAssignment } from "../../grammar/text.js";
|
|
29
30
|
|
|
30
31
|
export const ENCODING_CHANNEL_ORDER = Object.freeze([
|
|
31
32
|
"x", "y", "x2", "y2", "theta", "r", "xOffset", "yOffset",
|
|
32
33
|
"group", "pathOrder", "color", "stroke", "size", "shape",
|
|
33
|
-
"opacity", "strokeWidth", "strokeDash", "angle", "text"
|
|
34
|
+
"opacity", "strokeWidth", "strokeDash", "angle", "text", "dx", "dy"
|
|
34
35
|
]);
|
|
35
36
|
|
|
36
37
|
const CHANNEL_METHODS = Object.freeze({
|
|
@@ -52,7 +53,9 @@ const CHANNEL_METHODS = Object.freeze({
|
|
|
52
53
|
strokeWidth: "encodeStrokeWidth",
|
|
53
54
|
strokeDash: "encodeStrokeDash",
|
|
54
55
|
angle: "encodeAngle",
|
|
55
|
-
text: "encodeText"
|
|
56
|
+
text: "encodeText",
|
|
57
|
+
dx: "encodeDx",
|
|
58
|
+
dy: "encodeDy"
|
|
56
59
|
});
|
|
57
60
|
|
|
58
61
|
const FORBIDDEN_PAYLOAD_KEYS = Object.freeze(["target", "coordinate", "id"]);
|
|
@@ -340,6 +343,14 @@ function applyEncodingDraft(program, target, requests) {
|
|
|
340
343
|
target,
|
|
341
344
|
requests
|
|
342
345
|
);
|
|
346
|
+
if (isIndependentText(findLayer(draft, target))) {
|
|
347
|
+
const offsets = requests.filter(request => ["dx", "dy"].includes(request.channel));
|
|
348
|
+
if (offsets.length) {
|
|
349
|
+
draft = withPreviewLayerEncodings(draft, { id: target, encodings: Object.fromEntries(
|
|
350
|
+
offsets.map(({ channel, payload }) => [channel, normalizeTextOffsetAssignment(payload, channel)])
|
|
351
|
+
) });
|
|
352
|
+
}
|
|
353
|
+
}
|
|
343
354
|
for (const { channel, payload } of requests) {
|
|
344
355
|
const focusedAction = draft[CHANNEL_METHODS[channel]];
|
|
345
356
|
draft = invokeWrappedActionImplementation(
|
|
@@ -5,13 +5,14 @@ import {
|
|
|
5
5
|
resolveTemporalUnit
|
|
6
6
|
} from "../../../grammar/scales/index.js";
|
|
7
7
|
import { validateContinuousColorConsumer } from "../../../grammar/scales/colorConsumers.js";
|
|
8
|
-
import {
|
|
8
|
+
import { validatePathGroupAppearance } from "../../../materialization/marks/grouping.js";
|
|
9
|
+
import { readArcColorValues } from "../../../grammar/arcs.js";
|
|
9
10
|
import { validateAggregateFieldValues } from "../../../grammar/aggregate.js";
|
|
10
11
|
import {
|
|
11
12
|
resolveQuantitativeColorScaleDefinition
|
|
12
13
|
} from "../../scales/definitions.js";
|
|
13
14
|
import {
|
|
14
|
-
|
|
15
|
+
releaseDetachedEncodingScale,
|
|
15
16
|
resolveTarget
|
|
16
17
|
} from "../shared.js";
|
|
17
18
|
import {
|
|
@@ -23,7 +24,9 @@ import {
|
|
|
23
24
|
} from "../../../materialization/encodings.js";
|
|
24
25
|
import {
|
|
25
26
|
assertNoConstantColor,
|
|
26
|
-
|
|
27
|
+
withoutConstantColor,
|
|
28
|
+
resolveColorScaleOptions,
|
|
29
|
+
resolveColorReassignmentScaleOptions
|
|
27
30
|
} from "./policy.js";
|
|
28
31
|
|
|
29
32
|
import { applyTemporalUnit } from "../temporal.js";
|
|
@@ -40,14 +43,15 @@ export function encodeContinuousColor(program, args) {
|
|
|
40
43
|
const { id: target, dataset: sourceDataset, layer } = resolveTarget(
|
|
41
44
|
program,
|
|
42
45
|
args.target,
|
|
43
|
-
["point", "bar", "rect", "text", "area"],
|
|
46
|
+
["point", "line", "bar", "rect", "text", "area", "arc", "tick"],
|
|
44
47
|
"continuous color mark"
|
|
45
48
|
);
|
|
46
49
|
const dataset = applyRequestedItemMissingPolicy(layer, sourceDataset, args.field);
|
|
47
|
-
|
|
50
|
+
const baseline = withoutConstantColor(program, layer);
|
|
51
|
+
assertNoConstantColor(baseline, layer);
|
|
48
52
|
const temporalUnit = resolveTemporalUnit(args, args.fieldType, layer.encoding?.color);
|
|
49
|
-
const requestedScale =
|
|
50
|
-
layer
|
|
53
|
+
const requestedScale = resolveColorReassignmentScaleOptions(
|
|
54
|
+
program, layer, "color", args.fieldType,
|
|
51
55
|
resolveColorScaleOptions(args)
|
|
52
56
|
);
|
|
53
57
|
const scale = resolveQuantitativeColorScaleDefinition(
|
|
@@ -56,9 +60,15 @@ export function encodeContinuousColor(program, args) {
|
|
|
56
60
|
requestedScale
|
|
57
61
|
);
|
|
58
62
|
const aggregate = validateContinuousColorConsumer(layer, args, scale, { inferAggregate: true });
|
|
59
|
-
if (layer.mark.type
|
|
60
|
-
|
|
63
|
+
if (["line", "area"].includes(layer.mark.type)) {
|
|
64
|
+
validatePathGroupAppearance(program, {
|
|
61
65
|
...layer, encoding: { ...layer.encoding, color: { field: args.field, fieldType: args.fieldType } }
|
|
66
|
+
}, dataset);
|
|
67
|
+
}
|
|
68
|
+
if (layer.mark.type === "arc") {
|
|
69
|
+
readArcColorValues(dataset.values, {
|
|
70
|
+
...layer, encoding: { ...layer.encoding,
|
|
71
|
+
color: { field: args.field, fieldType: args.fieldType, temporalUnit } }
|
|
62
72
|
});
|
|
63
73
|
}
|
|
64
74
|
if (aggregate !== undefined) validateAggregateFieldValues(dataset.values, args.field, args.fieldType);
|
|
@@ -75,7 +85,8 @@ export function encodeContinuousColor(program, args) {
|
|
|
75
85
|
} else {
|
|
76
86
|
readQuantitativeField(dataset.values, args.field);
|
|
77
87
|
}
|
|
78
|
-
const
|
|
88
|
+
const prepared = baseline === program ? baseline : releaseDetachedEncodingScale(baseline, scale.id);
|
|
89
|
+
let next = applyTemporalUnit(prepared, target, "color", temporalUnit, layer.encoding?.color)
|
|
79
90
|
.editSemantic({
|
|
80
91
|
property: `layer[${target}].encoding.color.field`,
|
|
81
92
|
value: args.field
|
|
@@ -88,6 +99,11 @@ export function encodeContinuousColor(program, args) {
|
|
|
88
99
|
property: `layer[${target}].encoding.color.scale`,
|
|
89
100
|
value: scale.id
|
|
90
101
|
});
|
|
102
|
+
if (layer.mark.type === "tick") {
|
|
103
|
+
const config = { ...next.markConfigs[target] };
|
|
104
|
+
delete config.stroke;
|
|
105
|
+
next = next._withMarkConfig(target, config);
|
|
106
|
+
}
|
|
91
107
|
const encoded = aggregate === undefined
|
|
92
108
|
? next
|
|
93
109
|
: next.editSemantic({
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { findRaincloudParent } from "../../../selectors/raincloud.js";
|
|
2
|
+
import { APPEARANCE_MARK_TYPES } from "../../../core/vocabulary.js";
|
|
3
|
+
import { removeEncoding } from "../remove.js";
|
|
4
|
+
import { validateNonEmptyString } from "../../../core/validation.js";
|
|
1
5
|
import { inferSeriesGroup } from "../shared.js";
|
|
2
6
|
import { action } from "../../../core/action.js";
|
|
3
7
|
import {
|
|
@@ -12,7 +16,7 @@ import {
|
|
|
12
16
|
import { resolveColorScaleDefinition } from "../../scales/definitions.js";
|
|
13
17
|
import {
|
|
14
18
|
applyEncodingScale,
|
|
15
|
-
|
|
19
|
+
releaseDetachedEncodingScale,
|
|
16
20
|
resolveTarget,
|
|
17
21
|
validateOptions
|
|
18
22
|
} from "../shared.js";
|
|
@@ -26,15 +30,18 @@ import {
|
|
|
26
30
|
import { findUpstreamTransform } from
|
|
27
31
|
"../../../materialization/dataProvenance.js";
|
|
28
32
|
import { validatePathSeriesAppearance } from "../../../grammar/pathSeries.js";
|
|
33
|
+
import { readCategoricalLineColor, validateCategoricalColorConsumer } from "../../../grammar/scales/colorConsumers.js";
|
|
29
34
|
import { encodeContinuousColor } from "./continuous.js";
|
|
30
35
|
import {
|
|
31
36
|
applyColorLayoutCompanion
|
|
32
37
|
} from "./layout.js";
|
|
33
38
|
import {
|
|
34
39
|
assertNoConstantColor,
|
|
40
|
+
withoutConstantColor,
|
|
35
41
|
COLOR_ENCODING_OPTIONS,
|
|
36
42
|
resolveColorLayout,
|
|
37
|
-
resolveColorScaleOptions
|
|
43
|
+
resolveColorScaleOptions,
|
|
44
|
+
resolveColorReassignmentScaleOptions
|
|
38
45
|
} from "./policy.js";
|
|
39
46
|
|
|
40
47
|
import { applyTemporalUnit } from "../temporal.js";
|
|
@@ -44,10 +51,32 @@ import { applyRequestedItemMissingPolicy } from
|
|
|
44
51
|
const encodeColor = /* @__PURE__ */ action(
|
|
45
52
|
{
|
|
46
53
|
op: "encodeColor",
|
|
47
|
-
description: "
|
|
54
|
+
description: "Assign constant or field-driven graphical color."
|
|
48
55
|
},
|
|
49
56
|
function (args = {}) {
|
|
50
57
|
validateOptions(args, COLOR_ENCODING_OPTIONS, "encodeColor");
|
|
58
|
+
const hasValue = Object.hasOwn(args, "value");
|
|
59
|
+
if (hasValue === Object.hasOwn(args, "field")) {
|
|
60
|
+
throw new Error("encodeColor requires exactly one of value or field.");
|
|
61
|
+
}
|
|
62
|
+
const raincloud = findRaincloudParent(this, args.target);
|
|
63
|
+
if (raincloud !== undefined) {
|
|
64
|
+
const { target, ...color } = args;
|
|
65
|
+
return this.editRaincloudPlot({ target, color });
|
|
66
|
+
}
|
|
67
|
+
if (hasValue) {
|
|
68
|
+
if (COLOR_ENCODING_OPTIONS.some(key => !["target", "value"].includes(key) && args[key] !== undefined)) {
|
|
69
|
+
throw new Error("Constant color accepts only value and target.");
|
|
70
|
+
}
|
|
71
|
+
const value = validateNonEmptyString(args.value, "Color value");
|
|
72
|
+
const { id: target, layer } = resolveTarget(this, args.target,
|
|
73
|
+
APPEARANCE_MARK_TYPES.encodeColor, "color mark");
|
|
74
|
+
const next = layer.encoding?.color === undefined ? this
|
|
75
|
+
: removeEncoding.call(this, { target, channel: "color" });
|
|
76
|
+
const method = { point: "editPointMark", line: "editLineMark", bar: "editBarMark",
|
|
77
|
+
area: "editAreaMark", arc: "editArcMark", rect: "editRectMark", text: "editTextMark" }[layer.mark.type];
|
|
78
|
+
return next[method]({ target, [layer.mark.type === "line" ? "stroke" : "fill"]: value });
|
|
79
|
+
}
|
|
51
80
|
const requestedFieldType = args.fieldType ?? "nominal";
|
|
52
81
|
if (!["nominal", "ordinal"].includes(requestedFieldType)) {
|
|
53
82
|
return encodeContinuousColor(this, {
|
|
@@ -63,11 +92,12 @@ const encodeColor = /* @__PURE__ */ action(
|
|
|
63
92
|
const { id: target, dataset: sourceDataset, layer } = resolveTarget(
|
|
64
93
|
this,
|
|
65
94
|
args.target,
|
|
66
|
-
|
|
95
|
+
APPEARANCE_MARK_TYPES.encodeColor,
|
|
67
96
|
"color mark"
|
|
68
97
|
);
|
|
69
98
|
const dataset = applyRequestedItemMissingPolicy(layer, sourceDataset, args.field);
|
|
70
|
-
|
|
99
|
+
const baseline = withoutConstantColor(this, layer);
|
|
100
|
+
assertNoConstantColor(baseline, layer);
|
|
71
101
|
const densityTransform = dataset.transform?.length === 1 &&
|
|
72
102
|
dataset.transform[0].type === "density"
|
|
73
103
|
? dataset.transform[0]
|
|
@@ -86,7 +116,7 @@ const encodeColor = /* @__PURE__ */ action(
|
|
|
86
116
|
"Bar color encoding requires a complete histogram encoding or a complete ordinal aggregate encoding."
|
|
87
117
|
);
|
|
88
118
|
}
|
|
89
|
-
const layout = resolveColorLayout(layer, args.layout, barGrain);
|
|
119
|
+
const layout = resolveColorLayout(layer, args.layout, barGrain, args.field);
|
|
90
120
|
const createsCenterGroup = layer.mark.type === "area" &&
|
|
91
121
|
layout === "center" && layer.encoding?.group === undefined;
|
|
92
122
|
const ordinaryGroup = layer.mark.type === "area" && !createsCenterGroup &&
|
|
@@ -104,22 +134,21 @@ const encodeColor = /* @__PURE__ */ action(
|
|
|
104
134
|
"Area color encoding must match an existing group encoding."
|
|
105
135
|
);
|
|
106
136
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
...layer, encoding: { ...layer.encoding, color: { field: args.field, fieldType } }
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
const requestedScale = resolveReassignmentScaleOptions(
|
|
113
|
-
layer.encoding?.color,
|
|
137
|
+
const requestedScale = resolveColorReassignmentScaleOptions(
|
|
138
|
+
this, layer, "color", fieldType,
|
|
114
139
|
resolveColorScaleOptions(args)
|
|
115
140
|
);
|
|
116
141
|
const scale = resolveColorScaleDefinition(this, requestedScale);
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
142
|
+
const color = { field: args.field, fieldType, scale: scale.id };
|
|
143
|
+
const lineFallback = validateCategoricalColorConsumer(layer, color, scale);
|
|
144
|
+
if (layer.mark.type === "line" || ordinaryGroup) {
|
|
145
|
+
validatePathSeriesAppearance(dataset.values, {
|
|
146
|
+
...layer, encoding: { ...layer.encoding, color }
|
|
147
|
+
}, [...this.semanticSpec.scales.filter(item => item.id !== scale.id), scale]);
|
|
121
148
|
}
|
|
122
|
-
if (
|
|
149
|
+
if (lineFallback) {
|
|
150
|
+
readCategoricalLineColor(dataset.values, args.field);
|
|
151
|
+
} else if (layer.mark.type === "rect") {
|
|
123
152
|
readScaleField(dataset.values, args.field, fieldType, {
|
|
124
153
|
allowUnknown: true
|
|
125
154
|
});
|
|
@@ -131,9 +160,10 @@ const encodeColor = /* @__PURE__ */ action(
|
|
|
131
160
|
readNominalField(dataset.values, args.field);
|
|
132
161
|
}
|
|
133
162
|
|
|
163
|
+
const prepared = baseline === this ? baseline : releaseDetachedEncodingScale(baseline, scale.id);
|
|
134
164
|
let next = layer.mark.type === "bar" || createsCenterGroup
|
|
135
|
-
? inferSeriesGroup(
|
|
136
|
-
:
|
|
165
|
+
? inferSeriesGroup(prepared, layer, args.field, "color")
|
|
166
|
+
: prepared;
|
|
137
167
|
next = applyTemporalUnit(next, target, "color", undefined, layer.encoding?.color).editSemantic({
|
|
138
168
|
property: `layer[${target}].encoding.color.field`,
|
|
139
169
|
value: args.field
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
BAR_GRAINS,
|
|
3
|
-
inferBarColorLayout
|
|
3
|
+
inferBarColorLayout,
|
|
4
|
+
resolveBarChannels
|
|
4
5
|
} from "../../../grammar/bars/policy.js";
|
|
5
6
|
import { validateColorLayout } from "../../../grammar/seriesLayout.js";
|
|
7
|
+
import { findScaleConsumers } from "../../scales/consumers/common.js";
|
|
8
|
+
import { resolveReassignmentScaleOptions } from "../shared.js";
|
|
6
9
|
|
|
7
10
|
export const COLOR_ENCODING_OPTIONS = Object.freeze([
|
|
8
|
-
"field", "target", "fieldType", "scale", "palette", "layout", "aggregate", "temporalUnit"
|
|
11
|
+
"value", "field", "target", "fieldType", "scale", "palette", "layout", "aggregate", "temporalUnit"
|
|
9
12
|
]);
|
|
10
13
|
|
|
11
14
|
export function resolveColorScaleOptions(args) {
|
|
@@ -19,6 +22,50 @@ export function resolveColorScaleOptions(args) {
|
|
|
19
22
|
return { ...options, palette: args.palette };
|
|
20
23
|
}
|
|
21
24
|
|
|
25
|
+
export function resolveColorReassignmentScaleOptions(
|
|
26
|
+
program, layer, channel, fieldType, options = {}
|
|
27
|
+
) {
|
|
28
|
+
const previous = layer.encoding?.[channel];
|
|
29
|
+
const requested = resolveReassignmentScaleOptions(previous, options);
|
|
30
|
+
if (options.id !== undefined || previous?.scale === undefined) return requested;
|
|
31
|
+
const categorical = type => ["nominal", "ordinal"].includes(type);
|
|
32
|
+
if (categorical(previous.fieldType) === categorical(fieldType)) return requested;
|
|
33
|
+
const otherConsumer = consumer => consumer.layer.id !== layer.id ||
|
|
34
|
+
consumer.channel !== channel;
|
|
35
|
+
if (!findScaleConsumers(program, previous.scale).some(otherConsumer)) return requested;
|
|
36
|
+
// Re-encoding one mark must not change the scale family of its siblings.
|
|
37
|
+
// Explicit scale IDs still opt into normal shared-resource validation.
|
|
38
|
+
const id = `${layer.id}-${channel}-${fieldType}`;
|
|
39
|
+
if (findScaleConsumers(program, id).some(otherConsumer)) {
|
|
40
|
+
throw new Error(`Color reassignment requires scale.id because "${id}" has other consumers.`);
|
|
41
|
+
}
|
|
42
|
+
return { ...options, id };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function withoutConstantColor(program, layer) {
|
|
46
|
+
const previous = program.markConfigs[layer.id];
|
|
47
|
+
const explicit = { point: previous?.fill !== undefined, line: previous?.stroke !== undefined,
|
|
48
|
+
bar: previous?.barAppearance?.fill !== undefined, area: previous?.errorBand?.fill !== undefined,
|
|
49
|
+
arc: previous?.fill !== undefined, rect: previous?.fillExplicit === true,
|
|
50
|
+
text: previous?.fillExplicit === true }[layer.mark.type];
|
|
51
|
+
if (!explicit) return program;
|
|
52
|
+
const config = { ...previous };
|
|
53
|
+
if (layer.mark.type === "bar") {
|
|
54
|
+
config.barAppearance = { ...config.barAppearance };
|
|
55
|
+
delete config.barAppearance.fill;
|
|
56
|
+
} else if (layer.mark.type === "line") {
|
|
57
|
+
delete config.stroke;
|
|
58
|
+
} else {
|
|
59
|
+
if (["point", "arc"].includes(layer.mark.type)) delete config.fill;
|
|
60
|
+
if (["rect", "text"].includes(layer.mark.type)) config.fillExplicit = false;
|
|
61
|
+
if (config.errorBand !== undefined) {
|
|
62
|
+
config.errorBand = { ...config.errorBand };
|
|
63
|
+
delete config.errorBand.fill;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return program._withMarkConfig(layer.id, config);
|
|
67
|
+
}
|
|
68
|
+
|
|
22
69
|
export function assertNoConstantColor(program, layer) {
|
|
23
70
|
if (layer.mark.type === "text" && layer.source !== undefined) {
|
|
24
71
|
throw new Error("Source-owned text uses inheritColor or constant fill; field color requires row-backed text.");
|
|
@@ -47,7 +94,13 @@ export function isRangedArea(layer) {
|
|
|
47
94
|
);
|
|
48
95
|
}
|
|
49
96
|
|
|
50
|
-
export function resolveColorLayout(layer, requested, barGrain) {
|
|
97
|
+
export function resolveColorLayout(layer, requested, barGrain, field) {
|
|
98
|
+
const category = layer.encoding?.[resolveBarChannels(layer)?.category];
|
|
99
|
+
// Coloring the category does not introduce another partition. An explicit
|
|
100
|
+
// group or a stored/requested layout still owns series placement.
|
|
101
|
+
const categoryColorOnly = barGrain === BAR_GRAINS.aggregate &&
|
|
102
|
+
["nominal", "ordinal"].includes(category?.fieldType) &&
|
|
103
|
+
category.field === field && layer.encoding?.group === undefined;
|
|
51
104
|
const existing = layer.encoding?.color === undefined
|
|
52
105
|
? undefined
|
|
53
106
|
: layer.encoding.color.layout ?? (
|
|
@@ -62,7 +115,7 @@ export function resolveColorLayout(layer, requested, barGrain) {
|
|
|
62
115
|
layer.mark.type === "bar"
|
|
63
116
|
? barGrain === BAR_GRAINS.histogram
|
|
64
117
|
? "stack"
|
|
65
|
-
: [BAR_GRAINS.ranged, BAR_GRAINS.centered].includes(barGrain) ? "overlay" : "group"
|
|
118
|
+
: [BAR_GRAINS.ranged, BAR_GRAINS.centered].includes(barGrain) || categoryColorOnly ? "overlay" : "group"
|
|
66
119
|
: layer.mark.type === "area"
|
|
67
120
|
? "overlay"
|
|
68
121
|
: undefined
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { closedAction } from "../../core/action.js";
|
|
2
|
+
import { validateMarkClipCoordinate } from "../../selectors/markClip.js";
|
|
3
|
+
import { validateLineMissingScope } from "../../grammar/lineMissing.js";
|
|
2
4
|
|
|
3
5
|
import {
|
|
4
6
|
normalizeParallelDimensions,
|
|
@@ -31,6 +33,7 @@ export const encodeParallelCoordinates = /* @__PURE__ */ closedAction(
|
|
|
31
33
|
["line"],
|
|
32
34
|
"line mark"
|
|
33
35
|
);
|
|
36
|
+
validateLineMissingScope({ ...layer, encoding: { ...layer.encoding, parallel: {} } });
|
|
34
37
|
if (
|
|
35
38
|
["x", "y", "theta", "radius"].some(
|
|
36
39
|
channel => layer.encoding?.[channel] !== undefined
|
|
@@ -45,6 +48,7 @@ export const encodeParallelCoordinates = /* @__PURE__ */ closedAction(
|
|
|
45
48
|
layer,
|
|
46
49
|
operation: "encodeParallelCoordinates"
|
|
47
50
|
});
|
|
51
|
+
validateMarkClipCoordinate(this, layer, this.markConfigs[target]?.clip, "parallel");
|
|
48
52
|
const dimensions = normalizeParallelDimensions(
|
|
49
53
|
dataset.values,
|
|
50
54
|
args.dimensions,
|
|
@@ -50,6 +50,9 @@ function resolveBarChannelPolicy({
|
|
|
50
50
|
const weight = args.weight === undefined
|
|
51
51
|
? undefined
|
|
52
52
|
: normalizeStatisticalWeight(args.weight, "Histogram weight");
|
|
53
|
+
if (weight !== undefined && opposite?.aggregate !== undefined && opposite.aggregate !== "count") {
|
|
54
|
+
throw new Error("Binned bar weights require count aggregation.");
|
|
55
|
+
}
|
|
53
56
|
return { bin, aggregate, stack, weight };
|
|
54
57
|
} else if (
|
|
55
58
|
fieldType === "quantitative" &&
|
|
@@ -59,14 +62,15 @@ function resolveBarChannelPolicy({
|
|
|
59
62
|
if (args.bin !== undefined) {
|
|
60
63
|
throw new Error("Histogram bar y encoding does not support bin.");
|
|
61
64
|
}
|
|
62
|
-
|
|
63
|
-
|
|
65
|
+
aggregate = validateAggregate(args.aggregate ?? "count");
|
|
66
|
+
validateAggregateFieldType(aggregate, fieldType);
|
|
67
|
+
if (aggregate === "count" && field !== xEncoding.field) {
|
|
68
|
+
throw new Error("Count bar y field must match the binned x field.");
|
|
64
69
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if (aggregate !== "count") {
|
|
68
|
-
throw new Error('Histogram bar y aggregate must be "count".');
|
|
70
|
+
if (aggregate !== "count" && xEncoding.weight !== undefined) {
|
|
71
|
+
throw new Error("Binned bar weights require count aggregation.");
|
|
69
72
|
}
|
|
73
|
+
stack = Object.hasOwn(args, "stack") ? args.stack : aggregate === "count" ? "zero" : null;
|
|
70
74
|
stack = validateStack(stack, "Histogram bar y encoding");
|
|
71
75
|
if (stack === "center") {
|
|
72
76
|
throw new Error("Centered bars are not supported.");
|