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
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { action } from "../../core/action.js";
|
|
2
|
+
import {
|
|
3
|
+
applyFacadeGuides,
|
|
4
|
+
normalizeAppearance,
|
|
5
|
+
normalizeEncoding,
|
|
6
|
+
normalizeFieldEncoding,
|
|
7
|
+
normalizeGuides,
|
|
8
|
+
normalizeTargetOptions,
|
|
9
|
+
positionArgs,
|
|
10
|
+
resolveFacadeData,
|
|
11
|
+
resolveFacadeId,
|
|
12
|
+
targetArgs,
|
|
13
|
+
validateFacadeOptions
|
|
14
|
+
} from "./shared.js";
|
|
15
|
+
|
|
16
|
+
const OPTIONS = Object.freeze([
|
|
17
|
+
"id", "data", "coordinate", "x", "y", "color", "width", "bar", "guides"
|
|
18
|
+
]);
|
|
19
|
+
const BAR_OPTIONS = Object.freeze([
|
|
20
|
+
"fill", "opacity", "stroke", "strokeWidth"
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
export const createBarPlot = action(
|
|
24
|
+
{
|
|
25
|
+
op: "createBarPlot",
|
|
26
|
+
description: "Create a Cartesian bar plot from existing chart data."
|
|
27
|
+
},
|
|
28
|
+
function (args = {}) {
|
|
29
|
+
validateFacadeOptions(args, OPTIONS, "createBarPlot");
|
|
30
|
+
const id = resolveFacadeId(this, args.id, {
|
|
31
|
+
defaultId: "barPlot",
|
|
32
|
+
operation: "createBarPlot"
|
|
33
|
+
});
|
|
34
|
+
const data = resolveFacadeData(this, args.data, "createBarPlot");
|
|
35
|
+
const bar = normalizeAppearance(args.bar, BAR_OPTIONS, "createBarPlot bar");
|
|
36
|
+
const x = normalizeFieldEncoding(args.x, "createBarPlot x");
|
|
37
|
+
const y = normalizeFieldEncoding(args.y, "createBarPlot y");
|
|
38
|
+
const color = normalizeEncoding(args.color, "createBarPlot color");
|
|
39
|
+
const width = normalizeTargetOptions(args.width, "createBarPlot width");
|
|
40
|
+
const guides = normalizeGuides(args.guides, "createBarPlot");
|
|
41
|
+
|
|
42
|
+
let next = this
|
|
43
|
+
.createBarMark({ id, data, ...bar })
|
|
44
|
+
.encodeX(positionArgs(x, { target: id, coordinate: args.coordinate }))
|
|
45
|
+
.encodeY(positionArgs(y, { target: id, coordinate: args.coordinate }));
|
|
46
|
+
if (color !== undefined) next = next.encodeColor(targetArgs(color, id));
|
|
47
|
+
if (width !== undefined) next = next.encodeBarWidth(targetArgs(width, id));
|
|
48
|
+
return applyFacadeGuides(next, guides);
|
|
49
|
+
}
|
|
50
|
+
);
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { action } from "../../core/action.js";
|
|
2
|
+
import { isPlainObject } from "../../core/immutable.js";
|
|
3
|
+
import {
|
|
4
|
+
validateNonEmptyString,
|
|
5
|
+
validateOptionObject
|
|
6
|
+
} from "../../core/validation.js";
|
|
7
|
+
import { requireDataset } from "../../selectors/datasets.js";
|
|
8
|
+
import {
|
|
9
|
+
applyFacadeGuides,
|
|
10
|
+
normalizeAppearance,
|
|
11
|
+
normalizeFieldEncoding,
|
|
12
|
+
normalizeGuides,
|
|
13
|
+
positionArgs,
|
|
14
|
+
resolveFacadeData,
|
|
15
|
+
resolveFacadeId,
|
|
16
|
+
targetArgs,
|
|
17
|
+
validateFacadeOptions
|
|
18
|
+
} from "./shared.js";
|
|
19
|
+
|
|
20
|
+
const OPTIONS = Object.freeze([
|
|
21
|
+
"id", "data", "coordinate", "x", "y", "bin", "color", "rect", "guides"
|
|
22
|
+
]);
|
|
23
|
+
const RECT_OPTIONS = Object.freeze([
|
|
24
|
+
"opacity", "stroke", "strokeWidth"
|
|
25
|
+
]);
|
|
26
|
+
const BIN_OPTIONS = Object.freeze([
|
|
27
|
+
"bins", "extent", "includeEmpty"
|
|
28
|
+
]);
|
|
29
|
+
const BINNED_POSITION_OPTIONS = Object.freeze([
|
|
30
|
+
"field", "fieldType", "scale"
|
|
31
|
+
]);
|
|
32
|
+
const BINNED_COLOR_OPTIONS = Object.freeze([
|
|
33
|
+
"scale", "palette"
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
function normalizeBin(value) {
|
|
37
|
+
if (!isPlainObject(value)) {
|
|
38
|
+
throw new TypeError("createHeatmap bin must be a plain object.");
|
|
39
|
+
}
|
|
40
|
+
validateOptionObject(value, BIN_OPTIONS, "createHeatmap bin");
|
|
41
|
+
return {
|
|
42
|
+
...value,
|
|
43
|
+
includeEmpty: value.includeEmpty ?? true
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function normalizeBinnedPosition(value, channel) {
|
|
48
|
+
const label = `createHeatmap ${channel}`;
|
|
49
|
+
const encoding = normalizeFieldEncoding(value, label);
|
|
50
|
+
validateOptionObject(encoding, BINNED_POSITION_OPTIONS, label);
|
|
51
|
+
const field = validateNonEmptyString(encoding.field, `${label} field`);
|
|
52
|
+
const fieldType = encoding.fieldType ?? "quantitative";
|
|
53
|
+
if (fieldType !== "quantitative") {
|
|
54
|
+
throw new Error(`${label} requires a quantitative field.`);
|
|
55
|
+
}
|
|
56
|
+
if (encoding.scale !== undefined && !isPlainObject(encoding.scale)) {
|
|
57
|
+
throw new TypeError(`${label} scale must be a plain object.`);
|
|
58
|
+
}
|
|
59
|
+
return { ...encoding, field, fieldType };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function normalizeBinnedColor(value) {
|
|
63
|
+
if (value === undefined) return {};
|
|
64
|
+
if (!isPlainObject(value)) {
|
|
65
|
+
throw new TypeError("createHeatmap binned color must be a plain object.");
|
|
66
|
+
}
|
|
67
|
+
validateOptionObject(value, BINNED_COLOR_OPTIONS, "createHeatmap binned color");
|
|
68
|
+
return { ...value };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function resolvedPosition(encoding, field, extent) {
|
|
72
|
+
const scale = encoding.scale ?? {};
|
|
73
|
+
return {
|
|
74
|
+
field,
|
|
75
|
+
fieldType: "quantitative",
|
|
76
|
+
scale: {
|
|
77
|
+
type: "linear",
|
|
78
|
+
nice: false,
|
|
79
|
+
zero: false,
|
|
80
|
+
...scale,
|
|
81
|
+
domain: scale.domain === undefined || scale.domain === "auto"
|
|
82
|
+
? extent
|
|
83
|
+
: scale.domain
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function axisWithTitle(value, text) {
|
|
89
|
+
if (value === false) return false;
|
|
90
|
+
if (value !== undefined && !isPlainObject(value)) {
|
|
91
|
+
throw new TypeError("createHeatmap binned axis must be false or a plain object.");
|
|
92
|
+
}
|
|
93
|
+
if (value?.title !== undefined && !isPlainObject(value.title)) {
|
|
94
|
+
throw new TypeError("createHeatmap binned axis title must be a plain object.");
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
...value,
|
|
98
|
+
title: {
|
|
99
|
+
text,
|
|
100
|
+
...value?.title
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function binnedGuides(guides, xTitle, yTitle) {
|
|
106
|
+
if (guides === false) return false;
|
|
107
|
+
for (const key of ["axes", "legend"]) {
|
|
108
|
+
if (
|
|
109
|
+
guides[key] !== undefined &&
|
|
110
|
+
guides[key] !== false &&
|
|
111
|
+
!isPlainObject(guides[key])
|
|
112
|
+
) {
|
|
113
|
+
throw new TypeError(
|
|
114
|
+
`createHeatmap guides ${key} must be false or a plain object.`
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const axes = guides.axes === false
|
|
119
|
+
? false
|
|
120
|
+
: {
|
|
121
|
+
...guides.axes,
|
|
122
|
+
x: axisWithTitle(guides.axes?.x, xTitle),
|
|
123
|
+
y: axisWithTitle(guides.axes?.y, yTitle)
|
|
124
|
+
};
|
|
125
|
+
const legend = guides.legend === false
|
|
126
|
+
? false
|
|
127
|
+
: { title: "Count", ...guides.legend };
|
|
128
|
+
return {
|
|
129
|
+
...guides,
|
|
130
|
+
axes,
|
|
131
|
+
grid: guides.grid ?? false,
|
|
132
|
+
legend
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function createPreGriddedHeatmap(program, args, { id, data, rect, guides }) {
|
|
137
|
+
const x = normalizeFieldEncoding(args.x, "createHeatmap x");
|
|
138
|
+
const y = normalizeFieldEncoding(args.y, "createHeatmap y");
|
|
139
|
+
const color = normalizeFieldEncoding(args.color, "createHeatmap color");
|
|
140
|
+
const next = program
|
|
141
|
+
.createRectMark({ id, data, ...rect })
|
|
142
|
+
.encodeX(positionArgs(x, { target: id, coordinate: args.coordinate }))
|
|
143
|
+
.encodeY(positionArgs(y, { target: id, coordinate: args.coordinate }))
|
|
144
|
+
.encodeColor(targetArgs(color, id));
|
|
145
|
+
return applyFacadeGuides(next, guides);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function createBinnedHeatmap(program, args, { id, data, rect, guides }) {
|
|
149
|
+
const x = normalizeBinnedPosition(args.x, "x");
|
|
150
|
+
const y = normalizeBinnedPosition(args.y, "y");
|
|
151
|
+
const bin = normalizeBin(args.bin);
|
|
152
|
+
const color = normalizeBinnedColor(args.color);
|
|
153
|
+
const resolvedGuides = binnedGuides(guides, x.field, y.field);
|
|
154
|
+
const generatedData = `${id}Bin2DData`;
|
|
155
|
+
const binned = program.createBin2DData({
|
|
156
|
+
id: generatedData,
|
|
157
|
+
source: data,
|
|
158
|
+
x: x.field,
|
|
159
|
+
y: y.field,
|
|
160
|
+
...bin
|
|
161
|
+
});
|
|
162
|
+
const dataset = requireDataset(binned, generatedData);
|
|
163
|
+
const transform = dataset.transform[0];
|
|
164
|
+
const resolved = transform.resolved;
|
|
165
|
+
const xEncoding = resolvedPosition(x, transform.as.x0, resolved.extent.x);
|
|
166
|
+
const yEncoding = resolvedPosition(y, transform.as.y0, resolved.extent.y);
|
|
167
|
+
|
|
168
|
+
const next = binned
|
|
169
|
+
.createRectMark({ id, data: generatedData, ...rect })
|
|
170
|
+
.encodeX(positionArgs(xEncoding, { target: id, coordinate: args.coordinate }))
|
|
171
|
+
.encodeX2({ target: id, field: transform.as.x1, fieldType: "quantitative" })
|
|
172
|
+
.encodeY(positionArgs(yEncoding, { target: id, coordinate: args.coordinate }))
|
|
173
|
+
.encodeY2({ target: id, field: transform.as.y1, fieldType: "quantitative" })
|
|
174
|
+
.encodeColor({
|
|
175
|
+
target: id,
|
|
176
|
+
field: transform.as.count,
|
|
177
|
+
fieldType: "quantitative",
|
|
178
|
+
...color
|
|
179
|
+
});
|
|
180
|
+
return applyFacadeGuides(
|
|
181
|
+
next,
|
|
182
|
+
resolvedGuides
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export const createHeatmap = action(
|
|
187
|
+
{
|
|
188
|
+
op: "createHeatmap",
|
|
189
|
+
description: "Create a pre-gridded or rectangularly binned heatmap."
|
|
190
|
+
},
|
|
191
|
+
function (args = {}) {
|
|
192
|
+
validateFacadeOptions(args, OPTIONS, "createHeatmap");
|
|
193
|
+
const id = resolveFacadeId(this, args.id, {
|
|
194
|
+
defaultId: "heatmap",
|
|
195
|
+
operation: "createHeatmap"
|
|
196
|
+
});
|
|
197
|
+
const data = resolveFacadeData(this, args.data, "createHeatmap");
|
|
198
|
+
const rect = normalizeAppearance(args.rect, RECT_OPTIONS, "createHeatmap rect");
|
|
199
|
+
const guides = normalizeGuides(args.guides, "createHeatmap");
|
|
200
|
+
const shared = { id, data, rect, guides };
|
|
201
|
+
return args.bin === undefined
|
|
202
|
+
? createPreGriddedHeatmap(this, args, shared)
|
|
203
|
+
: createBinnedHeatmap(this, args, shared);
|
|
204
|
+
}
|
|
205
|
+
);
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { action } from "../../core/action.js";
|
|
2
|
+
import { validateNonEmptyString } from "../../core/validation.js";
|
|
3
|
+
import {
|
|
4
|
+
applyFacadeGuides,
|
|
5
|
+
normalizeAppearance,
|
|
6
|
+
normalizeEncoding,
|
|
7
|
+
normalizeGuides,
|
|
8
|
+
resolveFacadeData,
|
|
9
|
+
resolveFacadeId,
|
|
10
|
+
targetArgs,
|
|
11
|
+
validateFacadeOptions
|
|
12
|
+
} from "./shared.js";
|
|
13
|
+
|
|
14
|
+
const OPTIONS = Object.freeze([
|
|
15
|
+
"id", "data", "coordinate", "field", "maxBins", "binStep",
|
|
16
|
+
"binBoundaries", "stack", "xScale", "yScale", "color", "bar", "guides"
|
|
17
|
+
]);
|
|
18
|
+
const BAR_OPTIONS = Object.freeze([
|
|
19
|
+
"fill", "opacity", "stroke", "strokeWidth"
|
|
20
|
+
]);
|
|
21
|
+
const HISTOGRAM_OPTIONS = Object.freeze([
|
|
22
|
+
"maxBins", "binStep", "binBoundaries", "stack", "xScale", "yScale"
|
|
23
|
+
]);
|
|
24
|
+
|
|
25
|
+
export const createHistogram = action(
|
|
26
|
+
{
|
|
27
|
+
op: "createHistogram",
|
|
28
|
+
description: "Create a binned count histogram from existing chart data."
|
|
29
|
+
},
|
|
30
|
+
function (args = {}) {
|
|
31
|
+
validateFacadeOptions(args, OPTIONS, "createHistogram");
|
|
32
|
+
const id = resolveFacadeId(this, args.id, {
|
|
33
|
+
defaultId: "histogram",
|
|
34
|
+
operation: "createHistogram"
|
|
35
|
+
});
|
|
36
|
+
const data = resolveFacadeData(this, args.data, "createHistogram");
|
|
37
|
+
const field = validateNonEmptyString(args.field, "createHistogram field");
|
|
38
|
+
const bar = normalizeAppearance(args.bar, BAR_OPTIONS, "createHistogram bar");
|
|
39
|
+
const color = normalizeEncoding(args.color, "createHistogram color");
|
|
40
|
+
const guides = normalizeGuides(args.guides, "createHistogram");
|
|
41
|
+
const histogram = Object.fromEntries(
|
|
42
|
+
HISTOGRAM_OPTIONS
|
|
43
|
+
.filter(key => Object.hasOwn(args, key))
|
|
44
|
+
.map(key => [key, args[key]])
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
let next = this
|
|
48
|
+
.createBarMark({ id, data, ...bar })
|
|
49
|
+
.encodeHistogram({
|
|
50
|
+
field,
|
|
51
|
+
target: id,
|
|
52
|
+
...(args.coordinate === undefined ? {} : { coordinate: args.coordinate }),
|
|
53
|
+
...histogram
|
|
54
|
+
});
|
|
55
|
+
if (color !== undefined) next = next.encodeColor(targetArgs(color, id));
|
|
56
|
+
return applyFacadeGuides(next, guides);
|
|
57
|
+
}
|
|
58
|
+
);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createBarPlot } from "./bar.js";
|
|
2
|
+
import { createHeatmap } from "./heatmap.js";
|
|
3
|
+
import { createHistogram } from "./histogram.js";
|
|
4
|
+
import { createLinePlot } from "./line.js";
|
|
5
|
+
import { createScatterPlot } from "./scatter.js";
|
|
6
|
+
import { createParallelCoordinates } from "./parallel.js";
|
|
7
|
+
|
|
8
|
+
export function registerChartActions(ProgramClass) {
|
|
9
|
+
ProgramClass.prototype.createBarPlot = createBarPlot;
|
|
10
|
+
ProgramClass.prototype.createHeatmap = createHeatmap;
|
|
11
|
+
ProgramClass.prototype.createHistogram = createHistogram;
|
|
12
|
+
ProgramClass.prototype.createScatterPlot = createScatterPlot;
|
|
13
|
+
ProgramClass.prototype.createLinePlot = createLinePlot;
|
|
14
|
+
ProgramClass.prototype.createParallelCoordinates = createParallelCoordinates;
|
|
15
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { action } from "../../core/action.js";
|
|
2
|
+
import { validateNonEmptyString } from "../../core/validation.js";
|
|
3
|
+
import {
|
|
4
|
+
applyFacadeGuides,
|
|
5
|
+
normalizeAppearance,
|
|
6
|
+
normalizeEncoding,
|
|
7
|
+
normalizeFieldEncoding,
|
|
8
|
+
normalizeGuides,
|
|
9
|
+
normalizeStrokeDashEncoding,
|
|
10
|
+
positionArgs,
|
|
11
|
+
resolveFacadeData,
|
|
12
|
+
resolveFacadeId,
|
|
13
|
+
targetArgs,
|
|
14
|
+
validateFacadeOptions
|
|
15
|
+
} from "./shared.js";
|
|
16
|
+
|
|
17
|
+
const OPTIONS = Object.freeze([
|
|
18
|
+
"id", "data", "coordinate", "x", "y", "color", "groupBy",
|
|
19
|
+
"strokeDash", "line", "guides"
|
|
20
|
+
]);
|
|
21
|
+
const LINE_OPTIONS = Object.freeze([
|
|
22
|
+
"strokeWidth", "curve", "stroke", "opacity", "closed"
|
|
23
|
+
]);
|
|
24
|
+
|
|
25
|
+
export const createLinePlot = action(
|
|
26
|
+
{
|
|
27
|
+
op: "createLinePlot",
|
|
28
|
+
description: "Create a Cartesian line plot from existing chart data."
|
|
29
|
+
},
|
|
30
|
+
function (args = {}) {
|
|
31
|
+
validateFacadeOptions(args, OPTIONS, "createLinePlot");
|
|
32
|
+
const id = resolveFacadeId(this, args.id, {
|
|
33
|
+
defaultId: "linePlot",
|
|
34
|
+
operation: "createLinePlot"
|
|
35
|
+
});
|
|
36
|
+
const data = resolveFacadeData(this, args.data, "createLinePlot");
|
|
37
|
+
const line = normalizeAppearance(
|
|
38
|
+
args.line,
|
|
39
|
+
LINE_OPTIONS,
|
|
40
|
+
"createLinePlot line"
|
|
41
|
+
);
|
|
42
|
+
if (line.closed === true) {
|
|
43
|
+
throw new Error(
|
|
44
|
+
"createLinePlot is Cartesian; closed lines require Polar line authoring."
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
const x = normalizeFieldEncoding(args.x, "createLinePlot x");
|
|
48
|
+
const y = normalizeFieldEncoding(args.y, "createLinePlot y");
|
|
49
|
+
const color = normalizeEncoding(args.color, "createLinePlot color");
|
|
50
|
+
const strokeDash = normalizeStrokeDashEncoding(args.strokeDash);
|
|
51
|
+
const groupBy = args.groupBy === undefined
|
|
52
|
+
? undefined
|
|
53
|
+
: validateNonEmptyString(args.groupBy, "createLinePlot groupBy");
|
|
54
|
+
const guides = normalizeGuides(args.guides, "createLinePlot");
|
|
55
|
+
|
|
56
|
+
let next = this
|
|
57
|
+
.createLineMark({ id, data, ...line })
|
|
58
|
+
.encodeX(positionArgs(x, { target: id, coordinate: args.coordinate }))
|
|
59
|
+
.encodeY(positionArgs(y, { target: id, coordinate: args.coordinate }));
|
|
60
|
+
if (color !== undefined) next = next.encodeColor(targetArgs(color, id));
|
|
61
|
+
if (groupBy !== undefined) {
|
|
62
|
+
next = next.encodeGroup({ field: groupBy, target: id });
|
|
63
|
+
}
|
|
64
|
+
if (strokeDash !== undefined) {
|
|
65
|
+
next = next.encodeStrokeDash(targetArgs(strokeDash, id));
|
|
66
|
+
}
|
|
67
|
+
return applyFacadeGuides(next, guides);
|
|
68
|
+
}
|
|
69
|
+
);
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { action } from "../../core/action.js";
|
|
2
|
+
import { resolveParallelCoordinate } from "../coordinates/parallel.js";
|
|
3
|
+
import {
|
|
4
|
+
applyFacadeGuides,
|
|
5
|
+
normalizeAppearance,
|
|
6
|
+
normalizeEncoding,
|
|
7
|
+
normalizeGuides,
|
|
8
|
+
normalizeStrokeDashEncoding,
|
|
9
|
+
resolveFacadeData,
|
|
10
|
+
resolveFacadeId,
|
|
11
|
+
targetArgs,
|
|
12
|
+
validateFacadeOptions
|
|
13
|
+
} from "./shared.js";
|
|
14
|
+
|
|
15
|
+
const OPTIONS = Object.freeze([
|
|
16
|
+
"id", "data", "coordinate", "dimensions", "key", "missing",
|
|
17
|
+
"color", "strokeDash", "line", "guides"
|
|
18
|
+
]);
|
|
19
|
+
const LINE_OPTIONS = Object.freeze([
|
|
20
|
+
"strokeWidth", "stroke", "opacity", "curve", "closed"
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
export const createParallelCoordinates = action(
|
|
24
|
+
{
|
|
25
|
+
op: "createParallelCoordinates",
|
|
26
|
+
description: "Create a complete Parallel-coordinates chart."
|
|
27
|
+
},
|
|
28
|
+
function (args = {}) {
|
|
29
|
+
validateFacadeOptions(args, OPTIONS, "createParallelCoordinates");
|
|
30
|
+
const id = resolveFacadeId(this, args.id, {
|
|
31
|
+
defaultId: "parallelCoordinates",
|
|
32
|
+
operation: "createParallelCoordinates"
|
|
33
|
+
});
|
|
34
|
+
const data = resolveFacadeData(this, args.data, "createParallelCoordinates");
|
|
35
|
+
const coordinate = resolveParallelCoordinate(this, {
|
|
36
|
+
requested: args.coordinate,
|
|
37
|
+
operation: "createParallelCoordinates",
|
|
38
|
+
useCurrent: true
|
|
39
|
+
}).id;
|
|
40
|
+
const line = normalizeAppearance(
|
|
41
|
+
args.line,
|
|
42
|
+
LINE_OPTIONS,
|
|
43
|
+
"createParallelCoordinates line"
|
|
44
|
+
);
|
|
45
|
+
if (line.closed === true || (line.curve !== undefined && line.curve !== "linear")) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
"createParallelCoordinates requires a linear open line."
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
const color = normalizeEncoding(
|
|
51
|
+
args.color,
|
|
52
|
+
"createParallelCoordinates color"
|
|
53
|
+
);
|
|
54
|
+
const strokeDash = normalizeStrokeDashEncoding(
|
|
55
|
+
args.strokeDash,
|
|
56
|
+
"createParallelCoordinates strokeDash"
|
|
57
|
+
);
|
|
58
|
+
const guides = normalizeGuides(args.guides, "createParallelCoordinates");
|
|
59
|
+
|
|
60
|
+
let next = this
|
|
61
|
+
.createCoordinate({ id: coordinate, type: "parallel" })
|
|
62
|
+
.createLineMark({ id, data, ...line })
|
|
63
|
+
.encodeParallelCoordinates({
|
|
64
|
+
target: id,
|
|
65
|
+
coordinate,
|
|
66
|
+
dimensions: args.dimensions,
|
|
67
|
+
...(args.key === undefined ? {} : { key: args.key }),
|
|
68
|
+
...(args.missing === undefined ? {} : { missing: args.missing })
|
|
69
|
+
});
|
|
70
|
+
if (color !== undefined) next = next.encodeColor(targetArgs(color, id));
|
|
71
|
+
if (strokeDash !== undefined) {
|
|
72
|
+
next = next.encodeStrokeDash(targetArgs(strokeDash, id));
|
|
73
|
+
}
|
|
74
|
+
return applyFacadeGuides(next, guides);
|
|
75
|
+
}
|
|
76
|
+
);
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { action } from "../../core/action.js";
|
|
2
|
+
import {
|
|
3
|
+
applyFacadeGuides,
|
|
4
|
+
normalizeAppearance,
|
|
5
|
+
normalizeEncoding,
|
|
6
|
+
normalizeFieldEncoding,
|
|
7
|
+
normalizeGuides,
|
|
8
|
+
positionArgs,
|
|
9
|
+
resolveFacadeData,
|
|
10
|
+
resolveFacadeId,
|
|
11
|
+
targetArgs,
|
|
12
|
+
validateFacadeOptions
|
|
13
|
+
} from "./shared.js";
|
|
14
|
+
|
|
15
|
+
const OPTIONS = Object.freeze([
|
|
16
|
+
"id", "data", "coordinate", "x", "y", "color", "size", "shape",
|
|
17
|
+
"point", "guides"
|
|
18
|
+
]);
|
|
19
|
+
const POINT_OPTIONS = Object.freeze([
|
|
20
|
+
"shape", "fill", "opacity", "stroke", "strokeWidth"
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
export const createScatterPlot = action(
|
|
24
|
+
{
|
|
25
|
+
op: "createScatterPlot",
|
|
26
|
+
description: "Create a Cartesian scatter plot from existing chart data."
|
|
27
|
+
},
|
|
28
|
+
function (args = {}) {
|
|
29
|
+
validateFacadeOptions(args, OPTIONS, "createScatterPlot");
|
|
30
|
+
const id = resolveFacadeId(this, args.id, {
|
|
31
|
+
defaultId: "scatterPlot",
|
|
32
|
+
operation: "createScatterPlot"
|
|
33
|
+
});
|
|
34
|
+
const data = resolveFacadeData(this, args.data, "createScatterPlot");
|
|
35
|
+
const point = normalizeAppearance(
|
|
36
|
+
args.point,
|
|
37
|
+
POINT_OPTIONS,
|
|
38
|
+
"createScatterPlot point"
|
|
39
|
+
);
|
|
40
|
+
const x = normalizeFieldEncoding(args.x, "createScatterPlot x");
|
|
41
|
+
const y = normalizeFieldEncoding(args.y, "createScatterPlot y");
|
|
42
|
+
const color = normalizeEncoding(args.color, "createScatterPlot color");
|
|
43
|
+
const size = normalizeEncoding(args.size, "createScatterPlot size");
|
|
44
|
+
const shape = normalizeEncoding(args.shape, "createScatterPlot shape");
|
|
45
|
+
const guides = normalizeGuides(args.guides, "createScatterPlot");
|
|
46
|
+
|
|
47
|
+
let next = this
|
|
48
|
+
.createPointMark({ id, data, ...point })
|
|
49
|
+
.encodeX(positionArgs(x, { target: id, coordinate: args.coordinate }))
|
|
50
|
+
.encodeY(positionArgs(y, { target: id, coordinate: args.coordinate }));
|
|
51
|
+
if (color !== undefined) next = next.encodeColor(targetArgs(color, id));
|
|
52
|
+
if (size !== undefined) next = next.encodeSize(targetArgs(size, id));
|
|
53
|
+
if (shape !== undefined) next = next.encodeShape(targetArgs(shape, id));
|
|
54
|
+
return applyFacadeGuides(next, guides);
|
|
55
|
+
}
|
|
56
|
+
);
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { resolveOptionalUserId, validateUserId } from "../../core/identifiers.js";
|
|
2
|
+
import { isPlainObject } from "../../core/immutable.js";
|
|
3
|
+
import { validateOptionObject } from "../../core/validation.js";
|
|
4
|
+
import { findDataset } from "../../selectors/datasets.js";
|
|
5
|
+
import { hasLayer } from "../../selectors/layers.js";
|
|
6
|
+
|
|
7
|
+
export function validateFacadeOptions(args, supported, operation) {
|
|
8
|
+
return validateOptionObject(args, supported, operation);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function resolveFacadeData(program, requested, operation) {
|
|
12
|
+
if (requested !== undefined) {
|
|
13
|
+
const id = validateUserId(requested, `${operation} dataset id`);
|
|
14
|
+
if (findDataset(program, id) === undefined) {
|
|
15
|
+
throw new Error(`Unknown dataset "${id}".`);
|
|
16
|
+
}
|
|
17
|
+
return id;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const current = program.context.currentData;
|
|
21
|
+
if (current !== undefined && findDataset(program, current) !== undefined) {
|
|
22
|
+
return current;
|
|
23
|
+
}
|
|
24
|
+
const datasets = program.semanticSpec.datasets;
|
|
25
|
+
if (datasets.length === 1) return datasets[0].id;
|
|
26
|
+
if (datasets.length > 1) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
`${operation} requires data when multiple datasets are available.`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
throw new Error(`${operation} requires data or one inferable dataset.`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function resolveFacadeId(program, requested, {
|
|
35
|
+
defaultId,
|
|
36
|
+
operation
|
|
37
|
+
}) {
|
|
38
|
+
return resolveOptionalUserId(requested, {
|
|
39
|
+
defaultId,
|
|
40
|
+
label: `${operation} id`,
|
|
41
|
+
operation,
|
|
42
|
+
ambiguous: hasLayer(program, defaultId) ||
|
|
43
|
+
program.graphicSpec.objects[defaultId] !== undefined
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function normalizeFieldEncoding(value, label) {
|
|
48
|
+
if (typeof value === "string") return { field: value };
|
|
49
|
+
if (!isPlainObject(value)) {
|
|
50
|
+
throw new TypeError(`${label} must be a field string or a plain object.`);
|
|
51
|
+
}
|
|
52
|
+
if (Object.hasOwn(value, "target") || Object.hasOwn(value, "coordinate")) {
|
|
53
|
+
throw new Error(
|
|
54
|
+
`${label} target and coordinate are owned by the chart facade.`
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
return { ...value };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function normalizeEncoding(value, label) {
|
|
61
|
+
if (value === undefined) return undefined;
|
|
62
|
+
return normalizeFieldEncoding(value, label);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function normalizeStrokeDashEncoding(
|
|
66
|
+
value,
|
|
67
|
+
label = "createLinePlot strokeDash"
|
|
68
|
+
) {
|
|
69
|
+
if (value === undefined) return undefined;
|
|
70
|
+
if (!isPlainObject(value)) {
|
|
71
|
+
throw new TypeError(
|
|
72
|
+
`${label} must be an encodeStrokeDash option object.`
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
if (Object.hasOwn(value, "target")) {
|
|
76
|
+
throw new Error(`${label} target is owned by the chart facade.`);
|
|
77
|
+
}
|
|
78
|
+
return { ...value };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function normalizeAppearance(value, supported, label) {
|
|
82
|
+
if (value === undefined) return {};
|
|
83
|
+
validateOptionObject(value, supported, label);
|
|
84
|
+
return { ...value };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function normalizeTargetOptions(value, label) {
|
|
88
|
+
if (value === undefined) return undefined;
|
|
89
|
+
if (!isPlainObject(value)) {
|
|
90
|
+
throw new TypeError(`${label} must be a plain object.`);
|
|
91
|
+
}
|
|
92
|
+
if (Object.hasOwn(value, "target")) {
|
|
93
|
+
throw new Error(`${label} target is owned by the chart facade.`);
|
|
94
|
+
}
|
|
95
|
+
return { ...value };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function normalizeGuides(value, operation) {
|
|
99
|
+
if (value === false) return false;
|
|
100
|
+
if (value === undefined) return {};
|
|
101
|
+
if (!isPlainObject(value)) {
|
|
102
|
+
throw new TypeError(`${operation} guides must be false or a plain object.`);
|
|
103
|
+
}
|
|
104
|
+
return { ...value };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function positionArgs(encoding, { target, coordinate }) {
|
|
108
|
+
return {
|
|
109
|
+
...encoding,
|
|
110
|
+
target,
|
|
111
|
+
...(coordinate === undefined ? {} : { coordinate })
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function targetArgs(encoding, target) {
|
|
116
|
+
return { ...encoding, target };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function applyFacadeGuides(program, guides) {
|
|
120
|
+
return guides === false ? program : program.createGuides(guides);
|
|
121
|
+
}
|