ggaction 0.0.9 → 0.0.11
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 +84 -0
- package/README.md +5 -4
- package/knowledge/action-card.schema.json +1 -0
- package/knowledge/action-cards.json +1 -10941
- package/knowledge/action-cards.schema.json +1 -0
- package/knowledge/extension-authoring.md +248 -0
- package/knowledge/intent-taxonomy.json +1 -183
- package/knowledge/intent-taxonomy.schema.json +1 -0
- package/knowledge/mcp-resources.json +1 -95
- package/knowledge/mcp-resources.schema.json +1 -0
- package/knowledge/task-packet.schema.json +1 -159
- package/knowledge/task-resolver.js +1 -1230
- package/package.json +19 -2
- package/src/actions/boxPlots/edit.js +45 -298
- package/src/actions/boxPlots/options.js +46 -77
- package/src/actions/boxPlots/resolve.js +1 -1
- package/src/actions/charts/bar.js +3 -0
- package/src/actions/charts/heatmap.js +73 -83
- package/src/actions/charts/index.js +1 -1
- package/src/actions/charts/parallel.js +35 -1
- package/src/actions/charts/shared.js +20 -0
- package/src/actions/composition/actions.js +46 -49
- package/src/actions/data/bin2d.js +105 -2
- package/src/actions/data/box.js +17 -17
- package/src/actions/data/density.js +42 -78
- package/src/actions/data/gradientProfile.js +22 -52
- package/src/actions/data/horizon.js +16 -41
- package/src/actions/data/index.js +10 -10
- package/src/actions/data/interval.js +16 -38
- package/src/actions/data/intervalEdit.js +55 -0
- package/src/actions/data/regression.js +7 -17
- package/src/actions/data/shared.js +47 -0
- package/src/actions/data/timeUnit.js +14 -38
- package/src/actions/data/window.js +14 -38
- package/src/actions/distributions/revision.js +154 -34
- package/src/actions/encodings/angle.js +9 -10
- package/src/actions/encodings/appearance.js +17 -51
- package/src/actions/encodings/density/resolve.js +3 -1
- package/src/actions/encodings/density.js +1 -15
- package/src/actions/encodings/horizon/resolve.js +13 -3
- package/src/actions/encodings/horizon.js +8 -7
- package/src/actions/encodings/index.js +9 -9
- package/src/actions/encodings/offset.js +8 -24
- package/src/actions/encodings/parallel.js +9 -10
- package/src/actions/encodings/pathOrder.js +6 -13
- package/src/actions/encodings/position/policies/index.js +4 -4
- package/src/actions/encodings/position/policies/line.js +47 -17
- package/src/actions/encodings/position/resolve.js +24 -5
- package/src/actions/encodings/ranged.js +18 -23
- package/src/actions/encodings/remove.js +15 -25
- package/src/actions/encodings/ruleAppearance.js +9 -24
- package/src/actions/encodings/shared.js +37 -0
- package/src/actions/encodings/strokeDash.js +8 -23
- package/src/actions/encodings/text.js +9 -10
- package/src/actions/errorBands/create.js +105 -39
- package/src/actions/errorBands/edit.js +89 -93
- package/src/actions/errorBands/index.js +1 -2
- package/src/actions/errorBars/create.js +78 -27
- package/src/actions/errorBars/edit.js +81 -48
- package/src/actions/errorBars/index.js +1 -2
- package/src/actions/facets/actions.js +45 -69
- package/src/actions/facets/derive.js +38 -53
- package/src/actions/facets/replay.js +0 -1
- package/src/actions/gradientPlots/components.js +59 -48
- package/src/actions/gradientPlots/edit.js +35 -216
- package/src/actions/gradientPlots/index.js +11 -10
- package/src/actions/gradientPlots/materialize.js +21 -35
- package/src/actions/gradientPlots/options.js +5 -7
- package/src/actions/gradientPlots/paint.js +9 -6
- package/src/actions/gradientPlots/resolve.js +1 -1
- package/src/actions/guides/axes/index.js +3 -3
- package/src/actions/guides/axes/labels.js +68 -26
- package/src/actions/guides/axes/lines.js +22 -2
- package/src/actions/guides/axes/policy.js +28 -8
- package/src/actions/guides/axes/ticks.js +27 -9
- package/src/actions/guides/axes/titles.js +113 -65
- package/src/actions/guides/grids/resolve.js +14 -0
- package/src/actions/guides/guides.js +2 -4
- package/src/actions/guides/index.js +2 -2
- package/src/actions/guides/legends/categorical/actions.js +44 -68
- package/src/actions/guides/legends/categorical/components.js +6 -1
- package/src/actions/guides/legends/categorical/index.js +2 -2
- package/src/actions/guides/legends/categorical/layout.js +148 -175
- package/src/actions/guides/legends/categorical/recipes.js +5 -0
- package/src/actions/guides/legends/categorical/resolve.js +8 -1
- package/src/actions/guides/legends/continuous/common.js +117 -66
- package/src/actions/guides/legends/continuous/gradient.js +38 -37
- package/src/actions/guides/legends/continuous/interval.js +97 -76
- package/src/actions/guides/legends/continuous/opacity.js +28 -38
- package/src/actions/guides/legends/edit.js +14 -1
- package/src/actions/guides/legends/index.js +1 -1
- package/src/actions/guides/legends/lane.js +2 -1
- package/src/actions/guides/legends/remove.js +8 -0
- package/src/actions/guides/legends/size.js +36 -43
- package/src/actions/guides/legends/strokeWidth.js +20 -26
- package/src/actions/guides/polar/axes/labels.js +17 -1
- package/src/actions/guides/polar/axes/shared.js +7 -1
- package/src/actions/guides/polar/axes/titles.js +19 -2
- package/src/actions/guides/polar/grids.js +4 -0
- package/src/actions/guides/polar/resolve.js +6 -0
- package/src/actions/index.js +13 -13
- package/src/actions/intervals/resolve.js +87 -117
- package/src/actions/marks/arc/actions.js +31 -36
- package/src/actions/marks/area/actions.js +23 -31
- package/src/actions/marks/index.js +6 -6
- package/src/actions/marks/line/actions.js +33 -52
- package/src/actions/marks/line/materialize.js +7 -5
- package/src/actions/marks/point/materialize.js +5 -3
- package/src/actions/marks/remove.js +21 -0
- package/src/actions/marks/rule/actions.js +53 -53
- package/src/actions/marks/shared.js +8 -4
- package/src/actions/marks/text/actions.js +14 -6
- package/src/actions/marks/text/layout.js +21 -45
- package/src/actions/marks/tick/actions.js +22 -21
- package/src/actions/primitives/createGraphics.js +4 -0
- package/src/actions/primitives/editGraphics.js +4 -0
- package/src/actions/primitives/index.js +3 -3
- package/src/actions/primitives/semantic.js +1 -1
- package/src/actions/primitives/semanticValidation/index.js +2 -2
- package/src/actions/primitives/semanticValidation/layer.js +1 -1
- package/src/actions/regression/edit.js +3 -43
- package/src/actions/scales/consumers/families.js +24 -4
- package/src/actions/scales/consumers/index.js +1 -1
- package/src/actions/scales/consumers/seriesLayout.js +17 -0
- package/src/actions/scales/definitions.js +100 -103
- package/src/actions/scales/materialize.js +5 -0
- package/src/actions/selection/actions.js +79 -121
- package/src/actions/titles/actions.js +48 -94
- package/src/actions/titles/resolve.js +14 -27
- package/src/actions/violinPlots/create.js +37 -44
- package/src/core/action.js +20 -5
- package/src/core/extensionRegistry.js +104 -0
- package/src/core/textMetrics.js +74 -37
- package/src/core/validation.js +18 -0
- package/src/extension.js +8 -1
- package/src/grammar/aggregate.js +48 -28
- package/src/grammar/arcs.js +58 -11
- package/src/grammar/areaSeries.js +14 -16
- package/src/grammar/bin2d.js +37 -23
- package/src/grammar/boxPlot.js +14 -13
- package/src/grammar/categoricalDensity.js +14 -14
- package/src/grammar/categoryOrder.js +8 -14
- package/src/grammar/curveCommands.js +87 -20
- package/src/grammar/density.js +162 -141
- package/src/grammar/facets/index.js +14 -0
- package/src/grammar/facets/scales.js +7 -4
- package/src/grammar/gradientProfile.js +12 -15
- package/src/grammar/histogram.js +145 -83
- package/src/grammar/horizon.js +60 -23
- package/src/grammar/interval.js +14 -13
- package/src/grammar/lineSeries.js +91 -61
- package/src/grammar/numeric.js +290 -0
- package/src/grammar/paint.js +3 -4
- package/src/grammar/palettes.js +21 -32
- package/src/grammar/pathCommands.js +6 -0
- package/src/grammar/polarPaths.js +5 -5
- package/src/grammar/regression/derive.js +48 -9
- package/src/grammar/regression/models.js +248 -69
- package/src/grammar/regression/parameters.js +8 -0
- package/src/grammar/scales/color.js +35 -26
- package/src/grammar/scales/continuous.js +71 -24
- package/src/grammar/scales/definition.js +1 -1
- package/src/grammar/scales/discretized.js +42 -12
- package/src/grammar/scales/fields.js +21 -4
- package/src/grammar/scales/ordinal.js +117 -61
- package/src/grammar/scales/temporal.js +40 -11
- package/src/grammar/scales/transformed.js +199 -52
- package/src/grammar/schemas/graphicBounds.js +270 -79
- package/src/grammar/seriesLayout.js +50 -18
- package/src/grammar/ticks.js +123 -53
- package/src/grammar/timeUnit.js +18 -8
- package/src/grammar/transforms.js +4 -1
- package/src/grammar/window.js +70 -57
- package/src/index.js +1 -1
- package/src/layout/canvas.js +4 -8
- package/src/layout/composition.js +39 -40
- package/src/layout/facets.js +10 -14
- package/src/layout/labels.js +91 -15
- package/src/layout/legend.js +22 -20
- package/src/layout/legendLane.js +54 -40
- package/src/layout/title.js +7 -7
- package/src/materialization/bars/aggregate.js +14 -40
- package/src/materialization/bars/grouped.js +7 -18
- package/src/materialization/bars/histogram.js +2 -19
- package/src/materialization/bars/ranged.js +20 -15
- package/src/materialization/bars/resolve.js +21 -0
- package/src/materialization/facetGuides/legacyCategorical.js +2 -1
- package/src/materialization/facetGuides/placement.js +34 -19
- package/src/materialization/facetGuides/preparation.js +9 -5
- package/src/materialization/facets.js +39 -27
- package/src/materialization/guides/resources.js +2 -1
- package/src/materialization/marks/capabilities.js +5 -2
- package/src/materialization/marks/index.js +24 -4
- package/src/materialization/scales/policies/bar.js +61 -59
- package/src/materialization/scales/policies/binnedPosition.js +34 -0
- package/src/materialization/scales/policies/series.js +13 -7
- package/src/materialization/scales/resolve.js +4 -5
- package/src/materialization/selection/items/arc.js +16 -2
- package/src/materialization/selection/items/bar.js +4 -2
- package/src/materialization/selection/items/common.js +21 -1
- package/src/materialization/selection/items/path.js +19 -2
- package/src/materialization/selection/items/point.js +4 -16
- package/src/materialization/selection/items/tick.js +13 -21
- package/src/materialization/selection/point.js +11 -4
- package/src/materialization/selection/styles.js +19 -27
- package/src/materialization/text.js +4 -1
- package/src/mcp/adapter.js +8 -3
- package/src/renderers/canvas/fill.js +2 -2
- package/src/renderers/canvas/index.js +35 -2
- package/src/renderers/canvas/native.js +160 -0
- package/src/renderers/pdf.js +14 -0
- package/src/renderers/svg.js +37 -4
- package/types/extension.d.ts +19 -0
- package/types/index.d.ts +3 -0
- package/types/program.d.ts +509 -131
- package/types/svg.d.ts +1 -0
- package/src/actions/errorBands/components.js +0 -81
- package/src/actions/errorBands/options.js +0 -39
- package/src/actions/errorBands/resolve.js +0 -28
- package/src/actions/errorBars/components.js +0 -62
- package/src/actions/errorBars/options.js +0 -58
- package/src/actions/errorBars/resolve.js +0 -25
|
@@ -2,8 +2,10 @@ import { action } from "../../core/action.js";
|
|
|
2
2
|
import { validateKeys } from "../../core/validation.js";
|
|
3
3
|
import { findLayer } from "../../selectors/layers.js";
|
|
4
4
|
import { DEFAULT_COLORS } from "../../theme/defaults.js";
|
|
5
|
-
import {
|
|
6
|
-
|
|
5
|
+
import { createResolvedIntervalData, ownOptions } from
|
|
6
|
+
"../data/intervalEdit.js";
|
|
7
|
+
import { resolveIntervalComposite } from "../intervals/resolve.js";
|
|
8
|
+
import { errorBarCaps, resolveErrorBarAppearance } from "./edit.js";
|
|
7
9
|
|
|
8
10
|
const OPTIONS = Object.freeze([
|
|
9
11
|
"id",
|
|
@@ -21,13 +23,34 @@ const OPTIONS = Object.freeze([
|
|
|
21
23
|
"opacity"
|
|
22
24
|
]);
|
|
23
25
|
|
|
26
|
+
const ERROR_BAR_POLICY = Object.freeze({
|
|
27
|
+
operation: "createErrorBar",
|
|
28
|
+
resourceLabel: "error-bar",
|
|
29
|
+
defaultId: "errorBar",
|
|
30
|
+
ownerLabel: "Error-bar id",
|
|
31
|
+
positionTypes: Object.freeze(["nominal", "ordinal", "temporal"]),
|
|
32
|
+
defaultPositionType: "nominal",
|
|
33
|
+
defaultIntervalChannel: "y",
|
|
34
|
+
scaleDefaults: () => ({}),
|
|
35
|
+
intervalScaleDefaults: Object.freeze({ nice: true, zero: false }),
|
|
36
|
+
allowExplicitGrouping: false,
|
|
37
|
+
ambiguousMessage:
|
|
38
|
+
"createErrorBar requires one quantitative interval axis and one nominal, ordinal, or temporal position axis."
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
function resolveErrorBar(program, args) {
|
|
42
|
+
const resolved = resolveIntervalComposite(program, args, ERROR_BAR_POLICY);
|
|
43
|
+
return {
|
|
44
|
+
...resolved,
|
|
45
|
+
lowerCapId: `${resolved.id}LowerCap`,
|
|
46
|
+
upperCapId: `${resolved.id}UpperCap`
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
24
50
|
function resolveAppearance(args) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
.map(key => [key, args[key]])
|
|
29
|
-
);
|
|
30
|
-
return resolveErrorBarAppearance(appearanceArgs, {
|
|
51
|
+
return resolveErrorBarAppearance(ownOptions(args, [
|
|
52
|
+
"caps", "capSize", "stroke", "strokeWidth", "strokeDash", "opacity"
|
|
53
|
+
]), {
|
|
31
54
|
defaults: {
|
|
32
55
|
caps: true,
|
|
33
56
|
capSize: 8,
|
|
@@ -40,6 +63,51 @@ function resolveAppearance(args) {
|
|
|
40
63
|
});
|
|
41
64
|
}
|
|
42
65
|
|
|
66
|
+
export const createErrorBarCap = action(
|
|
67
|
+
{
|
|
68
|
+
op: "createErrorBarCap",
|
|
69
|
+
description: "Create one fixed-pixel error-bar cap."
|
|
70
|
+
},
|
|
71
|
+
function (args = {}) {
|
|
72
|
+
validateKeys(args, [
|
|
73
|
+
"id", "data", "orientation", "positionField", "positionFieldType",
|
|
74
|
+
"intervalField", "coordinate", "positionScale", "intervalScale",
|
|
75
|
+
"capSize", "stroke", "strokeWidth", "strokeDash", "opacity"
|
|
76
|
+
], "createErrorBarCap");
|
|
77
|
+
if (!["vertical", "horizontal"].includes(args.orientation)) {
|
|
78
|
+
throw new Error(`Unsupported error-bar orientation "${args.orientation}".`);
|
|
79
|
+
}
|
|
80
|
+
const vertical = args.orientation === "vertical";
|
|
81
|
+
const positionAction = vertical ? "encodeX" : "encodeY";
|
|
82
|
+
const intervalAction = vertical ? "encodeY" : "encodeX";
|
|
83
|
+
return this
|
|
84
|
+
.createRuleMark({ id: args.id, data: args.data })
|
|
85
|
+
[positionAction]({
|
|
86
|
+
target: args.id,
|
|
87
|
+
field: args.positionField,
|
|
88
|
+
fieldType: args.positionFieldType,
|
|
89
|
+
coordinate: args.coordinate,
|
|
90
|
+
scale: { id: args.positionScale }
|
|
91
|
+
})
|
|
92
|
+
[intervalAction]({
|
|
93
|
+
target: args.id,
|
|
94
|
+
field: args.intervalField,
|
|
95
|
+
fieldType: "quantitative",
|
|
96
|
+
coordinate: args.coordinate,
|
|
97
|
+
scale: { id: args.intervalScale }
|
|
98
|
+
})
|
|
99
|
+
.encodeStroke({ target: args.id, value: args.stroke })
|
|
100
|
+
.encodeStrokeWidth({ target: args.id, value: args.strokeWidth })
|
|
101
|
+
.encodeStrokeDash({ target: args.id, value: args.strokeDash })
|
|
102
|
+
.encodeOpacity({ target: args.id, value: args.opacity })
|
|
103
|
+
.materializeRuleSpan({
|
|
104
|
+
id: args.id,
|
|
105
|
+
orientation: vertical ? "horizontal" : "vertical",
|
|
106
|
+
size: args.capSize
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
|
|
43
111
|
function positionArgs(resolved) {
|
|
44
112
|
return {
|
|
45
113
|
target: resolved.id,
|
|
@@ -69,21 +137,7 @@ export const createErrorBar = action(
|
|
|
69
137
|
validateKeys(args, OPTIONS, "createErrorBar");
|
|
70
138
|
const resolved = resolveErrorBar(this, args);
|
|
71
139
|
const appearance = resolveAppearance(args);
|
|
72
|
-
let next = this;
|
|
73
|
-
|
|
74
|
-
if (resolved.interval.mode === "statistical") {
|
|
75
|
-
next = next.createIntervalData({
|
|
76
|
-
id: resolved.dataId,
|
|
77
|
-
source: resolved.source,
|
|
78
|
-
field: resolved.interval.field,
|
|
79
|
-
groupBy: resolved.groupBy,
|
|
80
|
-
center: resolved.interval.center,
|
|
81
|
-
extent: resolved.interval.extent,
|
|
82
|
-
level: resolved.interval.level,
|
|
83
|
-
as: resolved.fields
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
|
|
140
|
+
let next = createResolvedIntervalData(this, resolved);
|
|
87
141
|
next = next.createRuleMark({ id: resolved.id, data: resolved.dataId });
|
|
88
142
|
const positionAction = resolved.position.channel === "x" ? "encodeX" : "encodeY";
|
|
89
143
|
const intervalAction = resolved.interval.channel === "x" ? "encodeX" : "encodeY";
|
|
@@ -108,10 +162,7 @@ export const createErrorBar = action(
|
|
|
108
162
|
|
|
109
163
|
const intervalLayer = findLayer(next, resolved.id);
|
|
110
164
|
if (appearance.caps) {
|
|
111
|
-
for (const [id, field] of
|
|
112
|
-
[resolved.lowerCapId, resolved.fields.lower],
|
|
113
|
-
[resolved.upperCapId, resolved.fields.upper]
|
|
114
|
-
]) {
|
|
165
|
+
for (const [id, field] of errorBarCaps(resolved)) {
|
|
115
166
|
next = next.createErrorBarCap({
|
|
116
167
|
id,
|
|
117
168
|
data: resolved.dataId,
|
|
@@ -1,26 +1,77 @@
|
|
|
1
1
|
import { action } from "../../core/action.js";
|
|
2
2
|
import { validateUserId } from "../../core/identifiers.js";
|
|
3
|
-
import { validateKeys } from "../../core/validation.js";
|
|
4
|
-
import { findLayer, resolveEligibleLayer } from "../../selectors/layers.js";
|
|
5
|
-
import { planIntervalEdit } from "../data/intervalEdit.js";
|
|
6
3
|
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
validateKeys,
|
|
5
|
+
validatePositiveFinite
|
|
6
|
+
} from "../../core/validation.js";
|
|
7
|
+
import {
|
|
8
|
+
normalizeStrokeDashPattern,
|
|
9
|
+
validateOpacityValue
|
|
10
|
+
} from "../../grammar/scales/index.js";
|
|
11
|
+
import {
|
|
12
|
+
validateRuleStroke,
|
|
13
|
+
validateRuleStrokeWidth
|
|
14
|
+
} from "../../grammar/ruleAppearance.js";
|
|
15
|
+
import { findLayer } from "../../selectors/layers.js";
|
|
16
|
+
import {
|
|
17
|
+
applyIntervalRevision,
|
|
18
|
+
ownOptions,
|
|
19
|
+
planIntervalEdit,
|
|
20
|
+
releaseIntervalRevision,
|
|
21
|
+
resolveIntervalOwner
|
|
22
|
+
} from "../data/intervalEdit.js";
|
|
23
|
+
|
|
24
|
+
export const ERROR_BAR_EDIT_OPTIONS = Object.freeze([
|
|
25
|
+
"target", "caps", "capSize", "stroke", "strokeWidth",
|
|
26
|
+
"strokeDash", "opacity", "statistics"
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
export const ERROR_BAR_APPEARANCE_OPTIONS = Object.freeze(
|
|
30
|
+
ERROR_BAR_EDIT_OPTIONS.filter(option =>
|
|
31
|
+
option !== "target" && option !== "statistics"
|
|
32
|
+
)
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
export function resolveErrorBarAppearance(args, { defaults, operation }) {
|
|
36
|
+
validateKeys(args, ERROR_BAR_EDIT_OPTIONS, operation);
|
|
37
|
+
const caps = args.caps ?? defaults.caps;
|
|
38
|
+
if (typeof caps !== "boolean") {
|
|
39
|
+
throw new TypeError(`${operation} caps must be a boolean.`);
|
|
40
|
+
}
|
|
41
|
+
const capSize = args.capSize ?? defaults.capSize;
|
|
42
|
+
validatePositiveFinite(capSize, `${operation} capSize`);
|
|
43
|
+
const stroke = validateRuleStroke(
|
|
44
|
+
args.stroke ?? defaults.stroke,
|
|
45
|
+
`${operation} stroke`
|
|
46
|
+
);
|
|
47
|
+
const strokeWidth = validateRuleStrokeWidth(
|
|
48
|
+
args.strokeWidth ?? defaults.strokeWidth,
|
|
49
|
+
`${operation} strokeWidth`
|
|
50
|
+
);
|
|
51
|
+
const strokeDash = args.strokeDash ?? defaults.strokeDash;
|
|
52
|
+
normalizeStrokeDashPattern(strokeDash);
|
|
53
|
+
const opacity = validateOpacityValue(
|
|
54
|
+
args.opacity ?? defaults.opacity,
|
|
55
|
+
`${operation} opacity`
|
|
56
|
+
);
|
|
57
|
+
return { caps, capSize, stroke, strokeWidth, strokeDash, opacity };
|
|
58
|
+
}
|
|
11
59
|
|
|
12
60
|
const REMATERIALIZE_OPTIONS = Object.freeze(["id"]);
|
|
13
61
|
|
|
62
|
+
export function errorBarCaps(config) {
|
|
63
|
+
return [
|
|
64
|
+
[config.lowerCapId, config.fields?.lower ?? config.lowerField],
|
|
65
|
+
[config.upperCapId, config.fields?.upper ?? config.upperField]
|
|
66
|
+
];
|
|
67
|
+
}
|
|
68
|
+
|
|
14
69
|
function resolveOwner(program, requested) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
: validateUserId(requested, "Error-bar id");
|
|
18
|
-
return resolveEligibleLayer(program, {
|
|
19
|
-
target,
|
|
70
|
+
return resolveIntervalOwner(program, requested, {
|
|
71
|
+
idLabel: "Error-bar id",
|
|
20
72
|
label: "error bar",
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
program.markConfigs[layer.id]?.errorBar !== undefined
|
|
73
|
+
mark: "rule",
|
|
74
|
+
config: "errorBar"
|
|
24
75
|
});
|
|
25
76
|
}
|
|
26
77
|
|
|
@@ -82,10 +133,7 @@ export const rematerializeErrorBar = action(
|
|
|
82
133
|
throw new Error(`Unknown error bar "${id}".`);
|
|
83
134
|
}
|
|
84
135
|
let next = rematerializeRuleAppearance(this, id, config);
|
|
85
|
-
const capDefinitions =
|
|
86
|
-
[config.lowerCapId, config.lowerField],
|
|
87
|
-
[config.upperCapId, config.upperField]
|
|
88
|
-
];
|
|
136
|
+
const capDefinitions = errorBarCaps(config);
|
|
89
137
|
if (!config.caps) {
|
|
90
138
|
for (const [capId] of capDefinitions) next = removeCap(next, capId);
|
|
91
139
|
return next;
|
|
@@ -118,22 +166,20 @@ export const editErrorBar = action(
|
|
|
118
166
|
}
|
|
119
167
|
const owner = resolveOwner(this, args.target);
|
|
120
168
|
const current = this.markConfigs[owner.id].errorBar;
|
|
121
|
-
const
|
|
122
|
-
ERROR_BAR_APPEARANCE_OPTIONS
|
|
123
|
-
.filter(key => Object.hasOwn(args, key))
|
|
124
|
-
.map(key => [key, args[key]])
|
|
125
|
-
);
|
|
126
|
-
const appearance = resolveErrorBarAppearance(appearanceArgs, {
|
|
169
|
+
const appearance = resolveErrorBarAppearance(
|
|
170
|
+
ownOptions(args, ERROR_BAR_APPEARANCE_OPTIONS), {
|
|
127
171
|
defaults: current,
|
|
128
172
|
operation: "editErrorBar"
|
|
129
|
-
|
|
173
|
+
}
|
|
174
|
+
);
|
|
130
175
|
const interval = Object.hasOwn(args, "statistics")
|
|
131
176
|
? planIntervalEdit(this, {
|
|
132
177
|
owner: owner.id,
|
|
133
178
|
data: current.data,
|
|
134
179
|
consumers: [
|
|
135
180
|
owner.id,
|
|
136
|
-
...
|
|
181
|
+
...errorBarCaps(current)
|
|
182
|
+
.map(([id]) => id)
|
|
137
183
|
.filter(id => findLayer(this, id) !== undefined)
|
|
138
184
|
],
|
|
139
185
|
statistics: args.statistics,
|
|
@@ -141,27 +187,14 @@ export const editErrorBar = action(
|
|
|
141
187
|
})
|
|
142
188
|
: { changed: false };
|
|
143
189
|
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
190
|
+
const next = applyIntervalRevision(this, interval);
|
|
191
|
+
return releaseIntervalRevision(next._withMarkConfig(owner.id, {
|
|
192
|
+
...next.markConfigs[owner.id],
|
|
193
|
+
errorBar: {
|
|
194
|
+
...current,
|
|
195
|
+
...appearance,
|
|
196
|
+
...(interval.changed ? { data: interval.revision.id } : {})
|
|
151
197
|
}
|
|
152
|
-
|
|
153
|
-
...next.markConfigs[owner.id],
|
|
154
|
-
errorBar: {
|
|
155
|
-
...current,
|
|
156
|
-
...appearance,
|
|
157
|
-
...(interval.changed ? { data: interval.revision.id } : {})
|
|
158
|
-
}
|
|
159
|
-
}).rematerializeErrorBar({ id: owner.id });
|
|
160
|
-
return interval.changed
|
|
161
|
-
? next.releaseDerivedData(interval.revision.release)
|
|
162
|
-
: next;
|
|
163
|
-
};
|
|
164
|
-
if (interval.changed) applyEdit(this);
|
|
165
|
-
return applyEdit(this);
|
|
198
|
+
}).rematerializeErrorBar({ id: owner.id }), interval);
|
|
166
199
|
}
|
|
167
200
|
);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { createErrorBarCap } from "./
|
|
2
|
-
import { createErrorBar } from "./create.js";
|
|
1
|
+
import { createErrorBar, createErrorBarCap } from "./create.js";
|
|
3
2
|
import { editErrorBar, rematerializeErrorBar } from "./edit.js";
|
|
4
3
|
|
|
5
4
|
export function registerErrorBarActions(ProgramClass) {
|
|
@@ -7,9 +7,10 @@ import {
|
|
|
7
7
|
validatePositiveFinite
|
|
8
8
|
} from "../../core/validation.js";
|
|
9
9
|
import { resolveFacetDefinition } from "../../grammar/facets/index.js";
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
import {
|
|
11
|
+
FACET_SCALE_CHANNELS,
|
|
12
|
+
normalizeFacetScalePolicies
|
|
13
|
+
} from "../../grammar/facets/scales.js";
|
|
13
14
|
import { resolveFacetLayout } from "../../layout/facets.js";
|
|
14
15
|
import { compositionChildDescriptor } from
|
|
15
16
|
"../../materialization/composition.js";
|
|
@@ -17,6 +18,7 @@ import { DEFAULT_COLORS, DEFAULT_FONT_FAMILY } from "../../theme/defaults.js";
|
|
|
17
18
|
import { deriveFacetChildren } from "./derive.js";
|
|
18
19
|
import { replayDerivedData } from "./replay.js";
|
|
19
20
|
import { composeFacetGuides } from "./guides.js";
|
|
21
|
+
import { applyCompositionState } from "../composition/actions.js";
|
|
20
22
|
|
|
21
23
|
const FACET_OPTIONS = Object.freeze([
|
|
22
24
|
"id", "field", "data", "columns", "gap", "align", "padding", "scales",
|
|
@@ -48,25 +50,6 @@ function normalizeGuides(guides) {
|
|
|
48
50
|
return { axes, legend };
|
|
49
51
|
}
|
|
50
52
|
|
|
51
|
-
function normalizeHeaderPatch(args, previous) {
|
|
52
|
-
validateOptionObject(args, HEADER_OPTIONS, "editFacetHeaders", {
|
|
53
|
-
allowEmpty: false,
|
|
54
|
-
emptyMessage: "editFacetHeaders requires at least one change."
|
|
55
|
-
});
|
|
56
|
-
const next = { ...previous, ...args };
|
|
57
|
-
validatePositiveFinite(next.fontSize, "Facet header fontSize");
|
|
58
|
-
validateNonEmptyString(next.fontFamily, "Facet header fontFamily");
|
|
59
|
-
validateNonEmptyString(next.color, "Facet header color");
|
|
60
|
-
validateNonNegativeFinite(next.offset, "Facet header offset");
|
|
61
|
-
if (!(
|
|
62
|
-
(typeof next.fontWeight === "string" && next.fontWeight.length > 0) ||
|
|
63
|
-
Number.isFinite(next.fontWeight)
|
|
64
|
-
)) {
|
|
65
|
-
throw new TypeError("Facet header fontWeight must be a non-empty string or number.");
|
|
66
|
-
}
|
|
67
|
-
return next;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
53
|
function requireFacetProgram(program, operation) {
|
|
71
54
|
program._assertCompositionProgram(operation);
|
|
72
55
|
if (program.compositionSpec.type !== "facet") {
|
|
@@ -74,16 +57,6 @@ function requireFacetProgram(program, operation) {
|
|
|
74
57
|
}
|
|
75
58
|
}
|
|
76
59
|
|
|
77
|
-
function currentFacetDefinition(program) {
|
|
78
|
-
const current = program.compositionSpec;
|
|
79
|
-
return resolveFacetDefinition(program.semanticSpec, {
|
|
80
|
-
id: current.id,
|
|
81
|
-
data: current.facet.data,
|
|
82
|
-
field: current.facet.field,
|
|
83
|
-
values: current.facet.values
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
|
|
87
60
|
function facetUnitTemplate(program) {
|
|
88
61
|
const seed = program.children[program.compositionSpec.children[0]];
|
|
89
62
|
if (seed === undefined) {
|
|
@@ -106,20 +79,19 @@ function facetUnitTemplate(program) {
|
|
|
106
79
|
});
|
|
107
80
|
}
|
|
108
81
|
|
|
109
|
-
function
|
|
110
|
-
|
|
82
|
+
function rederiveFacet(program, { scales, guides }) {
|
|
83
|
+
const current = program.compositionSpec;
|
|
84
|
+
const definition = resolveFacetDefinition(program.semanticSpec, {
|
|
85
|
+
id: current.id,
|
|
86
|
+
data: current.facet.data,
|
|
87
|
+
field: current.facet.field,
|
|
88
|
+
values: current.facet.values
|
|
89
|
+
});
|
|
90
|
+
const request = Object.fromEntries(FACET_SCALE_CHANNELS.flatMap(channel =>
|
|
111
91
|
program.semanticSpec.layers.some(
|
|
112
92
|
layer => layer.encoding?.[channel]?.scale !== undefined
|
|
113
|
-
)
|
|
114
|
-
? [[channel, channels[channel]]]
|
|
115
|
-
: []
|
|
93
|
+
) ? [[channel, scales[channel]]] : []
|
|
116
94
|
));
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function rederiveFacet(program, { scales, guides }) {
|
|
120
|
-
const current = program.compositionSpec;
|
|
121
|
-
const definition = currentFacetDefinition(program);
|
|
122
|
-
const request = applicableScaleRequest(program, scales);
|
|
123
95
|
const normalized = normalizeFacetScalePolicies(program.semanticSpec, request);
|
|
124
96
|
const derived = deriveFacetChildren(facetUnitTemplate(program), definition, {
|
|
125
97
|
closeInheritedAction: true,
|
|
@@ -134,14 +106,10 @@ function rederiveFacet(program, { scales, guides }) {
|
|
|
134
106
|
guides
|
|
135
107
|
}
|
|
136
108
|
};
|
|
137
|
-
|
|
109
|
+
return applyCompositionState(program, {
|
|
138
110
|
children: derived.children,
|
|
139
111
|
compositionSpec
|
|
140
|
-
});
|
|
141
|
-
for (const id of compositionSpec.children) {
|
|
142
|
-
next = next.useProgram({ id });
|
|
143
|
-
}
|
|
144
|
-
return next.materializeComposition();
|
|
112
|
+
}, compositionSpec.children);
|
|
145
113
|
}
|
|
146
114
|
|
|
147
115
|
export const facet = action(
|
|
@@ -189,18 +157,16 @@ export const facet = action(
|
|
|
189
157
|
guides
|
|
190
158
|
}
|
|
191
159
|
};
|
|
192
|
-
|
|
193
|
-
.
|
|
160
|
+
return applyCompositionState(
|
|
161
|
+
this._withMaterializationConfig(["facets", definition.id], {
|
|
162
|
+
headers: DEFAULT_HEADERS
|
|
163
|
+
}),
|
|
164
|
+
{
|
|
194
165
|
children: derived.children,
|
|
195
166
|
compositionSpec
|
|
196
|
-
}
|
|
197
|
-
.
|
|
198
|
-
|
|
199
|
-
});
|
|
200
|
-
for (const id of compositionSpec.children) {
|
|
201
|
-
next = next.useProgram({ id });
|
|
202
|
-
}
|
|
203
|
-
return next.materializeComposition();
|
|
167
|
+
},
|
|
168
|
+
compositionSpec.children
|
|
169
|
+
);
|
|
204
170
|
}
|
|
205
171
|
);
|
|
206
172
|
|
|
@@ -217,7 +183,23 @@ export const editFacetHeaders = action(
|
|
|
217
183
|
if (!isPlainObject(config?.headers)) {
|
|
218
184
|
throw new Error(`Facet "${id}" requires header configuration.`);
|
|
219
185
|
}
|
|
220
|
-
|
|
186
|
+
validateOptionObject(args, HEADER_OPTIONS, "editFacetHeaders", {
|
|
187
|
+
allowEmpty: false,
|
|
188
|
+
emptyMessage: "editFacetHeaders requires at least one change."
|
|
189
|
+
});
|
|
190
|
+
const headers = { ...config.headers, ...args };
|
|
191
|
+
validatePositiveFinite(headers.fontSize, "Facet header fontSize");
|
|
192
|
+
validateNonEmptyString(headers.fontFamily, "Facet header fontFamily");
|
|
193
|
+
validateNonEmptyString(headers.color, "Facet header color");
|
|
194
|
+
validateNonNegativeFinite(headers.offset, "Facet header offset");
|
|
195
|
+
if (!(
|
|
196
|
+
(typeof headers.fontWeight === "string" && headers.fontWeight.length > 0) ||
|
|
197
|
+
Number.isFinite(headers.fontWeight)
|
|
198
|
+
)) {
|
|
199
|
+
throw new TypeError(
|
|
200
|
+
"Facet header fontWeight must be a non-empty string or number."
|
|
201
|
+
);
|
|
202
|
+
}
|
|
221
203
|
return this
|
|
222
204
|
._withMaterializationConfig(["facets", id], { ...config, headers })
|
|
223
205
|
.materializeComposition();
|
|
@@ -252,14 +234,10 @@ export const editFacetScales = action(
|
|
|
252
234
|
)) {
|
|
253
235
|
throw new Error("editFacetScales requires at least one channel policy change.");
|
|
254
236
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
const applyEdit = program => rederiveFacet(program, {
|
|
258
|
-
scales: normalized.channels,
|
|
237
|
+
return rederiveFacet(this, {
|
|
238
|
+
scales,
|
|
259
239
|
guides: current.guides
|
|
260
240
|
});
|
|
261
|
-
applyEdit(this);
|
|
262
|
-
return applyEdit(this);
|
|
263
241
|
}
|
|
264
242
|
);
|
|
265
243
|
|
|
@@ -277,12 +255,10 @@ export const editFacetGuides = action(
|
|
|
277
255
|
});
|
|
278
256
|
const current = this.compositionSpec.facet;
|
|
279
257
|
const guides = normalizeGuides({ ...current.guides, ...args });
|
|
280
|
-
|
|
258
|
+
return rederiveFacet(this, {
|
|
281
259
|
scales: current.scales,
|
|
282
260
|
guides
|
|
283
261
|
});
|
|
284
|
-
applyEdit(this);
|
|
285
|
-
return applyEdit(this);
|
|
286
262
|
}
|
|
287
263
|
);
|
|
288
264
|
|
|
@@ -56,40 +56,6 @@ function applySharedHistogramBoundaries(program, layerId, boundaries) {
|
|
|
56
56
|
});
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
function replayDatasetId(cell, owner) {
|
|
60
|
-
return `${cell.id}-${owner}-data`;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function rebindReplayedGradientPlots(child, base, datasets) {
|
|
64
|
-
let next = child;
|
|
65
|
-
for (const [id, markConfig] of Object.entries(base.markConfigs)) {
|
|
66
|
-
const gradientPlot = markConfig.gradientPlot;
|
|
67
|
-
if (gradientPlot?.materialized !== true) continue;
|
|
68
|
-
const profile = datasets.get(gradientPlot.profileId);
|
|
69
|
-
const source = datasets.get(gradientPlot.source);
|
|
70
|
-
if (profile === undefined || source === undefined) {
|
|
71
|
-
throw new Error(
|
|
72
|
-
`Facet gradient plot "${id}" is missing replayed profile dependencies.`
|
|
73
|
-
);
|
|
74
|
-
}
|
|
75
|
-
next = next.rebindGradientPlotProfile({ id, profile, source });
|
|
76
|
-
}
|
|
77
|
-
return next;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function cellMaterializationPlan(program) {
|
|
81
|
-
const scaleIds = [...new Set(program.semanticSpec.layers.flatMap(layer =>
|
|
82
|
-
Object.values(layer.encoding ?? {})
|
|
83
|
-
.map(encoding => encoding?.scale)
|
|
84
|
-
.filter(id => id !== undefined)
|
|
85
|
-
))];
|
|
86
|
-
const scales = scaleIds.map(id => ({
|
|
87
|
-
op: "rematerializeScale",
|
|
88
|
-
args: { id, guides: false, marks: false }
|
|
89
|
-
}));
|
|
90
|
-
return buildMaterializationPlan({ scales });
|
|
91
|
-
}
|
|
92
|
-
|
|
93
59
|
function deriveCellProgram(
|
|
94
60
|
base,
|
|
95
61
|
definition,
|
|
@@ -111,7 +77,7 @@ function deriveCellProgram(
|
|
|
111
77
|
`Facet replay source "${replay.source}" is not available in cell "${cell.id}".`
|
|
112
78
|
);
|
|
113
79
|
}
|
|
114
|
-
const id =
|
|
80
|
+
const id = `${cell.id}-${replay.id}-data`;
|
|
115
81
|
const policy = findTransformPolicy(replay.transform.type);
|
|
116
82
|
const transform = policy?.facetReplayTransform?.(
|
|
117
83
|
replay.transform,
|
|
@@ -138,8 +104,29 @@ function deriveCellProgram(
|
|
|
138
104
|
histogramBoundaries.get(layer.id)
|
|
139
105
|
);
|
|
140
106
|
}
|
|
141
|
-
|
|
142
|
-
|
|
107
|
+
for (const [id, markConfig] of Object.entries(base.markConfigs)) {
|
|
108
|
+
const gradientPlot = markConfig.gradientPlot;
|
|
109
|
+
if (gradientPlot?.materialized !== true) continue;
|
|
110
|
+
const profile = datasets.get(gradientPlot.profileId);
|
|
111
|
+
const source = datasets.get(gradientPlot.source);
|
|
112
|
+
if (profile === undefined || source === undefined) {
|
|
113
|
+
throw new Error(
|
|
114
|
+
`Facet gradient plot "${id}" is missing replayed profile dependencies.`
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
child = child.rebindGradientPlotProfile({ id, profile, source });
|
|
118
|
+
}
|
|
119
|
+
const scaleIds = [...new Set(child.semanticSpec.layers.flatMap(layer =>
|
|
120
|
+
Object.values(layer.encoding ?? {})
|
|
121
|
+
.map(encoding => encoding?.scale)
|
|
122
|
+
.filter(id => id !== undefined)
|
|
123
|
+
))];
|
|
124
|
+
return applyMaterializationPlan(child, buildMaterializationPlan({
|
|
125
|
+
scales: scaleIds.map(id => ({
|
|
126
|
+
op: "rematerializeScale",
|
|
127
|
+
args: { id, guides: false, marks: false }
|
|
128
|
+
}))
|
|
129
|
+
}));
|
|
143
130
|
}
|
|
144
131
|
|
|
145
132
|
function applyResolvedDomains(program, childId, resolution, baseResolved) {
|
|
@@ -186,14 +173,14 @@ export function deriveFacetChildren(
|
|
|
186
173
|
const template = stripTitle && base.semanticSpec.title.text !== undefined
|
|
187
174
|
? base.removeTitle()
|
|
188
175
|
: base;
|
|
189
|
-
|
|
190
|
-
const xPolicy =
|
|
176
|
+
scales ??= {};
|
|
177
|
+
const xPolicy = scales.x ?? "shared";
|
|
191
178
|
const bins = xPolicy === "shared"
|
|
192
179
|
? sharedHistogramBoundaries(template)
|
|
193
180
|
: new Map();
|
|
194
181
|
const independentlyResolved = Object.fromEntries(definition.cells.map(cell => [
|
|
195
182
|
cell.id,
|
|
196
|
-
deriveCellProgram(template, definition, cell, bins,
|
|
183
|
+
deriveCellProgram(template, definition, cell, bins, scales)
|
|
197
184
|
]));
|
|
198
185
|
const resolution = resolveFacetScaleDomains(
|
|
199
186
|
template.semanticSpec,
|
|
@@ -201,14 +188,19 @@ export function deriveFacetChildren(
|
|
|
201
188
|
id,
|
|
202
189
|
child.resolvedScales
|
|
203
190
|
])),
|
|
204
|
-
|
|
191
|
+
scales,
|
|
205
192
|
template.resolvedScales
|
|
206
193
|
);
|
|
207
194
|
const resolvedChildren = Object.fromEntries(
|
|
208
|
-
Object.entries(independentlyResolved).map(([id, child]) =>
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
195
|
+
Object.entries(independentlyResolved).map(([id, child]) => {
|
|
196
|
+
const resolved = applyResolvedDomains(
|
|
197
|
+
child,
|
|
198
|
+
id,
|
|
199
|
+
resolution,
|
|
200
|
+
template.resolvedScales
|
|
201
|
+
);
|
|
202
|
+
return [id, closeInheritedAction ? resolved._exitAction() : resolved];
|
|
203
|
+
})
|
|
212
204
|
);
|
|
213
205
|
const sharedScales = Object.fromEntries(
|
|
214
206
|
Object.entries(resolution.scales)
|
|
@@ -216,14 +208,7 @@ export function deriveFacetChildren(
|
|
|
216
208
|
.map(([id]) => [id, resolvedChildren[definition.cells[0].id].resolvedScales[id]])
|
|
217
209
|
);
|
|
218
210
|
return freezeOwned({
|
|
219
|
-
children: freezeOwned(
|
|
220
|
-
Object.entries(resolvedChildren).map(([id, child]) => [
|
|
221
|
-
id,
|
|
222
|
-
closeInheritedAction
|
|
223
|
-
? child._exitAction()
|
|
224
|
-
: child
|
|
225
|
-
])
|
|
226
|
-
)),
|
|
211
|
+
children: freezeOwned(resolvedChildren),
|
|
227
212
|
sharedScales: freezeOwned(sharedScales),
|
|
228
213
|
resolution
|
|
229
214
|
});
|
|
@@ -3,7 +3,6 @@ import { isPlainObject } from "../../core/immutable.js";
|
|
|
3
3
|
import { validateUserId } from "../../core/identifiers.js";
|
|
4
4
|
import { validateKeys } from "../../core/validation.js";
|
|
5
5
|
import { findTransformPolicy } from "../../grammar/transforms.js";
|
|
6
|
-
import { hasDataset } from "../../selectors/datasets.js";
|
|
7
6
|
|
|
8
7
|
const REPLAY_OPTIONS = Object.freeze(["id", "source", "transform"]);
|
|
9
8
|
|