ggaction 0.0.3 → 0.0.5
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 +58 -3
- package/README.md +117 -83
- package/package.json +27 -11
- package/src/actions/boxPlots/create.js +13 -7
- package/src/actions/boxPlots/edit.js +31 -35
- package/src/actions/boxPlots/materialize.js +3 -0
- package/src/actions/boxPlots/options.js +8 -1
- package/src/actions/boxPlots/resolve.js +10 -2
- package/src/actions/charts/bar.js +50 -0
- package/src/actions/charts/heatmap.js +205 -0
- package/src/actions/charts/histogram.js +58 -0
- package/src/actions/charts/index.js +15 -0
- package/src/actions/charts/line.js +69 -0
- package/src/actions/charts/parallel.js +76 -0
- package/src/actions/charts/scatter.js +56 -0
- package/src/actions/charts/shared.js +121 -0
- package/src/actions/coordinates/parallel.js +50 -0
- package/src/actions/data/bin2d.js +137 -0
- package/src/actions/data/density.js +54 -13
- package/src/actions/data/derived.js +22 -0
- package/src/actions/data/filter.js +5 -0
- package/src/actions/data/gradientProfile.js +65 -0
- package/src/actions/data/horizon.js +71 -0
- package/src/actions/data/index.js +29 -2
- package/src/actions/data/regression.js +1 -1
- package/src/actions/data/window.js +50 -0
- package/src/actions/encodings/appearance.js +1 -1
- package/src/actions/encodings/color/continuous.js +137 -0
- package/src/actions/encodings/color/index.js +167 -0
- package/src/actions/encodings/color/layout.js +99 -0
- package/src/actions/encodings/color/policy.js +88 -0
- package/src/actions/encodings/density/resolve.js +109 -0
- package/src/actions/encodings/density.js +193 -88
- package/src/actions/encodings/horizon/resolve.js +283 -0
- package/src/actions/encodings/horizon.js +364 -0
- package/src/actions/encodings/index.js +8 -2
- package/src/actions/encodings/offset.js +1 -1
- package/src/actions/encodings/parallel.js +115 -0
- package/src/actions/encodings/pathOrder.js +131 -0
- package/src/actions/encodings/position/apply.js +73 -4
- package/src/actions/encodings/{position.js → position/index.js} +21 -8
- package/src/actions/encodings/position/policies/arc.js +27 -4
- package/src/actions/encodings/position/policies/area.js +33 -2
- package/src/actions/encodings/position/policies/index.js +7 -2
- package/src/actions/encodings/position/policies/line.js +18 -2
- package/src/actions/encodings/position/resolve.js +25 -3
- package/src/actions/encodings/ranged.js +5 -3
- package/src/actions/encodings/ruleAppearance.js +88 -10
- package/src/actions/encodings/strokeDash.js +1 -1
- package/src/actions/encodings/text.js +1 -1
- package/src/actions/errorBands/options.js +1 -1
- package/src/actions/errorBars/options.js +1 -1
- package/src/actions/facets/actions.js +2 -3
- package/src/actions/facets/derive.js +35 -5
- package/src/actions/facets/guides.js +1 -1
- package/src/actions/facets/replay.js +7 -36
- package/src/actions/gradientPlots/components.js +177 -0
- package/src/actions/gradientPlots/create.js +96 -0
- package/src/actions/gradientPlots/edit.js +153 -0
- package/src/actions/gradientPlots/index.js +24 -0
- package/src/actions/gradientPlots/materialize.js +287 -0
- package/src/actions/gradientPlots/options.js +120 -0
- package/src/actions/gradientPlots/paint.js +107 -0
- package/src/actions/gradientPlots/rebind.js +51 -0
- package/src/actions/gradientPlots/resolve.js +81 -0
- package/src/actions/guides/applicability.js +44 -17
- package/src/actions/guides/axes/axes.js +37 -3
- package/src/actions/guides/axes/index.js +2 -0
- package/src/actions/guides/axes/labels.js +12 -7
- package/src/actions/guides/axes/parallel/resolve.js +86 -0
- package/src/actions/guides/axes/parallel.js +228 -0
- package/src/actions/guides/axes/ticks.js +1 -1
- package/src/actions/guides/axes/titles.js +12 -9
- package/src/actions/guides/grids/resolve.js +1 -1
- package/src/actions/guides/guides.js +60 -5
- package/src/actions/guides/legends/categorical/actions.js +48 -11
- package/src/actions/guides/legends/categorical/layout.js +4 -2
- package/src/actions/guides/legends/continuous/interval.js +1 -1
- package/src/actions/guides/legends/continuous/opacity.js +1 -1
- package/src/actions/guides/legends/edit.js +7 -20
- package/src/actions/guides/legends/index.js +2 -0
- package/src/actions/guides/legends/remove.js +6 -31
- package/src/actions/guides/legends/size.js +1 -1
- package/src/actions/guides/legends/strokeWidth.js +170 -0
- package/src/actions/guides/legends/target.js +25 -0
- package/src/actions/guides/polar/{axes.js → axes/index.js} +5 -5
- package/src/actions/guides/polar/index.js +1 -1
- package/src/actions/guides/polar/resolve.js +1 -1
- package/src/actions/guides/tickValues.js +1 -1
- package/src/actions/index.js +6 -0
- package/src/actions/intervals/resolve.js +1 -1
- package/src/actions/marks/arc/actions.js +2 -2
- package/src/actions/marks/area/actions.js +61 -137
- package/src/actions/marks/area/materialize.js +163 -0
- package/src/actions/marks/bar/edit.js +1 -1
- package/src/actions/marks/index.js +5 -1
- package/src/actions/marks/line/actions.js +100 -81
- package/src/actions/marks/line/materialize.js +146 -0
- package/src/actions/marks/point/create.js +76 -0
- package/src/actions/marks/point/edit.js +85 -0
- package/src/actions/marks/point/index.js +11 -1
- package/src/actions/marks/point/jitter.js +72 -0
- package/src/actions/marks/point/{actions.js → materialize.js} +175 -170
- package/src/actions/marks/rect/actions.js +4 -1
- package/src/actions/marks/remove.js +13 -2
- package/src/actions/marks/rule/actions.js +26 -4
- package/src/actions/marks/shared.js +1 -1
- package/src/actions/marks/text/actions.js +20 -5
- package/src/actions/marks/text/index.js +7 -1
- package/src/actions/marks/text/layout.js +321 -0
- package/src/actions/primitives/semanticValidation/dataset.js +22 -0
- package/src/actions/primitives/semanticValidation/guide.js +43 -0
- package/src/actions/primitives/semanticValidation/index.js +23 -0
- package/src/actions/primitives/semanticValidation/layer.js +74 -0
- package/src/actions/primitives/semanticValidation/scale.js +74 -0
- package/src/actions/primitives/semanticValidation/shared.js +5 -0
- package/src/actions/primitives/semanticValue.js +1 -204
- package/src/actions/regression/components.js +1 -1
- package/src/actions/regression/create.js +1 -1
- package/src/actions/regression/edit.js +20 -22
- package/src/actions/scales/consumers/common.js +109 -0
- package/src/actions/scales/consumers/families.js +58 -0
- package/src/actions/scales/consumers/index.js +41 -0
- package/src/actions/scales/{consumers.js → consumers/seriesLayout.js} +10 -132
- package/src/actions/scales/create.js +1 -1
- package/src/actions/scales/definitions.js +32 -2
- package/src/actions/scales/edit.js +8 -206
- package/src/actions/scales/editPolicy.js +214 -0
- package/src/actions/scales/materialize.js +3 -3
- package/src/actions/scales/patch.js +26 -0
- package/src/actions/selection/actions.js +28 -3
- package/src/actions/violinPlots/create.js +198 -0
- package/src/actions/violinPlots/index.js +5 -0
- package/src/core/ChartProgram.js +22 -236
- package/src/core/compositionState.js +169 -0
- package/src/core/materializationState.js +65 -0
- package/src/core/programState.js +28 -0
- package/src/core/textMetrics.js +47 -0
- package/src/core/vocabulary.js +7 -2
- package/src/grammar/arcs.js +50 -13
- package/src/grammar/areaSeries.js +42 -6
- package/src/grammar/bars/aggregate.js +1 -1
- package/src/grammar/bin2d.js +333 -0
- package/src/grammar/categoricalDensity.js +192 -0
- package/src/grammar/coordinates.js +1 -1
- package/src/grammar/curveCommands.js +3 -1
- package/src/grammar/density.js +253 -27
- package/src/grammar/facets/dependencies.js +3 -11
- package/src/grammar/{facets.js → facets/index.js} +8 -6
- package/src/grammar/gradientProfile.js +213 -0
- package/src/grammar/horizon.js +455 -0
- package/src/grammar/interval.js +2 -96
- package/src/grammar/jitter.js +197 -0
- package/src/grammar/lineSeries.js +70 -1
- package/src/grammar/paint.js +88 -0
- package/src/grammar/palettes.js +5 -7
- package/src/grammar/parallelCoordinates.js +213 -0
- package/src/grammar/pathOrder.js +35 -0
- package/src/grammar/pointShapes.js +24 -0
- package/src/grammar/polarLineCommands.js +1 -1
- package/src/grammar/regression/derive.js +97 -0
- package/src/grammar/regression/index.js +22 -0
- package/src/grammar/regression/models.js +282 -0
- package/src/grammar/regression/parameters.js +106 -0
- package/src/grammar/rules.js +2 -1
- package/src/grammar/scales/appearance.js +21 -0
- package/src/grammar/scales/color.js +4 -2
- package/src/grammar/schemas/concreteGraphic.js +11 -1
- package/src/grammar/schemas/graphicBounds.js +19 -37
- package/src/grammar/schemas/semanticPath.js +17 -2
- package/src/grammar/statistics/studentT.js +98 -0
- package/src/grammar/transforms.js +101 -12
- package/src/grammar/window.js +339 -0
- package/src/layout/labels.js +270 -0
- package/src/layout/text.js +4 -1
- package/src/layout/title.js +10 -20
- package/src/materialization/bars/aggregate.js +1 -1
- package/src/materialization/bars/grouped.js +1 -1
- package/src/materialization/bars/histogram.js +1 -1
- package/src/materialization/bars/ranged.js +1 -1
- package/src/materialization/dataProvenance.js +56 -2
- package/src/materialization/density.js +1 -1
- package/src/materialization/dependencies.js +15 -101
- package/src/materialization/encodings.js +2 -2
- package/src/materialization/facetGuides/index.js +2 -0
- package/src/materialization/facetGuides/legacyCategorical.js +125 -0
- package/src/materialization/facetGuides/placement.js +126 -0
- package/src/materialization/facetGuides/preparation.js +188 -0
- package/src/materialization/facets.js +1 -1
- package/src/materialization/guides/resources.js +74 -24
- package/src/materialization/horizon.js +29 -0
- package/src/materialization/layout.js +15 -0
- package/src/materialization/marks/capabilities.js +161 -0
- package/src/materialization/marks/index.js +108 -0
- package/src/materialization/marks/pathOrder.js +71 -0
- package/src/materialization/marks/policies.js +91 -0
- package/src/materialization/rect.js +1 -1
- package/src/materialization/rowEncoding.js +1 -1
- package/src/materialization/scaleGuideDependencies.js +102 -0
- package/src/materialization/scales/map.js +1 -1
- package/src/materialization/scales/policies/series.js +1 -1
- package/src/materialization/scales/resolve.js +3 -1
- package/src/materialization/selection/items/bar.js +1 -1
- package/src/materialization/selection/items/path.js +27 -1
- package/src/materialization/selection/items/point.js +1 -5
- package/src/materialization/selection/items/rect.js +21 -0
- package/src/materialization/selection/styles.js +21 -10
- package/src/materialization/text.js +12 -4
- package/src/renderers/canvas/fill.js +33 -0
- package/src/renderers/canvas/index.js +7 -0
- package/src/renderers/canvas/path.js +7 -4
- package/src/renderers/canvas/rect.js +9 -3
- package/src/renderers/canvas/text.js +14 -1
- package/src/selectors/datasets.js +4 -0
- package/src/theme/defaults.js +4 -0
- package/types/extension.d.ts +10 -1
- package/types/index.d.ts +76 -0
- package/types/program.d.ts +632 -10
- package/src/actions/encodings/color.js +0 -424
- package/src/grammar/regression.js +0 -578
- package/src/grammar/scales.js +0 -1
- package/src/materialization/facetGuides.js +0 -426
- package/src/materialization/marks.js +0 -316
|
@@ -1,144 +1,26 @@
|
|
|
1
|
-
import { deriveBarAggregates } from "
|
|
1
|
+
import { deriveBarAggregates } from "../../../grammar/bars/aggregate.js";
|
|
2
2
|
import {
|
|
3
3
|
BAR_GRAINS,
|
|
4
4
|
resolveBarChannels,
|
|
5
5
|
resolveBarColorLayout,
|
|
6
6
|
resolveBarGrain
|
|
7
|
-
} from "
|
|
7
|
+
} from "../../../grammar/bars/policy.js";
|
|
8
8
|
import {
|
|
9
9
|
countHistogramBins,
|
|
10
10
|
findHistogramBinIndex,
|
|
11
11
|
resolveHistogramBins
|
|
12
|
-
} from "
|
|
13
|
-
import { deriveDensityAreaSeries } from "
|
|
14
|
-
import {
|
|
15
|
-
|
|
12
|
+
} from "../../../grammar/histogram.js";
|
|
13
|
+
import { deriveDensityAreaSeries } from "../../../grammar/areaSeries.js";
|
|
14
|
+
import {
|
|
15
|
+
resolveSeriesLayoutDomainValues
|
|
16
|
+
} from "../../../grammar/seriesLayout.js";
|
|
16
17
|
import {
|
|
17
18
|
readNominalField,
|
|
18
19
|
readQuantitativeField,
|
|
19
|
-
readScaleField,
|
|
20
|
-
readTemporalField,
|
|
21
20
|
resolveOrdinalDomain
|
|
22
|
-
} from "
|
|
23
|
-
import { findDataset } from "
|
|
24
|
-
import {
|
|
25
|
-
import { isAggregate } from "../../grammar/aggregate.js";
|
|
26
|
-
import { normalizeRuleDatum } from "../../grammar/rules.js";
|
|
27
|
-
import { SCALED_ENCODING_CHANNELS } from "../../core/vocabulary.js";
|
|
28
|
-
import { resolveRectConsumerValues } from "../../materialization/rect.js";
|
|
29
|
-
|
|
30
|
-
export function findScale(program, id) {
|
|
31
|
-
return requireSemanticScale(program, id);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function findScaleConsumers(program, id) {
|
|
35
|
-
const consumers = [];
|
|
36
|
-
for (const layer of program.semanticSpec.layers) {
|
|
37
|
-
for (const channel of SCALED_ENCODING_CHANNELS) {
|
|
38
|
-
const encoding = layer.encoding?.[channel];
|
|
39
|
-
if (encoding?.scale === id) consumers.push({ layer, channel, encoding });
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return consumers;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function resolveConsumerValues(program, consumer) {
|
|
46
|
-
const dataset = findDataset(program, consumer.layer.data);
|
|
47
|
-
if (dataset === undefined) {
|
|
48
|
-
throw new Error(
|
|
49
|
-
`Mark "${consumer.layer.id}" references unknown dataset "${consumer.layer.data}".`
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (Object.hasOwn(consumer.encoding, "datum")) {
|
|
54
|
-
return [normalizeRuleDatum(
|
|
55
|
-
consumer.encoding.datum,
|
|
56
|
-
consumer.encoding.fieldType,
|
|
57
|
-
consumer.channel
|
|
58
|
-
)];
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (
|
|
62
|
-
consumer.channel === "strokeDash" &&
|
|
63
|
-
!["line", "rule"].includes(consumer.layer.mark?.type)
|
|
64
|
-
) {
|
|
65
|
-
throw new Error("strokeDash scale materialization requires a line mark or rule mark.");
|
|
66
|
-
}
|
|
67
|
-
const scale = findScale(program, consumer.encoding.scale);
|
|
68
|
-
const allowUnknown = Object.hasOwn(scale, "unknown");
|
|
69
|
-
if (consumer.layer.mark?.type === "rect") {
|
|
70
|
-
return resolveRectConsumerValues(
|
|
71
|
-
consumer.layer,
|
|
72
|
-
dataset,
|
|
73
|
-
consumer.channel
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (
|
|
78
|
-
["color", "strokeDash", "xOffset", "yOffset", "shape"].includes(consumer.channel) &&
|
|
79
|
-
consumer.encoding.fieldType === "nominal"
|
|
80
|
-
) {
|
|
81
|
-
return allowUnknown
|
|
82
|
-
? readScaleField(
|
|
83
|
-
dataset.values,
|
|
84
|
-
consumer.encoding.field,
|
|
85
|
-
consumer.encoding.fieldType,
|
|
86
|
-
{ allowUnknown: true }
|
|
87
|
-
)
|
|
88
|
-
: readNominalField(dataset.values, consumer.encoding.field);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (
|
|
92
|
-
consumer.layer.mark?.type === "line" &&
|
|
93
|
-
isAggregate(consumer.layer.encoding?.y?.aggregate)
|
|
94
|
-
) {
|
|
95
|
-
const derived = deriveLineSeries(dataset.values, consumer.layer);
|
|
96
|
-
return consumer.channel === "x" ? derived.xValues : derived.yValues;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (
|
|
100
|
-
resolveBarGrain(consumer.layer) === BAR_GRAINS.aggregate
|
|
101
|
-
) {
|
|
102
|
-
const derived = deriveBarAggregates(dataset.values, consumer.layer);
|
|
103
|
-
if (consumer.channel === "color") {
|
|
104
|
-
return derived.values.map(value => value.color);
|
|
105
|
-
}
|
|
106
|
-
return consumer.channel === "x" ? derived.xValues : derived.yValues;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
if (consumer.encoding.fieldType === "temporal") {
|
|
110
|
-
return allowUnknown
|
|
111
|
-
? readScaleField(dataset.values, consumer.encoding.field, "temporal", {
|
|
112
|
-
allowUnknown: true
|
|
113
|
-
})
|
|
114
|
-
: readTemporalField(dataset.values, consumer.encoding.field);
|
|
115
|
-
}
|
|
116
|
-
if (["nominal", "ordinal"].includes(consumer.encoding.fieldType)) {
|
|
117
|
-
if (!["ordinal", "band", "point"].includes(scale.type)) {
|
|
118
|
-
throw new Error(
|
|
119
|
-
`Scale materialization requires a quantitative encoding on mark "${consumer.layer.id}".`
|
|
120
|
-
);
|
|
121
|
-
}
|
|
122
|
-
return allowUnknown
|
|
123
|
-
? readScaleField(
|
|
124
|
-
dataset.values,
|
|
125
|
-
consumer.encoding.field,
|
|
126
|
-
consumer.encoding.fieldType,
|
|
127
|
-
{ allowUnknown: true }
|
|
128
|
-
)
|
|
129
|
-
: readNominalField(dataset.values, consumer.encoding.field);
|
|
130
|
-
}
|
|
131
|
-
if (consumer.encoding.fieldType !== "quantitative") {
|
|
132
|
-
throw new Error(
|
|
133
|
-
`Scale materialization requires a quantitative encoding on mark "${consumer.layer.id}".`
|
|
134
|
-
);
|
|
135
|
-
}
|
|
136
|
-
return allowUnknown
|
|
137
|
-
? readScaleField(dataset.values, consumer.encoding.field, "quantitative", {
|
|
138
|
-
allowUnknown: true
|
|
139
|
-
})
|
|
140
|
-
: readQuantitativeField(dataset.values, consumer.encoding.field);
|
|
141
|
-
}
|
|
21
|
+
} from "../../../grammar/scales/index.js";
|
|
22
|
+
import { findDataset } from "../../../selectors/datasets.js";
|
|
23
|
+
import { findScale } from "./common.js";
|
|
142
24
|
|
|
143
25
|
export function resolveHistogramCountValues(program, consumer) {
|
|
144
26
|
const xEncoding = consumer.layer.encoding?.x;
|
|
@@ -146,7 +28,6 @@ export function resolveHistogramCountValues(program, consumer) {
|
|
|
146
28
|
? undefined
|
|
147
29
|
: findScale(program, xEncoding.scale);
|
|
148
30
|
const dataset = findDataset(program, consumer.layer.data);
|
|
149
|
-
|
|
150
31
|
if (xEncoding?.bin === undefined || xScale === undefined) {
|
|
151
32
|
throw new Error(
|
|
152
33
|
`Histogram mark "${consumer.layer.id}" requires a binned x scale.`
|
|
@@ -157,7 +38,6 @@ export function resolveHistogramCountValues(program, consumer) {
|
|
|
157
38
|
`Histogram mark "${consumer.layer.id}" requires an existing dataset.`
|
|
158
39
|
);
|
|
159
40
|
}
|
|
160
|
-
|
|
161
41
|
const xValues = readQuantitativeField(dataset.values, xEncoding.field);
|
|
162
42
|
const bins = resolveHistogramBins({
|
|
163
43
|
values: xValues,
|
|
@@ -186,7 +66,6 @@ function resolveHistogramPartitions(program, consumer) {
|
|
|
186
66
|
if (colorEncoding?.scale === undefined) {
|
|
187
67
|
return countHistogramBins(xValues, bins.boundaries).map(value => [value]);
|
|
188
68
|
}
|
|
189
|
-
|
|
190
69
|
const colorScale = findScale(program, colorEncoding.scale);
|
|
191
70
|
const colorValues = readNominalField(dataset.values, colorEncoding.field);
|
|
192
71
|
const colorDomain = resolveOrdinalDomain(colorScale.domain, colorValues);
|
|
@@ -224,7 +103,6 @@ function resolveAggregatePartitions(program, consumer) {
|
|
|
224
103
|
]));
|
|
225
104
|
return categoryDomain.map(value => [byCategory.get(value) ?? 0]);
|
|
226
105
|
}
|
|
227
|
-
|
|
228
106
|
const colorScale = findScale(program, colorEncoding.scale);
|
|
229
107
|
const colorValues = readNominalField(dataset.values, colorEncoding.field);
|
|
230
108
|
const colorDomain = resolveOrdinalDomain(colorScale.domain, colorValues);
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
isContinuousColorScaleType,
|
|
16
16
|
isDiscretizedColorScaleType,
|
|
17
17
|
normalizeScaleDefinition
|
|
18
|
-
} from "../../grammar/scales.js";
|
|
18
|
+
} from "../../grammar/scales/index.js";
|
|
19
19
|
import { findSemanticScale } from "../../selectors/scales.js";
|
|
20
20
|
|
|
21
21
|
const CREATE_SCALE_OPTIONS = Object.freeze([
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
validateShapeRange,
|
|
14
14
|
validateSizeRange,
|
|
15
15
|
validateStrokeDashRange,
|
|
16
|
+
validateStrokeWidthRange,
|
|
16
17
|
validateSequentialColorRange,
|
|
17
18
|
validateDiscretizedColorDomain,
|
|
18
19
|
validateDiscretizedColorRange,
|
|
@@ -22,12 +23,12 @@ import {
|
|
|
22
23
|
validateScalePropertyForType,
|
|
23
24
|
validateScaleTypeForRole,
|
|
24
25
|
isDiscretePositionScaleType
|
|
25
|
-
} from "../../grammar/scales.js";
|
|
26
|
+
} from "../../grammar/scales/index.js";
|
|
26
27
|
import {
|
|
27
28
|
validateRadialRange,
|
|
28
29
|
validateThetaRange
|
|
29
30
|
} from "../../grammar/polar.js";
|
|
30
|
-
import { withScaleUnknown } from "../../grammar/scales.js";
|
|
31
|
+
import { withScaleUnknown } from "../../grammar/scales/index.js";
|
|
31
32
|
import { findSemanticScale } from "../../selectors/scales.js";
|
|
32
33
|
|
|
33
34
|
const BASE_OPTIONS = Object.freeze(["id", "type", "domain", "range"]);
|
|
@@ -314,6 +315,35 @@ export function resolveOpacityScaleDefinition(program, options) {
|
|
|
314
315
|
return withScaleUnknown(scale, { ...existing, ...options }, "opacity");
|
|
315
316
|
}
|
|
316
317
|
|
|
318
|
+
export function resolveStrokeWidthScaleDefinition(program, options) {
|
|
319
|
+
optionsObject(options);
|
|
320
|
+
validateKeys(options, OPACITY_OPTIONS, "scale");
|
|
321
|
+
const id = validateUserId(options.id ?? "strokeWidth", "Scale id");
|
|
322
|
+
const existing = findSemanticScale(program, id);
|
|
323
|
+
const type = options.type ?? existing?.type ?? "linear";
|
|
324
|
+
validateScaleTypeForRole(type, SCALE_ROLES.quantitativePosition);
|
|
325
|
+
for (const property of ["nice", "zero", "clamp", "reverse"]) {
|
|
326
|
+
if (options[property] !== undefined && typeof options[property] !== "boolean") {
|
|
327
|
+
throw new TypeError(`Scale ${property} must be a boolean.`);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
const domain = validateScaleDomain(options.domain ?? existing?.domain ?? "auto");
|
|
331
|
+
if (domain !== "auto" && domain.some(value => value < 0)) {
|
|
332
|
+
throw new RangeError("StrokeWidth scale domain cannot contain negative values.");
|
|
333
|
+
}
|
|
334
|
+
const scale = {
|
|
335
|
+
id,
|
|
336
|
+
type,
|
|
337
|
+
domain,
|
|
338
|
+
range: validateStrokeWidthRange(options.range ?? existing?.range ?? "auto")
|
|
339
|
+
};
|
|
340
|
+
for (const property of ["nice", "zero", "clamp", "reverse", "base", "exponent", "constant"]) {
|
|
341
|
+
const value = options[property] ?? existing?.[property];
|
|
342
|
+
if (value !== undefined) scale[property] = value;
|
|
343
|
+
}
|
|
344
|
+
return scale;
|
|
345
|
+
}
|
|
346
|
+
|
|
317
347
|
export function resolveOffsetScaleDefinition(program, options, channel = "xOffset") {
|
|
318
348
|
optionsObject(options);
|
|
319
349
|
validateKeys(options, [...BASE_OPTIONS, "unknown"], "scale");
|
|
@@ -1,32 +1,7 @@
|
|
|
1
1
|
import { action } from "../../core/action.js";
|
|
2
2
|
import { validateUserId } from "../../core/identifiers.js";
|
|
3
3
|
import { validateOptionObject } from "../../core/validation.js";
|
|
4
|
-
import {
|
|
5
|
-
isTransformedScaleType,
|
|
6
|
-
SCALE_ROLES,
|
|
7
|
-
validateColorRange,
|
|
8
|
-
validateDiscretizedColorDomain,
|
|
9
|
-
validateDiscretizedColorRange,
|
|
10
|
-
validateOrdinalDomain,
|
|
11
|
-
validateScaleDomain,
|
|
12
|
-
validateScaleRange,
|
|
13
|
-
validateScaleType,
|
|
14
|
-
validateScaleTypeForRole,
|
|
15
|
-
validateShapeRange,
|
|
16
|
-
validateSizeRange,
|
|
17
|
-
validateStrokeDashRange,
|
|
18
|
-
validateSequentialColorRange,
|
|
19
|
-
validateScaleUnknown,
|
|
20
|
-
isDiscretizedColorScaleType,
|
|
21
|
-
isDiscretePositionScaleType,
|
|
22
|
-
hasOrdinalDomain,
|
|
23
|
-
normalizeScaleDefinition
|
|
24
|
-
} from "../../grammar/scales.js";
|
|
25
|
-
import {
|
|
26
|
-
validateRadialRange,
|
|
27
|
-
validateThetaRange
|
|
28
|
-
} from "../../grammar/polar.js";
|
|
29
|
-
import { getMarkMaterializationStep } from "../../materialization/marks.js";
|
|
4
|
+
import { getMarkMaterializationStep } from "../../materialization/marks/index.js";
|
|
30
5
|
import {
|
|
31
6
|
applyMaterializationPlan
|
|
32
7
|
} from "../../materialization/dependencies.js";
|
|
@@ -34,8 +9,11 @@ import {
|
|
|
34
9
|
findSemanticScale,
|
|
35
10
|
requireSemanticScale
|
|
36
11
|
} from "../../selectors/scales.js";
|
|
37
|
-
import { findScaleConsumers } from "./consumers.js";
|
|
38
|
-
import {
|
|
12
|
+
import { findScaleConsumers } from "./consumers/index.js";
|
|
13
|
+
import {
|
|
14
|
+
prepareScaleEdit,
|
|
15
|
+
resolveScaleConsumerChannel
|
|
16
|
+
} from "./editPolicy.js";
|
|
39
17
|
|
|
40
18
|
const OPTIONS = Object.freeze([
|
|
41
19
|
"id", "type", "domain", "range", "nice", "zero", "clamp", "reverse",
|
|
@@ -67,162 +45,6 @@ function resolveScaleId(program, requested) {
|
|
|
67
45
|
);
|
|
68
46
|
}
|
|
69
47
|
|
|
70
|
-
function resolveChannel(consumers, id) {
|
|
71
|
-
const channels = new Set(
|
|
72
|
-
consumers.map(consumer => normalizePositionScaleChannel(consumer.channel))
|
|
73
|
-
);
|
|
74
|
-
if (channels.size > 1) {
|
|
75
|
-
throw new Error(`Scale "${id}" cannot be shared across channels.`);
|
|
76
|
-
}
|
|
77
|
-
return channels.values().next().value;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function validateRangeForChannel(scale, channel, value) {
|
|
81
|
-
if (value === "auto") return value;
|
|
82
|
-
if (channel === "theta") return validateThetaRange(value);
|
|
83
|
-
if (channel === "radius") return validateRadialRange(value);
|
|
84
|
-
if (scale.type === "sequential") {
|
|
85
|
-
return validateSequentialColorRange(value);
|
|
86
|
-
}
|
|
87
|
-
if (isDiscretizedColorScaleType(scale.type)) {
|
|
88
|
-
return validateDiscretizedColorRange(value);
|
|
89
|
-
}
|
|
90
|
-
if (scale.type !== "ordinal") return validateScaleRange(value);
|
|
91
|
-
if (channel === "color") return validateColorRange(value);
|
|
92
|
-
if (channel === "shape") return validateShapeRange(value);
|
|
93
|
-
if (channel === "strokeDash") return validateStrokeDashRange(value);
|
|
94
|
-
if (channel === "size") return validateSizeRange(value);
|
|
95
|
-
return validateScaleRange(value);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function validateTypeTransition(scale, nextType, channel, consumers) {
|
|
99
|
-
if (nextType === scale.type) return;
|
|
100
|
-
validateScaleType(nextType);
|
|
101
|
-
if (consumers.length === 0) return;
|
|
102
|
-
if (nextType === "sequential" || isDiscretizedColorScaleType(nextType)) {
|
|
103
|
-
const discretized = isDiscretizedColorScaleType(nextType);
|
|
104
|
-
if (channel !== "color" || consumers.some(consumer =>
|
|
105
|
-
["nominal", "ordinal"].includes(consumer.encoding.fieldType) ||
|
|
106
|
-
(discretized && (
|
|
107
|
-
consumer.encoding.fieldType !== "quantitative" ||
|
|
108
|
-
consumer.layer.mark?.type !== "point"
|
|
109
|
-
)) ||
|
|
110
|
-
(!discretized && !["point", "bar"].includes(consumer.layer.mark?.type))
|
|
111
|
-
)) {
|
|
112
|
-
throw new Error(
|
|
113
|
-
`Scale "${scale.id}" has a consumer incompatible with type "${nextType}".`
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
if (nextType === "time") {
|
|
119
|
-
if (
|
|
120
|
-
consumers.length > 0 &&
|
|
121
|
-
(!["x", "y", "theta"].includes(channel) ||
|
|
122
|
-
consumers.some(consumer => consumer.encoding.fieldType !== "temporal"))
|
|
123
|
-
) {
|
|
124
|
-
throw new Error(
|
|
125
|
-
`Scale "${scale.id}" has a consumer incompatible with type "time".`
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
const discrete = isDiscretePositionScaleType(nextType);
|
|
131
|
-
if (discrete) {
|
|
132
|
-
if (
|
|
133
|
-
consumers.length > 0 &&
|
|
134
|
-
(!["x", "y", "theta"].includes(channel) || consumers.some(consumer =>
|
|
135
|
-
!["nominal", "ordinal"].includes(consumer.encoding.fieldType)
|
|
136
|
-
))
|
|
137
|
-
) {
|
|
138
|
-
throw new Error(
|
|
139
|
-
`Scale "${scale.id}" has a consumer incompatible with type "${nextType}".`
|
|
140
|
-
);
|
|
141
|
-
}
|
|
142
|
-
if (
|
|
143
|
-
nextType === "point" &&
|
|
144
|
-
consumers.some(consumer => consumer.layer.mark?.type === "bar")
|
|
145
|
-
) {
|
|
146
|
-
throw new Error("Point scales cannot provide bar bandwidth.");
|
|
147
|
-
}
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
const quantitative = nextType === "linear" || isTransformedScaleType(nextType);
|
|
151
|
-
if (
|
|
152
|
-
!quantitative ||
|
|
153
|
-
(consumers.length > 0 && !["x", "y", "theta", "radius"].includes(channel))
|
|
154
|
-
) {
|
|
155
|
-
throw new Error(
|
|
156
|
-
"editScale type transition currently requires a quantitative position scale."
|
|
157
|
-
);
|
|
158
|
-
}
|
|
159
|
-
validateScaleTypeForRole(nextType, SCALE_ROLES.quantitativePosition);
|
|
160
|
-
if (channel === "theta" && nextType !== "linear") {
|
|
161
|
-
throw new Error("Theta quantitative position currently requires a linear scale.");
|
|
162
|
-
}
|
|
163
|
-
if (consumers.some(consumer => consumer.encoding.fieldType !== "quantitative")) {
|
|
164
|
-
throw new Error(
|
|
165
|
-
`Scale "${scale.id}" has a consumer incompatible with type "${nextType}".`
|
|
166
|
-
);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function validateLegendTypeTransition(program, scale, nextType) {
|
|
171
|
-
if (nextType === scale.type) return;
|
|
172
|
-
const legends = program.guideConfigs.legend ?? {};
|
|
173
|
-
if (
|
|
174
|
-
legends.gradient?.scale === scale.id &&
|
|
175
|
-
nextType !== "sequential"
|
|
176
|
-
) {
|
|
177
|
-
throw new Error(
|
|
178
|
-
`Scale "${scale.id}" cannot change type while its gradient legend is active.`
|
|
179
|
-
);
|
|
180
|
-
}
|
|
181
|
-
if (
|
|
182
|
-
legends.interval?.scale === scale.id &&
|
|
183
|
-
!isDiscretizedColorScaleType(nextType)
|
|
184
|
-
) {
|
|
185
|
-
throw new Error(
|
|
186
|
-
`Scale "${scale.id}" cannot change type while its interval legend is active.`
|
|
187
|
-
);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
function normalizeDefinition(scale, channel, consumers, args) {
|
|
192
|
-
const type = args.type ?? scale.type;
|
|
193
|
-
validateTypeTransition(scale, type, channel, consumers);
|
|
194
|
-
const definition = normalizeScaleDefinition({
|
|
195
|
-
type,
|
|
196
|
-
previous: scale,
|
|
197
|
-
patch: args,
|
|
198
|
-
retainCoreOnTypeChange: true,
|
|
199
|
-
retainCompatibleOnTypeChange: true,
|
|
200
|
-
validateDomain: (scaleType, value) =>
|
|
201
|
-
isDiscretizedColorScaleType(scaleType)
|
|
202
|
-
? validateDiscretizedColorDomain(scaleType, value)
|
|
203
|
-
: hasOrdinalDomain(scaleType)
|
|
204
|
-
? validateOrdinalDomain(value)
|
|
205
|
-
: validateScaleDomain(value),
|
|
206
|
-
validateRange: (scaleType, value) =>
|
|
207
|
-
validateRangeForChannel({ type: scaleType }, channel, value)
|
|
208
|
-
});
|
|
209
|
-
const typeChanged = type !== scale.type;
|
|
210
|
-
const unknown = Object.hasOwn(args, "unknown")
|
|
211
|
-
? args.unknown
|
|
212
|
-
: typeChanged ? undefined : scale.unknown;
|
|
213
|
-
if (unknown !== undefined) {
|
|
214
|
-
if (consumers.some(consumer => consumer.layer.mark?.type !== "point")) {
|
|
215
|
-
throw new Error(
|
|
216
|
-
"Scale unknown currently requires row-owned point consumers."
|
|
217
|
-
);
|
|
218
|
-
}
|
|
219
|
-
definition.unknown = consumers.length === 0
|
|
220
|
-
? unknown
|
|
221
|
-
: validateScaleUnknown(channel, unknown);
|
|
222
|
-
}
|
|
223
|
-
return definition;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
48
|
function sameValue(left, right) {
|
|
227
49
|
return JSON.stringify(left) === JSON.stringify(right);
|
|
228
50
|
}
|
|
@@ -254,31 +76,11 @@ export const editScale = action(
|
|
|
254
76
|
if (!REQUESTED_CHANGES.some(property => Object.hasOwn(args, property))) {
|
|
255
77
|
throw new Error("editScale requires at least one editable property.");
|
|
256
78
|
}
|
|
257
|
-
const hasPalette = Object.hasOwn(args, "palette");
|
|
258
|
-
if (hasPalette && Object.hasOwn(args, "range")) {
|
|
259
|
-
throw new Error("editScale cannot specify both palette and range.");
|
|
260
|
-
}
|
|
261
|
-
|
|
262
79
|
const id = resolveScaleId(this, args.id);
|
|
263
80
|
const scale = requireSemanticScale(this, id);
|
|
264
81
|
const consumers = findScaleConsumers(this, id);
|
|
265
|
-
const channel =
|
|
266
|
-
|
|
267
|
-
hasPalette &&
|
|
268
|
-
channel !== "color" &&
|
|
269
|
-
scale.type !== "sequential" &&
|
|
270
|
-
!isDiscretizedColorScaleType(scale.type)
|
|
271
|
-
) {
|
|
272
|
-
throw new Error("editScale palette requires a color scale.");
|
|
273
|
-
}
|
|
274
|
-
const patch = !hasPalette
|
|
275
|
-
? args
|
|
276
|
-
: Object.fromEntries([
|
|
277
|
-
...Object.entries(args).filter(([key]) => key !== "palette"),
|
|
278
|
-
["range", { palette: args.palette }]
|
|
279
|
-
]);
|
|
280
|
-
validateLegendTypeTransition(this, scale, args.type ?? scale.type);
|
|
281
|
-
const definition = normalizeDefinition(scale, channel, consumers, patch);
|
|
82
|
+
const channel = resolveScaleConsumerChannel(consumers, id);
|
|
83
|
+
const definition = prepareScaleEdit(this, scale, channel, consumers, args);
|
|
282
84
|
|
|
283
85
|
let next = this;
|
|
284
86
|
for (const property of EDITABLE) {
|