ggaction 0.0.4 → 0.0.6
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 +55 -0
- package/README.md +35 -13
- package/package.json +18 -7
- package/src/actions/boxPlots/create.js +13 -7
- package/src/actions/boxPlots/edit.js +484 -36
- package/src/actions/boxPlots/materialize.js +3 -0
- package/src/actions/boxPlots/options.js +11 -4
- 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/composition/actions.js +13 -3
- package/src/actions/coordinates/parallel.js +50 -0
- package/src/actions/data/bin2d.js +260 -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 +34 -2
- package/src/actions/data/intervalEdit.js +76 -0
- package/src/actions/data/window.js +50 -0
- package/src/actions/distributions/revision.js +326 -0
- package/src/actions/encodings/appearance.js +35 -1
- package/src/actions/encodings/color/index.js +17 -1
- package/src/actions/encodings/color/layout.js +8 -0
- package/src/actions/encodings/density/resolve.js +109 -0
- package/src/actions/encodings/density.js +330 -90
- package/src/actions/encodings/horizon/resolve.js +283 -0
- package/src/actions/encodings/horizon.js +364 -0
- package/src/actions/encodings/index.js +8 -0
- 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/index.js +15 -2
- 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 +24 -2
- package/src/actions/encodings/ranged.js +3 -1
- package/src/actions/encodings/remove.js +268 -0
- package/src/actions/encodings/ruleAppearance.js +88 -10
- package/src/actions/errorBands/edit.js +121 -13
- package/src/actions/errorBars/edit.js +45 -8
- package/src/actions/errorBars/options.js +7 -1
- package/src/actions/facets/actions.js +137 -3
- package/src/actions/facets/derive.js +33 -3
- 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 +501 -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/edit.js +129 -47
- package/src/actions/guides/axes/index.js +2 -0
- package/src/actions/guides/axes/labels.js +11 -6
- package/src/actions/guides/axes/parallel/resolve.js +86 -0
- package/src/actions/guides/axes/parallel.js +228 -0
- package/src/actions/guides/axes/titles.js +11 -8
- package/src/actions/guides/guides.js +60 -5
- package/src/actions/guides/legends/categorical/actions.js +67 -19
- package/src/actions/guides/legends/continuous/common.js +3 -3
- package/src/actions/guides/legends/edit.js +63 -0
- package/src/actions/guides/legends/index.js +2 -0
- package/src/actions/guides/legends/remove.js +124 -30
- package/src/actions/guides/legends/strokeWidth.js +198 -0
- package/src/actions/index.js +6 -0
- package/src/actions/marks/arc/actions.js +46 -14
- package/src/actions/marks/area/actions.js +60 -136
- package/src/actions/marks/area/materialize.js +163 -0
- package/src/actions/marks/index.js +5 -1
- package/src/actions/marks/line/actions.js +99 -80
- 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 +97 -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} +183 -172
- package/src/actions/marks/rect/actions.js +3 -0
- package/src/actions/marks/remove.js +13 -2
- package/src/actions/marks/rule/actions.js +29 -4
- package/src/actions/marks/text/actions.js +19 -4
- 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/edit.js +247 -88
- package/src/actions/scales/consumers/common.js +18 -0
- package/src/actions/scales/consumers/families.js +14 -3
- package/src/actions/scales/definitions.js +30 -0
- package/src/actions/scales/edit.js +6 -204
- package/src/actions/scales/editPolicy.js +214 -0
- package/src/actions/scales/materialize.js +1 -1
- package/src/actions/scales/patch.js +26 -0
- package/src/actions/selection/actions.js +150 -5
- package/src/actions/selection/index.js +6 -0
- package/src/actions/violinPlots/create.js +198 -0
- package/src/actions/violinPlots/index.js +5 -0
- package/src/core/textMetrics.js +47 -0
- package/src/core/vocabulary.js +14 -2
- package/src/grammar/arcs.js +49 -12
- package/src/grammar/areaSeries.js +41 -5
- 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 +252 -26
- package/src/grammar/facets/dependencies.js +3 -11
- package/src/grammar/facets/index.js +3 -1
- package/src/grammar/gradientProfile.js +213 -0
- package/src/grammar/horizon.js +455 -0
- package/src/grammar/jitter.js +197 -0
- package/src/grammar/lineSeries.js +69 -0
- package/src/grammar/paint.js +88 -0
- package/src/grammar/parallelCoordinates.js +213 -0
- package/src/grammar/pathOrder.js +35 -0
- package/src/grammar/pointShapes.js +24 -0
- package/src/grammar/rules.js +1 -0
- package/src/grammar/scales/appearance.js +21 -0
- 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/transforms.js +100 -11
- 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/dataProvenance.js +56 -2
- package/src/materialization/dependencies.js +9 -14
- 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 +21 -1
- package/src/materialization/marks/index.js +3 -0
- package/src/materialization/marks/pathOrder.js +71 -0
- package/src/materialization/marks/policies.js +6 -2
- package/src/materialization/scaleGuideDependencies.js +8 -0
- package/src/materialization/scales/resolve.js +2 -0
- 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 +20 -9
- 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/selectors/datasets.js +4 -0
- package/src/theme/defaults.js +4 -0
- package/types/extension.d.ts +6 -0
- package/types/index.d.ts +77 -0
- package/types/program.d.ts +703 -17
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { isAggregate } from "../../grammar/aggregate.js";
|
|
2
|
+
import { findTransformPolicy } from "../../grammar/transforms.js";
|
|
3
|
+
import { findDataset } from "../../selectors/datasets.js";
|
|
4
|
+
|
|
5
|
+
function hasRowPreservingProvenance(program, dataset) {
|
|
6
|
+
const visited = new Set();
|
|
7
|
+
let current = dataset;
|
|
8
|
+
while (current?.source !== undefined) {
|
|
9
|
+
if (visited.has(current.id) || current.transform?.length !== 1) return false;
|
|
10
|
+
visited.add(current.id);
|
|
11
|
+
const policy = findTransformPolicy(current.transform[0].type);
|
|
12
|
+
if (
|
|
13
|
+
policy?.facetTopology !== "rowPreserving" &&
|
|
14
|
+
policy?.provenanceTransparent !== true
|
|
15
|
+
) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
current = findDataset(program, current.source);
|
|
19
|
+
if (current === undefined) return false;
|
|
20
|
+
}
|
|
21
|
+
return current !== undefined;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function isPolar(layer) {
|
|
25
|
+
return layer.encoding?.theta !== undefined ||
|
|
26
|
+
layer.encoding?.radius !== undefined;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function isCompositePath(program, layer) {
|
|
30
|
+
const config = program.markConfigs[layer.id];
|
|
31
|
+
return config?.errorBand !== undefined ||
|
|
32
|
+
config?.errorBandBoundary !== undefined ||
|
|
33
|
+
config?.regression !== undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function pathOrderCompatibility(program, layer) {
|
|
37
|
+
if (!["line", "area"].includes(layer?.mark?.type)) {
|
|
38
|
+
return { compatible: false, reason: "requires a line or area mark" };
|
|
39
|
+
}
|
|
40
|
+
if (isPolar(layer)) {
|
|
41
|
+
return { compatible: false, reason: "does not support Polar paths" };
|
|
42
|
+
}
|
|
43
|
+
const dataset = findDataset(program, layer.data);
|
|
44
|
+
if (dataset === undefined) {
|
|
45
|
+
return { compatible: false, reason: "requires an existing dataset" };
|
|
46
|
+
}
|
|
47
|
+
if (!hasRowPreservingProvenance(program, dataset)) {
|
|
48
|
+
return {
|
|
49
|
+
compatible: false,
|
|
50
|
+
reason: "requires raw or row-preserving source data"
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
if (isCompositePath(program, layer)) {
|
|
54
|
+
return {
|
|
55
|
+
compatible: false,
|
|
56
|
+
reason: "does not support generated error or regression paths"
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
if (layer.mark.type === "line" && isAggregate(layer.encoding?.y?.aggregate)) {
|
|
60
|
+
return { compatible: false, reason: "does not support aggregate lines" };
|
|
61
|
+
}
|
|
62
|
+
return { compatible: true, dataset };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function assertPathOrderCompatible(program, layer, operation) {
|
|
66
|
+
const result = pathOrderCompatibility(program, layer);
|
|
67
|
+
if (!result.compatible) {
|
|
68
|
+
throw new Error(`${operation} ${result.reason} on mark "${layer?.id}".`);
|
|
69
|
+
}
|
|
70
|
+
return result.dataset;
|
|
71
|
+
}
|
|
@@ -6,7 +6,8 @@ import {
|
|
|
6
6
|
canMaterializePoint,
|
|
7
7
|
canMaterializeRect,
|
|
8
8
|
canMaterializeRule,
|
|
9
|
-
canMaterializeText
|
|
9
|
+
canMaterializeText,
|
|
10
|
+
isIntentionallyEmptyArea
|
|
10
11
|
} from "./capabilities.js";
|
|
11
12
|
|
|
12
13
|
const MARK_MATERIALIZATION_POLICIES = Object.freeze({
|
|
@@ -33,7 +34,10 @@ const MARK_MATERIALIZATION_POLICIES = Object.freeze({
|
|
|
33
34
|
canMaterialize: canMaterializeArea,
|
|
34
35
|
op: "rematerializeAreaMark",
|
|
35
36
|
positionEncoding: Object.freeze({ incomplete: "scale", scaleFirst: true }),
|
|
36
|
-
encoding: Object.freeze({
|
|
37
|
+
encoding: Object.freeze({
|
|
38
|
+
sharedChannels: Object.freeze(["color"]),
|
|
39
|
+
skipRematerialization: isIntentionallyEmptyArea
|
|
40
|
+
}),
|
|
37
41
|
scaleApplication: Object.freeze({ default: "defer" })
|
|
38
42
|
}),
|
|
39
43
|
arc: Object.freeze({
|
|
@@ -21,6 +21,8 @@ function usesPositionalScale(program, id) {
|
|
|
21
21
|
return program.semanticSpec.layers.some(layer =>
|
|
22
22
|
POSITION_ENCODING_CHANNELS.some(
|
|
23
23
|
channel => layer.encoding?.[channel]?.scale === id
|
|
24
|
+
) || (layer.encoding?.parallel?.dimensions ?? []).some(
|
|
25
|
+
dimension => dimension.scale === id
|
|
24
26
|
)
|
|
25
27
|
);
|
|
26
28
|
}
|
|
@@ -62,6 +64,12 @@ export function planScaleGuideRematerialization(program, id) {
|
|
|
62
64
|
const guides = [];
|
|
63
65
|
const objects = program.graphicSpec?.objects ?? {};
|
|
64
66
|
const guideConfigs = program.guideConfigs ?? {};
|
|
67
|
+
if (guideConfigs.axis?.parallel?.axes?.scales?.includes(id)) {
|
|
68
|
+
guides.push({
|
|
69
|
+
op: "rematerializeParallelAxes",
|
|
70
|
+
args: { target: guideConfigs.axis.parallel.axes.target }
|
|
71
|
+
});
|
|
72
|
+
}
|
|
65
73
|
for (const axis of CARTESIAN_AXES) {
|
|
66
74
|
if (
|
|
67
75
|
objects[axis.lineObject] &&
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
resolveShapeRange,
|
|
19
19
|
resolveSizeRange,
|
|
20
20
|
resolveStrokeDashRange,
|
|
21
|
+
resolveStrokeWidthRange,
|
|
21
22
|
resolveTransformedDomain,
|
|
22
23
|
SCALE_ROLES,
|
|
23
24
|
validateLinearScaleType,
|
|
@@ -92,6 +93,7 @@ function resolveRange({
|
|
|
92
93
|
if (channel === "shape") return resolveShapeRange(scale.range);
|
|
93
94
|
if (channel === "size") return resolveSizeRange(scale.range);
|
|
94
95
|
if (channel === "opacity") return resolveOpacityRange(scale.range);
|
|
96
|
+
if (channel === "strokeWidth") return resolveStrokeWidthRange(scale.range);
|
|
95
97
|
if (isOrdinalOffset) return undefined;
|
|
96
98
|
return resolveArcAutoPositionRange({
|
|
97
99
|
consumers,
|
|
@@ -4,7 +4,12 @@ import {
|
|
|
4
4
|
} from "../../../grammar/areaSeries.js";
|
|
5
5
|
import { deriveLineSeries } from "../../../grammar/lineSeries.js";
|
|
6
6
|
import { findUpstreamTransform } from "../../dataProvenance.js";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
channelMapFromRow,
|
|
9
|
+
finalizeItems,
|
|
10
|
+
ownFields,
|
|
11
|
+
uniqueFields
|
|
12
|
+
} from "./common.js";
|
|
8
13
|
|
|
9
14
|
function rowsForSeries(rows, key) {
|
|
10
15
|
const entries = Object.entries(key);
|
|
@@ -34,6 +39,27 @@ function seriesDefinitions(layer, rows, series) {
|
|
|
34
39
|
}
|
|
35
40
|
|
|
36
41
|
export function resolveLineItems(program, layer, dataset) {
|
|
42
|
+
if (layer.encoding?.parallel !== undefined) {
|
|
43
|
+
const parallel = layer.encoding.parallel;
|
|
44
|
+
const definitions = dataset.values.flatMap((row, index) => {
|
|
45
|
+
const incomplete = parallel.dimensions.some(dimension => {
|
|
46
|
+
const value = row[dimension.field];
|
|
47
|
+
return dimension.fieldType === "quantitative"
|
|
48
|
+
? !Number.isFinite(value)
|
|
49
|
+
: typeof value !== "string" && !Number.isFinite(value);
|
|
50
|
+
});
|
|
51
|
+
if (incomplete && parallel.missing === "drop-row") return [];
|
|
52
|
+
return [{
|
|
53
|
+
key: parallel.key === undefined
|
|
54
|
+
? `${layer.id}/row/source:${index}`
|
|
55
|
+
: `${layer.id}/row/${String(row[parallel.key])}`,
|
|
56
|
+
fields: ownFields(row),
|
|
57
|
+
channels: channelMapFromRow(row, layer),
|
|
58
|
+
members: [row]
|
|
59
|
+
}];
|
|
60
|
+
});
|
|
61
|
+
return finalizeItems(program, layer, "row", definitions, "path");
|
|
62
|
+
}
|
|
37
63
|
const derived = deriveLineSeries(dataset.values, layer);
|
|
38
64
|
return finalizeItems(
|
|
39
65
|
program,
|
|
@@ -18,11 +18,7 @@ export function resolvePointItems(program, layer, dataset) {
|
|
|
18
18
|
);
|
|
19
19
|
if (
|
|
20
20
|
!Array.isArray(graphic?.items) ||
|
|
21
|
-
!completePosition
|
|
22
|
-
(
|
|
23
|
-
layer.encoding?.size?.scale === undefined &&
|
|
24
|
-
!Number.isFinite(program.markConfigs[layer.id]?.radius)
|
|
25
|
-
)
|
|
21
|
+
!completePosition
|
|
26
22
|
) {
|
|
27
23
|
throw new Error(`Point mark "${layer.id}" is incomplete for selection.`);
|
|
28
24
|
}
|
|
@@ -1,16 +1,37 @@
|
|
|
1
1
|
import { resolveRectRows } from "../../rect.js";
|
|
2
2
|
import {
|
|
3
|
+
channelMapFromRow,
|
|
3
4
|
concreteProperties,
|
|
4
5
|
finalizeItems,
|
|
5
6
|
itemKey,
|
|
6
7
|
ownFields
|
|
7
8
|
} from "./common.js";
|
|
8
9
|
|
|
10
|
+
function resolveGradientPlotItems(program, layer, dataset) {
|
|
11
|
+
const graphic = program.graphicSpec.objects[layer.id];
|
|
12
|
+
return finalizeItems(
|
|
13
|
+
program,
|
|
14
|
+
layer,
|
|
15
|
+
"rect",
|
|
16
|
+
dataset.values.map((row, index) => ({
|
|
17
|
+
key: itemKey(layer, "rect", index),
|
|
18
|
+
fields: ownFields(row),
|
|
19
|
+
channels: channelMapFromRow(row, layer),
|
|
20
|
+
properties: concreteProperties(graphic.items[index]?.properties),
|
|
21
|
+
members: [row]
|
|
22
|
+
})),
|
|
23
|
+
"rect"
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
9
27
|
export function resolveRectItems(program, layer, dataset) {
|
|
10
28
|
const graphic = program.graphicSpec.objects[layer.id];
|
|
11
29
|
if (graphic?.type !== "rect" || !Array.isArray(graphic.items)) {
|
|
12
30
|
throw new Error(`Rect mark "${layer.id}" is incomplete for selection.`);
|
|
13
31
|
}
|
|
32
|
+
if (program.markConfigs[layer.id]?.gradientPlot?.materialized === true) {
|
|
33
|
+
return resolveGradientPlotItems(program, layer, dataset);
|
|
34
|
+
}
|
|
14
35
|
const rows = resolveRectRows(program, layer, dataset);
|
|
15
36
|
const definitions = rows.map((item, graphicIndex) => ({
|
|
16
37
|
key: itemKey(layer, "rect", item.sourceIndex),
|
|
@@ -80,19 +80,19 @@ export function normalizePointHighlightStyle(args) {
|
|
|
80
80
|
};
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
function normalizeRectangularHighlightStyle(args, mark) {
|
|
84
|
-
for (const option of ["shape", "size", "
|
|
83
|
+
function normalizeRectangularHighlightStyle(args, mark, { offset = false } = {}) {
|
|
84
|
+
for (const option of ["shape", "size", "strokeDash"]) {
|
|
85
85
|
if (args[option] !== undefined) {
|
|
86
86
|
throw new Error(`${mark} highlight does not support ${option}.`);
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
|
+
if (!offset && args.offset !== undefined) {
|
|
90
|
+
throw new Error(`${mark} highlight does not support offset.`);
|
|
91
|
+
}
|
|
89
92
|
if (args.color !== undefined && args.fill !== undefined) {
|
|
90
93
|
throw new Error(`${mark} highlight accepts color or fill, not both.`);
|
|
91
94
|
}
|
|
92
|
-
const
|
|
93
|
-
args.fill ?? args.color ?? DEFAULT_COLORS.highlight,
|
|
94
|
-
`${mark} highlight fill`
|
|
95
|
-
);
|
|
95
|
+
const explicitFill = args.fill ?? args.color;
|
|
96
96
|
const opacity = args.opacity === undefined
|
|
97
97
|
? undefined
|
|
98
98
|
: validateUnitInterval(args.opacity, `${mark} highlight opacity`);
|
|
@@ -105,11 +105,22 @@ function normalizeRectangularHighlightStyle(args, mark) {
|
|
|
105
105
|
if (strokeWidth !== undefined && stroke === undefined) {
|
|
106
106
|
throw new Error(`${mark} highlight strokeWidth requires stroke.`);
|
|
107
107
|
}
|
|
108
|
+
const hasAppearanceOverride = opacity !== undefined || stroke !== undefined ||
|
|
109
|
+
strokeWidth !== undefined || args.offset !== undefined;
|
|
110
|
+
const fill = explicitFill === undefined && hasAppearanceOverride
|
|
111
|
+
? undefined
|
|
112
|
+
: validateNonEmptyString(
|
|
113
|
+
explicitFill ?? DEFAULT_COLORS.highlight,
|
|
114
|
+
`${mark} highlight fill`
|
|
115
|
+
);
|
|
108
116
|
return {
|
|
109
|
-
fill,
|
|
117
|
+
...(fill === undefined ? {} : { fill }),
|
|
110
118
|
...(opacity === undefined ? {} : { opacity }),
|
|
111
119
|
...(stroke === undefined ? {} : { stroke }),
|
|
112
|
-
...(strokeWidth === undefined ? {} : { strokeWidth })
|
|
120
|
+
...(strokeWidth === undefined ? {} : { strokeWidth }),
|
|
121
|
+
...(offset && args.offset !== undefined
|
|
122
|
+
? { offset: normalizeOffset(args.offset) }
|
|
123
|
+
: {})
|
|
113
124
|
};
|
|
114
125
|
}
|
|
115
126
|
|
|
@@ -118,7 +129,7 @@ export function normalizeBarHighlightStyle(args) {
|
|
|
118
129
|
}
|
|
119
130
|
|
|
120
131
|
export function normalizeRectHighlightStyle(args) {
|
|
121
|
-
return normalizeRectangularHighlightStyle(args, "Rect");
|
|
132
|
+
return normalizeRectangularHighlightStyle(args, "Rect", { offset: true });
|
|
122
133
|
}
|
|
123
134
|
|
|
124
135
|
function rejectHighlightOptions(args, mark, options) {
|
|
@@ -120,13 +120,14 @@ function resolveSourceTextItems(program, layer, config) {
|
|
|
120
120
|
}
|
|
121
121
|
const items = resolveMarkItems(program, source.id);
|
|
122
122
|
return items.flatMap(item => {
|
|
123
|
+
const anchor = sourceAnchor(program, source, item);
|
|
123
124
|
const concrete = concreteItem(
|
|
124
125
|
sourceTextConfig(config, source, item),
|
|
125
|
-
|
|
126
|
+
anchor,
|
|
126
127
|
contentValue(layer.encoding.text, item, source),
|
|
127
128
|
layer.encoding.text.format
|
|
128
129
|
);
|
|
129
|
-
return concrete === undefined ? [] : [concrete];
|
|
130
|
+
return concrete === undefined ? [] : [{ graphic: concrete, anchor }];
|
|
130
131
|
});
|
|
131
132
|
}
|
|
132
133
|
|
|
@@ -145,12 +146,19 @@ function resolveRowTextItems(program, layer, config) {
|
|
|
145
146
|
contentValue(layer.encoding.text, row),
|
|
146
147
|
layer.encoding.text.format
|
|
147
148
|
);
|
|
148
|
-
return concrete === undefined
|
|
149
|
+
return concrete === undefined
|
|
150
|
+
? []
|
|
151
|
+
: [{ graphic: concrete, anchor: { x: x[index], y: y[index] } }];
|
|
149
152
|
});
|
|
150
153
|
}
|
|
151
154
|
|
|
152
|
-
export function
|
|
155
|
+
export function resolveTextGraphicEntries(program, layer, config) {
|
|
153
156
|
return layer.source === undefined
|
|
154
157
|
? resolveRowTextItems(program, layer, config)
|
|
155
158
|
: resolveSourceTextItems(program, layer, config);
|
|
156
159
|
}
|
|
160
|
+
|
|
161
|
+
export function resolveTextGraphicItems(program, layer, config) {
|
|
162
|
+
return resolveTextGraphicEntries(program, layer, config)
|
|
163
|
+
.map(entry => entry.graphic);
|
|
164
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isLinearGradientPaint,
|
|
3
|
+
resolveLinearGradientCoordinates,
|
|
4
|
+
validateFillPaint
|
|
5
|
+
} from "../../grammar/paint.js";
|
|
6
|
+
|
|
7
|
+
export function applyCanvasFill(context, fill, bounds, graphicId) {
|
|
8
|
+
validateFillPaint(fill, `${graphicId}.fill`);
|
|
9
|
+
if (!isLinearGradientPaint(fill)) {
|
|
10
|
+
context.fillStyle = fill;
|
|
11
|
+
return fill;
|
|
12
|
+
}
|
|
13
|
+
if (typeof context.createLinearGradient !== "function") {
|
|
14
|
+
throw new TypeError(
|
|
15
|
+
`Graphic "${graphicId}" requires Canvas context createLinearGradient().`
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
const coordinates = resolveLinearGradientCoordinates(fill, bounds);
|
|
19
|
+
const gradient = context.createLinearGradient(
|
|
20
|
+
coordinates.from.x,
|
|
21
|
+
coordinates.from.y,
|
|
22
|
+
coordinates.to.x,
|
|
23
|
+
coordinates.to.y
|
|
24
|
+
);
|
|
25
|
+
if (gradient === null || typeof gradient !== "object" || typeof gradient.addColorStop !== "function") {
|
|
26
|
+
throw new TypeError(
|
|
27
|
+
`Graphic "${graphicId}" requires a Canvas linear gradient with addColorStop().`
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
for (const stop of fill.stops) gradient.addColorStop(stop.offset, stop.color);
|
|
31
|
+
context.fillStyle = gradient;
|
|
32
|
+
return gradient;
|
|
33
|
+
}
|
|
@@ -120,6 +120,13 @@ export function render(program, context, { pixelRatio = 1 } = {}) {
|
|
|
120
120
|
|
|
121
121
|
context.canvas.width = Math.round(width * pixelRatio);
|
|
122
122
|
context.canvas.height = Math.round(height * pixelRatio);
|
|
123
|
+
if (
|
|
124
|
+
context.canvas.style !== null &&
|
|
125
|
+
typeof context.canvas.style === "object"
|
|
126
|
+
) {
|
|
127
|
+
context.canvas.style.width = `${width}px`;
|
|
128
|
+
context.canvas.style.height = `${height}px`;
|
|
129
|
+
}
|
|
123
130
|
context.save();
|
|
124
131
|
|
|
125
132
|
try {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { requireFiniteProperty } from "./validation.js";
|
|
2
2
|
import { validateConcreteGraphicProperties } from
|
|
3
3
|
"../../grammar/schemas/concreteGraphic.js";
|
|
4
|
+
import { resolvePathCommandBounds } from "../../grammar/schemas/graphicBounds.js";
|
|
5
|
+
import { applyCanvasFill } from "./fill.js";
|
|
4
6
|
|
|
5
7
|
function drawPath(context, child, collectionId) {
|
|
6
8
|
const properties = child.properties ?? {};
|
|
@@ -19,9 +21,6 @@ function drawPath(context, child, collectionId) {
|
|
|
19
21
|
if (!hasFill && !hasStroke) {
|
|
20
22
|
throw new Error(`Graphic "${graphicId}" requires a fill or stroke property.`);
|
|
21
23
|
}
|
|
22
|
-
if (hasFill && typeof properties.fill !== "string") {
|
|
23
|
-
throw new Error(`Graphic "${graphicId}" requires a string fill property.`);
|
|
24
|
-
}
|
|
25
24
|
if (hasFill && commands.at(-1).op !== "Z") {
|
|
26
25
|
throw new Error(`Graphic "${graphicId}" requires a final Z command when filled.`);
|
|
27
26
|
}
|
|
@@ -75,7 +74,11 @@ function drawPath(context, child, collectionId) {
|
|
|
75
74
|
}
|
|
76
75
|
|
|
77
76
|
if (hasFill) {
|
|
78
|
-
|
|
77
|
+
const bounds = resolvePathCommandBounds(commands);
|
|
78
|
+
if (bounds === undefined) {
|
|
79
|
+
throw new Error(`Graphic "${graphicId}" requires finite path fill bounds.`);
|
|
80
|
+
}
|
|
81
|
+
applyCanvasFill(context, properties.fill, bounds, graphicId);
|
|
79
82
|
context.fill();
|
|
80
83
|
}
|
|
81
84
|
if (hasStroke) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { requireFiniteProperty } from "./validation.js";
|
|
2
2
|
import { validateConcreteGraphicProperties } from
|
|
3
3
|
"../../grammar/schemas/concreteGraphic.js";
|
|
4
|
+
import { applyCanvasFill } from "./fill.js";
|
|
4
5
|
|
|
5
6
|
function drawRect(context, child, collectionId) {
|
|
6
7
|
const properties = child.properties ?? {};
|
|
@@ -21,8 +22,8 @@ function drawRect(context, child, collectionId) {
|
|
|
21
22
|
`Graphic "${graphicId}" requires non-negative rect dimensions and strokeWidth.`
|
|
22
23
|
);
|
|
23
24
|
}
|
|
24
|
-
if (
|
|
25
|
-
throw new Error(`Graphic "${graphicId}" requires a
|
|
25
|
+
if (properties.fill === undefined) {
|
|
26
|
+
throw new Error(`Graphic "${graphicId}" requires a fill property.`);
|
|
26
27
|
}
|
|
27
28
|
if (typeof properties.stroke !== "string") {
|
|
28
29
|
throw new Error(`Graphic "${graphicId}" requires a string stroke property.`);
|
|
@@ -34,7 +35,12 @@ function drawRect(context, child, collectionId) {
|
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
context.globalAlpha = opacity;
|
|
37
|
-
context
|
|
38
|
+
applyCanvasFill(context, properties.fill, {
|
|
39
|
+
left: x,
|
|
40
|
+
right: x + width,
|
|
41
|
+
top: y,
|
|
42
|
+
bottom: y + height
|
|
43
|
+
}, graphicId);
|
|
38
44
|
context.fillRect(x, y, width, height);
|
|
39
45
|
context.strokeStyle = properties.stroke;
|
|
40
46
|
context.lineWidth = strokeWidth;
|
|
@@ -6,6 +6,10 @@ export function hasDataset(program, id) {
|
|
|
6
6
|
return findDataset(program, id) !== undefined;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
export function findDatasetConsumer(program, source) {
|
|
10
|
+
return program.semanticSpec.datasets.find(dataset => dataset.source === source);
|
|
11
|
+
}
|
|
12
|
+
|
|
9
13
|
export function requireDataset(program, id, label = `Dataset "${id}"`) {
|
|
10
14
|
const dataset = findDataset(program, id);
|
|
11
15
|
if (dataset === undefined) throw new Error(`${label} does not exist.`);
|
package/src/theme/defaults.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
export const DEFAULT_FONT_FAMILY = "sans-serif";
|
|
2
2
|
|
|
3
|
+
export const DEFAULT_POINT_RADIUS = 3;
|
|
4
|
+
|
|
3
5
|
export const DEFAULT_COLORS = Object.freeze({
|
|
4
6
|
mark: "#4c78a8",
|
|
5
7
|
text: "#334155",
|
|
6
8
|
strongText: "#0f172a",
|
|
7
9
|
mutedText: "#64748b",
|
|
10
|
+
axis: "#475569",
|
|
11
|
+
axisTitle: "#1e293b",
|
|
8
12
|
grid: "#e2e8f0",
|
|
9
13
|
border: "#cbd5e1",
|
|
10
14
|
sizeSymbol: "#94a3b8",
|
package/types/extension.d.ts
CHANGED
|
@@ -2,6 +2,12 @@ import type { ActionOptions } from "./program.js";
|
|
|
2
2
|
import { ChartProgram } from "./program.js";
|
|
3
3
|
|
|
4
4
|
export { ChartProgram };
|
|
5
|
+
export type {
|
|
6
|
+
FillPaint,
|
|
7
|
+
LinearGradientPaint,
|
|
8
|
+
LinearGradientPoint,
|
|
9
|
+
LinearGradientStop
|
|
10
|
+
} from "./program.js";
|
|
5
11
|
|
|
6
12
|
export interface ActionMetadata {
|
|
7
13
|
op: string;
|
package/types/index.d.ts
CHANGED
|
@@ -13,6 +13,14 @@ export type {
|
|
|
13
13
|
AxisTitleOptions,
|
|
14
14
|
AxisValue,
|
|
15
15
|
BarWidthOptions,
|
|
16
|
+
BoxPlotOptions,
|
|
17
|
+
BinnedHeatmapColorOptions,
|
|
18
|
+
BinnedHeatmapOptions,
|
|
19
|
+
BinnedHeatmapPositionChannel,
|
|
20
|
+
Bin2DCounts,
|
|
21
|
+
Bin2DDataOptions,
|
|
22
|
+
Bin2DExtent,
|
|
23
|
+
Bin2DOutputFields,
|
|
16
24
|
CanvasOptions,
|
|
17
25
|
CategoricalEncodingOptions,
|
|
18
26
|
ColorLayout,
|
|
@@ -32,23 +40,45 @@ export type {
|
|
|
32
40
|
CreateCoordinateOptions,
|
|
33
41
|
CreateGuidesOptions,
|
|
34
42
|
CreateGridOptions,
|
|
43
|
+
CreateBarPlotOptions,
|
|
44
|
+
CreateHeatmapOptions,
|
|
45
|
+
CreateHistogramOptions,
|
|
35
46
|
CreateRegressionBandOptions,
|
|
36
47
|
CreateRegressionLineOptions,
|
|
48
|
+
CreateLinePlotOptions,
|
|
49
|
+
CreateParallelCoordinatesOptions,
|
|
50
|
+
CreateScatterPlotOptions,
|
|
37
51
|
CreateScaleOptions,
|
|
38
52
|
DashPattern,
|
|
39
53
|
DashScaleOptions,
|
|
40
54
|
DashStyle,
|
|
41
55
|
DatasetDensityTransform,
|
|
56
|
+
DatasetHorizonTransform,
|
|
57
|
+
DensityPlacement,
|
|
58
|
+
BaselineDensityPlacement,
|
|
59
|
+
CategoryDensityPlacement,
|
|
60
|
+
DensityPlacementSide,
|
|
61
|
+
DensityPlacementSplit,
|
|
62
|
+
DensityPlacementWidth,
|
|
63
|
+
DensityWidthResolution,
|
|
64
|
+
DatasetBin2DOutputFields,
|
|
65
|
+
DatasetBin2DTransform,
|
|
42
66
|
DatasetFilterTransform,
|
|
43
67
|
DatasetIntervalOutputFields,
|
|
44
68
|
DatasetIntervalTransform,
|
|
69
|
+
DatasetWindowOperation,
|
|
70
|
+
DatasetWindowSort,
|
|
71
|
+
DatasetWindowTransform,
|
|
45
72
|
DatasetRegressionTransform,
|
|
46
73
|
DatasetScalar,
|
|
47
74
|
DatasetTransform,
|
|
48
75
|
EditGraphicsOptions,
|
|
76
|
+
EditBin2DDataOptions,
|
|
77
|
+
EditHorizonOptions,
|
|
49
78
|
EditCompositionLayoutOptions,
|
|
50
79
|
EditFacetHeadersOptions,
|
|
51
80
|
EditAxisOptions,
|
|
81
|
+
EditGradientPlotOptions,
|
|
52
82
|
EditGridDirectionsOptions,
|
|
53
83
|
EditGridOptions,
|
|
54
84
|
EditErrorBandBoundaryOptions,
|
|
@@ -63,6 +93,7 @@ export type {
|
|
|
63
93
|
EditScaleOptions,
|
|
64
94
|
EditSemanticOptions,
|
|
65
95
|
FieldType,
|
|
96
|
+
FillPaint,
|
|
66
97
|
FacetCompositionSpec,
|
|
67
98
|
FacetGuideOptions,
|
|
68
99
|
FacetOptions,
|
|
@@ -71,9 +102,39 @@ export type {
|
|
|
71
102
|
GraphicObject,
|
|
72
103
|
GraphicSpec,
|
|
73
104
|
GraphicType,
|
|
105
|
+
GradientPlotAppearanceOptions,
|
|
106
|
+
GradientPlotCenterOptions,
|
|
107
|
+
GradientPlotDensityOptions,
|
|
108
|
+
GradientPlotOptions,
|
|
109
|
+
GradientPlotPositionChannel,
|
|
110
|
+
ViolinPlotAreaOptions,
|
|
111
|
+
ViolinPlotColorOptions,
|
|
112
|
+
ViolinPlotDensityOptions,
|
|
113
|
+
ViolinPlotOptions,
|
|
114
|
+
ViolinPlotPositionChannel,
|
|
115
|
+
ViolinPlotSplitOptions,
|
|
116
|
+
LinearGradientPaint,
|
|
117
|
+
LinearGradientPoint,
|
|
118
|
+
LinearGradientStop,
|
|
74
119
|
GridDirectionOptions,
|
|
120
|
+
HeatmapBaseOptions,
|
|
121
|
+
HeatmapBinOptions,
|
|
75
122
|
HistogramEncodingOptions,
|
|
123
|
+
HorizonEncodingOptions,
|
|
124
|
+
HorizonMissingPolicy,
|
|
125
|
+
HorizonOutputFields,
|
|
126
|
+
HorizonOverflowPolicy,
|
|
127
|
+
HorizonPaletteOptions,
|
|
128
|
+
HorizonResolution,
|
|
129
|
+
HorizonXEncoding,
|
|
130
|
+
HorizonYEncoding,
|
|
76
131
|
HighlightMarksOptions,
|
|
132
|
+
JitterMaxOffset,
|
|
133
|
+
JitterPointsOptions,
|
|
134
|
+
LabelLayoutAxis,
|
|
135
|
+
LabelLayoutBounds,
|
|
136
|
+
LabelLayoutOptions,
|
|
137
|
+
LabelLeaderOptions,
|
|
77
138
|
LegendOptions,
|
|
78
139
|
EditLegendOptions,
|
|
79
140
|
ContinuousColorInterpolation,
|
|
@@ -81,7 +142,12 @@ export type {
|
|
|
81
142
|
OpacityEncodingOptions,
|
|
82
143
|
OpacityScaleOptions,
|
|
83
144
|
OffsetScaleOptions,
|
|
145
|
+
PathOrderEncodingOptions,
|
|
146
|
+
ParallelCoordinatesEncodingOptions,
|
|
147
|
+
ParallelDimension,
|
|
148
|
+
ParallelMissingPolicy,
|
|
84
149
|
PointShape,
|
|
150
|
+
PreGriddedHeatmapOptions,
|
|
85
151
|
MarkSelector,
|
|
86
152
|
MarkGraphicProperty,
|
|
87
153
|
Palette,
|
|
@@ -100,8 +166,11 @@ export type {
|
|
|
100
166
|
EditRectMarkOptions,
|
|
101
167
|
RemoveAxisOptions,
|
|
102
168
|
RemoveGridOptions,
|
|
169
|
+
RemoveJitterOptions,
|
|
170
|
+
RemoveLabelLayoutOptions,
|
|
103
171
|
RemoveLegendOptions,
|
|
104
172
|
RemoveMarkOptions,
|
|
173
|
+
RemovePathOrderOptions,
|
|
105
174
|
ScaleRange,
|
|
106
175
|
ScaleOptions,
|
|
107
176
|
ScaleType,
|
|
@@ -113,6 +182,10 @@ export type {
|
|
|
113
182
|
SemanticSpec,
|
|
114
183
|
SelectMarksOptions,
|
|
115
184
|
StrokeDashEncodingOptions,
|
|
185
|
+
StrokeWidthEncodingOptions,
|
|
186
|
+
StrokeWidthScaleOptions,
|
|
187
|
+
ThetaEncodingOptions,
|
|
188
|
+
ThetaScaleOptions,
|
|
116
189
|
TraceNode,
|
|
117
190
|
TitleOptions,
|
|
118
191
|
EditTitleOptions,
|
|
@@ -123,6 +196,10 @@ export type {
|
|
|
123
196
|
TextMarkOptions,
|
|
124
197
|
XAxisPosition,
|
|
125
198
|
YAxisPosition,
|
|
199
|
+
WindowDataOptions,
|
|
200
|
+
WindowOperation,
|
|
201
|
+
WindowSort,
|
|
202
|
+
WindowSortOrder,
|
|
126
203
|
XOffsetEncodingOptions
|
|
127
204
|
} from "./program.js";
|
|
128
205
|
|