ggaction 0.0.17 → 0.0.19
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 +96 -0
- package/README.md +6 -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 +3 -3
- 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 +20 -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 +8 -7
- 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/box.js +2 -1
- package/src/actions/data/derived.js +11 -1
- package/src/actions/data/edit.js +3 -1
- package/src/actions/data/index.js +4 -0
- package/src/actions/data/regression.js +25 -16
- package/src/actions/data/regressionStatistics.js +73 -0
- package/src/actions/data/shared.js +8 -4
- package/src/actions/data/stack.js +3 -1
- package/src/actions/distributions/revision.js +24 -18
- package/src/actions/encodings/appearance.js +37 -29
- package/src/actions/encodings/barWidth.js +36 -11
- package/src/actions/encodings/channels.js +0 -1
- package/src/actions/encodings/color/continuous.js +1 -1
- package/src/actions/encodings/color/index.js +1 -1
- package/src/actions/encodings/color/policy.js +10 -3
- package/src/actions/encodings/density/resolve.js +1 -6
- 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 +3 -2
- package/src/actions/encodings/ranged.js +17 -0
- package/src/actions/encodings/remove.js +14 -8
- package/src/actions/encodings/seriesLayout.js +1 -0
- package/src/actions/encodings/shared.js +26 -24
- package/src/actions/encodings/text.js +6 -3
- package/src/actions/errorBands/create.js +2 -2
- package/src/actions/errorBands/edit.js +2 -1
- package/src/actions/facets/actions.js +176 -247
- package/src/actions/facets/guides.js +2 -1
- package/src/actions/gradientPlots/resolve.js +2 -17
- package/src/actions/guides/applicability.js +1 -1
- package/src/actions/guides/axes/axes.js +30 -11
- package/src/actions/guides/axes/axis.js +72 -10
- package/src/actions/guides/axes/components.js +13 -13
- package/src/actions/guides/axes/edit.js +36 -24
- package/src/actions/guides/axes/labels.js +155 -71
- package/src/actions/guides/axes/lines.js +27 -24
- 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 +13 -8
- package/src/actions/guides/axes/tickGroups.js +11 -7
- package/src/actions/guides/axes/ticks.js +36 -24
- package/src/actions/guides/axes/titles.js +107 -69
- package/src/actions/guides/basic.js +2 -0
- package/src/actions/guides/facadeAxes.js +33 -24
- package/src/actions/guides/facadeLegend.js +12 -4
- package/src/actions/guides/grids/grid.js +8 -2
- package/src/actions/guides/grids/resolve.js +102 -58
- package/src/actions/guides/legends/blocks.js +46 -20
- package/src/actions/guides/legends/categorical/actions.js +95 -19
- package/src/actions/guides/legends/categorical/components.js +15 -18
- package/src/actions/guides/legends/categorical/index.js +8 -0
- package/src/actions/guides/legends/categorical/layout.js +45 -20
- package/src/actions/guides/legends/categorical/options.js +39 -22
- package/src/actions/guides/legends/categorical/recipes.js +7 -3
- package/src/actions/guides/legends/categorical/resolve.js +11 -4
- package/src/actions/guides/legends/categorical/symbols.js +38 -18
- package/src/actions/guides/legends/categorical/validation.js +1 -6
- package/src/actions/guides/legends/colorSize.js +110 -0
- package/src/actions/guides/legends/combine.js +37 -0
- 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 +73 -46
- package/src/actions/guides/legends/continuous/interval.js +4 -4
- package/src/actions/guides/legends/continuous/opacity.js +33 -21
- package/src/actions/guides/legends/creation.js +2 -0
- package/src/actions/guides/legends/edit.js +1 -604
- package/src/actions/guides/legends/focused.js +8 -7
- package/src/actions/guides/legends/group.js +1 -0
- package/src/actions/guides/legends/lane.js +1 -516
- package/src/actions/guides/legends/lifecycle.js +2 -1
- package/src/actions/guides/legends/remove.js +9 -3
- package/src/actions/guides/legends/sampling.js +1 -1
- package/src/actions/guides/legends/size.js +65 -47
- package/src/actions/guides/legends/strokeWidth.js +13 -29
- package/src/actions/guides/legends/target.js +2 -79
- package/src/actions/guides/legends/transition.js +44 -11
- 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/guides/tickValues.js +8 -5
- 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 +10 -4
- package/src/actions/marks/bar/edit.js +29 -14
- package/src/actions/marks/bar/materialize.js +15 -7
- package/src/actions/marks/line/actions.js +11 -4
- package/src/actions/marks/line/materialize.js +9 -5
- package/src/actions/marks/point/edit.js +1 -1
- package/src/actions/marks/point/materialize.js +5 -1
- package/src/actions/marks/rect/actions.js +3 -3
- package/src/actions/marks/remove.js +12 -10
- package/src/actions/marks/rule/actions.js +7 -4
- package/src/actions/marks/text/actions.js +17 -5
- package/src/actions/marks/text/layout.js +3 -0
- package/src/actions/primitives/createGraphics.js +3 -2
- package/src/actions/primitives/editGraphics.js +15 -11
- package/src/actions/primitives/graphicProperties.js +1 -6
- package/src/actions/primitives/semanticAction.js +27 -1
- package/src/actions/primitives/semanticValidation/guide.js +11 -1
- package/src/actions/primitives/semanticValidation/layer.js +8 -0
- package/src/actions/regression/create.js +1 -1
- package/src/actions/regression/edit.js +18 -6
- package/src/actions/regression/resolve.js +2 -6
- package/src/actions/scales/channels.js +9 -4
- package/src/actions/scales/consumers/families.js +16 -5
- package/src/actions/scales/create.js +10 -5
- package/src/actions/scales/definitions.js +1 -425
- package/src/actions/scales/edit.js +3 -3
- package/src/actions/scales/editPolicy.js +12 -3
- package/src/actions/scales/materialize.js +1 -1
- package/src/actions/scales/patch.js +1 -1
- package/src/actions/scales/preview.js +21 -1
- package/src/actions/scales/quantitativeColor.js +8 -3
- package/src/actions/selection/actions.js +1 -13
- package/src/actions/theme/reconcile.js +126 -45
- 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/compositionState.js +6 -3
- package/src/core/font.js +9 -2
- package/src/core/graphicProperties.js +1 -0
- package/src/core/optionRequirements.js +1 -0
- package/src/core/programState.js +4 -0
- package/src/core/resourceReferences.js +5 -2
- package/src/core/textMetricProfile.js +3 -2
- package/src/core/textMetrics.js +41 -4
- package/src/core/validation.js +35 -0
- package/src/core/vocabulary.js +7 -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/axisTextPlacement.js +1 -0
- package/src/grammar/bars/geometry.js +30 -3
- package/src/grammar/bars/policy.js +12 -1
- package/src/grammar/bin.js +2 -12
- package/src/grammar/bin2d.js +1 -6
- package/src/grammar/boxPlot.js +56 -9
- package/src/grammar/categoryOrder.js +1 -6
- package/src/grammar/colorSizeLegend.js +39 -0
- package/src/grammar/complete.js +15 -19
- package/src/grammar/computed.js +115 -26
- package/src/grammar/curveCommands.js +29 -16
- package/src/grammar/datasetSchema.js +13 -9
- package/src/grammar/displayLabels.js +36 -0
- package/src/grammar/ecdf.js +2 -9
- package/src/grammar/facets/dependencies.js +6 -2
- package/src/grammar/facets/guides.js +58 -30
- package/src/grammar/facets/index.js +2 -5
- package/src/grammar/facets/legendIntent.js +65 -0
- package/src/grammar/fold.js +2 -12
- package/src/grammar/gradientProfile.js +1 -6
- package/src/grammar/guideDash.js +1 -0
- package/src/grammar/impute.js +21 -48
- package/src/grammar/interval.js +2 -9
- package/src/grammar/legendBlocks.js +1 -0
- package/src/grammar/legendBorder.js +9 -0
- package/src/grammar/legendClip.js +19 -0
- package/src/grammar/legendCombinations.js +45 -0
- package/src/grammar/legendGradient.js +27 -0
- package/src/grammar/legendGroups.js +1 -0
- package/src/grammar/legendOpacity.js +6 -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/pointShapes.js +16 -2
- package/src/grammar/polar.js +1 -184
- package/src/grammar/positionCompatibility.js +3 -3
- package/src/grammar/regression/derive.js +16 -2
- package/src/grammar/regression/models.js +142 -54
- package/src/grammar/regression/parameters.js +23 -4
- package/src/grammar/regression/statistics.js +64 -0
- 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 -197
- 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 +14 -0
- package/src/grammar/schemas/graphic.js +6 -0
- package/src/grammar/schemas/graphicBounds.js +31 -7
- package/src/grammar/schemas/semanticPath.js +19 -3
- package/src/grammar/stack.js +2 -10
- package/src/grammar/statisticalReference.js +1 -6
- package/src/grammar/summary.js +4 -27
- package/src/grammar/text.js +19 -0
- package/src/grammar/timeUnit.js +22 -11
- package/src/grammar/transformKeys.js +47 -0
- package/src/grammar/transformTopology.js +1 -0
- package/src/grammar/transforms.js +15 -2
- package/src/grammar/valueFormat.js +17 -5
- package/src/grammar/weightedStatistics.js +1 -6
- package/src/grammar/window.js +4 -58
- package/src/inspectionDescriptors.js +1 -1
- package/src/layout/canvas.js +1 -110
- package/src/layout/facets.js +1 -244
- package/src/layout/guideCollisions.js +9 -1
- 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 +22 -1
- package/src/materialization/compositionSnapshot.js +2 -7
- package/src/materialization/coordinateBounds.js +21 -0
- package/src/materialization/dependencies.js +4 -44
- package/src/materialization/facetGuides/edit.js +30 -0
- package/src/materialization/facetGuides/layout.js +1 -0
- package/src/materialization/facetGuides/owner.js +125 -0
- package/src/materialization/facetGuides/ownerLayout.js +71 -0
- package/src/materialization/facetGuides/placement.js +71 -14
- package/src/materialization/facetGuides/preparation.js +22 -7
- package/src/materialization/facetHeaders.js +1 -347
- package/src/materialization/facets.js +1 -185
- package/src/materialization/graphicHierarchy.js +5 -1
- package/src/materialization/graphicTranslation.js +73 -0
- 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/legendBorder.js +28 -0
- package/src/materialization/legendClip.js +76 -0
- package/src/materialization/legendGroups.js +1 -0
- package/src/materialization/legendText.js +1 -0
- package/src/materialization/legends.js +2 -2
- package/src/materialization/marks/capabilities.js +5 -6
- package/src/materialization/rect.js +1 -1
- package/src/materialization/scaleGuideDependencies.js +14 -15
- package/src/materialization/scales/map.js +6 -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 +7 -3
- 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 +13 -4
- package/src/materialization/textLines.js +15 -0
- package/src/materialization/typography.js +10 -2
- package/src/persistence/codec.js +37 -6
- package/src/persistence/validation.js +220 -0
- package/src/persistence.js +19 -6
- package/src/renderers/canvas/circle.js +6 -3
- package/src/renderers/canvas/fill.js +7 -1
- package/src/renderers/canvas/index.js +22 -11
- package/src/renderers/canvas/isolated.js +166 -0
- package/src/renderers/canvas/limits.js +3 -0
- 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/pdf.js +2 -2
- package/src/renderers/png.js +2 -2
- package/src/renderers/svg.js +14 -4
- package/src/selectors/axes.js +1 -0
- package/src/selectors/graphicCapacity.js +38 -0
- 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 +3 -0
- package/types/index.d.ts +12 -0
- package/types/program.d.ts +536 -129
|
@@ -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", "emptyDomain"];
|
|
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 U}from"./editPolicy.js";import{validateUserId as p}from"../../core/identifiers.js";import{isPlainObject as B}from"../../core/immutable.js";import{validateKeys as f}from"../../core/validation.js";import{validateColorRange as F,isContinuousColorScaleType as E,normalizeTransformParameters as K,validateTransformedDomain as Q,validateContinuousColorInterpolation as H,normalizeColorInterpolationOptions as V,validateSequentialMidpoint as Y,validateLinearScaleType as _,validateOpacityRange as Z,validateOrdinalDomain as h,validateOrdinalScaleType as j,validateScaleDomain as P,validateScaleRange as k,validateShapeRange as G,validateStrokeDashRange as J,validateStrokeWidthRange as X,validateSequentialColorRange as ee,validateDiscretizedColorDomain as te,validateDiscretizedColorRange as ne,validateTimeScaleType as ae,normalizeScaleDefinition as A,SCALE_ROLES as R,validateScalePropertyForType as L,validateScaleTypeForRole as I,isDiscretePositionScaleType as ie,normalizeSizeScaleDefinition as re,withScaleUnknown as w}from"../../grammar/scales/index.js";import{validateRadialRange as oe,validateThetaRange as ce}from"../../grammar/polar.js";import{findSemanticScale as g}from"../../selectors/scales.js";import{normalizeOffsetScalePolicy as le}from"../../grammar/bars/geometry.js";import{resolveRequestedOffsetPolicy as de}from"../../materialization/scales/policies/offset.js";import{findScaleConsumers as se}from"./consumers/index.js";const C=["id","type","domain","range","emptyDomain"],M=[...C,"unknown"],S=["clamp","reverse"],O=["nice","zero",...S],q=["base","exponent","constant"],ue=[...C,...O,...q,"paddingInner","paddingOuter","padding","align","unknown"],D=[...C,"palette","unknown"],$=[...D,"interpolate","whitePoint","gamma","midpoint","base","exponent","constant",...S],fe=[...C,...O,"unknown"],me=[...C,...O,...q],pe=[...M,"clamp","reverse","base","exponent"];function y(i){if(!B(i))throw new TypeError("Encoding scale must be a plain object.")}function b(i,t,e){for(const n of t){if(i[n]!==void 0&&typeof i[n]!="boolean")throw new TypeError(`Scale ${n} must be a boolean.`);e!==void 0&&i[n]!==void 0&&L(e,n)}}function x(i,t,e,n){for(const a of n){const o=t[a]??e?.[a];o!==void 0&&(i[a]=o)}return i}function z(i){if(i.palette!==void 0&&i.range!==void 0)throw new Error("Color scale cannot specify both palette and range.")}function be(i,t,e,n,a={}){y(n),f(n,ue,"scale");const o=p(n.id??t,"Scale id"),r=g(i,o),c=["ordinal","nominal"].includes(e),m=e==="temporal"?"time":c?a.discreteType??"point":"linear",l=n.type??r?.type??m;if(e==="temporal")ae(l);else if(c){if(!ie(l))throw new Error(`Scale type "${l}" is not valid for discrete position.`)}else t==="theta"?_(l):I(l,R.quantitativePosition);const s={id:o,...A({type:l,previous:r,patch:{...n,...a.radialMapping===void 0?{}:{radialMapping:a.radialMapping}},defaults:{...r===void 0&&a.nice!==void 0?{nice:a.nice}:{},...r===void 0&&a.zero!==void 0&&(l!=="log"||a.zero)?{zero:a.zero}:{}},retainCoreOnTypeChange:!0,retainCompatibleOnTypeChange:!0,validateDomain:(v,u)=>c?h(u):P(u),validateRange:(v,u)=>t==="theta"?ce(u):t==="radius"?oe(u):k(u)})};return w(s,{...r,...n},t)}function xe(i,t,e="color"){y(t),f(t,D,"scale"),z(t);const n=p(t.id??e,"Scale id"),a=g(i,n),o=a?.type==="ordinal"?a:void 0,r=t.palette===void 0?t.range:{palette:t.palette};return w({id:n,type:j(t.type??o?.type??"ordinal"),domain:h(t.domain??o?.domain??"auto"),range:F(r??o?.range??"auto")},{...o,...t},e)}function ge(i,t){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 e=i.map(n=>t==="quantitative"?n:typeof n=="string"?Date.parse(n):n);if(!e.every(Number.isFinite)||e[0]===e[1])throw new TypeError("Continuous color domain requires two distinct valid values.");return e}function ye(i,t,e,n="color"){y(e),f(e,$,"scale"),z(e);const a=p(e.id??n,"Scale id"),o=g(i,a),r=E(o?.type)?o:void 0,c=e.type??r?.type??"sequential";if(!E(c))throw new Error(`Unsupported continuous color scale type "${c}".`);b(e,S);const m=e.palette===void 0?e.range:{palette:e.palette},l={id:a,type:c,domain:ge(e.domain??r?.domain??"auto",t),range:ee(m??r?.range??{palette:"viridis"}),interpolate:H(e.interpolate??r?.interpolate??"rgb")},s=Object.hasOwn(e,"interpolate")&&e.interpolate!==r?.interpolate,v=Object.fromEntries(["whitePoint","gamma"].flatMap(d=>{const T=Object.hasOwn(e,d)?e[d]:s?void 0:r?.[d];return T===void 0?[]:[[d,T]]}));if(Object.assign(l,V(l.interpolate,v)),c!=="sequential"&&t!=="quantitative")throw new Error(`Scale type "${c}" requires quantitative color.`);const u=Object.fromEntries(["base","exponent","constant"].flatMap(d=>{const T=e[d]??(c===r?.type?r?.[d]:void 0);return T===void 0?[]:[[d,T]]}));if(c!=="sequential"){const d=K(c,u);c==="log"&&(l.base=d.base),c==="pow"&&(l.exponent=d.exponent),c==="symlog"&&(l.constant=d.constant),l.domain!=="auto"&&Q(c,l.domain,u)}else if(Object.keys(u).length)throw new Error("Sequential color does not support transform parameters.");const N=Y(Object.hasOwn(e,"midpoint")?e.midpoint:r?.midpoint,c,l.domain);if(N!==void 0){if(t!=="quantitative")throw new Error("Scale midpoint requires quantitative color consumers.");l.midpoint=N}return w(x(l,e,r,S),{...r,...e},n)}function Pe(i,t,e,n="color"){y(e);const a=g(i,e.id??n),o=a!==void 0&&(E(a.type)||["quantize","quantile","threshold"].includes(a.type)),r=e.type??(o?a.type:"sequential");if(o&&a.type!==r){if(f(e,E(r)?$:[...D,...S],"scale"),t!=="quantitative")throw new Error("Color scale type transition requires quantitative color.");return{id:a.id,...U(i,a,n,[],e)}}if(E(r))return ye(i,t,e,n);if(f(e,[...D,...S],"scale"),t!=="quantitative")throw new Error(`Scale type "${r}" requires quantitative color.`);I(r,R.discretizedColor),z(e),b(e,S,r);const c=p(e.id??n,"Scale id"),m=a?.type===r?a:void 0,l=e.palette===void 0?e.range:{palette:e.palette},s={id:c,type:r,domain:te(r,e.domain??m?.domain??"auto"),range:ne(l??m?.range??{palette:"viridis"})};return w(x(s,e,m,S),{...a,...e},n)}function W(i,t,e,n){y(t),f(t,M,"scale");const a=p(t.id??e,"Scale id"),o=g(i,a);return w({id:a,type:j(t.type??o?.type??"ordinal"),domain:h(t.domain??o?.domain??"auto"),range:n(t.range??o?.range??"auto")},{...o,...t},e)}function Re(i,t){return W(i,t,"strokeDash",J)}function Ie(i,t,e,n="quantitative"){if(y(e),t==="size"){f(e,pe,"scale");const a=p(e.id??t,"Scale id"),o=g(i,a),r=["nominal","ordinal"].includes(n),c=re({previous:o,patch:{...e,type:e.type??o?.type??(r?"ordinal":"linear")}});if(c.type==="ordinal"!==r)throw new Error("Size scale type must match its field type.");return{id:a,...c}}return W(i,e,t,G)}function ze(i,t){y(t),f(t,fe,"scale");const e=p(t.id??"opacity","Scale id"),n=g(i,e),a=_(t.type??n?.type??"linear");b(t,O,a);const o={id:e,type:a,domain:P(t.domain??n?.domain??"auto"),range:Z(t.range??n?.range??"auto")};return w(x(o,t,n,O),{...n,...t},"opacity")}function Ne(i,t){y(t),f(t,me,"scale");const e=p(t.id??"strokeWidth","Scale id"),n=g(i,e),a=t.type??n?.type??"linear";I(a,R.quantitativePosition),b(t,O,a);for(const c of q)t[c]!==void 0&&L(a,c);const o=P(t.domain??n?.domain??"auto");if(o!=="auto"&&o.some(c=>c<0))throw new RangeError("StrokeWidth scale domain cannot contain negative values.");const r={id:e,type:a,domain:o,range:X(t.range??n?.range??"auto")};return x(r,t,n,[...O,...q])}function _e(i,t,e="xOffset"){y(t),f(t,["id","type","domain","range","reverse","padding","paddingInner","paddingOuter","align"],"scale");const n=p(t.id??e,"Scale id"),a=g(i,n),o=t.type??a?.type??"ordinal";if(o!=="ordinal")throw new Error(`Scale type "${o}" is not valid for ${e}.`);if(Object.hasOwn(t,"range")&&t.range!=="auto")throw new Error(`${e} scale range is derived from its parent categorical slot.`);const r=se(i,n).filter(s=>s.channel===e),c=de({scale:a,consumers:r,markConfigs:i.markConfigs,id:n,channel:e}),m=le(t,c,e),l=Object.fromEntries(Object.entries(t).filter(([s])=>!["id","padding"].includes(s)));return{id:n,...A({type:o,previous:a,patch:{...l,...m},allowOrdinalBandParameters:!0,validateDomain:(s,v)=>h(v),validateRange:(s,v)=>k(v)})}}export{Ie as resolveAppearanceScaleDefinition,xe as resolveColorScaleDefinition,_e as resolveOffsetScaleDefinition,ze as resolveOpacityScaleDefinition,be as resolvePositionScaleDefinition,Pe as resolveQuantitativeColorScaleDefinition,ye as resolveSequentialColorScaleDefinition,Re as resolveStrokeDashScaleDefinition,Ne 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,
|
|
@@ -28,7 +28,7 @@ import { hasExactLegendSamplingForScale } from
|
|
|
28
28
|
const OPTIONS = Object.freeze([
|
|
29
29
|
"id", "type", "domain", "range", "emptyDomain", "nice", "zero", "clamp", "reverse",
|
|
30
30
|
"base", "exponent", "constant", "paddingInner", "paddingOuter", "padding",
|
|
31
|
-
"align", "interpolate", "midpoint", "unknown", "palette", "radialMapping"
|
|
31
|
+
"align", "interpolate", "whitePoint", "gamma", "midpoint", "unknown", "palette", "radialMapping"
|
|
32
32
|
]);
|
|
33
33
|
const EDITABLE = Object.freeze(
|
|
34
34
|
OPTIONS.filter(option => !["id", "palette"].includes(option))
|
|
@@ -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,
|
|
@@ -11,6 +12,7 @@ import {
|
|
|
11
12
|
validateDiscretizedColorDomain,
|
|
12
13
|
validateDiscretizedColorRange,
|
|
13
14
|
validateOrdinalDomain,
|
|
15
|
+
validateOrdinalRange,
|
|
14
16
|
validateScaleDomain,
|
|
15
17
|
validateScaleRange,
|
|
16
18
|
validateScaleType,
|
|
@@ -53,7 +55,7 @@ function validateRangeForChannel(scale, channel, value) {
|
|
|
53
55
|
if (channel === "size") return validateSizeRange(value);
|
|
54
56
|
if (channel === "theta") return validateThetaRange(value);
|
|
55
57
|
if (channel === "radius") return validateRadialRange(value);
|
|
56
|
-
if (scale.type === "sequential") {
|
|
58
|
+
if (scale.type === "sequential" || (channel === "color" && isContinuousColorScaleType(scale.type))) {
|
|
57
59
|
return validateSequentialColorRange(value);
|
|
58
60
|
}
|
|
59
61
|
if (isDiscretizedColorScaleType(scale.type)) {
|
|
@@ -65,6 +67,7 @@ function validateRangeForChannel(scale, channel, value) {
|
|
|
65
67
|
if (channel === "strokeDash") return validateStrokeDashRange(value);
|
|
66
68
|
if (channel === "size") return validateSizeRange(value);
|
|
67
69
|
if (channel === "strokeWidth") return validateStrokeWidthRange(value);
|
|
70
|
+
if (channel === undefined) return validateOrdinalRange(value);
|
|
68
71
|
return validateScaleRange(value);
|
|
69
72
|
}
|
|
70
73
|
|
|
@@ -72,7 +75,8 @@ function validateTypeTransition(scale, nextType, channel, consumers) {
|
|
|
72
75
|
if (nextType === scale.type) return;
|
|
73
76
|
validateScaleType(nextType);
|
|
74
77
|
if (consumers.length === 0) return;
|
|
75
|
-
if (nextType === "sequential" || isDiscretizedColorScaleType(nextType)
|
|
78
|
+
if (nextType === "sequential" || isDiscretizedColorScaleType(nextType) ||
|
|
79
|
+
(channel === "color" && isContinuousColorScaleType(nextType))) {
|
|
76
80
|
if (channel !== "color") {
|
|
77
81
|
throw new Error(`Scale "${scale.id}" has a consumer incompatible with type "${nextType}".`);
|
|
78
82
|
}
|
|
@@ -162,7 +166,9 @@ function normalizeDefinition(program, scale, channel, consumers, patch) {
|
|
|
162
166
|
if (channel === "size") {
|
|
163
167
|
if (consumers.some(consumer =>
|
|
164
168
|
consumer.layer.mark?.type !== "point" ||
|
|
165
|
-
|
|
169
|
+
((patch.type ?? scale.type) === "ordinal"
|
|
170
|
+
? !["nominal", "ordinal"].includes(consumer.encoding.fieldType)
|
|
171
|
+
: consumer.encoding.fieldType !== "quantitative")
|
|
166
172
|
)) {
|
|
167
173
|
throw new Error(
|
|
168
174
|
`Scale "${scale.id}" has a consumer incompatible with size mapping.`
|
|
@@ -208,10 +214,12 @@ function normalizeDefinition(program, scale, channel, consumers, patch) {
|
|
|
208
214
|
: patch;
|
|
209
215
|
const definition = normalizeScaleDefinition({
|
|
210
216
|
type,
|
|
217
|
+
color: channel === "color" || scale.interpolate !== undefined,
|
|
211
218
|
previous: scale,
|
|
212
219
|
patch: normalizedPatch,
|
|
213
220
|
retainCoreOnTypeChange: true,
|
|
214
221
|
retainCompatibleOnTypeChange: true,
|
|
222
|
+
allowColorReset: true,
|
|
215
223
|
allowOrdinalBandParameters: offset,
|
|
216
224
|
validateDomain: (scaleType, value) =>
|
|
217
225
|
isDiscretizedColorScaleType(scaleType)
|
|
@@ -252,6 +260,7 @@ function normalizeDefinition(program, scale, channel, consumers, patch) {
|
|
|
252
260
|
}
|
|
253
261
|
|
|
254
262
|
export function prepareScaleEdit(program, scale, channel, consumers, args) {
|
|
263
|
+
channel ??= scale.interpolate === undefined ? undefined : "color";
|
|
255
264
|
const hasPalette = Object.hasOwn(args, "palette");
|
|
256
265
|
if (hasPalette && Object.hasOwn(args, "range")) {
|
|
257
266
|
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);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const SCALE_EDIT_PROPERTIES = Object.freeze([
|
|
2
2
|
"type", "domain", "range", "emptyDomain", "nice", "zero", "clamp", "reverse",
|
|
3
3
|
"base", "exponent", "constant", "paddingInner", "paddingOuter",
|
|
4
|
-
"padding", "align", "interpolate", "midpoint", "unknown"
|
|
4
|
+
"padding", "align", "interpolate", "whitePoint", "gamma", "midpoint", "unknown"
|
|
5
5
|
]);
|
|
6
6
|
|
|
7
7
|
function sameValue(left, right) {
|
|
@@ -5,6 +5,8 @@ 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";
|
|
@@ -19,10 +21,15 @@ import { findScale, findScaleConsumers, resolveConsumerCategoryOrder,
|
|
|
19
21
|
function resolveScalePreviewAtBounds(program, id, bounds) {
|
|
20
22
|
const scale = findScale(program, id);
|
|
21
23
|
const consumers = findScaleConsumers(program, id);
|
|
22
|
-
|
|
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
|
+
}
|
|
23
29
|
const consumerChannels = new Set(consumers.map(
|
|
24
30
|
consumer => normalizePositionScaleChannel(consumer.channel)
|
|
25
31
|
));
|
|
32
|
+
for (const binding of axes) consumerChannels.add(binding.channel);
|
|
26
33
|
const families = new Set([...consumerChannels].map(
|
|
27
34
|
channel => channel === "stroke" ? "color" : channel
|
|
28
35
|
));
|
|
@@ -99,6 +106,12 @@ function dataAspectScaleIds(program) {
|
|
|
99
106
|
|
|
100
107
|
export function resolveScalePreview(program, id) {
|
|
101
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
|
+
}
|
|
102
115
|
const positionChannels = new Set([
|
|
103
116
|
"x", "y", "x2", "y2", "theta", "radius"
|
|
104
117
|
]);
|
|
@@ -126,6 +139,12 @@ export function resolveScalePreview(program, id) {
|
|
|
126
139
|
provisional.consumers,
|
|
127
140
|
{ resolvedScales }
|
|
128
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
|
+
}
|
|
129
148
|
return resolveScalePreviewAtBounds(program, id, bounds);
|
|
130
149
|
}
|
|
131
150
|
|
|
@@ -137,6 +156,7 @@ export function restoreResolvedScaleBindings(program) {
|
|
|
137
156
|
const channels = new Set(consumers.map(consumer =>
|
|
138
157
|
normalizePositionScaleChannel(consumer.channel)
|
|
139
158
|
));
|
|
159
|
+
for (const binding of axisScaleBindings(program, scale.id)) channels.add(binding.channel);
|
|
140
160
|
const families = new Set([...channels].map(channel =>
|
|
141
161
|
channel === "stroke" ? "color" : channel
|
|
142
162
|
));
|
|
@@ -7,7 +7,7 @@ const OPTIONS = Object.freeze([
|
|
|
7
7
|
"type",
|
|
8
8
|
"domain",
|
|
9
9
|
"range",
|
|
10
|
-
"interpolate", "midpoint",
|
|
10
|
+
"interpolate", "whitePoint", "gamma", "midpoint", "base", "exponent", "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", "pow", "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);
|
|
@@ -30,11 +30,16 @@ export const setQuantitativeColorScale = /* @__PURE__ */ closedAction(
|
|
|
30
30
|
return this.editScale(args);
|
|
31
31
|
}
|
|
32
32
|
let next = this;
|
|
33
|
+
for (const property of ["whitePoint", "gamma"]) {
|
|
34
|
+
if (existing?.[property] !== undefined && args[property] === undefined) {
|
|
35
|
+
next = next.editSemantic({ property: `scale[${args.id}].${property}`, remove: true });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
33
38
|
if (existing?.midpoint !== undefined && args.midpoint === undefined) {
|
|
34
39
|
next = next.editSemantic({ property: `scale[${args.id}].midpoint`, remove: true });
|
|
35
40
|
}
|
|
36
41
|
for (const property of [
|
|
37
|
-
"type", "domain", "range", "interpolate", "midpoint", "clamp", "reverse", "unknown"
|
|
42
|
+
"type", "domain", "range", "interpolate", "whitePoint", "gamma", "midpoint", "base", "exponent", "constant", "clamp", "reverse", "unknown"
|
|
38
43
|
]) {
|
|
39
44
|
if (!Object.hasOwn(args, property)) continue;
|
|
40
45
|
if (existing?.[property] === args[property]) continue;
|