ggaction 0.0.12 → 0.0.13
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 +49 -0
- package/README.md +2 -2
- package/knowledge/action-card.schema.json +1 -1
- package/knowledge/action-cards.json +1 -1
- package/knowledge/action-cards.schema.json +1 -1
- package/knowledge/extension-authoring.md +2 -1
- package/knowledge/intent-taxonomy.json +1 -1
- package/knowledge/mcp-resources.json +1 -1
- package/knowledge/task-resolver.js +1 -1
- package/package.json +4 -2
- package/src/actions/basic.js +2 -0
- package/src/actions/boxPlots/materialize.js +2 -1
- package/src/actions/canvas/actions.js +3 -1
- package/src/actions/canvas/fitting.js +211 -0
- package/src/actions/canvas/index.js +2 -0
- package/src/actions/categoryOrder/index.js +3 -2
- package/src/actions/charts/area.js +47 -0
- package/src/actions/charts/bar.js +11 -5
- package/src/actions/charts/beeswarm.js +70 -0
- package/src/actions/charts/density.js +44 -0
- package/src/actions/charts/ecdf.js +183 -0
- package/src/actions/charts/endpoints.js +451 -0
- package/src/actions/charts/heatmap.js +2 -2
- package/src/actions/charts/histogram.js +1 -1
- package/src/actions/charts/horizon.js +71 -0
- package/src/actions/charts/index.js +37 -0
- package/src/actions/charts/interval-regression.js +169 -0
- package/src/actions/charts/line.js +5 -5
- package/src/actions/charts/parallel.js +1 -1
- package/src/actions/charts/pie.js +44 -0
- package/src/actions/charts/polar.js +123 -0
- package/src/actions/charts/radar.js +299 -0
- package/src/actions/charts/radial.js +42 -0
- package/src/actions/charts/raincloud.js +422 -0
- package/src/actions/charts/rug-strip.js +279 -0
- package/src/actions/charts/scatter.js +9 -3
- package/src/actions/charts/shared.js +69 -10
- package/src/actions/composition/actions.js +118 -1
- package/src/actions/data/bin.js +29 -0
- package/src/actions/data/computed.js +24 -0
- package/src/actions/data/derived.js +66 -0
- package/src/actions/data/ecdf.js +38 -0
- package/src/actions/data/filter.js +212 -28
- package/src/actions/data/fold.js +24 -0
- package/src/actions/data/index.js +24 -0
- package/src/actions/data/interval.js +2 -0
- package/src/actions/data/intervalEdit.js +86 -4
- package/src/actions/data/regression.js +5 -2
- package/src/actions/data/stack.js +26 -0
- package/src/actions/data/summary.js +26 -0
- package/src/actions/data/timeUnit.js +1 -1
- package/src/actions/encodings/appearance.js +32 -13
- package/src/actions/encodings/barWidth.js +8 -0
- package/src/actions/encodings/basic.js +2 -0
- package/src/actions/encodings/color/continuous.js +19 -59
- package/src/actions/encodings/color/index.js +28 -22
- package/src/actions/encodings/color/layout.js +3 -97
- package/src/actions/encodings/color/policy.js +5 -8
- package/src/actions/encodings/density/resolve.js +37 -5
- package/src/actions/encodings/density.js +120 -33
- package/src/actions/encodings/horizon/resolve.js +8 -4
- package/src/actions/encodings/horizon.js +18 -8
- package/src/actions/encodings/index.js +2 -0
- package/src/actions/encodings/offset.js +10 -16
- package/src/actions/encodings/parallel.js +2 -8
- package/src/actions/encodings/position/apply.js +10 -4
- package/src/actions/encodings/position/index.js +43 -5
- package/src/actions/encodings/position/policies/arc.js +14 -0
- package/src/actions/encodings/position/policies/area.js +4 -42
- package/src/actions/encodings/position/policies/bar.js +48 -13
- package/src/actions/encodings/position/resolve.js +67 -22
- package/src/actions/encodings/ranged.js +123 -97
- package/src/actions/encodings/remove.js +37 -32
- package/src/actions/encodings/ruleAppearance.js +23 -9
- package/src/actions/encodings/seriesLayout.js +104 -0
- package/src/actions/encodings/shared.js +25 -23
- package/src/actions/encodings/strokeDash.js +9 -6
- package/src/actions/encodings/temporal.js +7 -0
- package/src/actions/encodings/text.js +30 -15
- package/src/actions/errorBands/create.js +18 -5
- package/src/actions/errorBands/edit.js +341 -21
- package/src/actions/errorBars/create.js +21 -10
- package/src/actions/errorBars/edit.js +355 -19
- package/src/actions/facets/actions.js +410 -12
- package/src/actions/facets/derive.js +57 -8
- package/src/actions/gradientPlots/components.js +33 -11
- package/src/actions/gradientPlots/materialize.js +5 -7
- package/src/actions/guides/applicability.js +20 -17
- package/src/actions/guides/axes/axes.js +4 -3
- package/src/actions/guides/axes/axis.js +18 -18
- package/src/actions/guides/axes/components.js +43 -0
- package/src/actions/guides/axes/edit.js +16 -51
- package/src/actions/guides/axes/labels.js +129 -19
- package/src/actions/guides/axes/lines.js +7 -5
- package/src/actions/guides/axes/parallel/lifecycle.js +117 -0
- package/src/actions/guides/axes/parallel/policy.js +137 -0
- package/src/actions/guides/axes/parallel/resolve.js +44 -23
- package/src/actions/guides/axes/parallel.js +93 -217
- package/src/actions/guides/axes/policy.js +21 -67
- package/src/actions/guides/axes/tickGroups.js +14 -8
- package/src/actions/guides/axes/ticks.js +5 -4
- package/src/actions/guides/axes/titles.js +21 -8
- package/src/actions/guides/basic.js +2 -0
- package/src/actions/guides/facade.js +106 -0
- package/src/actions/guides/facadeAxes.js +200 -0
- package/src/actions/guides/facadeLegend.js +63 -0
- package/src/actions/guides/grids/grid.js +37 -33
- package/src/actions/guides/grids/resolve.js +10 -2
- package/src/actions/guides/guides.js +35 -28
- package/src/actions/guides/legends/categorical/actions.js +193 -249
- package/src/actions/guides/legends/categorical/components.js +4 -5
- package/src/actions/guides/legends/categorical/layout.js +36 -349
- package/src/actions/guides/legends/categorical/options.js +30 -14
- package/src/actions/guides/legends/categorical/recipes.js +14 -0
- package/src/actions/guides/legends/categorical/resolve.js +21 -10
- package/src/actions/guides/legends/categorical/symbols.js +2 -3
- package/src/actions/guides/legends/continuous/common.js +69 -14
- package/src/actions/guides/legends/continuous/gradient.js +111 -83
- package/src/actions/guides/legends/continuous/index.js +6 -2
- package/src/actions/guides/legends/continuous/interval.js +71 -92
- package/src/actions/guides/legends/continuous/opacity.js +105 -84
- package/src/actions/guides/legends/creation.js +27 -0
- package/src/actions/guides/legends/edit.js +198 -66
- package/src/actions/guides/legends/focused.js +2 -2
- package/src/actions/guides/legends/lane.js +98 -86
- package/src/actions/guides/legends/lifecycle.js +132 -0
- package/src/actions/guides/legends/remove.js +31 -76
- package/src/actions/guides/legends/size.js +149 -105
- package/src/actions/guides/legends/strokeWidth.js +106 -64
- package/src/actions/guides/legends/target.js +12 -0
- package/src/actions/guides/legends/transition.js +43 -0
- package/src/actions/guides/polar/axes/facade.js +77 -62
- package/src/actions/guides/polar/axes/labels.js +5 -4
- package/src/actions/guides/polar/axes/lines.js +2 -1
- package/src/actions/guides/polar/axes/shared.js +6 -0
- package/src/actions/guides/polar/axes/ticks.js +5 -4
- package/src/actions/guides/polar/axes/titles.js +5 -4
- package/src/actions/guides/polar/grids.js +7 -7
- package/src/actions/guides/reuse.js +44 -0
- package/src/actions/guides/tickValues.js +19 -21
- package/src/actions/index.js +2 -0
- package/src/actions/intervals/resolve.js +35 -15
- package/src/actions/marks/arc/actions.js +1 -1
- package/src/actions/marks/area/actions.js +18 -4
- package/src/actions/marks/area/materialize.js +5 -2
- package/src/actions/marks/bar/materialize.js +11 -7
- package/src/actions/marks/index.js +5 -0
- package/src/actions/marks/line/actions.js +43 -108
- package/src/actions/marks/line/materialize.js +34 -52
- package/src/actions/marks/point/edit.js +11 -15
- package/src/actions/marks/point/index.js +1 -0
- package/src/actions/marks/point/jitter.js +3 -0
- package/src/actions/marks/point/materialize.js +106 -2
- package/src/actions/marks/point/packing.js +68 -0
- package/src/actions/marks/references.js +87 -0
- package/src/actions/marks/remove.js +17 -4
- package/src/actions/marks/rule/actions.js +34 -3
- package/src/actions/marks/rule/appearance.js +26 -0
- package/src/actions/marks/shared.js +12 -12
- package/src/actions/marks/text/actions.js +224 -6
- package/src/actions/marks/text/layout.js +2 -2
- package/src/actions/primitives/createGraphics.js +3 -1
- package/src/actions/primitives/semantic.js +1 -1
- package/src/actions/primitives/semanticAction.js +39 -2
- package/src/actions/primitives/semanticValidation/guide.js +10 -0
- package/src/actions/primitives/semanticValidation/layer.js +18 -1
- package/src/actions/primitives/semanticValidation/scale.js +10 -1
- package/src/actions/regression/create.js +1 -1
- package/src/actions/regression/edit.js +19 -6
- package/src/actions/regression/resolve.js +1 -1
- package/src/actions/scales/consumers/common.js +20 -53
- package/src/actions/scales/consumers/families.js +27 -14
- package/src/actions/scales/consumers/index.js +23 -2
- package/src/actions/scales/consumers/seriesLayout.js +33 -2
- package/src/actions/scales/create.js +4 -4
- package/src/actions/scales/definitions.js +19 -3
- package/src/actions/scales/edit.js +46 -34
- package/src/actions/scales/editPolicy.js +21 -31
- package/src/actions/scales/materialize.js +10 -46
- package/src/actions/scales/patch.js +1 -1
- package/src/actions/scales/preview.js +39 -0
- package/src/actions/scales/quantitativeColor.js +9 -5
- package/src/actions/theme/actions.js +43 -0
- package/src/actions/theme/index.js +9 -0
- package/src/actions/theme/reconcile.js +882 -0
- package/src/actions/titles/actions.js +11 -10
- package/src/actions/titles/resolve.js +1 -40
- package/src/actions/violinPlots/create.js +86 -42
- package/src/actions/violinPlots/edit.js +191 -0
- package/src/actions/violinPlots/index.js +2 -0
- package/src/core/ChartProgram.js +7 -0
- package/src/core/action.js +22 -2
- package/src/core/compositionState.js +136 -8
- package/src/grammar/aggregate.js +66 -23
- package/src/grammar/arcs.js +58 -1
- package/src/grammar/areaEndpoints.js +46 -0
- package/src/grammar/areaSeries.js +87 -41
- package/src/grammar/bars/aggregate.js +33 -12
- package/src/grammar/bars/policy.js +9 -7
- package/src/grammar/bin.js +181 -0
- package/src/grammar/categoryOrder.js +34 -0
- package/src/grammar/computed.js +150 -0
- package/src/grammar/ecdf.js +198 -0
- package/src/grammar/facets/index.js +117 -6
- package/src/grammar/fold.js +123 -0
- package/src/grammar/horizon.js +6 -4
- package/src/grammar/interval.js +37 -20
- package/src/grammar/lineSeries.js +20 -63
- package/src/grammar/markFilter.js +22 -4
- package/src/grammar/markLabels.js +93 -0
- package/src/grammar/parallelAxisTitles.js +16 -0
- package/src/grammar/pathSeries.js +91 -0
- package/src/grammar/pointPacking.js +183 -0
- package/src/grammar/positionDatum.js +12 -0
- package/src/grammar/rects.js +9 -1
- package/src/grammar/regression/derive.js +4 -0
- package/src/grammar/regression/index.js +7 -2
- package/src/grammar/regression/models.js +22 -7
- package/src/grammar/regression/parameters.js +40 -13
- package/src/grammar/rotation.js +29 -0
- package/src/grammar/rules.js +7 -21
- package/src/grammar/scales/color.js +23 -1
- package/src/grammar/scales/colorConsumers.js +38 -0
- package/src/grammar/scales/definition.js +15 -1
- package/src/grammar/scales/discretized.js +11 -5
- package/src/grammar/scales/fields.js +38 -8
- package/src/grammar/scales/mapping.js +2 -0
- package/src/grammar/scales/radial.js +55 -0
- package/src/grammar/scales/types.js +1 -0
- package/src/grammar/schemas/semanticPath.js +17 -3
- package/src/grammar/stack.js +170 -0
- package/src/grammar/statistics/confidenceInterval.js +101 -0
- package/src/grammar/summary.js +183 -0
- package/src/grammar/text.js +22 -16
- package/src/grammar/timeUnit.js +21 -11
- package/src/grammar/transformTopology.js +6 -0
- package/src/grammar/transforms.js +36 -0
- package/src/grammar/valueFormat.js +112 -0
- package/src/grammar/window.js +13 -7
- package/src/layout/facets.js +41 -7
- package/src/layout/guideCollisions.js +12 -0
- package/src/layout/legend.js +2 -10
- package/src/layout/legendItems.js +102 -0
- package/src/layout/legendLane.js +100 -11
- package/src/materialization/bars/aggregate.js +38 -152
- package/src/materialization/bars/histogram.js +24 -0
- package/src/materialization/categorySlotOffset.js +40 -0
- package/src/materialization/compositionSnapshot.js +1 -1
- package/src/materialization/dependencies.js +57 -16
- package/src/materialization/encodings.js +1 -1
- package/src/materialization/facets.js +8 -1
- package/src/materialization/guides/layout.js +57 -0
- package/src/materialization/guides/resources.js +2 -2
- package/src/materialization/legends.js +10 -1
- package/src/materialization/marks/capabilities.js +20 -5
- package/src/materialization/marks/grouping.js +52 -0
- package/src/materialization/marks/index.js +58 -23
- package/src/materialization/marks/policies.js +2 -0
- package/src/materialization/rect.js +33 -33
- package/src/materialization/rowEncoding.js +13 -13
- package/src/materialization/scaleGuideDependencies.js +8 -5
- package/src/materialization/scales/map.js +1 -0
- package/src/materialization/scales/policies/arc.js +42 -0
- package/src/materialization/scales/policies/binnedPosition.js +7 -4
- package/src/materialization/scales/policies/series.js +2 -1
- package/src/materialization/scales/resolve.js +46 -14
- package/src/materialization/selection/compatibility.js +11 -0
- package/src/materialization/selection/items/bar.js +12 -68
- package/src/materialization/selection/items/common.js +8 -2
- package/src/materialization/selection/items/path.js +3 -13
- package/src/materialization/selection/items/rect.js +7 -4
- package/src/materialization/text.js +64 -5
- package/src/mcp/adapter.js +2 -2
- package/src/selectors/datasets.js +13 -0
- package/src/selectors/layers.js +3 -2
- package/src/selectors/scales.js +6 -0
- package/src/theme/defaults.js +39 -0
- package/types/basic.d.ts +11 -2
- package/types/index.d.ts +126 -0
- package/types/program.d.ts +1320 -135
- package/src/materialization/bars/grouped.js +0 -144
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
import { action } from "../../core/action.js";
|
|
2
|
+
import { isPlainObject } from "../../core/immutable.js";
|
|
3
|
+
import { validateUserId } from "../../core/identifiers.js";
|
|
4
|
+
import { isNominalValue } from "../../grammar/scales/fields.js";
|
|
5
|
+
import {
|
|
6
|
+
validateNonEmptyString,
|
|
7
|
+
validateNonNegativeFinite,
|
|
8
|
+
validateOptionObject
|
|
9
|
+
} from "../../core/validation.js";
|
|
10
|
+
import { findDataset } from "../../selectors/datasets.js";
|
|
11
|
+
import { findSemanticScale } from "../../selectors/scales.js";
|
|
12
|
+
import {
|
|
13
|
+
applyFacadeGuides,
|
|
14
|
+
normalizeAppearance,
|
|
15
|
+
normalizeFieldEncoding,
|
|
16
|
+
normalizeGuides,
|
|
17
|
+
omitUndefinedOptions,
|
|
18
|
+
resolveFacadeData,
|
|
19
|
+
resolveFacadeId,
|
|
20
|
+
validateFacadeOptions
|
|
21
|
+
} from "./shared.js";
|
|
22
|
+
|
|
23
|
+
const COMMON_OPTIONS = Object.freeze([
|
|
24
|
+
"id", "data", "coordinate", "category", "orientation", "summary", "guides", "labels"
|
|
25
|
+
]);
|
|
26
|
+
const DOT_OPTIONS = Object.freeze([...COMMON_OPTIONS, "value", "point"]);
|
|
27
|
+
const LOLLIPOP_OPTIONS = Object.freeze([...DOT_OPTIONS, "baseline", "stem"]);
|
|
28
|
+
const DUMBBELL_OPTIONS = Object.freeze([
|
|
29
|
+
...COMMON_OPTIONS, "start", "end", "startPoint", "endPoint", "connector"
|
|
30
|
+
]);
|
|
31
|
+
const CATEGORY_OPTIONS = Object.freeze(["field", "fieldType", "scale"]);
|
|
32
|
+
const VALUE_OPTIONS = Object.freeze(["field", "fieldType", "scale"]);
|
|
33
|
+
const POINT_OPTIONS = Object.freeze([
|
|
34
|
+
"shape", "fill", "opacity", "stroke", "strokeWidth", "radius"
|
|
35
|
+
]);
|
|
36
|
+
const RULE_OPTIONS = Object.freeze(["stroke", "strokeWidth", "strokeDash", "opacity"]);
|
|
37
|
+
const LABEL_OPTIONS = Object.freeze([
|
|
38
|
+
"endpoint", "field", "value", "content", "normalizeBy", "format", "layout",
|
|
39
|
+
"fill", "opacity", "fontSize", "fontFamily", "fontWeight", "align", "baseline",
|
|
40
|
+
"rotation", "dx", "dy"
|
|
41
|
+
]);
|
|
42
|
+
const SUMMARY_OPERATIONS = Object.freeze(["mean", "median", "sum", "min", "max"]);
|
|
43
|
+
const EDIT_OPTIONS = Object.freeze([
|
|
44
|
+
"target", "data", "coordinate", "category", "value", "start", "end",
|
|
45
|
+
"orientation", "summary", "baseline"
|
|
46
|
+
]);
|
|
47
|
+
|
|
48
|
+
function normalizeCategory(value, operation) {
|
|
49
|
+
const category = normalizeFieldEncoding(value, `${operation} category`);
|
|
50
|
+
validateOptionObject(category, CATEGORY_OPTIONS, `${operation} category`);
|
|
51
|
+
validateNonEmptyString(category.field, `${operation} category field`);
|
|
52
|
+
const fieldType = category.fieldType ?? "nominal";
|
|
53
|
+
if (!['nominal', 'ordinal'].includes(fieldType)) {
|
|
54
|
+
throw new TypeError(`${operation} category must be nominal or ordinal.`);
|
|
55
|
+
}
|
|
56
|
+
return { ...omitUndefinedOptions(category), fieldType };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function normalizeValue(value, operation, role) {
|
|
60
|
+
const result = normalizeFieldEncoding(value, `${operation} ${role}`);
|
|
61
|
+
validateOptionObject(result, VALUE_OPTIONS, `${operation} ${role}`);
|
|
62
|
+
validateNonEmptyString(result.field, `${operation} ${role} field`);
|
|
63
|
+
if (result.fieldType !== undefined && result.fieldType !== "quantitative") {
|
|
64
|
+
throw new TypeError(`${operation} ${role} must be quantitative.`);
|
|
65
|
+
}
|
|
66
|
+
return { ...omitUndefinedOptions(result), fieldType: "quantitative" };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function normalizeOrientation(value, operation) {
|
|
70
|
+
const orientation = value ?? "horizontal";
|
|
71
|
+
if (!['horizontal', 'vertical'].includes(orientation)) {
|
|
72
|
+
throw new Error(`${operation} orientation must be horizontal or vertical.`);
|
|
73
|
+
}
|
|
74
|
+
return orientation;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function normalizeSummary(value, operation) {
|
|
78
|
+
if (value === undefined || value === false) return false;
|
|
79
|
+
if (!SUMMARY_OPERATIONS.includes(value)) {
|
|
80
|
+
throw new Error(`${operation} summary must be false or ${SUMMARY_OPERATIONS.join(", ")}.`);
|
|
81
|
+
}
|
|
82
|
+
return value;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function sameOptionValue(left, right) {
|
|
86
|
+
if (Object.is(left, right)) return true;
|
|
87
|
+
if (Array.isArray(left) || Array.isArray(right)) {
|
|
88
|
+
return Array.isArray(left) && Array.isArray(right)
|
|
89
|
+
&& left.length === right.length
|
|
90
|
+
&& left.every((value, index) => sameOptionValue(value, right[index]));
|
|
91
|
+
}
|
|
92
|
+
if (!isPlainObject(left) || !isPlainObject(right)) return false;
|
|
93
|
+
const leftKeys = Object.keys(left).sort();
|
|
94
|
+
const rightKeys = Object.keys(right).sort();
|
|
95
|
+
return leftKeys.length === rightKeys.length
|
|
96
|
+
&& leftKeys.every((key, index) => key === rightKeys[index]
|
|
97
|
+
&& sameOptionValue(left[key], right[key]));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function validateRows(program, data, category, measures, operation) {
|
|
101
|
+
const dataset = findDataset(program, data);
|
|
102
|
+
if (dataset?.values === undefined || dataset.values.length === 0) {
|
|
103
|
+
throw new Error(`${operation} requires at least one source row.`);
|
|
104
|
+
}
|
|
105
|
+
dataset.values.forEach((row, index) => {
|
|
106
|
+
if (!isNominalValue(row?.[category])) {
|
|
107
|
+
throw new TypeError(`${operation} category field "${category}" has an invalid value at row ${index}.`);
|
|
108
|
+
}
|
|
109
|
+
for (const field of measures) {
|
|
110
|
+
if (!Number.isFinite(row?.[field])) {
|
|
111
|
+
throw new TypeError(`${operation} ${field} must be finite at row ${index}.`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function outputField(id, role) {
|
|
118
|
+
return `__${id}_${role}`;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function prepareData(program, { id, data, category, measures, summary, operation }) {
|
|
122
|
+
validateRows(program, data, category.field, measures.map(item => item.field), operation);
|
|
123
|
+
if (summary === false) return { program, data, measures };
|
|
124
|
+
const derived = `${id}SummaryData`;
|
|
125
|
+
const outputs = measures.map((item, index) => ({
|
|
126
|
+
...item,
|
|
127
|
+
field: outputField(id, item.role ?? `value${index + 1}`)
|
|
128
|
+
}));
|
|
129
|
+
return {
|
|
130
|
+
program: program.createSummaryData({
|
|
131
|
+
id: derived,
|
|
132
|
+
source: data,
|
|
133
|
+
groupBy: category.field,
|
|
134
|
+
aggregates: measures.map((item, index) => ({
|
|
135
|
+
op: summary,
|
|
136
|
+
field: item.field,
|
|
137
|
+
as: outputs[index].field
|
|
138
|
+
}))
|
|
139
|
+
}),
|
|
140
|
+
data: derived,
|
|
141
|
+
measures: outputs
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function positionBindings(id, category, value, orientation, coordinate) {
|
|
146
|
+
const categoryScale = { ...(category.scale ?? {}), id: category.scale?.id ?? `${id}Category` };
|
|
147
|
+
const valueScale = { ...(value.scale ?? {}), id: value.scale?.id ?? `${id}Value` };
|
|
148
|
+
const categoryBinding = {
|
|
149
|
+
field: category.field,
|
|
150
|
+
fieldType: category.fieldType,
|
|
151
|
+
scale: categoryScale,
|
|
152
|
+
...(coordinate === undefined ? {} : { coordinate })
|
|
153
|
+
};
|
|
154
|
+
const valueBinding = {
|
|
155
|
+
field: value.field,
|
|
156
|
+
fieldType: "quantitative",
|
|
157
|
+
scale: valueScale,
|
|
158
|
+
...(coordinate === undefined ? {} : { coordinate })
|
|
159
|
+
};
|
|
160
|
+
return orientation === "horizontal"
|
|
161
|
+
? { x: valueBinding, y: categoryBinding }
|
|
162
|
+
: { x: categoryBinding, y: valueBinding };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function createPoint(program, { id, data, bindings, appearance }) {
|
|
166
|
+
const { radius, ...style } = appearance;
|
|
167
|
+
let next = program
|
|
168
|
+
.createPointMark({ id, data, ...style })
|
|
169
|
+
.encodeX({ target: id, ...bindings.x })
|
|
170
|
+
.encodeY({ target: id, ...bindings.y });
|
|
171
|
+
return radius === undefined
|
|
172
|
+
? next
|
|
173
|
+
: next.encodePointRadius({ target: id, value: radius });
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function normalizePoint(value, operation, role = "point") {
|
|
177
|
+
const point = normalizeAppearance(value, POINT_OPTIONS, `${operation} ${role}`);
|
|
178
|
+
if (point.radius !== undefined) {
|
|
179
|
+
validateNonNegativeFinite(point.radius, `${operation} ${role} radius`);
|
|
180
|
+
}
|
|
181
|
+
return point;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function normalizeRule(value, operation, role) {
|
|
185
|
+
return normalizeAppearance(value, RULE_OPTIONS, `${operation} ${role}`);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function createConnector(program, { id, data, bindings, end, appearance }) {
|
|
189
|
+
let next = program
|
|
190
|
+
.createRuleMark({ id, data, ...appearance })
|
|
191
|
+
.encodeX({ target: id, ...bindings.x })
|
|
192
|
+
.encodeY({ target: id, ...bindings.y });
|
|
193
|
+
const valueChannel = bindings.x.fieldType === "quantitative" ? "x" : "y";
|
|
194
|
+
return next[valueChannel === "x" ? "encodeX2" : "encodeY2"]({
|
|
195
|
+
target: id,
|
|
196
|
+
...end,
|
|
197
|
+
fieldType: "quantitative"
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function normalizeLabels(value, operation, allowEndpoint) {
|
|
202
|
+
if (value === undefined || value === false) return false;
|
|
203
|
+
if (!isPlainObject(value)) {
|
|
204
|
+
throw new TypeError(`${operation} labels must be false or a plain object.`);
|
|
205
|
+
}
|
|
206
|
+
validateOptionObject(value, LABEL_OPTIONS, `${operation} labels`);
|
|
207
|
+
if (!allowEndpoint && Object.hasOwn(value, "endpoint")) {
|
|
208
|
+
throw new Error(`${operation} labels.endpoint is only available for Dumbbell endpoints.`);
|
|
209
|
+
}
|
|
210
|
+
return { ...value };
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function labelOptions(labels, defaultField) {
|
|
214
|
+
const { endpoint: _endpoint, ...options } = labels;
|
|
215
|
+
const hasContent = ["field", "value", "content"].some(key => Object.hasOwn(options, key));
|
|
216
|
+
return { ...options, ...(hasContent ? {} : { field: defaultField }) };
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function attachLabels(program, id, labels, defaultField) {
|
|
220
|
+
if (labels === false) return program;
|
|
221
|
+
return program.createMarkLabels({
|
|
222
|
+
id: `${id}Label`,
|
|
223
|
+
source: id,
|
|
224
|
+
...labelOptions(labels, defaultField)
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function finish(program, { id, source, data, kind, roles, summary, orientation, options }) {
|
|
229
|
+
return program._withMarkConfig(id, {
|
|
230
|
+
...program.markConfigs[id],
|
|
231
|
+
endpointPlot: { kind, source, data, roles, summary, orientation, options }
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export const createDotPlot = action(
|
|
236
|
+
{ op: "createDotPlot", description: "Create raw or explicitly summarized categorical dots." },
|
|
237
|
+
function (args = {}) {
|
|
238
|
+
const operation = "createDotPlot";
|
|
239
|
+
validateFacadeOptions(args, DOT_OPTIONS, operation);
|
|
240
|
+
const id = resolveFacadeId(this, args.id, { defaultId: "dotPlot", operation });
|
|
241
|
+
const source = resolveFacadeData(this, args.data, operation);
|
|
242
|
+
const category = normalizeCategory(args.category, operation);
|
|
243
|
+
const value = normalizeValue(args.value, operation, "value");
|
|
244
|
+
const orientation = normalizeOrientation(args.orientation, operation);
|
|
245
|
+
const summary = normalizeSummary(args.summary, operation);
|
|
246
|
+
const point = normalizePoint(args.point, operation);
|
|
247
|
+
const guides = normalizeGuides(args.guides, operation);
|
|
248
|
+
const labels = normalizeLabels(args.labels, operation, false);
|
|
249
|
+
const prepared = prepareData(this, {
|
|
250
|
+
id, data: source, category, measures: [{ ...value, role: "value" }], summary, operation
|
|
251
|
+
});
|
|
252
|
+
const finalValue = prepared.measures[0];
|
|
253
|
+
const bindings = positionBindings(id, category, finalValue, orientation, args.coordinate);
|
|
254
|
+
let next = createPoint(prepared.program, { id, data: prepared.data, bindings, appearance: point });
|
|
255
|
+
next = attachLabels(next, id, labels, finalValue.field);
|
|
256
|
+
next = applyFacadeGuides(next, guides, id, guides);
|
|
257
|
+
return finish(next, {
|
|
258
|
+
id, source, data: prepared.data, kind: "dot", roles: { category, value }, summary, orientation,
|
|
259
|
+
options: { id, data: source, coordinate: args.coordinate, category, value, orientation,
|
|
260
|
+
summary, point, guides, labels }
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
export const createLollipopPlot = action(
|
|
266
|
+
{ op: "createLollipopPlot", description: "Create categorical value points with baseline stems." },
|
|
267
|
+
function (args = {}) {
|
|
268
|
+
const operation = "createLollipopPlot";
|
|
269
|
+
validateFacadeOptions(args, LOLLIPOP_OPTIONS, operation);
|
|
270
|
+
const id = resolveFacadeId(this, args.id, { defaultId: "lollipopPlot", operation });
|
|
271
|
+
const source = resolveFacadeData(this, args.data, operation);
|
|
272
|
+
const category = normalizeCategory(args.category, operation);
|
|
273
|
+
const value = normalizeValue(args.value, operation, "value");
|
|
274
|
+
const orientation = normalizeOrientation(args.orientation, operation);
|
|
275
|
+
const summary = normalizeSummary(args.summary, operation);
|
|
276
|
+
const baseline = args.baseline ?? 0;
|
|
277
|
+
if (!Number.isFinite(baseline)) throw new TypeError(`${operation} baseline must be finite.`);
|
|
278
|
+
const point = normalizePoint(args.point, operation);
|
|
279
|
+
const stem = normalizeRule(args.stem, operation, "stem");
|
|
280
|
+
const guides = normalizeGuides(args.guides, operation);
|
|
281
|
+
const labels = normalizeLabels(args.labels, operation, false);
|
|
282
|
+
const prepared = prepareData(this, {
|
|
283
|
+
id, data: source, category, measures: [{ ...value, role: "value" }], summary, operation
|
|
284
|
+
});
|
|
285
|
+
const finalValue = prepared.measures[0];
|
|
286
|
+
const bindings = positionBindings(id, category, finalValue, orientation, args.coordinate);
|
|
287
|
+
const stemId = `${id}Stem`;
|
|
288
|
+
let next = prepared.program;
|
|
289
|
+
next = createConnector(next, {
|
|
290
|
+
id: stemId,
|
|
291
|
+
data: prepared.data,
|
|
292
|
+
bindings,
|
|
293
|
+
end: { datum: baseline },
|
|
294
|
+
appearance: stem
|
|
295
|
+
});
|
|
296
|
+
next = createPoint(next, { id, data: prepared.data, bindings, appearance: point });
|
|
297
|
+
next = attachLabels(next, id, labels, finalValue.field);
|
|
298
|
+
next = applyFacadeGuides(next, guides, id, guides);
|
|
299
|
+
return finish(next, {
|
|
300
|
+
id, source, data: prepared.data, kind: "lollipop",
|
|
301
|
+
roles: { category, value, baseline, stemId }, summary, orientation,
|
|
302
|
+
options: { id, data: source, coordinate: args.coordinate, category, value, orientation,
|
|
303
|
+
summary, baseline, point, stem, guides, labels }
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
export const createDumbbellPlot = action(
|
|
309
|
+
{ op: "createDumbbellPlot", description: "Create two categorical endpoints joined by a connector." },
|
|
310
|
+
function (args = {}) {
|
|
311
|
+
const operation = "createDumbbellPlot";
|
|
312
|
+
validateFacadeOptions(args, DUMBBELL_OPTIONS, operation);
|
|
313
|
+
const id = resolveFacadeId(this, args.id, { defaultId: "dumbbellPlot", operation });
|
|
314
|
+
const source = resolveFacadeData(this, args.data, operation);
|
|
315
|
+
const category = normalizeCategory(args.category, operation);
|
|
316
|
+
const start = normalizeValue(args.start, operation, "start");
|
|
317
|
+
const end = normalizeValue(args.end, operation, "end");
|
|
318
|
+
if (start.field === end.field) throw new Error(`${operation} start and end fields must be distinct.`);
|
|
319
|
+
if (start.scale !== undefined && end.scale !== undefined
|
|
320
|
+
&& !sameOptionValue(start.scale, end.scale)) {
|
|
321
|
+
throw new Error(`${operation} start and end must use the same quantitative scale.`);
|
|
322
|
+
}
|
|
323
|
+
const orientation = normalizeOrientation(args.orientation, operation);
|
|
324
|
+
const summary = normalizeSummary(args.summary, operation);
|
|
325
|
+
const startPoint = normalizePoint(args.startPoint, operation, "startPoint");
|
|
326
|
+
const endPoint = normalizePoint(args.endPoint, operation, "endPoint");
|
|
327
|
+
const connector = normalizeRule(args.connector, operation, "connector");
|
|
328
|
+
const guides = normalizeGuides(args.guides, operation);
|
|
329
|
+
const labels = normalizeLabels(args.labels, operation, true);
|
|
330
|
+
const sharedScale = start.scale ?? end.scale;
|
|
331
|
+
const prepared = prepareData(this, {
|
|
332
|
+
id, data: source, category,
|
|
333
|
+
measures: [
|
|
334
|
+
{ ...start, scale: sharedScale, role: "start" },
|
|
335
|
+
{ ...end, scale: sharedScale, role: "end" }
|
|
336
|
+
],
|
|
337
|
+
summary, operation
|
|
338
|
+
});
|
|
339
|
+
const [finalStart, finalEnd] = prepared.measures;
|
|
340
|
+
const startBindings = positionBindings(id, category, finalStart, orientation, args.coordinate);
|
|
341
|
+
const endBindings = positionBindings(id, category, finalEnd, orientation, args.coordinate);
|
|
342
|
+
const connectorId = `${id}Connector`;
|
|
343
|
+
const startId = `${id}Start`;
|
|
344
|
+
let next = createConnector(prepared.program, {
|
|
345
|
+
id: connectorId,
|
|
346
|
+
data: prepared.data,
|
|
347
|
+
bindings: startBindings,
|
|
348
|
+
end: { field: finalEnd.field },
|
|
349
|
+
appearance: connector
|
|
350
|
+
});
|
|
351
|
+
next = createPoint(next, {
|
|
352
|
+
id: startId, data: prepared.data, bindings: startBindings, appearance: startPoint
|
|
353
|
+
});
|
|
354
|
+
next = createPoint(next, {
|
|
355
|
+
id, data: prepared.data, bindings: endBindings, appearance: endPoint
|
|
356
|
+
});
|
|
357
|
+
if (labels !== false) {
|
|
358
|
+
const endpoint = labels.endpoint ?? "end";
|
|
359
|
+
if (!['start', 'end', 'both'].includes(endpoint)) {
|
|
360
|
+
throw new Error(`${operation} labels.endpoint must be start, end, or both.`);
|
|
361
|
+
}
|
|
362
|
+
for (const [role, target] of [["start", startId], ["end", id]]) {
|
|
363
|
+
if (endpoint !== "both" && endpoint !== role) continue;
|
|
364
|
+
next = next.createMarkLabels({
|
|
365
|
+
id: endpoint === "both" ? `${target}Label` : `${id}Label`,
|
|
366
|
+
source: target,
|
|
367
|
+
...labelOptions(labels, role === "start" ? finalStart.field : finalEnd.field)
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
next = applyFacadeGuides(next, guides, id, guides);
|
|
372
|
+
return finish(next, {
|
|
373
|
+
id, source, data: prepared.data, kind: "dumbbell",
|
|
374
|
+
roles: { category, start, end, startId, connectorId }, summary, orientation,
|
|
375
|
+
options: { id, data: source, coordinate: args.coordinate, category, start, end, orientation,
|
|
376
|
+
summary, startPoint, endPoint, connector, guides, labels }
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
);
|
|
380
|
+
|
|
381
|
+
function resolveEndpointOwner(program, requested) {
|
|
382
|
+
const eligible = program.semanticSpec.layers.filter(
|
|
383
|
+
layer => program.markConfigs[layer.id]?.endpointPlot !== undefined
|
|
384
|
+
);
|
|
385
|
+
if (requested !== undefined) {
|
|
386
|
+
const id = validateUserId(requested, "Endpoint-plot owner id");
|
|
387
|
+
const owner = eligible.find(layer => layer.id === id);
|
|
388
|
+
if (owner === undefined) throw new Error(`Unknown endpoint-plot owner "${id}".`);
|
|
389
|
+
return owner;
|
|
390
|
+
}
|
|
391
|
+
const current = eligible.find(layer => layer.id === program.context.currentMark);
|
|
392
|
+
if (current !== undefined) return current;
|
|
393
|
+
if (eligible.length === 1) return eligible[0];
|
|
394
|
+
if (eligible.length === 0) throw new Error("editEndpointPlot requires an endpoint plot.");
|
|
395
|
+
throw new Error("editEndpointPlot target is ambiguous; provide target.");
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
const KIND_METHOD = Object.freeze({
|
|
399
|
+
dot: "createDotPlot",
|
|
400
|
+
lollipop: "createLollipopPlot",
|
|
401
|
+
dumbbell: "createDumbbellPlot"
|
|
402
|
+
});
|
|
403
|
+
const KIND_ROLES = Object.freeze({
|
|
404
|
+
dot: new Set(["data", "coordinate", "category", "value", "orientation", "summary"]),
|
|
405
|
+
lollipop: new Set([
|
|
406
|
+
"data", "coordinate", "category", "value", "orientation", "summary", "baseline"
|
|
407
|
+
]),
|
|
408
|
+
dumbbell: new Set([
|
|
409
|
+
"data", "coordinate", "category", "start", "end", "orientation", "summary"
|
|
410
|
+
])
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
function reviseRequestedScales(program, ownerId, revised, changes) {
|
|
414
|
+
let next = program;
|
|
415
|
+
for (const role of ["category", "value", "start", "end"]) {
|
|
416
|
+
if (!changes.includes(role) || !isPlainObject(revised[role]?.scale)) continue;
|
|
417
|
+
const requested = revised[role].scale;
|
|
418
|
+
const fallback = role === "category" ? `${ownerId}Category` : `${ownerId}Value`;
|
|
419
|
+
const id = requested.id ?? fallback;
|
|
420
|
+
if (findSemanticScale(next, id) === undefined) continue;
|
|
421
|
+
const { id: _id, ...definition } = requested;
|
|
422
|
+
void _id;
|
|
423
|
+
if (Object.keys(definition).length > 0) {
|
|
424
|
+
next = next.editScale({ id, ...definition });
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
return next;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
export const editEndpointPlot = action(
|
|
431
|
+
{ op: "editEndpointPlot", description: "Atomically revise an endpoint plot's semantic roles." },
|
|
432
|
+
function (args = {}) {
|
|
433
|
+
validateFacadeOptions(args, EDIT_OPTIONS, "editEndpointPlot");
|
|
434
|
+
const changes = Object.keys(args).filter(key => key !== "target");
|
|
435
|
+
if (changes.length === 0) {
|
|
436
|
+
throw new Error("editEndpointPlot requires at least one endpoint-plot option.");
|
|
437
|
+
}
|
|
438
|
+
const owner = resolveEndpointOwner(this, args.target);
|
|
439
|
+
const current = this.markConfigs[owner.id].endpointPlot;
|
|
440
|
+
const allowed = KIND_ROLES[current.kind];
|
|
441
|
+
const invalid = changes.find(key => !allowed.has(key));
|
|
442
|
+
if (invalid !== undefined) {
|
|
443
|
+
throw new Error(`editEndpointPlot ${invalid} is not available for ${current.kind} plots.`);
|
|
444
|
+
}
|
|
445
|
+
const revised = { ...current.options };
|
|
446
|
+
for (const key of changes) revised[key] = args[key];
|
|
447
|
+
let next = this.removeMark({ target: owner.id });
|
|
448
|
+
next = reviseRequestedScales(next, owner.id, revised, changes);
|
|
449
|
+
return next[KIND_METHOD[current.kind]](revised);
|
|
450
|
+
}
|
|
451
|
+
);
|
|
@@ -158,7 +158,7 @@ export const createHeatmap = action(
|
|
|
158
158
|
.encodeX(positionArgs(x, { target: id, coordinate: args.coordinate }))
|
|
159
159
|
.encodeY(positionArgs(y, { target: id, coordinate: args.coordinate }))
|
|
160
160
|
.encodeColor(targetArgs(color, id));
|
|
161
|
-
return applyFacadeGuides(next, guides);
|
|
161
|
+
return applyFacadeGuides(next, guides, id);
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
const x = normalizePosition(args.x, "x");
|
|
@@ -190,6 +190,6 @@ export const createHeatmap = action(
|
|
|
190
190
|
fieldType: "quantitative",
|
|
191
191
|
...color
|
|
192
192
|
});
|
|
193
|
-
return applyFacadeGuides(next, resolvedGuides);
|
|
193
|
+
return applyFacadeGuides(next, resolvedGuides, id, guides);
|
|
194
194
|
}
|
|
195
195
|
);
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { action } from "../../core/action.js";
|
|
2
|
+
import { validateNonEmptyString, validateOptionObject, validateUnitInterval } from "../../core/validation.js";
|
|
3
|
+
import {
|
|
4
|
+
applyFacadeGuides, normalizeAppearance, normalizeFieldEncoding, normalizeGuides,
|
|
5
|
+
omitUndefinedOptions, resolveFacadeData, resolveFacadeId, validateFacadeOptions
|
|
6
|
+
} from "./shared.js";
|
|
7
|
+
|
|
8
|
+
const OPERATION = "createHorizonPlot";
|
|
9
|
+
const OPTIONS = ["id", "data", "coordinate", "x", "y", "groupBy", "bands", "baseline", "extent",
|
|
10
|
+
"resolve", "missing", "overflow", "palette", "area", "guides"];
|
|
11
|
+
|
|
12
|
+
function position(value, role) {
|
|
13
|
+
const label = `${OPERATION} ${role}`;
|
|
14
|
+
const field = normalizeFieldEncoding(value, label);
|
|
15
|
+
validateOptionObject(field, ["field", "fieldType", "scale", ...(role === "x" ? ["temporalUnit"] : [])], label);
|
|
16
|
+
validateNonEmptyString(field.field, `${label} field`);
|
|
17
|
+
if (field.fieldType === null || field.temporalUnit === null) throw new TypeError(`${label} type/unit cannot be null.`);
|
|
18
|
+
if (field.scale !== undefined) validateOptionObject(field.scale, undefined, `${label}.scale`);
|
|
19
|
+
return { ...omitUndefinedOptions(field),
|
|
20
|
+
...(field.scale === undefined ? {} : { scale: omitUndefinedOptions(field.scale) }) };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function guideOptions(value) {
|
|
24
|
+
const guides = normalizeGuides(value, OPERATION);
|
|
25
|
+
if (guides === false) return false;
|
|
26
|
+
validateOptionObject(guides, ["axes", "grid", "legend"], `${OPERATION} guides`);
|
|
27
|
+
for (const [family, forbidden, supported] of [
|
|
28
|
+
["axes", "y", ["coordinate", "x", "y"]], ["grid", "horizontal", ["horizontal", "vertical"]]
|
|
29
|
+
]) {
|
|
30
|
+
const options = guides[family];
|
|
31
|
+
if (options === undefined || options === false) continue;
|
|
32
|
+
validateOptionObject(options, supported, `${OPERATION} guides.${family}`);
|
|
33
|
+
if (options[forbidden] !== undefined && options[forbidden] !== false) {
|
|
34
|
+
throw new Error(`${OPERATION} guides.${family}.${forbidden} only accepts false.`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (guides.legend !== undefined && guides.legend !== false) {
|
|
38
|
+
throw new Error(`${OPERATION} guides.legend only accepts false.`);
|
|
39
|
+
}
|
|
40
|
+
return guides;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const createHorizonPlot = action({
|
|
44
|
+
op: OPERATION, description: "Create a signed, folded horizon area with original-x guides."
|
|
45
|
+
}, function (args = {}) {
|
|
46
|
+
validateFacadeOptions(args, OPTIONS, OPERATION);
|
|
47
|
+
const id = resolveFacadeId(this, args.id, { defaultId: "horizonPlot", operation: OPERATION });
|
|
48
|
+
const data = resolveFacadeData(this, args.data, OPERATION);
|
|
49
|
+
const x = position(args.x, "x");
|
|
50
|
+
const y = position(args.y, "y");
|
|
51
|
+
const { id: requestedId, data: requestedData, coordinate, x: requestedX, y: requestedY,
|
|
52
|
+
area: requestedArea, guides: requestedGuides, ...horizon } = omitUndefinedOptions(args);
|
|
53
|
+
const { opacity, ...area } = omitUndefinedOptions(normalizeAppearance(requestedArea,
|
|
54
|
+
["opacity", "stroke", "strokeWidth", "curve"], `${OPERATION} area`));
|
|
55
|
+
for (const [key, value] of Object.entries({ ...horizon, ...area })) {
|
|
56
|
+
if (value === null) throw new TypeError(`${OPERATION} ${key} cannot be null.`);
|
|
57
|
+
}
|
|
58
|
+
if (horizon.palette !== undefined) {
|
|
59
|
+
validateOptionObject(horizon.palette, ["positive", "negative"], `${OPERATION} palette`);
|
|
60
|
+
if (Object.values(horizon.palette).some(value => value === null)) {
|
|
61
|
+
throw new TypeError(`${OPERATION} palette entries cannot be null.`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (opacity !== undefined) validateUnitInterval(opacity, `${OPERATION} area.opacity`);
|
|
65
|
+
const guides = guideOptions(requestedGuides);
|
|
66
|
+
let next = this.createAreaMark({ id, data, ...area });
|
|
67
|
+
if (coordinate !== undefined) next = next.createCoordinate({ id: coordinate, type: "cartesian", layers: [id] });
|
|
68
|
+
next = next.encodeHorizon({ ...horizon, target: id, x, y });
|
|
69
|
+
if (opacity !== undefined) next = next.editAreaMark({ target: id, opacity });
|
|
70
|
+
return applyFacadeGuides(next, guides, id, requestedGuides ?? {});
|
|
71
|
+
});
|
|
@@ -1,15 +1,52 @@
|
|
|
1
|
+
import { createRosePlot, createRadialBarPlot } from "./radial.js";
|
|
2
|
+
import { createAreaPlot } from "./area.js";
|
|
1
3
|
import { createHeatmap } from "./heatmap.js";
|
|
2
4
|
import { createBarPlot } from "./bar.js";
|
|
3
5
|
import { createHistogram } from "./histogram.js";
|
|
4
6
|
import { createLinePlot } from "./line.js";
|
|
5
7
|
import { createScatterPlot } from "./scatter.js";
|
|
6
8
|
import { createParallelCoordinates } from "./parallel.js";
|
|
9
|
+
import { createPiePlot } from "./pie.js";
|
|
10
|
+
import { createDensityPlot } from "./density.js";
|
|
11
|
+
import { createHorizonPlot } from "./horizon.js";
|
|
12
|
+
import { createPolarLinePlot, createPolarScatterPlot } from "./polar.js";
|
|
13
|
+
import { createRadarPlot } from "./radar.js";
|
|
14
|
+
import { createRugPlot, createStripPlot } from "./rug-strip.js";
|
|
15
|
+
import { createIntervalPlot, createRegressionPlot } from "./interval-regression.js";
|
|
16
|
+
import {
|
|
17
|
+
createDotPlot, createLollipopPlot, createDumbbellPlot, editEndpointPlot
|
|
18
|
+
} from "./endpoints.js";
|
|
19
|
+
import { createECDFPlot, editECDFPlot } from "./ecdf.js";
|
|
20
|
+
import { createBeeswarmPlot } from "./beeswarm.js";
|
|
21
|
+
import { createRaincloudPlot, editRaincloudPlot } from "./raincloud.js";
|
|
7
22
|
|
|
8
23
|
export function registerChartActions(ProgramClass) {
|
|
24
|
+
ProgramClass.prototype.createRosePlot = createRosePlot;
|
|
25
|
+
ProgramClass.prototype.createRadialBarPlot = createRadialBarPlot;
|
|
26
|
+
ProgramClass.prototype.createAreaPlot = createAreaPlot;
|
|
9
27
|
ProgramClass.prototype.createBarPlot = createBarPlot;
|
|
10
28
|
ProgramClass.prototype.createHeatmap = createHeatmap;
|
|
11
29
|
ProgramClass.prototype.createHistogram = createHistogram;
|
|
12
30
|
ProgramClass.prototype.createScatterPlot = createScatterPlot;
|
|
13
31
|
ProgramClass.prototype.createLinePlot = createLinePlot;
|
|
14
32
|
ProgramClass.prototype.createParallelCoordinates = createParallelCoordinates;
|
|
33
|
+
ProgramClass.prototype.createPiePlot = createPiePlot;
|
|
34
|
+
ProgramClass.prototype.createDensityPlot = createDensityPlot;
|
|
35
|
+
ProgramClass.prototype.createHorizonPlot = createHorizonPlot;
|
|
36
|
+
ProgramClass.prototype.createPolarScatterPlot = createPolarScatterPlot;
|
|
37
|
+
ProgramClass.prototype.createPolarLinePlot = createPolarLinePlot;
|
|
38
|
+
ProgramClass.prototype.createRadarPlot = createRadarPlot;
|
|
39
|
+
ProgramClass.prototype.createRugPlot = createRugPlot;
|
|
40
|
+
ProgramClass.prototype.createStripPlot = createStripPlot;
|
|
41
|
+
ProgramClass.prototype.createIntervalPlot = createIntervalPlot;
|
|
42
|
+
ProgramClass.prototype.createRegressionPlot = createRegressionPlot;
|
|
43
|
+
ProgramClass.prototype.createDotPlot = createDotPlot;
|
|
44
|
+
ProgramClass.prototype.createLollipopPlot = createLollipopPlot;
|
|
45
|
+
ProgramClass.prototype.createDumbbellPlot = createDumbbellPlot;
|
|
46
|
+
ProgramClass.prototype.editEndpointPlot = editEndpointPlot;
|
|
47
|
+
ProgramClass.prototype.createECDFPlot = createECDFPlot;
|
|
48
|
+
ProgramClass.prototype.editECDFPlot = editECDFPlot;
|
|
49
|
+
ProgramClass.prototype.createBeeswarmPlot = createBeeswarmPlot;
|
|
50
|
+
ProgramClass.prototype.createRaincloudPlot = createRaincloudPlot;
|
|
51
|
+
ProgramClass.prototype.editRaincloudPlot = editRaincloudPlot;
|
|
15
52
|
}
|