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
|
@@ -1,425 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { validateUserId } from "../../core/identifiers.js";
|
|
3
|
-
import { isPlainObject } from "../../core/immutable.js";
|
|
4
|
-
import { validateKeys } from "../../core/validation.js";
|
|
5
|
-
import {
|
|
6
|
-
validateColorRange,
|
|
7
|
-
validateContinuousColorInterpolation,
|
|
8
|
-
validateSequentialMidpoint,
|
|
9
|
-
validateLinearScaleType,
|
|
10
|
-
validateOpacityRange,
|
|
11
|
-
validateOrdinalDomain,
|
|
12
|
-
validateOrdinalScaleType,
|
|
13
|
-
validateScaleDomain,
|
|
14
|
-
validateScaleRange,
|
|
15
|
-
validateShapeRange,
|
|
16
|
-
validateStrokeDashRange,
|
|
17
|
-
validateStrokeWidthRange,
|
|
18
|
-
validateSequentialColorRange,
|
|
19
|
-
validateDiscretizedColorDomain,
|
|
20
|
-
validateDiscretizedColorRange,
|
|
21
|
-
validateTimeScaleType,
|
|
22
|
-
normalizeScaleDefinition,
|
|
23
|
-
SCALE_ROLES,
|
|
24
|
-
validateScalePropertyForType,
|
|
25
|
-
validateScaleTypeForRole,
|
|
26
|
-
isDiscretePositionScaleType,
|
|
27
|
-
normalizeSizeScaleDefinition,
|
|
28
|
-
withScaleUnknown
|
|
29
|
-
} from "../../grammar/scales/index.js";
|
|
30
|
-
import {
|
|
31
|
-
validateRadialRange,
|
|
32
|
-
validateThetaRange
|
|
33
|
-
} from "../../grammar/polar.js";
|
|
34
|
-
import { findSemanticScale } from "../../selectors/scales.js";
|
|
35
|
-
import { normalizeOffsetScalePolicy } from "../../grammar/bars/geometry.js";
|
|
36
|
-
import { resolveRequestedOffsetPolicy } from
|
|
37
|
-
"../../materialization/scales/policies/offset.js";
|
|
38
|
-
import { findScaleConsumers } from "./consumers/index.js";
|
|
39
|
-
|
|
40
|
-
const BASE_OPTIONS = ["id", "type", "domain", "range"];
|
|
41
|
-
const UNKNOWN_OPTIONS = [...BASE_OPTIONS, "unknown"];
|
|
42
|
-
const CLAMP_REVERSE = ["clamp", "reverse"];
|
|
43
|
-
const BOOLEAN_OPTIONS = ["nice", "zero", ...CLAMP_REVERSE];
|
|
44
|
-
const TRANSFORM_OPTIONS = ["base", "exponent", "constant"];
|
|
45
|
-
const POSITION_OPTIONS = [
|
|
46
|
-
...BASE_OPTIONS,
|
|
47
|
-
...BOOLEAN_OPTIONS,
|
|
48
|
-
...TRANSFORM_OPTIONS,
|
|
49
|
-
"paddingInner",
|
|
50
|
-
"paddingOuter",
|
|
51
|
-
"padding",
|
|
52
|
-
"align",
|
|
53
|
-
"unknown"
|
|
54
|
-
];
|
|
55
|
-
const COLOR_OPTIONS = [...BASE_OPTIONS, "palette", "unknown"];
|
|
56
|
-
const SEQUENTIAL_COLOR_OPTIONS = [
|
|
57
|
-
...COLOR_OPTIONS,
|
|
58
|
-
"interpolate", "midpoint",
|
|
59
|
-
...CLAMP_REVERSE
|
|
60
|
-
];
|
|
61
|
-
const OPACITY_OPTIONS = [...BASE_OPTIONS, ...BOOLEAN_OPTIONS, "unknown"];
|
|
62
|
-
const STROKE_WIDTH_OPTIONS = [
|
|
63
|
-
...BASE_OPTIONS, ...BOOLEAN_OPTIONS, ...TRANSFORM_OPTIONS
|
|
64
|
-
];
|
|
65
|
-
const SIZE_OPTIONS = [
|
|
66
|
-
...UNKNOWN_OPTIONS, "clamp", "reverse", "base", "exponent"
|
|
67
|
-
];
|
|
68
|
-
|
|
69
|
-
function optionsObject(options) {
|
|
70
|
-
if (!isPlainObject(options)) {
|
|
71
|
-
throw new TypeError("Encoding scale must be a plain object.");
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function validateBooleanOptions(options, properties, type) {
|
|
76
|
-
for (const property of properties) {
|
|
77
|
-
if (options[property] !== undefined && typeof options[property] !== "boolean") {
|
|
78
|
-
throw new TypeError(`Scale ${property} must be a boolean.`);
|
|
79
|
-
}
|
|
80
|
-
if (type !== undefined && options[property] !== undefined) {
|
|
81
|
-
validateScalePropertyForType(type, property);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function assignOptions(scale, options, existing, properties) {
|
|
87
|
-
for (const property of properties) {
|
|
88
|
-
const value = options[property] ?? existing?.[property];
|
|
89
|
-
if (value !== undefined) scale[property] = value;
|
|
90
|
-
}
|
|
91
|
-
return scale;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function validatePaletteRange(options) {
|
|
95
|
-
if (options.palette !== undefined && options.range !== undefined) {
|
|
96
|
-
throw new Error("Color scale cannot specify both palette and range.");
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export function resolvePositionScaleDefinition(
|
|
101
|
-
program,
|
|
102
|
-
channel,
|
|
103
|
-
fieldType,
|
|
104
|
-
options,
|
|
105
|
-
defaults = {}
|
|
106
|
-
) {
|
|
107
|
-
optionsObject(options);
|
|
108
|
-
validateKeys(options, POSITION_OPTIONS, "scale");
|
|
109
|
-
const id = validateUserId(options.id ?? channel, "Scale id");
|
|
110
|
-
const existing = findSemanticScale(program, id);
|
|
111
|
-
const discrete = ["ordinal", "nominal"].includes(fieldType);
|
|
112
|
-
const expectedType = fieldType === "temporal"
|
|
113
|
-
? "time"
|
|
114
|
-
: discrete
|
|
115
|
-
? defaults.discreteType ?? "point"
|
|
116
|
-
: "linear";
|
|
117
|
-
const type = options.type ?? existing?.type ?? expectedType;
|
|
118
|
-
if (fieldType === "temporal") validateTimeScaleType(type);
|
|
119
|
-
else if (discrete) {
|
|
120
|
-
if (!isDiscretePositionScaleType(type)) {
|
|
121
|
-
throw new Error(
|
|
122
|
-
`Scale type "${type}" is not valid for discrete position.`
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
else if (channel === "theta") validateLinearScaleType(type);
|
|
127
|
-
else validateScaleTypeForRole(type, SCALE_ROLES.quantitativePosition);
|
|
128
|
-
const scale = {
|
|
129
|
-
id,
|
|
130
|
-
...normalizeScaleDefinition({
|
|
131
|
-
type,
|
|
132
|
-
previous: existing,
|
|
133
|
-
patch: { ...options, ...(defaults.radialMapping === undefined ? {} : { radialMapping: defaults.radialMapping }) },
|
|
134
|
-
defaults: {
|
|
135
|
-
...(existing === undefined && defaults.nice !== undefined
|
|
136
|
-
? { nice: defaults.nice }
|
|
137
|
-
: {}),
|
|
138
|
-
...(
|
|
139
|
-
existing === undefined &&
|
|
140
|
-
defaults.zero !== undefined &&
|
|
141
|
-
(type !== "log" || defaults.zero)
|
|
142
|
-
? { zero: defaults.zero }
|
|
143
|
-
: {})
|
|
144
|
-
},
|
|
145
|
-
retainCoreOnTypeChange: true,
|
|
146
|
-
retainCompatibleOnTypeChange: true,
|
|
147
|
-
validateDomain: (_scaleType, value) =>
|
|
148
|
-
discrete
|
|
149
|
-
? validateOrdinalDomain(value)
|
|
150
|
-
: validateScaleDomain(value),
|
|
151
|
-
validateRange: (_scaleType, value) => channel === "theta"
|
|
152
|
-
? validateThetaRange(value)
|
|
153
|
-
: channel === "radius"
|
|
154
|
-
? validateRadialRange(value)
|
|
155
|
-
: validateScaleRange(value)
|
|
156
|
-
})
|
|
157
|
-
};
|
|
158
|
-
return withScaleUnknown(scale, { ...existing, ...options }, channel);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
export function resolveColorScaleDefinition(program, options, channel = "color") {
|
|
162
|
-
optionsObject(options);
|
|
163
|
-
validateKeys(options, COLOR_OPTIONS, "scale");
|
|
164
|
-
validatePaletteRange(options);
|
|
165
|
-
const id = validateUserId(options.id ?? channel, "Scale id");
|
|
166
|
-
const existing = findSemanticScale(program, id);
|
|
167
|
-
const previous = existing?.type === "ordinal" ? existing : undefined;
|
|
168
|
-
const range = options.palette === undefined
|
|
169
|
-
? options.range
|
|
170
|
-
: { palette: options.palette };
|
|
171
|
-
return withScaleUnknown({
|
|
172
|
-
id,
|
|
173
|
-
type: validateOrdinalScaleType(options.type ?? previous?.type ?? "ordinal"),
|
|
174
|
-
domain: validateOrdinalDomain(options.domain ?? previous?.domain ?? "auto"),
|
|
175
|
-
range: validateColorRange(range ?? previous?.range ?? "auto")
|
|
176
|
-
}, { ...previous, ...options }, channel);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
function continuousDomain(value, fieldType) {
|
|
180
|
-
if (value === "auto") return value;
|
|
181
|
-
if (!Array.isArray(value) || value.length !== 2) {
|
|
182
|
-
throw new TypeError("Continuous color domain must contain two values or auto.");
|
|
183
|
-
}
|
|
184
|
-
const normalized = value.map(item => {
|
|
185
|
-
if (fieldType === "quantitative") return item;
|
|
186
|
-
return typeof item === "string" ? Date.parse(item) : item;
|
|
187
|
-
});
|
|
188
|
-
if (!normalized.every(Number.isFinite) || normalized[0] === normalized[1]) {
|
|
189
|
-
throw new TypeError("Continuous color domain requires two distinct valid values.");
|
|
190
|
-
}
|
|
191
|
-
return normalized;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
export function resolveSequentialColorScaleDefinition(
|
|
195
|
-
program,
|
|
196
|
-
fieldType,
|
|
197
|
-
options,
|
|
198
|
-
channel = "color"
|
|
199
|
-
) {
|
|
200
|
-
optionsObject(options);
|
|
201
|
-
validateKeys(options, SEQUENTIAL_COLOR_OPTIONS, "scale");
|
|
202
|
-
validatePaletteRange(options);
|
|
203
|
-
const id = validateUserId(options.id ?? channel, "Scale id");
|
|
204
|
-
const existing = findSemanticScale(program, id);
|
|
205
|
-
const previous = existing?.type === "sequential" ? existing : undefined;
|
|
206
|
-
const type = options.type ?? previous?.type ?? "sequential";
|
|
207
|
-
if (type !== "sequential") {
|
|
208
|
-
throw new Error(`Unsupported continuous color scale type "${type}".`);
|
|
209
|
-
}
|
|
210
|
-
validateBooleanOptions(options, CLAMP_REVERSE);
|
|
211
|
-
const requestedRange = options.palette === undefined
|
|
212
|
-
? options.range
|
|
213
|
-
: { palette: options.palette };
|
|
214
|
-
const scale = {
|
|
215
|
-
id,
|
|
216
|
-
type,
|
|
217
|
-
domain: continuousDomain(options.domain ?? previous?.domain ?? "auto", fieldType),
|
|
218
|
-
range: validateSequentialColorRange(
|
|
219
|
-
requestedRange ?? previous?.range ?? { palette: "viridis" }
|
|
220
|
-
),
|
|
221
|
-
interpolate: validateContinuousColorInterpolation(
|
|
222
|
-
options.interpolate ?? previous?.interpolate ?? "rgb"
|
|
223
|
-
)
|
|
224
|
-
};
|
|
225
|
-
const midpoint = validateSequentialMidpoint(
|
|
226
|
-
Object.hasOwn(options, "midpoint") ? options.midpoint : previous?.midpoint, type, scale.domain
|
|
227
|
-
);
|
|
228
|
-
if (midpoint !== undefined) {
|
|
229
|
-
if (fieldType !== "quantitative") {
|
|
230
|
-
throw new Error("Scale midpoint requires quantitative color consumers.");
|
|
231
|
-
}
|
|
232
|
-
scale.midpoint = midpoint;
|
|
233
|
-
}
|
|
234
|
-
return withScaleUnknown(
|
|
235
|
-
assignOptions(scale, options, previous, CLAMP_REVERSE),
|
|
236
|
-
{ ...previous, ...options },
|
|
237
|
-
channel
|
|
238
|
-
);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
export function resolveQuantitativeColorScaleDefinition(
|
|
242
|
-
program,
|
|
243
|
-
fieldType,
|
|
244
|
-
options,
|
|
245
|
-
channel = "color"
|
|
246
|
-
) {
|
|
247
|
-
optionsObject(options);
|
|
248
|
-
const existing = findSemanticScale(program, options.id ?? channel);
|
|
249
|
-
const existingContinuous = existing !== undefined && (
|
|
250
|
-
existing.type === "sequential" ||
|
|
251
|
-
["quantize", "quantile", "threshold"].includes(existing.type)
|
|
252
|
-
);
|
|
253
|
-
const type = options.type ?? (
|
|
254
|
-
existingContinuous ? existing.type : "sequential"
|
|
255
|
-
);
|
|
256
|
-
if (existingContinuous && existing.type !== type) {
|
|
257
|
-
validateKeys(options, type === "sequential" ? SEQUENTIAL_COLOR_OPTIONS : [...COLOR_OPTIONS, ...CLAMP_REVERSE], "scale");
|
|
258
|
-
if (fieldType !== "quantitative") throw new Error("Color scale type transition requires quantitative color.");
|
|
259
|
-
return { id: existing.id, ...prepareScaleEdit(program, existing, channel, [], options) };
|
|
260
|
-
}
|
|
261
|
-
if (type === "sequential") {
|
|
262
|
-
return resolveSequentialColorScaleDefinition(program, fieldType, options, channel);
|
|
263
|
-
}
|
|
264
|
-
validateKeys(options, [...COLOR_OPTIONS, ...CLAMP_REVERSE], "scale");
|
|
265
|
-
if (fieldType !== "quantitative") {
|
|
266
|
-
throw new Error(`Scale type "${type}" requires quantitative color.`);
|
|
267
|
-
}
|
|
268
|
-
validateScaleTypeForRole(type, SCALE_ROLES.discretizedColor);
|
|
269
|
-
validatePaletteRange(options);
|
|
270
|
-
validateBooleanOptions(options, CLAMP_REVERSE, type);
|
|
271
|
-
const id = validateUserId(options.id ?? channel, "Scale id");
|
|
272
|
-
const previous = existing?.type === type ? existing : undefined;
|
|
273
|
-
const requestedRange = options.palette === undefined
|
|
274
|
-
? options.range
|
|
275
|
-
: { palette: options.palette };
|
|
276
|
-
const scale = {
|
|
277
|
-
id,
|
|
278
|
-
type,
|
|
279
|
-
domain: validateDiscretizedColorDomain(
|
|
280
|
-
type,
|
|
281
|
-
options.domain ?? previous?.domain ?? "auto"
|
|
282
|
-
),
|
|
283
|
-
range: validateDiscretizedColorRange(
|
|
284
|
-
requestedRange ?? previous?.range ?? { palette: "viridis" }
|
|
285
|
-
)
|
|
286
|
-
};
|
|
287
|
-
return withScaleUnknown(
|
|
288
|
-
assignOptions(scale, options, previous, CLAMP_REVERSE),
|
|
289
|
-
{ ...existing, ...options },
|
|
290
|
-
channel
|
|
291
|
-
);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
function resolveOrdinalScaleDefinition(program, options, channel, rangeValidator) {
|
|
295
|
-
optionsObject(options);
|
|
296
|
-
validateKeys(options, UNKNOWN_OPTIONS, "scale");
|
|
297
|
-
const id = validateUserId(options.id ?? channel, "Scale id");
|
|
298
|
-
const existing = findSemanticScale(program, id);
|
|
299
|
-
return withScaleUnknown({
|
|
300
|
-
id,
|
|
301
|
-
type: validateOrdinalScaleType(options.type ?? existing?.type ?? "ordinal"),
|
|
302
|
-
domain: validateOrdinalDomain(options.domain ?? existing?.domain ?? "auto"),
|
|
303
|
-
range: rangeValidator(options.range ?? existing?.range ?? "auto")
|
|
304
|
-
}, { ...existing, ...options }, channel);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
export function resolveStrokeDashScaleDefinition(program, options) {
|
|
308
|
-
return resolveOrdinalScaleDefinition(
|
|
309
|
-
program, options, "strokeDash", validateStrokeDashRange
|
|
310
|
-
);
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
export function resolveAppearanceScaleDefinition(program, channel, options) {
|
|
314
|
-
optionsObject(options);
|
|
315
|
-
if (channel === "size") {
|
|
316
|
-
validateKeys(options, SIZE_OPTIONS, "scale");
|
|
317
|
-
const id = validateUserId(options.id ?? channel, "Scale id");
|
|
318
|
-
const existing = findSemanticScale(program, id);
|
|
319
|
-
return {
|
|
320
|
-
id,
|
|
321
|
-
...normalizeSizeScaleDefinition({ previous: existing, patch: options })
|
|
322
|
-
};
|
|
323
|
-
}
|
|
324
|
-
validateKeys(options, UNKNOWN_OPTIONS, "scale");
|
|
325
|
-
const id = validateUserId(options.id ?? channel, "Scale id");
|
|
326
|
-
const existing = findSemanticScale(program, id);
|
|
327
|
-
return withScaleUnknown({
|
|
328
|
-
id,
|
|
329
|
-
type: validateOrdinalScaleType(options.type ?? existing?.type ?? "ordinal"),
|
|
330
|
-
domain: validateOrdinalDomain(options.domain ?? existing?.domain ?? "auto"),
|
|
331
|
-
range: validateShapeRange(options.range ?? existing?.range ?? "auto")
|
|
332
|
-
}, { ...existing, ...options }, channel);
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
export function resolveOpacityScaleDefinition(program, options) {
|
|
336
|
-
optionsObject(options);
|
|
337
|
-
validateKeys(options, OPACITY_OPTIONS, "scale");
|
|
338
|
-
const id = validateUserId(options.id ?? "opacity", "Scale id");
|
|
339
|
-
const existing = findSemanticScale(program, id);
|
|
340
|
-
const type = validateLinearScaleType(options.type ?? existing?.type ?? "linear");
|
|
341
|
-
validateBooleanOptions(options, BOOLEAN_OPTIONS, type);
|
|
342
|
-
const scale = {
|
|
343
|
-
id,
|
|
344
|
-
type,
|
|
345
|
-
domain: validateScaleDomain(options.domain ?? existing?.domain ?? "auto"),
|
|
346
|
-
range: validateOpacityRange(options.range ?? existing?.range ?? "auto")
|
|
347
|
-
};
|
|
348
|
-
return withScaleUnknown(
|
|
349
|
-
assignOptions(scale, options, existing, BOOLEAN_OPTIONS),
|
|
350
|
-
{ ...existing, ...options },
|
|
351
|
-
"opacity"
|
|
352
|
-
);
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
export function resolveStrokeWidthScaleDefinition(program, options) {
|
|
356
|
-
optionsObject(options);
|
|
357
|
-
validateKeys(options, STROKE_WIDTH_OPTIONS, "scale");
|
|
358
|
-
const id = validateUserId(options.id ?? "strokeWidth", "Scale id");
|
|
359
|
-
const existing = findSemanticScale(program, id);
|
|
360
|
-
const type = options.type ?? existing?.type ?? "linear";
|
|
361
|
-
validateScaleTypeForRole(type, SCALE_ROLES.quantitativePosition);
|
|
362
|
-
validateBooleanOptions(options, BOOLEAN_OPTIONS, type);
|
|
363
|
-
for (const property of TRANSFORM_OPTIONS) {
|
|
364
|
-
if (options[property] !== undefined) {
|
|
365
|
-
validateScalePropertyForType(type, property);
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
const domain = validateScaleDomain(options.domain ?? existing?.domain ?? "auto");
|
|
369
|
-
if (domain !== "auto" && domain.some(value => value < 0)) {
|
|
370
|
-
throw new RangeError("StrokeWidth scale domain cannot contain negative values.");
|
|
371
|
-
}
|
|
372
|
-
const scale = {
|
|
373
|
-
id,
|
|
374
|
-
type,
|
|
375
|
-
domain,
|
|
376
|
-
range: validateStrokeWidthRange(options.range ?? existing?.range ?? "auto")
|
|
377
|
-
};
|
|
378
|
-
return assignOptions(
|
|
379
|
-
scale, options, existing, [...BOOLEAN_OPTIONS, ...TRANSFORM_OPTIONS]
|
|
380
|
-
);
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
export function resolveOffsetScaleDefinition(program, options, channel = "xOffset") {
|
|
384
|
-
optionsObject(options);
|
|
385
|
-
validateKeys(options, [
|
|
386
|
-
"id", "type", "domain", "range", "reverse",
|
|
387
|
-
"padding", "paddingInner", "paddingOuter", "align"
|
|
388
|
-
], "scale");
|
|
389
|
-
const id = validateUserId(options.id ?? channel, "Scale id");
|
|
390
|
-
const existing = findSemanticScale(program, id);
|
|
391
|
-
const type = options.type ?? existing?.type ?? "ordinal";
|
|
392
|
-
if (type !== "ordinal") {
|
|
393
|
-
throw new Error(`Scale type "${type}" is not valid for ${channel}.`);
|
|
394
|
-
}
|
|
395
|
-
if (Object.hasOwn(options, "range") && options.range !== "auto") {
|
|
396
|
-
throw new Error(
|
|
397
|
-
`${channel} scale range is derived from its parent categorical slot.`
|
|
398
|
-
);
|
|
399
|
-
}
|
|
400
|
-
const consumers = findScaleConsumers(program, id).filter(
|
|
401
|
-
consumer => consumer.channel === channel
|
|
402
|
-
);
|
|
403
|
-
const currentPolicy = resolveRequestedOffsetPolicy({
|
|
404
|
-
scale: existing,
|
|
405
|
-
consumers,
|
|
406
|
-
markConfigs: program.markConfigs,
|
|
407
|
-
id,
|
|
408
|
-
channel
|
|
409
|
-
});
|
|
410
|
-
const policy = normalizeOffsetScalePolicy(options, currentPolicy, channel);
|
|
411
|
-
const patch = Object.fromEntries(Object.entries(options).filter(
|
|
412
|
-
([property]) => !["id", "padding"].includes(property)
|
|
413
|
-
));
|
|
414
|
-
return {
|
|
415
|
-
id,
|
|
416
|
-
...normalizeScaleDefinition({
|
|
417
|
-
type,
|
|
418
|
-
previous: existing,
|
|
419
|
-
patch: { ...patch, ...policy },
|
|
420
|
-
allowOrdinalBandParameters: true,
|
|
421
|
-
validateDomain: (_scaleType, value) => validateOrdinalDomain(value),
|
|
422
|
-
validateRange: (_scaleType, value) => validateScaleRange(value)
|
|
423
|
-
})
|
|
424
|
-
};
|
|
425
|
-
}
|
|
1
|
+
import{prepareScaleEdit as M}from"./editPolicy.js";import{validateUserId as m}from"../../core/identifiers.js";import{isPlainObject as W}from"../../core/immutable.js";import{validateKeys as u}from"../../core/validation.js";import{validateColorRange as U,isContinuousColorScaleType as w,normalizeTransformParameters as B,validateTransformedDomain as F,validateContinuousColorInterpolation as K,validateSequentialMidpoint as Q,validateLinearScaleType as z,validateOpacityRange as H,validateOrdinalDomain as E,validateOrdinalScaleType as N,validateScaleDomain as h,validateScaleRange as _,validateShapeRange as V,validateStrokeDashRange as Y,validateStrokeWidthRange as Z,validateSequentialColorRange as G,validateDiscretizedColorDomain as J,validateDiscretizedColorRange as X,validateTimeScaleType as ee,normalizeScaleDefinition as k,SCALE_ROLES as P,validateScalePropertyForType as A,validateScaleTypeForRole as b,isDiscretePositionScaleType as te,normalizeSizeScaleDefinition as ae,withScaleUnknown as O}from"../../grammar/scales/index.js";import{validateRadialRange as ne,validateThetaRange as ie}from"../../grammar/polar.js";import{findSemanticScale as p}from"../../selectors/scales.js";import{normalizeOffsetScalePolicy as re}from"../../grammar/bars/geometry.js";import{resolveRequestedOffsetPolicy as oe}from"../../materialization/scales/policies/offset.js";import{findScaleConsumers as ce}from"./consumers/index.js";const T=["id","type","domain","range","emptyDomain"],j=[...T,"unknown"],S=["clamp","reverse"],v=["nice","zero",...S],q=["base","exponent","constant"],de=[...T,...v,...q,"paddingInner","paddingOuter","padding","align","unknown"],C=[...T,"palette","unknown"],L=[...C,"interpolate","midpoint","base","constant",...S],le=[...T,...v,"unknown"],se=[...T,...v,...q],ue=[...j,"clamp","reverse","base","exponent"];function y(i){if(!W(i))throw new TypeError("Encoding scale must be a plain object.")}function D(i,e,t){for(const a of e){if(i[a]!==void 0&&typeof i[a]!="boolean")throw new TypeError(`Scale ${a} must be a boolean.`);t!==void 0&&i[a]!==void 0&&A(t,a)}}function R(i,e,t,a){for(const n of a){const r=e[n]??t?.[n];r!==void 0&&(i[n]=r)}return i}function x(i){if(i.palette!==void 0&&i.range!==void 0)throw new Error("Color scale cannot specify both palette and range.")}function Ce(i,e,t,a,n={}){y(a),u(a,de,"scale");const r=m(a.id??e,"Scale id"),o=p(i,r),c=["ordinal","nominal"].includes(t),f=t==="temporal"?"time":c?n.discreteType??"point":"linear",d=a.type??o?.type??f;if(t==="temporal")ee(d);else if(c){if(!te(d))throw new Error(`Scale type "${d}" is not valid for discrete position.`)}else e==="theta"?z(d):b(d,P.quantitativePosition);const l={id:r,...k({type:d,previous:o,patch:{...a,...n.radialMapping===void 0?{}:{radialMapping:n.radialMapping}},defaults:{...o===void 0&&n.nice!==void 0?{nice:n.nice}:{},...o===void 0&&n.zero!==void 0&&(d!=="log"||n.zero)?{zero:n.zero}:{}},retainCoreOnTypeChange:!0,retainCompatibleOnTypeChange:!0,validateDomain:(g,s)=>c?E(s):h(s),validateRange:(g,s)=>e==="theta"?ie(s):e==="radius"?ne(s):_(s)})};return O(l,{...o,...a},e)}function De(i,e,t="color"){y(e),u(e,C,"scale"),x(e);const a=m(e.id??t,"Scale id"),n=p(i,a),r=n?.type==="ordinal"?n:void 0,o=e.palette===void 0?e.range:{palette:e.palette};return O({id:a,type:N(e.type??r?.type??"ordinal"),domain:E(e.domain??r?.domain??"auto"),range:U(o??r?.range??"auto")},{...r,...e},t)}function fe(i,e){if(i==="auto")return i;if(!Array.isArray(i)||i.length!==2)throw new TypeError("Continuous color domain must contain two values or auto.");const t=i.map(a=>e==="quantitative"?a:typeof a=="string"?Date.parse(a):a);if(!t.every(Number.isFinite)||t[0]===t[1])throw new TypeError("Continuous color domain requires two distinct valid values.");return t}function me(i,e,t,a="color"){y(t),u(t,L,"scale"),x(t);const n=m(t.id??a,"Scale id"),r=p(i,n),o=w(r?.type)?r:void 0,c=t.type??o?.type??"sequential";if(!w(c))throw new Error(`Unsupported continuous color scale type "${c}".`);D(t,S);const f=t.palette===void 0?t.range:{palette:t.palette},d={id:n,type:c,domain:fe(t.domain??o?.domain??"auto",e),range:G(f??o?.range??{palette:"viridis"}),interpolate:K(t.interpolate??o?.interpolate??"rgb")};if(c!=="sequential"&&e!=="quantitative")throw new Error(`Scale type "${c}" requires quantitative color.`);const l=Object.fromEntries(["base","constant"].flatMap(s=>{const I=t[s]??o?.[s];return I===void 0?[]:[[s,I]]}));if(c!=="sequential"){const s=B(c,l);c==="log"&&(d.base=s.base),c==="symlog"&&(d.constant=s.constant),d.domain!=="auto"&&F(c,d.domain,l)}else if(Object.keys(l).length)throw new Error("Sequential color does not support transform parameters.");const g=Q(Object.hasOwn(t,"midpoint")?t.midpoint:o?.midpoint,c,d.domain);if(g!==void 0){if(e!=="quantitative")throw new Error("Scale midpoint requires quantitative color consumers.");d.midpoint=g}return O(R(d,t,o,S),{...o,...t},a)}function Re(i,e,t,a="color"){y(t);const n=p(i,t.id??a),r=n!==void 0&&(w(n.type)||["quantize","quantile","threshold"].includes(n.type)),o=t.type??(r?n.type:"sequential");if(r&&n.type!==o){if(u(t,w(o)?L:[...C,...S],"scale"),e!=="quantitative")throw new Error("Color scale type transition requires quantitative color.");return{id:n.id,...M(i,n,a,[],t)}}if(w(o))return me(i,e,t,a);if(u(t,[...C,...S],"scale"),e!=="quantitative")throw new Error(`Scale type "${o}" requires quantitative color.`);b(o,P.discretizedColor),x(t),D(t,S,o);const c=m(t.id??a,"Scale id"),f=n?.type===o?n:void 0,d=t.palette===void 0?t.range:{palette:t.palette},l={id:c,type:o,domain:J(o,t.domain??f?.domain??"auto"),range:X(d??f?.range??{palette:"viridis"})};return O(R(l,t,f,S),{...n,...t},a)}function $(i,e,t,a){y(e),u(e,j,"scale");const n=m(e.id??t,"Scale id"),r=p(i,n);return O({id:n,type:N(e.type??r?.type??"ordinal"),domain:E(e.domain??r?.domain??"auto"),range:a(e.range??r?.range??"auto")},{...r,...e},t)}function he(i,e){return $(i,e,"strokeDash",Y)}function Pe(i,e,t,a="quantitative"){if(y(t),e==="size"){u(t,ue,"scale");const n=m(t.id??e,"Scale id"),r=p(i,n),o=["nominal","ordinal"].includes(a),c=ae({previous:r,patch:{...t,type:t.type??r?.type??(o?"ordinal":"linear")}});if(c.type==="ordinal"!==o)throw new Error("Size scale type must match its field type.");return{id:n,...c}}return $(i,t,e,V)}function be(i,e){y(e),u(e,le,"scale");const t=m(e.id??"opacity","Scale id"),a=p(i,t),n=z(e.type??a?.type??"linear");D(e,v,n);const r={id:t,type:n,domain:h(e.domain??a?.domain??"auto"),range:H(e.range??a?.range??"auto")};return O(R(r,e,a,v),{...a,...e},"opacity")}function xe(i,e){y(e),u(e,se,"scale");const t=m(e.id??"strokeWidth","Scale id"),a=p(i,t),n=e.type??a?.type??"linear";b(n,P.quantitativePosition),D(e,v,n);for(const c of q)e[c]!==void 0&&A(n,c);const r=h(e.domain??a?.domain??"auto");if(r!=="auto"&&r.some(c=>c<0))throw new RangeError("StrokeWidth scale domain cannot contain negative values.");const o={id:t,type:n,domain:r,range:Z(e.range??a?.range??"auto")};return R(o,e,a,[...v,...q])}function Ie(i,e,t="xOffset"){y(e),u(e,["id","type","domain","range","reverse","padding","paddingInner","paddingOuter","align"],"scale");const a=m(e.id??t,"Scale id"),n=p(i,a),r=e.type??n?.type??"ordinal";if(r!=="ordinal")throw new Error(`Scale type "${r}" is not valid for ${t}.`);if(Object.hasOwn(e,"range")&&e.range!=="auto")throw new Error(`${t} scale range is derived from its parent categorical slot.`);const o=ce(i,a).filter(l=>l.channel===t),c=oe({scale:n,consumers:o,markConfigs:i.markConfigs,id:a,channel:t}),f=re(e,c,t),d=Object.fromEntries(Object.entries(e).filter(([l])=>!["id","padding"].includes(l)));return{id:a,...k({type:r,previous:n,patch:{...d,...f},allowOrdinalBandParameters:!0,validateDomain:(l,g)=>E(g),validateRange:(l,g)=>_(g)})}}export{Pe as resolveAppearanceScaleDefinition,De as resolveColorScaleDefinition,Ie as resolveOffsetScaleDefinition,be as resolveOpacityScaleDefinition,Ce as resolvePositionScaleDefinition,Re as resolveQuantitativeColorScaleDefinition,me as resolveSequentialColorScaleDefinition,he as resolveStrokeDashScaleDefinition,xe as resolveStrokeWidthScaleDefinition};
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
applyMaterializationPlan,
|
|
12
12
|
planCoordinateRematerialization
|
|
13
13
|
} from "../../materialization/dependencies.js";
|
|
14
|
-
import { findDataAspectCoordinatesForScale } from
|
|
14
|
+
import { axisScaleBindings, findDataAspectCoordinatesForScale } from
|
|
15
15
|
"../../materialization/coordinateBounds.js";
|
|
16
16
|
import {
|
|
17
17
|
findSemanticScale,
|
|
@@ -26,7 +26,7 @@ import { hasExactLegendSamplingForScale } from
|
|
|
26
26
|
"../guides/legends/sampling.js";
|
|
27
27
|
|
|
28
28
|
const OPTIONS = Object.freeze([
|
|
29
|
-
"id", "type", "domain", "range", "nice", "zero", "clamp", "reverse",
|
|
29
|
+
"id", "type", "domain", "range", "emptyDomain", "nice", "zero", "clamp", "reverse",
|
|
30
30
|
"base", "exponent", "constant", "paddingInner", "paddingOuter", "padding",
|
|
31
31
|
"align", "interpolate", "midpoint", "unknown", "palette", "radialMapping"
|
|
32
32
|
]);
|
|
@@ -115,7 +115,7 @@ function applyScaleEdit(program, {
|
|
|
115
115
|
value: definition[property]
|
|
116
116
|
});
|
|
117
117
|
}
|
|
118
|
-
if (consumers.length === 0) return next;
|
|
118
|
+
if (consumers.length === 0) return axisScaleBindings(next, id).length === 0 ? next : next.rematerializeScale({ id });
|
|
119
119
|
|
|
120
120
|
const aspectCoordinates = findDataAspectCoordinatesForScale(next, id);
|
|
121
121
|
if (aspectCoordinates.length > 0) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { validateContinuousColorConsumer } from "../../grammar/scales/colorConsumers.js";
|
|
2
2
|
import {
|
|
3
3
|
hasOrdinalDomain,
|
|
4
|
+
isContinuousColorScaleType,
|
|
4
5
|
isDiscretizedColorScaleType,
|
|
5
6
|
isDiscretePositionScaleType,
|
|
6
7
|
isTransformedScaleType,
|
|
@@ -53,7 +54,7 @@ function validateRangeForChannel(scale, channel, value) {
|
|
|
53
54
|
if (channel === "size") return validateSizeRange(value);
|
|
54
55
|
if (channel === "theta") return validateThetaRange(value);
|
|
55
56
|
if (channel === "radius") return validateRadialRange(value);
|
|
56
|
-
if (scale.type === "sequential") {
|
|
57
|
+
if (scale.type === "sequential" || (channel === "color" && isContinuousColorScaleType(scale.type))) {
|
|
57
58
|
return validateSequentialColorRange(value);
|
|
58
59
|
}
|
|
59
60
|
if (isDiscretizedColorScaleType(scale.type)) {
|
|
@@ -72,7 +73,8 @@ function validateTypeTransition(scale, nextType, channel, consumers) {
|
|
|
72
73
|
if (nextType === scale.type) return;
|
|
73
74
|
validateScaleType(nextType);
|
|
74
75
|
if (consumers.length === 0) return;
|
|
75
|
-
if (nextType === "sequential" || isDiscretizedColorScaleType(nextType)
|
|
76
|
+
if (nextType === "sequential" || isDiscretizedColorScaleType(nextType) ||
|
|
77
|
+
(channel === "color" && isContinuousColorScaleType(nextType))) {
|
|
76
78
|
if (channel !== "color") {
|
|
77
79
|
throw new Error(`Scale "${scale.id}" has a consumer incompatible with type "${nextType}".`);
|
|
78
80
|
}
|
|
@@ -162,7 +164,9 @@ function normalizeDefinition(program, scale, channel, consumers, patch) {
|
|
|
162
164
|
if (channel === "size") {
|
|
163
165
|
if (consumers.some(consumer =>
|
|
164
166
|
consumer.layer.mark?.type !== "point" ||
|
|
165
|
-
|
|
167
|
+
((patch.type ?? scale.type) === "ordinal"
|
|
168
|
+
? !["nominal", "ordinal"].includes(consumer.encoding.fieldType)
|
|
169
|
+
: consumer.encoding.fieldType !== "quantitative")
|
|
166
170
|
)) {
|
|
167
171
|
throw new Error(
|
|
168
172
|
`Scale "${scale.id}" has a consumer incompatible with size mapping.`
|
|
@@ -208,6 +212,7 @@ function normalizeDefinition(program, scale, channel, consumers, patch) {
|
|
|
208
212
|
: patch;
|
|
209
213
|
const definition = normalizeScaleDefinition({
|
|
210
214
|
type,
|
|
215
|
+
color: channel === "color" || scale.interpolate !== undefined,
|
|
211
216
|
previous: scale,
|
|
212
217
|
patch: normalizedPatch,
|
|
213
218
|
retainCoreOnTypeChange: true,
|
|
@@ -252,6 +257,7 @@ function normalizeDefinition(program, scale, channel, consumers, patch) {
|
|
|
252
257
|
}
|
|
253
258
|
|
|
254
259
|
export function prepareScaleEdit(program, scale, channel, consumers, args) {
|
|
260
|
+
channel ??= scale.interpolate === undefined ? undefined : "color";
|
|
255
261
|
const hasPalette = Object.hasOwn(args, "palette");
|
|
256
262
|
if (hasPalette && Object.hasOwn(args, "range")) {
|
|
257
263
|
throw new Error("editScale cannot specify both palette and range.");
|
|
@@ -37,7 +37,7 @@ export const rematerializeScale = /* @__PURE__ */ action(
|
|
|
37
37
|
const id = validateUserId(args.id, "Scale id");
|
|
38
38
|
const semanticScale = findScale(this, id);
|
|
39
39
|
const consumers = findScaleConsumers(this, id);
|
|
40
|
-
if (
|
|
40
|
+
if (consumers.length > 0 &&
|
|
41
41
|
consumers.every(isPendingMeasuredRadiusConsumer)) {
|
|
42
42
|
validatePendingMeasuredScale(this, semanticScale, consumers);
|
|
43
43
|
return this._withoutResolvedScale(id);
|
|
@@ -6,7 +6,7 @@ import { requireLayer } from "../../selectors/layers.js";
|
|
|
6
6
|
import { requireSemanticScale } from "../../selectors/scales.js";
|
|
7
7
|
|
|
8
8
|
const QUANTITATIVE_OPTIONS = Object.freeze([
|
|
9
|
-
"type", "domain", "range", "nice", "zero", "clamp", "reverse",
|
|
9
|
+
"type", "domain", "range", "emptyDomain", "nice", "zero", "clamp", "reverse",
|
|
10
10
|
"base", "exponent", "constant", "unknown"
|
|
11
11
|
]);
|
|
12
12
|
const ORDINAL_OPTIONS = Object.freeze([
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const SCALE_EDIT_PROPERTIES = Object.freeze([
|
|
2
|
-
"type", "domain", "range", "nice", "zero", "clamp", "reverse",
|
|
2
|
+
"type", "domain", "range", "emptyDomain", "nice", "zero", "clamp", "reverse",
|
|
3
3
|
"base", "exponent", "constant", "paddingInner", "paddingOuter",
|
|
4
4
|
"padding", "align", "interpolate", "midpoint", "unknown"
|
|
5
5
|
]);
|
|
@@ -5,21 +5,31 @@ import { findSemanticScale } from "../../selectors/scales.js";
|
|
|
5
5
|
import { normalizePositionScaleChannel } from "../../core/vocabulary.js";
|
|
6
6
|
import { resolveGraphicBounds } from "../../layout/canvas.js";
|
|
7
7
|
import {
|
|
8
|
+
axisScaleBindings,
|
|
9
|
+
resolveCoordinateBounds,
|
|
8
10
|
resolveDataAspectScalePair,
|
|
9
11
|
resolveScaleConsumerBounds
|
|
10
12
|
} from "../../materialization/coordinateBounds.js";
|
|
11
13
|
import { requireCoordinate } from "../../selectors/coordinates.js";
|
|
12
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
rememberResolvedScaleBinding,
|
|
16
|
+
resolveScaleMaterialization
|
|
17
|
+
} from "../../materialization/scales/resolve.js";
|
|
13
18
|
import { findScale, findScaleConsumers, resolveConsumerCategoryOrder,
|
|
14
19
|
resolveConsumerValues, resolveSeriesLayoutScaleValues } from "./consumers/index.js";
|
|
15
20
|
|
|
16
21
|
function resolveScalePreviewAtBounds(program, id, bounds) {
|
|
17
22
|
const scale = findScale(program, id);
|
|
18
23
|
const consumers = findScaleConsumers(program, id);
|
|
19
|
-
|
|
24
|
+
const axes = axisScaleBindings(program, id);
|
|
25
|
+
if (consumers.length === 0 && axes.length === 0) throw new Error(`Scale "${id}" has no supported consumers.`);
|
|
26
|
+
if (consumers.length === 0 && (!Array.isArray(scale.domain) || scale.domain.length === 0)) {
|
|
27
|
+
throw new Error("Standalone axis scales require an explicit nonempty domain.");
|
|
28
|
+
}
|
|
20
29
|
const consumerChannels = new Set(consumers.map(
|
|
21
30
|
consumer => normalizePositionScaleChannel(consumer.channel)
|
|
22
31
|
));
|
|
32
|
+
for (const binding of axes) consumerChannels.add(binding.channel);
|
|
23
33
|
const families = new Set([...consumerChannels].map(
|
|
24
34
|
channel => channel === "stroke" ? "color" : channel
|
|
25
35
|
));
|
|
@@ -96,6 +106,12 @@ function dataAspectScaleIds(program) {
|
|
|
96
106
|
|
|
97
107
|
export function resolveScalePreview(program, id) {
|
|
98
108
|
const requestedConsumers = findScaleConsumers(program, id);
|
|
109
|
+
const axes = axisScaleBindings(program, id);
|
|
110
|
+
if (requestedConsumers.length === 0 && axes.length > 0) {
|
|
111
|
+
const coordinate = requireCoordinate(program, axes[0].coordinate);
|
|
112
|
+
if (coordinate.type !== "cartesian") throw new Error("Standalone axes require a Cartesian coordinate.");
|
|
113
|
+
return resolveScalePreviewAtBounds(program, id, resolveCoordinateBounds(program, coordinate.id));
|
|
114
|
+
}
|
|
99
115
|
const positionChannels = new Set([
|
|
100
116
|
"x", "y", "x2", "y2", "theta", "radius"
|
|
101
117
|
]);
|
|
@@ -123,9 +139,40 @@ export function resolveScalePreview(program, id) {
|
|
|
123
139
|
provisional.consumers,
|
|
124
140
|
{ resolvedScales }
|
|
125
141
|
);
|
|
142
|
+
for (const binding of axes) {
|
|
143
|
+
const guideBounds = resolveCoordinateBounds(program, binding.coordinate, { resolvedScales });
|
|
144
|
+
if (["x", "y", "width", "height"].some(key => Math.abs(guideBounds[key] - bounds[key]) > 1e-9)) {
|
|
145
|
+
throw new Error("Axis and mark scale consumers require matching coordinate bounds.");
|
|
146
|
+
}
|
|
147
|
+
}
|
|
126
148
|
return resolveScalePreviewAtBounds(program, id, bounds);
|
|
127
149
|
}
|
|
128
150
|
|
|
151
|
+
export function restoreResolvedScaleBindings(program) {
|
|
152
|
+
for (const scale of program.semanticSpec.scales) {
|
|
153
|
+
const resolved = program.resolvedScales[scale.id];
|
|
154
|
+
if (resolved === undefined) continue;
|
|
155
|
+
const consumers = findScaleConsumers(program, scale.id);
|
|
156
|
+
const channels = new Set(consumers.map(consumer =>
|
|
157
|
+
normalizePositionScaleChannel(consumer.channel)
|
|
158
|
+
));
|
|
159
|
+
for (const binding of axisScaleBindings(program, scale.id)) channels.add(binding.channel);
|
|
160
|
+
const families = new Set([...channels].map(channel =>
|
|
161
|
+
channel === "stroke" ? "color" : channel
|
|
162
|
+
));
|
|
163
|
+
if (families.size === 1) {
|
|
164
|
+
const channel = channels.size === 1
|
|
165
|
+
? channels.values().next().value
|
|
166
|
+
: families.values().next().value;
|
|
167
|
+
rememberResolvedScaleBinding(resolved, channel, consumers);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
for (const child of Object.values(program.children)) {
|
|
171
|
+
restoreResolvedScaleBindings(child);
|
|
172
|
+
}
|
|
173
|
+
return program;
|
|
174
|
+
}
|
|
175
|
+
|
|
129
176
|
export function validatePendingMeasuredScale(program, scale, consumers) {
|
|
130
177
|
const bounds = resolveScaleConsumerBounds(program, consumers);
|
|
131
178
|
const range = resolveArcAutoPositionRange({ consumers, scale, channel: "radius",
|
|
@@ -7,7 +7,7 @@ const OPTIONS = Object.freeze([
|
|
|
7
7
|
"type",
|
|
8
8
|
"domain",
|
|
9
9
|
"range",
|
|
10
|
-
"interpolate", "midpoint",
|
|
10
|
+
"interpolate", "midpoint", "base", "constant",
|
|
11
11
|
"clamp",
|
|
12
12
|
"reverse",
|
|
13
13
|
"unknown"
|
|
@@ -19,7 +19,7 @@ export const setQuantitativeColorScale = /* @__PURE__ */ closedAction(
|
|
|
19
19
|
description: "Create or update an internal quantitative color scale."
|
|
20
20
|
}, OPTIONS,
|
|
21
21
|
function (args = {}) {
|
|
22
|
-
if (!["sequential", "quantize", "quantile", "threshold"].includes(args.type)) {
|
|
22
|
+
if (!["sequential", "log", "sqrt", "symlog", "quantize", "quantile", "threshold"].includes(args.type)) {
|
|
23
23
|
throw new Error(`Unsupported quantitative color scale type "${args.type}".`);
|
|
24
24
|
}
|
|
25
25
|
const existing = findSemanticScale(this, args.id);
|
|
@@ -34,7 +34,7 @@ export const setQuantitativeColorScale = /* @__PURE__ */ closedAction(
|
|
|
34
34
|
next = next.editSemantic({ property: `scale[${args.id}].midpoint`, remove: true });
|
|
35
35
|
}
|
|
36
36
|
for (const property of [
|
|
37
|
-
"type", "domain", "range", "interpolate", "midpoint", "clamp", "reverse", "unknown"
|
|
37
|
+
"type", "domain", "range", "interpolate", "midpoint", "base", "constant", "clamp", "reverse", "unknown"
|
|
38
38
|
]) {
|
|
39
39
|
if (!Object.hasOwn(args, property)) continue;
|
|
40
40
|
if (existing?.[property] === args[property]) continue;
|