ggaction 0.0.10 → 0.0.12
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 +97 -0
- package/README.md +5 -3
- 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/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 +18 -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 +41 -45
- package/src/actions/encodings/parallel.js +9 -10
- package/src/actions/encodings/pathOrder.js +6 -13
- package/src/actions/encodings/position/apply.js +30 -0
- package/src/actions/encodings/position/policies/arc.js +23 -2
- 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 +36 -7
- 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 +212 -30
- package/src/actions/errorBars/edit.js +91 -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 +38 -16
- package/src/actions/marks/remove.js +21 -0
- package/src/actions/marks/rule/actions.js +79 -54
- 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/textMetrics.js +74 -37
- package/src/core/validation.js +18 -0
- package/src/core/vocabulary.js +2 -2
- package/src/grammar/aggregate.js +48 -28
- package/src/grammar/arcs.js +170 -32
- 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/positionCompatibility.js +1 -1
- 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/rules.js +2 -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 +139 -60
- 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 +55 -30
- 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 +9 -2
- package/src/materialization/facetGuides/placement.js +49 -19
- package/src/materialization/facetGuides/preparation.js +35 -6
- package/src/materialization/facets.js +42 -28
- package/src/materialization/guides/resources.js +2 -1
- package/src/materialization/marks/capabilities.js +15 -4
- package/src/materialization/marks/index.js +29 -6
- package/src/materialization/rowEncoding.js +57 -1
- package/src/materialization/scales/policies/bar.js +61 -102
- package/src/materialization/scales/policies/binnedPosition.js +34 -0
- package/src/materialization/scales/policies/offset.js +54 -0
- package/src/materialization/scales/policies/series.js +13 -7
- package/src/materialization/scales/resolve.js +6 -9
- 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/index.d.ts +3 -0
- package/types/program.d.ts +523 -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
|
@@ -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
|
|
|
@@ -43,6 +94,16 @@ function capArgs(config, id, intervalField) {
|
|
|
43
94
|
coordinate: config.coordinate,
|
|
44
95
|
positionScale: config.positionScale,
|
|
45
96
|
intervalScale: config.intervalScale,
|
|
97
|
+
...(config.offset === undefined
|
|
98
|
+
? {}
|
|
99
|
+
: {
|
|
100
|
+
offsetChannel: config.offset.channel,
|
|
101
|
+
offsetField: config.offset.field,
|
|
102
|
+
offsetFieldType: config.offset.fieldType,
|
|
103
|
+
offsetScale: config.offset.scale,
|
|
104
|
+
offsetPaddingInner: config.offset.paddingInner,
|
|
105
|
+
offsetPaddingOuter: config.offset.paddingOuter
|
|
106
|
+
}),
|
|
46
107
|
capSize: config.capSize,
|
|
47
108
|
stroke: config.stroke,
|
|
48
109
|
strokeWidth: config.strokeWidth,
|
|
@@ -82,10 +143,7 @@ export const rematerializeErrorBar = action(
|
|
|
82
143
|
throw new Error(`Unknown error bar "${id}".`);
|
|
83
144
|
}
|
|
84
145
|
let next = rematerializeRuleAppearance(this, id, config);
|
|
85
|
-
const capDefinitions =
|
|
86
|
-
[config.lowerCapId, config.lowerField],
|
|
87
|
-
[config.upperCapId, config.upperField]
|
|
88
|
-
];
|
|
146
|
+
const capDefinitions = errorBarCaps(config);
|
|
89
147
|
if (!config.caps) {
|
|
90
148
|
for (const [capId] of capDefinitions) next = removeCap(next, capId);
|
|
91
149
|
return next;
|
|
@@ -118,22 +176,20 @@ export const editErrorBar = action(
|
|
|
118
176
|
}
|
|
119
177
|
const owner = resolveOwner(this, args.target);
|
|
120
178
|
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, {
|
|
179
|
+
const appearance = resolveErrorBarAppearance(
|
|
180
|
+
ownOptions(args, ERROR_BAR_APPEARANCE_OPTIONS), {
|
|
127
181
|
defaults: current,
|
|
128
182
|
operation: "editErrorBar"
|
|
129
|
-
|
|
183
|
+
}
|
|
184
|
+
);
|
|
130
185
|
const interval = Object.hasOwn(args, "statistics")
|
|
131
186
|
? planIntervalEdit(this, {
|
|
132
187
|
owner: owner.id,
|
|
133
188
|
data: current.data,
|
|
134
189
|
consumers: [
|
|
135
190
|
owner.id,
|
|
136
|
-
...
|
|
191
|
+
...errorBarCaps(current)
|
|
192
|
+
.map(([id]) => id)
|
|
137
193
|
.filter(id => findLayer(this, id) !== undefined)
|
|
138
194
|
],
|
|
139
195
|
statistics: args.statistics,
|
|
@@ -141,27 +197,14 @@ export const editErrorBar = action(
|
|
|
141
197
|
})
|
|
142
198
|
: { changed: false };
|
|
143
199
|
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
200
|
+
const next = applyIntervalRevision(this, interval);
|
|
201
|
+
return releaseIntervalRevision(next._withMarkConfig(owner.id, {
|
|
202
|
+
...next.markConfigs[owner.id],
|
|
203
|
+
errorBar: {
|
|
204
|
+
...current,
|
|
205
|
+
...appearance,
|
|
206
|
+
...(interval.changed ? { data: interval.revision.id } : {})
|
|
151
207
|
}
|
|
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);
|
|
208
|
+
}).rematerializeErrorBar({ id: owner.id }), interval);
|
|
166
209
|
}
|
|
167
210
|
);
|
|
@@ -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
|
|
|
@@ -65,6 +65,14 @@ function requireLegendOwner(program, owner) {
|
|
|
65
65
|
return { layer, config };
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
function editProperties(program, target, properties) {
|
|
69
|
+
let next = program;
|
|
70
|
+
for (const [property, value] of Object.entries(properties)) {
|
|
71
|
+
next = next.editGraphics({ target, property, value });
|
|
72
|
+
}
|
|
73
|
+
return next;
|
|
74
|
+
}
|
|
75
|
+
|
|
68
76
|
export const rematerializeGradientPlotLegend = action(
|
|
69
77
|
{
|
|
70
78
|
op: "rematerializeGradientPlotLegend",
|
|
@@ -82,35 +90,33 @@ export const rematerializeGradientPlotLegend = action(
|
|
|
82
90
|
const y = bounds.y + 50;
|
|
83
91
|
const width = 22;
|
|
84
92
|
const height = Math.min(170, Math.max(80, bounds.height - 110));
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
.editGraphics({ target: ids.title, property: "textAlign", value: "left" })
|
|
113
|
-
.editGraphics({ target: ids.title, property: "textBaseline", value: "middle" });
|
|
93
|
+
let next = editProperties(this, ids.strip, {
|
|
94
|
+
x, y, width, height,
|
|
95
|
+
fill: createDensityLegendPaint(config.gradient.opacity),
|
|
96
|
+
stroke: "#cbd5e1",
|
|
97
|
+
strokeWidth: 1
|
|
98
|
+
});
|
|
99
|
+
next = editProperties(next, ids.labels, {
|
|
100
|
+
x: x + width + 10,
|
|
101
|
+
y: [y + height, y],
|
|
102
|
+
text: ["Low", "High"],
|
|
103
|
+
fill: "#64748b",
|
|
104
|
+
fontSize: 11,
|
|
105
|
+
fontFamily: "sans-serif",
|
|
106
|
+
textAlign: "left",
|
|
107
|
+
textBaseline: "middle"
|
|
108
|
+
});
|
|
109
|
+
return editProperties(next, ids.title, {
|
|
110
|
+
x,
|
|
111
|
+
y: y - 18,
|
|
112
|
+
text: config.guides.legend.title,
|
|
113
|
+
fill: "#334155",
|
|
114
|
+
fontSize: 12,
|
|
115
|
+
fontFamily: "sans-serif",
|
|
116
|
+
fontWeight: 600,
|
|
117
|
+
textAlign: "left",
|
|
118
|
+
textBaseline: "middle"
|
|
119
|
+
});
|
|
114
120
|
}
|
|
115
121
|
);
|
|
116
122
|
|
|
@@ -130,24 +136,29 @@ export const createGradientPlotLegend = action(
|
|
|
130
136
|
}
|
|
131
137
|
const ids = legendIds(args.owner);
|
|
132
138
|
const densityScale = `${args.owner}Density`;
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
-
.
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
.createGraphics({
|
|
150
|
-
|
|
139
|
+
const title = args.title ?? "Relative density";
|
|
140
|
+
let next = this;
|
|
141
|
+
for (const [property, value] of [
|
|
142
|
+
[`scale[${densityScale}].type`, "sequential"],
|
|
143
|
+
[`scale[${densityScale}].domain`, this.markConfigs[args.owner].gradientPlot.intensityDomain],
|
|
144
|
+
[`scale[${densityScale}].range`, { palette: { name: "greys" } }],
|
|
145
|
+
["guide.legend.color.scale", densityScale],
|
|
146
|
+
["guide.legend.color.title", title]
|
|
147
|
+
]) {
|
|
148
|
+
next = next.editSemantic({ property, value });
|
|
149
|
+
}
|
|
150
|
+
for (const [id, type, length] of [
|
|
151
|
+
[ids.strip, "rect"],
|
|
152
|
+
[ids.labels, "text", 2],
|
|
153
|
+
[ids.title, "text"]
|
|
154
|
+
]) {
|
|
155
|
+
next = next.createGraphics({
|
|
156
|
+
id,
|
|
157
|
+
type,
|
|
158
|
+
...(length === undefined ? {} : { length }),
|
|
159
|
+
parent: "canvas"
|
|
160
|
+
});
|
|
161
|
+
}
|
|
151
162
|
next = next._withMarkConfig(args.owner, {
|
|
152
163
|
...next.markConfigs[args.owner],
|
|
153
164
|
gradientPlot: {
|
|
@@ -155,7 +166,7 @@ export const createGradientPlotLegend = action(
|
|
|
155
166
|
guides: {
|
|
156
167
|
...next.markConfigs[args.owner].gradientPlot.guides,
|
|
157
168
|
legend: {
|
|
158
|
-
title
|
|
169
|
+
title,
|
|
159
170
|
position: args.position ?? "right"
|
|
160
171
|
}
|
|
161
172
|
}
|