ggaction 0.0.17 → 0.0.18
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 +46 -0
- package/README.md +2 -2
- package/knowledge/action-card.schema.json +1 -1
- package/knowledge/action-cards.json +1 -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 +3 -3
- package/src/BasicChartProgram.js +1 -1
- package/src/accessibility.js +3 -2
- package/src/actions/boxPlots/create.js +14 -4
- package/src/actions/boxPlots/edit.js +39 -33
- package/src/actions/boxPlots/materialize.js +7 -3
- package/src/actions/boxPlots/options.js +37 -11
- package/src/actions/canvas/actions.js +70 -95
- package/src/actions/canvas/fitting.js +16 -1
- package/src/actions/canvas/index.js +3 -0
- package/src/actions/canvas/plotBorder.js +28 -0
- package/src/actions/charts/area.js +2 -2
- package/src/actions/charts/bar.js +34 -7
- package/src/actions/charts/ecdf.js +4 -18
- package/src/actions/charts/endpoints.js +4 -18
- package/src/actions/charts/heatmap.js +4 -4
- package/src/actions/charts/index.js +2 -0
- package/src/actions/charts/interval-regression.js +2 -2
- package/src/actions/charts/line.js +1 -1
- package/src/actions/charts/pie.js +3 -47
- package/src/actions/charts/radial.js +34 -12
- package/src/actions/charts/scatter.js +16 -2
- package/src/actions/charts/shared.js +2 -2
- package/src/actions/charts/text.js +35 -0
- package/src/actions/composition/actions.js +6 -2
- package/src/actions/data/box.js +2 -1
- package/src/actions/data/edit.js +1 -1
- package/src/actions/data/regression.js +2 -1
- package/src/actions/data/shared.js +3 -2
- package/src/actions/data/stack.js +3 -1
- package/src/actions/encodings/appearance.js +37 -29
- package/src/actions/encodings/barWidth.js +36 -11
- package/src/actions/encodings/channels.js +0 -1
- package/src/actions/encodings/color/continuous.js +1 -1
- package/src/actions/encodings/color/index.js +1 -1
- package/src/actions/encodings/color/policy.js +10 -3
- package/src/actions/encodings/density/resolve.js +1 -6
- package/src/actions/encodings/position/policies/arc.js +12 -10
- package/src/actions/encodings/position/policies/area.js +3 -3
- package/src/actions/encodings/position/policies/bar.js +15 -6
- package/src/actions/encodings/position/policies/line.js +17 -6
- package/src/actions/encodings/position/resolve.js +3 -2
- package/src/actions/encodings/ranged.js +17 -0
- package/src/actions/encodings/remove.js +3 -2
- package/src/actions/encodings/seriesLayout.js +1 -0
- package/src/actions/encodings/text.js +6 -3
- package/src/actions/errorBands/create.js +2 -2
- package/src/actions/errorBands/edit.js +2 -1
- package/src/actions/facets/actions.js +158 -240
- package/src/actions/gradientPlots/resolve.js +2 -17
- package/src/actions/guides/applicability.js +1 -1
- package/src/actions/guides/axes/axis.js +16 -4
- package/src/actions/guides/axes/edit.js +4 -4
- package/src/actions/guides/axes/labels.js +120 -46
- package/src/actions/guides/axes/lines.js +6 -5
- package/src/actions/guides/axes/parallel/lifecycle.js +1 -1
- package/src/actions/guides/axes/parallel/policy.js +8 -7
- package/src/actions/guides/axes/remove.js +3 -2
- package/src/actions/guides/axes/tickGroups.js +5 -2
- package/src/actions/guides/axes/ticks.js +6 -3
- package/src/actions/guides/axes/titles.js +71 -26
- package/src/actions/guides/basic.js +2 -0
- package/src/actions/guides/facadeLegend.js +10 -2
- package/src/actions/guides/grids/grid.js +7 -1
- package/src/actions/guides/grids/resolve.js +59 -12
- package/src/actions/guides/legends/blocks.js +3 -1
- package/src/actions/guides/legends/categorical/actions.js +70 -17
- package/src/actions/guides/legends/categorical/components.js +12 -8
- package/src/actions/guides/legends/categorical/index.js +6 -0
- package/src/actions/guides/legends/categorical/layout.js +37 -8
- package/src/actions/guides/legends/categorical/options.js +31 -20
- package/src/actions/guides/legends/categorical/resolve.js +2 -2
- package/src/actions/guides/legends/categorical/symbols.js +18 -12
- package/src/actions/guides/legends/categorical/validation.js +1 -6
- package/src/actions/guides/legends/continuous/colorSymbols.js +1 -0
- package/src/actions/guides/legends/continuous/common.js +1 -447
- package/src/actions/guides/legends/continuous/gradient.js +47 -34
- package/src/actions/guides/legends/continuous/interval.js +4 -4
- package/src/actions/guides/legends/continuous/opacity.js +19 -12
- package/src/actions/guides/legends/edit.js +1 -604
- package/src/actions/guides/legends/focused.js +2 -2
- package/src/actions/guides/legends/group.js +1 -0
- package/src/actions/guides/legends/lane.js +1 -516
- package/src/actions/guides/legends/remove.js +3 -0
- package/src/actions/guides/legends/sampling.js +1 -1
- package/src/actions/guides/legends/size.js +51 -43
- package/src/actions/guides/legends/strokeWidth.js +11 -29
- package/src/actions/guides/legends/target.js +3 -3
- package/src/actions/guides/legends/transition.js +21 -7
- package/src/actions/guides/polar/axes/labels.js +4 -2
- package/src/actions/guides/polar/axes/titles.js +7 -5
- package/src/actions/guides/polar/resolve.js +6 -14
- package/src/actions/index.js +16 -16
- package/src/actions/marks/arc/actions.js +3 -10
- package/src/actions/marks/area/actions.js +8 -5
- package/src/actions/marks/area/materialize.js +8 -9
- package/src/actions/marks/bar/create.js +10 -4
- package/src/actions/marks/bar/edit.js +8 -4
- package/src/actions/marks/bar/materialize.js +9 -6
- package/src/actions/marks/line/actions.js +11 -4
- package/src/actions/marks/line/materialize.js +9 -5
- package/src/actions/marks/point/edit.js +1 -1
- package/src/actions/marks/rect/actions.js +3 -3
- package/src/actions/marks/remove.js +2 -0
- package/src/actions/marks/rule/actions.js +7 -4
- package/src/actions/marks/text/actions.js +17 -5
- package/src/actions/marks/text/layout.js +3 -0
- package/src/actions/primitives/semanticValidation/layer.js +4 -0
- package/src/actions/regression/create.js +1 -1
- package/src/actions/regression/edit.js +3 -3
- package/src/actions/regression/resolve.js +2 -6
- package/src/actions/scales/channels.js +9 -4
- package/src/actions/scales/consumers/families.js +16 -5
- package/src/actions/scales/create.js +8 -3
- package/src/actions/scales/definitions.js +1 -425
- package/src/actions/scales/edit.js +2 -2
- package/src/actions/scales/editPolicy.js +9 -3
- package/src/actions/scales/materialize.js +1 -1
- package/src/actions/scales/preview.js +21 -1
- package/src/actions/scales/quantitativeColor.js +3 -3
- package/src/actions/selection/actions.js +1 -13
- package/src/actions/theme/reconcile.js +81 -27
- package/src/actions/theme/state.js +12 -0
- package/src/actions/titles/actions.js +5 -18
- package/src/actions/titles/resolve.js +41 -19
- package/src/actions/violinPlots/edit.js +3 -20
- package/src/core/compositionState.js +4 -1
- package/src/core/font.js +9 -2
- package/src/core/optionRequirements.js +1 -0
- package/src/core/resourceReferences.js +3 -0
- package/src/core/textMetricProfile.js +3 -2
- package/src/core/textMetrics.js +41 -4
- package/src/core/validation.js +26 -0
- package/src/core/vocabulary.js +4 -0
- package/src/grammar/aggregate.js +4 -18
- package/src/grammar/arcs.js +1 -386
- package/src/grammar/areaPaint.js +10 -0
- package/src/grammar/areaSeries.js +18 -10
- package/src/grammar/bars/geometry.js +30 -3
- package/src/grammar/bars/policy.js +12 -1
- package/src/grammar/bin.js +2 -12
- package/src/grammar/bin2d.js +1 -6
- package/src/grammar/boxPlot.js +56 -9
- package/src/grammar/categoryOrder.js +1 -6
- package/src/grammar/complete.js +15 -19
- package/src/grammar/computed.js +92 -25
- package/src/grammar/curveCommands.js +29 -16
- package/src/grammar/datasetSchema.js +3 -7
- package/src/grammar/displayLabels.js +28 -0
- package/src/grammar/ecdf.js +2 -9
- package/src/grammar/facets/dependencies.js +6 -2
- package/src/grammar/facets/guides.js +5 -0
- package/src/grammar/facets/index.js +2 -5
- package/src/grammar/fold.js +2 -12
- package/src/grammar/gradientProfile.js +1 -6
- package/src/grammar/impute.js +21 -48
- package/src/grammar/interval.js +2 -9
- package/src/grammar/legendGradient.js +18 -0
- package/src/grammar/legendGroups.js +1 -0
- package/src/grammar/legendTitleStyle.js +22 -0
- package/src/grammar/lineSeries.js +32 -17
- package/src/grammar/markLabels.js +5 -2
- package/src/grammar/normalize.js +4 -54
- package/src/grammar/numeric.js +1 -290
- package/src/grammar/parallelCoordinates.js +1 -6
- package/src/grammar/parseTime.js +41 -0
- package/src/grammar/pathCommands.js +11 -4
- package/src/grammar/polar.js +1 -184
- package/src/grammar/positionCompatibility.js +3 -3
- package/src/grammar/regression/derive.js +7 -1
- package/src/grammar/regression/models.js +83 -55
- package/src/grammar/regression/parameters.js +14 -3
- package/src/grammar/roundedRect.js +42 -54
- package/src/grammar/scales/appearance.js +1 -206
- package/src/grammar/scales/color.js +1 -382
- package/src/grammar/scales/colorConsumers.js +5 -2
- package/src/grammar/scales/definition.js +1 -197
- package/src/grammar/scales/discretized.js +1 -195
- package/src/grammar/scales/size.js +1 -456
- package/src/grammar/scales/temporal.js +2 -13
- package/src/grammar/scales/transformed.js +1 -383
- package/src/grammar/scales/types.js +1 -1
- package/src/grammar/scales/validation.js +1 -66
- package/src/grammar/schemas/concreteGraphic.js +11 -0
- package/src/grammar/schemas/graphic.js +3 -0
- package/src/grammar/schemas/semanticPath.js +2 -1
- package/src/grammar/stack.js +2 -10
- package/src/grammar/statisticalReference.js +1 -6
- package/src/grammar/summary.js +3 -26
- package/src/grammar/text.js +19 -0
- package/src/grammar/timeUnit.js +16 -7
- package/src/grammar/transformKeys.js +47 -0
- package/src/grammar/transforms.js +4 -1
- package/src/grammar/valueFormat.js +17 -5
- package/src/grammar/weightedStatistics.js +1 -6
- package/src/grammar/window.js +4 -58
- package/src/inspectionDescriptors.js +1 -1
- package/src/layout/canvas.js +1 -110
- package/src/layout/facets.js +1 -244
- package/src/layout/labels.js +1 -0
- package/src/layout/legend.js +13 -0
- package/src/layout/legendGroup.js +1 -0
- package/src/layout/legendItems.js +59 -15
- package/src/layout/legendLane.js +1 -441
- package/src/layout/legendText.js +1 -0
- package/src/layout/title.js +35 -13
- package/src/materialization/bars/aggregate.js +11 -5
- package/src/materialization/bars/histogram.js +4 -1
- package/src/materialization/bars/ranged.js +19 -6
- package/src/materialization/bars/resolve.js +22 -1
- package/src/materialization/coordinateBounds.js +8 -0
- package/src/materialization/dependencies.js +4 -44
- package/src/materialization/facetGuides/layout.js +1 -0
- package/src/materialization/facetGuides/placement.js +19 -8
- package/src/materialization/facetGuides/preparation.js +15 -7
- package/src/materialization/facetHeaders.js +1 -347
- package/src/materialization/facets.js +1 -185
- package/src/materialization/graphicHierarchy.js +1 -1
- package/src/materialization/guides/layout.js +1 -57
- package/src/materialization/guides/resources.js +1 -123
- package/src/materialization/layout.js +53 -0
- package/src/materialization/legendGroups.js +1 -0
- package/src/materialization/legendText.js +1 -0
- package/src/materialization/legends.js +1 -1
- package/src/materialization/marks/capabilities.js +5 -6
- package/src/materialization/rect.js +1 -1
- package/src/materialization/scaleGuideDependencies.js +1 -1
- package/src/materialization/scales/map.js +3 -0
- package/src/materialization/scales/policies/arc.js +8 -7
- package/src/materialization/scales/policies/bar.js +19 -7
- package/src/materialization/scales/policies/series.js +2 -1
- package/src/materialization/scales/resolve.js +5 -3
- package/src/materialization/selection/items/arc.js +1 -1
- package/src/materialization/selection/items/bar.js +7 -6
- package/src/materialization/selection/items/path.js +7 -6
- package/src/materialization/selection/items/rect.js +2 -2
- package/src/materialization/text.js +13 -4
- package/src/materialization/textLines.js +15 -0
- package/src/persistence/codec.js +37 -6
- package/src/persistence/validation.js +47 -0
- package/src/persistence.js +18 -5
- package/src/renderers/canvas/circle.js +6 -3
- package/src/renderers/canvas/fill.js +7 -1
- package/src/renderers/canvas/index.js +2 -1
- package/src/renderers/canvas/line.js +1 -0
- package/src/renderers/canvas/path.js +1 -1
- package/src/renderers/canvas/rect.js +11 -8
- package/src/renderers/canvas/stroke.js +2 -1
- package/src/renderers/canvas/text.js +5 -3
- package/src/renderers/svg.js +4 -1
- package/src/selectors/layers.js +19 -0
- package/src/theme/defaults.js +8 -2
- package/src/version.js +1 -1
- package/types/basic.d.ts +1 -0
- package/types/index.d.ts +7 -0
- package/types/program.d.ts +347 -85
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { validateFillPaint } from "../../../grammar/paint.js";
|
|
2
|
+
import { validateAreaPaintConfig } from "../../../grammar/areaPaint.js";
|
|
1
3
|
import { validateAreaMissing } from "../../../grammar/areaEndpoints.js";
|
|
2
4
|
import { action } from "../../../core/action.js";
|
|
3
5
|
import { validateUserId } from "../../../core/identifiers.js";
|
|
@@ -81,7 +83,7 @@ const createAreaMark = /* @__PURE__ */ action(
|
|
|
81
83
|
if (missing === "break" && ["density", "horizon"].some(type => findUpstreamTransform(this, findDataset(this, data), type))) {
|
|
82
84
|
throw new Error("Area missing:break does not reinterpret density or horizon data.");
|
|
83
85
|
}
|
|
84
|
-
const fill =
|
|
86
|
+
const fill = validateFillPaint(args.fill ?? DEFAULT_COLORS.mark, "Area fill");
|
|
85
87
|
const opacity = validateUnitInterval(args.opacity ?? 0.2, "Area opacity");
|
|
86
88
|
const curve = validateCurveInterpolation(args.curve ?? "linear");
|
|
87
89
|
const { stroke, strokeWidth } = validateAreaCreateOutline(args);
|
|
@@ -127,14 +129,14 @@ const configureAreaStrokeFromFill = /* @__PURE__ */ action(
|
|
|
127
129
|
}
|
|
128
130
|
const { stroke: _stroke, ...config } = this.markConfigs[id] ?? {};
|
|
129
131
|
void _stroke;
|
|
130
|
-
return this._withMarkConfig(id, {
|
|
132
|
+
return this._withMarkConfig(id, validateAreaPaintConfig({
|
|
131
133
|
...config,
|
|
132
134
|
strokeFromFill: true,
|
|
133
135
|
strokeWidth: validateNonNegativeFinite(
|
|
134
136
|
args.strokeWidth ?? 1,
|
|
135
137
|
"Area strokeWidth"
|
|
136
138
|
)
|
|
137
|
-
});
|
|
139
|
+
}));
|
|
138
140
|
}
|
|
139
141
|
);
|
|
140
142
|
|
|
@@ -199,7 +201,7 @@ const rematerializeAreaMark = /* @__PURE__ */ action(
|
|
|
199
201
|
if (args.scales !== false && strokeEncoding?.scale !== undefined) {
|
|
200
202
|
resolved = resolved.rematerializeScale({ id: strokeEncoding.scale });
|
|
201
203
|
}
|
|
202
|
-
const config = this.markConfigs[id];
|
|
204
|
+
const config = validateAreaPaintConfig(this.markConfigs[id]);
|
|
203
205
|
const { paths, fills, strokes } = resolveAreaMaterialization({
|
|
204
206
|
rows: dataset.values,
|
|
205
207
|
layer,
|
|
@@ -273,7 +275,7 @@ const editAreaMark = /* @__PURE__ */ action(
|
|
|
273
275
|
let config = { ...this.markConfigs[layer.id] };
|
|
274
276
|
Object.assign(config, strokeDetails);
|
|
275
277
|
if (Object.hasOwn(args, "fill")) {
|
|
276
|
-
config.fill =
|
|
278
|
+
config.fill = validateFillPaint(args.fill, "Area fill");
|
|
277
279
|
if (config.errorBand !== undefined) config.errorBand = { ...config.errorBand, fill: config.fill };
|
|
278
280
|
}
|
|
279
281
|
if (Object.hasOwn(args, "opacity")) {
|
|
@@ -314,6 +316,7 @@ const editAreaMark = /* @__PURE__ */ action(
|
|
|
314
316
|
);
|
|
315
317
|
}
|
|
316
318
|
|
|
319
|
+
validateAreaPaintConfig(config);
|
|
317
320
|
let next = this._withMarkConfig(layer.id, config);
|
|
318
321
|
if (Object.hasOwn(args, "missing")) {
|
|
319
322
|
const missing = validateAreaMissing(args.missing);
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
} from "../../../grammar/areaSeries.js";
|
|
7
7
|
import {
|
|
8
8
|
mapContinuousScaleValues,
|
|
9
|
+
mapOrdinalPositionValues,
|
|
9
10
|
mapOrdinalValues
|
|
10
11
|
} from "../../../grammar/scales/index.js";
|
|
11
12
|
import { mapScaleConsumerValues } from
|
|
@@ -37,9 +38,8 @@ function resolveAreaPaths({
|
|
|
37
38
|
}
|
|
38
39
|
return derived.series.map(series => {
|
|
39
40
|
if (densityTransform === undefined && derived.orientation === "horizontal") {
|
|
40
|
-
const y = mapContinuousScaleValues(
|
|
41
|
-
series.values.map(value => value.y),
|
|
42
|
-
yScale
|
|
41
|
+
const y = (["point", "band"].includes(yScale.type) ? mapOrdinalPositionValues : mapContinuousScaleValues)(
|
|
42
|
+
series.values.map(value => value.y), yScale
|
|
43
43
|
);
|
|
44
44
|
const lower = mapContinuousScaleValues(
|
|
45
45
|
series.values.map(value => value.x),
|
|
@@ -57,9 +57,8 @@ function resolveAreaPaths({
|
|
|
57
57
|
);
|
|
58
58
|
}
|
|
59
59
|
if (densityTransform !== undefined && derived.mode === "x-density") {
|
|
60
|
-
const y = mapContinuousScaleValues(
|
|
61
|
-
series.values.map(value => value.y),
|
|
62
|
-
yScale
|
|
60
|
+
const y = (["point", "band"].includes(yScale.type) ? mapOrdinalPositionValues : mapContinuousScaleValues)(
|
|
61
|
+
series.values.map(value => value.y), yScale
|
|
63
62
|
);
|
|
64
63
|
const upper = mapContinuousScaleValues(
|
|
65
64
|
series.values.map(value => value.x),
|
|
@@ -80,7 +79,7 @@ function resolveAreaPaths({
|
|
|
80
79
|
{ independentAxis: "y" }
|
|
81
80
|
);
|
|
82
81
|
}
|
|
83
|
-
const x = mapContinuousScaleValues(
|
|
82
|
+
const x = (["point", "band"].includes(xScale.type) ? mapOrdinalPositionValues : mapContinuousScaleValues)(
|
|
84
83
|
series.values.map(value => value.x),
|
|
85
84
|
xScale
|
|
86
85
|
);
|
|
@@ -134,8 +133,8 @@ export function resolveAreaMaterialization({
|
|
|
134
133
|
layer.encoding?.y?.stack === "center";
|
|
135
134
|
const rawDerived = densityTransform === undefined
|
|
136
135
|
? centered
|
|
137
|
-
? deriveCenteredAreaSeries(rows, layer)
|
|
138
|
-
: deriveAreaSeries(rows, layer)
|
|
136
|
+
? deriveCenteredAreaSeries(rows, layer, { xDomain: xScale.domain })
|
|
137
|
+
: deriveAreaSeries(rows, layer, { xDomain: xScale.domain, yDomain: yScale.domain })
|
|
139
138
|
: deriveDensityAreaSeries(rows, layer, densityTransform);
|
|
140
139
|
const colorEncoding = layer.encoding?.color;
|
|
141
140
|
const layout = layer.layout?.mode ?? colorEncoding?.layout ?? "overlay";
|
|
@@ -10,15 +10,15 @@ import {
|
|
|
10
10
|
} from "../shared.js";
|
|
11
11
|
import { resolveMarkGraphicPlacement } from
|
|
12
12
|
"../../../materialization/graphicHierarchy.js";
|
|
13
|
-
import { requestedRectStyleDetails } from
|
|
13
|
+
import { requestedRectStyleDetails, RECT_RADIUS_PROPERTIES } from
|
|
14
14
|
"../../../grammar/roundedRect.js";
|
|
15
15
|
import { STROKE_STYLE_PROPERTIES } from
|
|
16
16
|
"../../../grammar/strokeStyle.js";
|
|
17
17
|
import { validateItemMissing } from "../../../grammar/itemMissing.js";
|
|
18
18
|
|
|
19
19
|
const CREATE_OPTIONS = Object.freeze([
|
|
20
|
-
"id", "data", "missing", "fill", "opacity", "stroke", "strokeWidth",
|
|
21
|
-
|
|
20
|
+
"id", "data", "orientation", "missing", "fill", "opacity", "stroke", "strokeWidth",
|
|
21
|
+
...RECT_RADIUS_PROPERTIES, ...STROKE_STYLE_PROPERTIES
|
|
22
22
|
]);
|
|
23
23
|
|
|
24
24
|
export const createBarMark = /* @__PURE__ */ action(
|
|
@@ -29,6 +29,9 @@ export const createBarMark = /* @__PURE__ */ action(
|
|
|
29
29
|
function (args = {}) {
|
|
30
30
|
validateMarkOptions(args, CREATE_OPTIONS, "createBarMark");
|
|
31
31
|
requestedRectStyleDetails(args, "createBarMark");
|
|
32
|
+
if (args.orientation !== undefined && !["vertical", "horizontal"].includes(args.orientation)) {
|
|
33
|
+
throw new Error("Bar orientation must be vertical or horizontal.");
|
|
34
|
+
}
|
|
32
35
|
const id = resolveMarkId(this, args.id, {
|
|
33
36
|
defaultId: "bar",
|
|
34
37
|
label: "Bar mark id",
|
|
@@ -59,6 +62,9 @@ export const createBarMark = /* @__PURE__ */ action(
|
|
|
59
62
|
value: validateItemMissing(args.missing, "Bar missing")
|
|
60
63
|
});
|
|
61
64
|
}
|
|
65
|
+
if (args.orientation !== undefined) {
|
|
66
|
+
created = created.editSemantic({ property: `layer[${id}].mark.orientation`, value: args.orientation });
|
|
67
|
+
}
|
|
62
68
|
created = applyLayeredMarkInheritance(created, id, inherited);
|
|
63
69
|
created = created
|
|
64
70
|
.createGraphics({
|
|
@@ -70,7 +76,7 @@ export const createBarMark = /* @__PURE__ */ action(
|
|
|
70
76
|
created = materializeInheritedMark(created, id);
|
|
71
77
|
const appearance = Object.fromEntries(
|
|
72
78
|
[
|
|
73
|
-
"fill", "opacity", "stroke", "strokeWidth",
|
|
79
|
+
"fill", "opacity", "stroke", "strokeWidth", ...RECT_RADIUS_PROPERTIES,
|
|
74
80
|
...STROKE_STYLE_PROPERTIES
|
|
75
81
|
]
|
|
76
82
|
.filter(property => Object.hasOwn(args, property))
|
|
@@ -9,15 +9,16 @@ import { canMaterializeBar } from "../../../materialization/marks/index.js";
|
|
|
9
9
|
import { DEFAULT_BAR_STROKE_WIDTH } from "../../../materialization/bars/resolve.js";
|
|
10
10
|
import { resolveEligibleLayer } from "../../../selectors/layers.js";
|
|
11
11
|
import { validateMarkOptions } from "../shared.js";
|
|
12
|
-
import { requestedRectStyleDetails } from
|
|
12
|
+
import { requestedRectStyleDetails, RECT_RADIUS_PROPERTIES } from
|
|
13
13
|
"../../../grammar/roundedRect.js";
|
|
14
14
|
import { STROKE_STYLE_PROPERTIES } from
|
|
15
15
|
"../../../grammar/strokeStyle.js";
|
|
16
16
|
import { rematerializeExistingLegend } from "../../encodings/shared.js";
|
|
17
|
+
import { validateFillPaint } from "../../../grammar/paint.js";
|
|
17
18
|
import { validateItemMissing } from "../../../grammar/itemMissing.js";
|
|
18
19
|
|
|
19
20
|
const EDIT_OPTIONS = Object.freeze([
|
|
20
|
-
"target", "missing", "fill", "opacity", "stroke", "strokeWidth",
|
|
21
|
+
"target", "missing", "fill", "opacity", "stroke", "strokeWidth", ...RECT_RADIUS_PROPERTIES,
|
|
21
22
|
...STROKE_STYLE_PROPERTIES
|
|
22
23
|
]);
|
|
23
24
|
|
|
@@ -30,7 +31,7 @@ export const editBarMark = /* @__PURE__ */ action(
|
|
|
30
31
|
validateMarkOptions(args, EDIT_OPTIONS, "editBarMark");
|
|
31
32
|
const styleDetails = requestedRectStyleDetails(args, "editBarMark");
|
|
32
33
|
const changes = [
|
|
33
|
-
"missing", "fill", "opacity", "stroke", "strokeWidth",
|
|
34
|
+
"missing", "fill", "opacity", "stroke", "strokeWidth", ...RECT_RADIUS_PROPERTIES,
|
|
34
35
|
...STROKE_STYLE_PROPERTIES
|
|
35
36
|
];
|
|
36
37
|
if (!changes.some(key => Object.hasOwn(args, key))) {
|
|
@@ -56,6 +57,9 @@ export const editBarMark = /* @__PURE__ */ action(
|
|
|
56
57
|
"editBarMark stroke conflicts with a field encoding; use encodeStroke with value to replace it."
|
|
57
58
|
);
|
|
58
59
|
}
|
|
60
|
+
if (Object.hasOwn(args, "opacity") && layer.encoding?.opacity !== undefined) {
|
|
61
|
+
throw new Error("editBarMark opacity conflicts with a field encoding; use encodeOpacity with value to replace it.");
|
|
62
|
+
}
|
|
59
63
|
if (args.stroke === false && Object.hasOwn(args, "strokeWidth")) {
|
|
60
64
|
throw new Error(
|
|
61
65
|
"editBarMark cannot set strokeWidth while removing stroke."
|
|
@@ -72,7 +76,7 @@ export const editBarMark = /* @__PURE__ */ action(
|
|
|
72
76
|
const appearance = { ...config.barAppearance };
|
|
73
77
|
Object.assign(appearance, styleDetails);
|
|
74
78
|
if (Object.hasOwn(args, "fill")) {
|
|
75
|
-
appearance.fill =
|
|
79
|
+
appearance.fill = validateFillPaint(args.fill, "Bar fill");
|
|
76
80
|
}
|
|
77
81
|
if (Object.hasOwn(args, "opacity")) {
|
|
78
82
|
appearance.opacity = validateUnitInterval(args.opacity, "Bar opacity");
|
|
@@ -13,19 +13,17 @@ import {
|
|
|
13
13
|
import { rematerializeHighlightBaseline } from "../lifecycle.js";
|
|
14
14
|
import { offsetCategoryRectangles } from
|
|
15
15
|
"../../../materialization/categorySlotOffset.js";
|
|
16
|
-
import { materializeRectItem } from "../../../grammar/roundedRect.js";
|
|
17
|
-
import { STROKE_STYLE_PROPERTIES } from
|
|
18
|
-
"../../../grammar/strokeStyle.js";
|
|
16
|
+
import { materializeRectItem, RECT_STYLE_PROPERTIES } from "../../../grammar/roundedRect.js";
|
|
19
17
|
import { replaceGraphicItems } from "../../primitives/editGraphics.js";
|
|
20
18
|
|
|
21
19
|
const REMATERIALIZE_OPTIONS = Object.freeze(["id", "scales"]);
|
|
22
20
|
|
|
23
21
|
function editRectangles(program, id, rectangles) {
|
|
24
22
|
const appearance = program.markConfigs[id]?.barAppearance ?? {};
|
|
25
|
-
const hasShapeStyle =
|
|
23
|
+
const hasShapeStyle = RECT_STYLE_PROPERTIES
|
|
26
24
|
.some(property => Object.hasOwn(appearance, property));
|
|
27
25
|
if (hasShapeStyle) {
|
|
28
|
-
const radius = appearance
|
|
26
|
+
const radius = appearance;
|
|
29
27
|
return replaceGraphicItems(
|
|
30
28
|
program,
|
|
31
29
|
id,
|
|
@@ -92,6 +90,11 @@ export const rematerializeBarMark = /* @__PURE__ */ action(
|
|
|
92
90
|
resolved = resolved.rematerializeScale({ id: strokeScaleId });
|
|
93
91
|
}
|
|
94
92
|
|
|
93
|
+
const opacityScaleId = required.layer.encoding?.opacity?.scale;
|
|
94
|
+
if (opacityScaleId !== undefined && args.scales !== false) {
|
|
95
|
+
resolved = resolved.rematerializeScale({ id: opacityScaleId });
|
|
96
|
+
}
|
|
97
|
+
|
|
95
98
|
if (required.materialization === "aggregate") {
|
|
96
99
|
const offsetChannel = resolveBarOffsetChannel(required.layer);
|
|
97
100
|
const offsetScaleId = required.layer.encoding?.[offsetChannel]?.scale;
|
|
@@ -104,7 +107,7 @@ export const rematerializeBarMark = /* @__PURE__ */ action(
|
|
|
104
107
|
));
|
|
105
108
|
}
|
|
106
109
|
|
|
107
|
-
if (
|
|
110
|
+
if ([BAR_GRAINS.ranged, BAR_GRAINS.centered].includes(required.materialization)) {
|
|
108
111
|
const width = resolved.markConfigs[id]?.barWidth;
|
|
109
112
|
return editRectangles(resolved, id, offsetCategoryRectangles(
|
|
110
113
|
resolved, required.layer, deriveRangedRectangles(required, resolved, width)
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
import { DEFAULT_COLORS } from "../../../theme/defaults.js";
|
|
19
19
|
import { findDataset } from "../../../selectors/datasets.js";
|
|
20
20
|
import { findLayer, resolveEligibleLayer } from "../../../selectors/layers.js";
|
|
21
|
-
import { validateCurveInterpolation } from "../../../grammar/curveCommands.js";
|
|
21
|
+
import { validateCurveInterpolation, validateCardinalTension } from "../../../grammar/curveCommands.js";
|
|
22
22
|
import {
|
|
23
23
|
resolveCoordinateBounds,
|
|
24
24
|
resolveCoordinatePolarFrame
|
|
@@ -43,11 +43,11 @@ import {
|
|
|
43
43
|
const DEFAULT_LINE_STROKE = DEFAULT_COLORS.mark;
|
|
44
44
|
const DEFAULT_LINE_WIDTH = 2;
|
|
45
45
|
const CREATE_OPTIONS = Object.freeze([
|
|
46
|
-
"id", "data", "stroke", "strokeWidth", "opacity", "curve", "closed",
|
|
46
|
+
"id", "data", "stroke", "strokeWidth", "opacity", "curve", "tension", "closed",
|
|
47
47
|
...STROKE_STYLE_PROPERTIES
|
|
48
48
|
]);
|
|
49
49
|
const EDIT_OPTIONS = Object.freeze([
|
|
50
|
-
"target", "stroke", "strokeWidth", "opacity", "curve", "closed",
|
|
50
|
+
"target", "stroke", "strokeWidth", "opacity", "curve", "tension", "closed",
|
|
51
51
|
...STROKE_STYLE_PROPERTIES
|
|
52
52
|
]);
|
|
53
53
|
const REMATERIALIZE_OPTIONS = Object.freeze(["id", "scales"]);
|
|
@@ -118,6 +118,7 @@ const createLineMark = /* @__PURE__ */ action(
|
|
|
118
118
|
"Line strokeWidth"
|
|
119
119
|
);
|
|
120
120
|
const curve = validateCurveInterpolation(args.curve ?? "linear");
|
|
121
|
+
validateCardinalTension(args.tension, curve);
|
|
121
122
|
const closed = Object.hasOwn(args, "closed")
|
|
122
123
|
? validateClosed(args.closed)
|
|
123
124
|
: false;
|
|
@@ -151,6 +152,7 @@ const createLineMark = /* @__PURE__ */ action(
|
|
|
151
152
|
{
|
|
152
153
|
...(Object.hasOwn(args, "strokeWidth") ? { strokeWidth } : {}),
|
|
153
154
|
...(Object.hasOwn(args, "curve") ? { curve } : {}),
|
|
155
|
+
...(args.tension === undefined ? {} : { tension: args.tension }),
|
|
154
156
|
...(Object.hasOwn(args, "closed") ? { closed } : {}),
|
|
155
157
|
...strokeDetails
|
|
156
158
|
}
|
|
@@ -277,7 +279,7 @@ const editLineMark = /* @__PURE__ */ action(
|
|
|
277
279
|
validateMarkOptions(args, EDIT_OPTIONS, "editLineMark");
|
|
278
280
|
const strokeDetails = requestedStrokeDetails(args, "editLineMark");
|
|
279
281
|
if (!EDIT_OPTIONS.slice(1).some(key => Object.hasOwn(args, key))) {
|
|
280
|
-
throw new Error("editLineMark requires stroke, strokeWidth, opacity, curve, or closed.");
|
|
282
|
+
throw new Error("editLineMark requires stroke, strokeWidth, opacity, curve, tension, or closed.");
|
|
281
283
|
}
|
|
282
284
|
const target = Object.hasOwn(args, "target")
|
|
283
285
|
? validateUserId(args.target, "Line mark id")
|
|
@@ -315,6 +317,11 @@ const editLineMark = /* @__PURE__ */ action(
|
|
|
315
317
|
})) {
|
|
316
318
|
if (Object.hasOwn(args, key)) config[key] = validate(args[key], `Line ${key}`);
|
|
317
319
|
}
|
|
320
|
+
if (args.curve !== undefined && args.curve !== "cardinal" && args.tension === undefined) {
|
|
321
|
+
delete config.tension;
|
|
322
|
+
}
|
|
323
|
+
if (Object.hasOwn(args, "tension")) config.tension = args.tension;
|
|
324
|
+
validateCardinalTension(config.tension, config.curve ?? "linear");
|
|
318
325
|
validatePolarLineConfig(layer, config);
|
|
319
326
|
const next = this._withMarkConfig(layer.id, config);
|
|
320
327
|
const materialized = canMaterializeLine(next, layer)
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
} from "../../../grammar/lineSeries.js";
|
|
4
4
|
import {
|
|
5
5
|
mapContinuousScaleValues,
|
|
6
|
+
mapOrdinalPositionValues,
|
|
6
7
|
normalizeStrokeDashPattern
|
|
7
8
|
} from "../../../grammar/scales/index.js";
|
|
8
9
|
import { buildCurvePathCommands } from
|
|
@@ -97,9 +98,7 @@ export function resolvePositionedLineMaterialization({
|
|
|
97
98
|
const radiusScaleId = layer.encoding?.radius?.scale;
|
|
98
99
|
const lineOptions = polar
|
|
99
100
|
? { thetaDomain: resolvedScales[thetaScaleId].domain }
|
|
100
|
-
:
|
|
101
|
-
? undefined
|
|
102
|
-
: { xBinBoundaries };
|
|
101
|
+
: { xBinBoundaries, xDomain: resolvedScales[xScaleId].domain };
|
|
103
102
|
const derived = deriveLineSeries(rows, layer, lineOptions);
|
|
104
103
|
const commands = polar
|
|
105
104
|
? derived.series.map(series => buildPolarLinePathCommands({
|
|
@@ -111,7 +110,9 @@ export function resolvePositionedLineMaterialization({
|
|
|
111
110
|
closed: config.closed ?? false
|
|
112
111
|
}))
|
|
113
112
|
: derived.series.map(series => {
|
|
114
|
-
const
|
|
113
|
+
const mapX = ["nominal", "ordinal"].includes(layer.encoding.x.fieldType)
|
|
114
|
+
? mapOrdinalPositionValues : mapContinuousScaleValues;
|
|
115
|
+
const x = mapX(
|
|
115
116
|
series.values.map(value => value.x),
|
|
116
117
|
resolvedScales[xScaleId]
|
|
117
118
|
);
|
|
@@ -119,9 +120,12 @@ export function resolvePositionedLineMaterialization({
|
|
|
119
120
|
series.values.map(value => value.y),
|
|
120
121
|
resolvedScales[yScaleId]
|
|
121
122
|
);
|
|
123
|
+
// A raw singleton retains its data position and series identity, but
|
|
124
|
+
// contributes no segment. Do not fabricate another observation.
|
|
125
|
+
if (series.values.length === 1) return [{ op: "M", x: x[0], y: y[0] }];
|
|
122
126
|
return buildCurvePathCommands(
|
|
123
127
|
series.values.map((_, index) => ({ x: x[index], y: y[index] })),
|
|
124
|
-
config.curve ?? "linear"
|
|
128
|
+
config.curve ?? "linear", config.tension
|
|
125
129
|
);
|
|
126
130
|
});
|
|
127
131
|
const appearance = appearanceMapper(layer, resolvedScales,
|
|
@@ -96,7 +96,7 @@ export const editPointMark = /* @__PURE__ */ action(
|
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
if (Object.hasOwn(args, "strokeWidth")) {
|
|
99
|
-
if (typeof config.stroke !== "string") {
|
|
99
|
+
if (typeof config.stroke !== "string" && layer.encoding?.stroke?.field === undefined) {
|
|
100
100
|
throw new Error("Point strokeWidth requires an active stroke.");
|
|
101
101
|
}
|
|
102
102
|
config.strokeWidth = validateNonNegativeFinite(
|
|
@@ -25,7 +25,7 @@ import { resolveMarkGraphicPlacement } from
|
|
|
25
25
|
import { rematerializeExistingLegend } from "../../encodings/shared.js";
|
|
26
26
|
import { rematerializeHighlightBaseline } from "../lifecycle.js";
|
|
27
27
|
import {
|
|
28
|
-
requestedRectStyleDetails
|
|
28
|
+
requestedRectStyleDetails, hasRectRadius, RECT_RADIUS_PROPERTIES
|
|
29
29
|
} from "../../../grammar/roundedRect.js";
|
|
30
30
|
import { STROKE_STYLE_PROPERTIES } from
|
|
31
31
|
"../../../grammar/strokeStyle.js";
|
|
@@ -33,7 +33,7 @@ import { applyItemMissingPolicy, validateItemMissing } from
|
|
|
33
33
|
"../../../grammar/itemMissing.js";
|
|
34
34
|
|
|
35
35
|
const STYLE_OPTIONS = Object.freeze([
|
|
36
|
-
"fill", "opacity", "stroke", "strokeWidth",
|
|
36
|
+
"fill", "opacity", "stroke", "strokeWidth", ...RECT_RADIUS_PROPERTIES,
|
|
37
37
|
...STROKE_STYLE_PROPERTIES
|
|
38
38
|
]);
|
|
39
39
|
const CREATE_OPTIONS = Object.freeze(["id", "data", "missing", ...STYLE_OPTIONS]);
|
|
@@ -117,7 +117,7 @@ const rematerializeRectMark = /* @__PURE__ */ action(
|
|
|
117
117
|
const graphic = this.graphicSpec.objects[id];
|
|
118
118
|
if (layer?.mark?.type !== "rect") throw new Error(`Unknown rect mark "${id}".`);
|
|
119
119
|
const roundedCollection = graphic?.type === "collection" &&
|
|
120
|
-
|
|
120
|
+
hasRectRadius(this.markConfigs[id]) &&
|
|
121
121
|
graphic.items?.every(item => ["rect", "path"].includes(item.type));
|
|
122
122
|
if (
|
|
123
123
|
(graphic?.type !== "rect" && !roundedCollection) ||
|
|
@@ -163,6 +163,7 @@ export function removeOwnedMark(program, id, partial = false) {
|
|
|
163
163
|
next = next.editGraphics({ target: id, remove: true });
|
|
164
164
|
}
|
|
165
165
|
return next
|
|
166
|
+
._withoutMaterializationConfig(["guides", "legendGroups", id])
|
|
166
167
|
._withoutMaterializationConfig(["marks", id])
|
|
167
168
|
._withContext({
|
|
168
169
|
...(program.context.currentMark === id ? { currentMark: undefined } : {}),
|
|
@@ -262,6 +263,7 @@ export const removeMark = /* @__PURE__ */ closedAction(
|
|
|
262
263
|
if (next.graphicSpec.objects[id] !== undefined) {
|
|
263
264
|
next = next.editGraphics({ target: id, remove: true });
|
|
264
265
|
}
|
|
266
|
+
next = next._withoutMaterializationConfig(["guides", "legendGroups", id]);
|
|
265
267
|
next = next._withoutMaterializationConfig(["marks", id]);
|
|
266
268
|
next = next._withoutMaterializationConfig(["jitters", id]);
|
|
267
269
|
next = next._withoutMaterializationConfig(["pointPacking", id]);
|
|
@@ -298,13 +298,16 @@ const rematerializeRuleMark = /* @__PURE__ */ action(
|
|
|
298
298
|
properties: item?.properties
|
|
299
299
|
});
|
|
300
300
|
if (box === undefined) throw new Error(`Rule mark "${id}" requires box span owner "${boxSpan}".`);
|
|
301
|
+
const pixels = resolved.markConfigs[boxSpan]?.boxPlot?.median?.width?.pixels;
|
|
301
302
|
if (layer.encoding?.x?.fieldType === "quantitative") {
|
|
302
303
|
startX = endX = mapped.x[index];
|
|
303
|
-
|
|
304
|
-
|
|
304
|
+
const height = pixels ?? box.height;
|
|
305
|
+
startY = box.y + (box.height - height) / 2;
|
|
306
|
+
endY = startY + height;
|
|
305
307
|
} else {
|
|
306
|
-
|
|
307
|
-
|
|
308
|
+
const width = pixels ?? box.width;
|
|
309
|
+
startX = box.x + (box.width - width) / 2;
|
|
310
|
+
endX = startX + width;
|
|
308
311
|
startY = endY = mapped.y[index];
|
|
309
312
|
}
|
|
310
313
|
} else if (mode === "fixed-span") {
|
|
@@ -3,6 +3,7 @@ import { action } from "../../../core/action.js";
|
|
|
3
3
|
import { validateUserId } from "../../../core/identifiers.js";
|
|
4
4
|
import {
|
|
5
5
|
DEFAULT_TEXT_MARK,
|
|
6
|
+
TEXT_MARK_STYLE_OPTIONS as STYLE_OPTIONS,
|
|
6
7
|
isSourceOwnedText,
|
|
7
8
|
normalizeTextMarkConfig
|
|
8
9
|
} from "../../../grammar/text.js";
|
|
@@ -41,10 +42,6 @@ import { resolveMarkGraphicPlacement } from
|
|
|
41
42
|
"../../../materialization/graphicHierarchy.js";
|
|
42
43
|
import { validateItemMissing } from "../../../grammar/itemMissing.js";
|
|
43
44
|
|
|
44
|
-
const STYLE_OPTIONS = Object.freeze([
|
|
45
|
-
"fill", "opacity", "fontSize", "fontFamily", "fontWeight",
|
|
46
|
-
"align", "baseline", "rotation", "dx", "dy"
|
|
47
|
-
]);
|
|
48
45
|
const CREATE_OPTIONS = Object.freeze(["id", "data", "source", "text", "missing", ...STYLE_OPTIONS]);
|
|
49
46
|
const EDIT_OPTIONS = Object.freeze(["target", "missing", ...STYLE_OPTIONS]);
|
|
50
47
|
const REMATERIALIZE_OPTIONS = Object.freeze(["id", "replayLayout"]);
|
|
@@ -195,7 +192,7 @@ const LABEL_OPTIONS = Object.freeze([
|
|
|
195
192
|
]);
|
|
196
193
|
|
|
197
194
|
const ANNOTATION_OPTIONS = Object.freeze([
|
|
198
|
-
"id", "text", "format", "x", "y", "space", "source", "data", "coordinate", "layout",
|
|
195
|
+
"id", "text", "format", "anchor", "x", "y", "space", "source", "data", "coordinate", "layout",
|
|
199
196
|
...STYLE_OPTIONS
|
|
200
197
|
]);
|
|
201
198
|
|
|
@@ -271,6 +268,15 @@ const createAnnotation = /* @__PURE__ */ action(
|
|
|
271
268
|
},
|
|
272
269
|
function (args = {}) {
|
|
273
270
|
validateMarkOptions(args, ANNOTATION_OPTIONS, "createAnnotation");
|
|
271
|
+
if (Object.hasOwn(args, "anchor")) {
|
|
272
|
+
if (args.anchor !== "plot-center") {
|
|
273
|
+
throw new Error('createAnnotation anchor must be "plot-center".');
|
|
274
|
+
}
|
|
275
|
+
if (["x", "y", "space", "source", "align", "baseline"].some(key => Object.hasOwn(args, key))) {
|
|
276
|
+
throw new Error("createAnnotation plot-center owns x, y, space, align, and baseline and does not accept source.");
|
|
277
|
+
}
|
|
278
|
+
args = { ...args, space: "plot", x: 0.5, y: 0.5, align: "center", baseline: "middle" };
|
|
279
|
+
}
|
|
274
280
|
if (!Object.hasOwn(args, "text")) {
|
|
275
281
|
throw new Error("createAnnotation requires text.");
|
|
276
282
|
}
|
|
@@ -482,6 +488,12 @@ const editTextMark = /* @__PURE__ */ action(
|
|
|
482
488
|
if (Object.hasOwn(args, "missing") && layer.source !== undefined) {
|
|
483
489
|
throw new Error("Text missing policy requires a row-backed text mark, not source-owned labels.");
|
|
484
490
|
}
|
|
491
|
+
if (args.inheritColor && layer.source === undefined) {
|
|
492
|
+
throw new Error("Text inheritColor requires a source-owned label.");
|
|
493
|
+
}
|
|
494
|
+
if (Object.hasOwn(args, "fill") && layer.encoding?.color !== undefined) {
|
|
495
|
+
throw new Error("Text fill conflicts with its color encoding.");
|
|
496
|
+
}
|
|
485
497
|
const semantic = Object.hasOwn(args, "missing")
|
|
486
498
|
? this.editSemantic({
|
|
487
499
|
property: `layer[${layer.id}].mark.missing`,
|
|
@@ -144,6 +144,9 @@ function layoutInputs(program, layer) {
|
|
|
144
144
|
sourceX: entry.anchor.x,
|
|
145
145
|
sourceY: entry.anchor.y,
|
|
146
146
|
text: entry.graphic.properties.text,
|
|
147
|
+
lines: entry.graphic.properties.lines,
|
|
148
|
+
fontFamily: entry.graphic.properties.fontFamily,
|
|
149
|
+
fontWeight: entry.graphic.properties.fontWeight,
|
|
147
150
|
fontSize: entry.graphic.properties.fontSize,
|
|
148
151
|
textAlign: entry.graphic.properties.textAlign,
|
|
149
152
|
textBaseline: entry.graphic.properties.textBaseline,
|
|
@@ -66,6 +66,10 @@ export function validateLayerSemanticValue(
|
|
|
66
66
|
throw new Error(`Mark type "${markType}" does not support a missing policy.`);
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
|
+
if (property === "mark.orientation" && (findLayer(program, parsed.id)?.mark?.type !== "bar" ||
|
|
70
|
+
!["vertical", "horizontal"].includes(value))) {
|
|
71
|
+
throw new Error("Bar orientation must be vertical or horizontal.");
|
|
72
|
+
}
|
|
69
73
|
if (property === "derivedBindings.regression") {
|
|
70
74
|
if (!value || typeof value !== "object" || Array.isArray(value) ||
|
|
71
75
|
Object.keys(value).length !== 2 || value.mode !== "follow" ||
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
} from "./resolve.js";
|
|
11
11
|
|
|
12
12
|
const REGRESSION_OPTIONS = Object.freeze([
|
|
13
|
-
"target", "x", "y", "groupBy", "method", "degree", "span",
|
|
13
|
+
"target", "x", "y", "groupBy", "method", "degree", "span", "robustIterations",
|
|
14
14
|
"confidenceMethod", "level", "confidence", "interval", "band", "line"
|
|
15
15
|
, "predict", "sourceBinding", "missing"
|
|
16
16
|
]);
|
|
@@ -21,7 +21,7 @@ import { removeOwnedColorLegends } from "../guides/legends/remove.js";
|
|
|
21
21
|
import { requireRegressionField } from "./resolve.js";
|
|
22
22
|
|
|
23
23
|
const OPTIONS = Object.freeze([
|
|
24
|
-
"target", "data", "x", "y", "groupBy", "method", "degree", "span",
|
|
24
|
+
"target", "data", "x", "y", "groupBy", "method", "degree", "span", "robustIterations",
|
|
25
25
|
"confidenceMethod", "level", "confidence", "interval", "band", "line"
|
|
26
26
|
, "predict", "sourceBinding", "missing"
|
|
27
27
|
]);
|
|
@@ -111,14 +111,14 @@ function resolveParameters(previous, args) {
|
|
|
111
111
|
const method = args.method ?? previous.method;
|
|
112
112
|
const raw = { method };
|
|
113
113
|
for (const key of [
|
|
114
|
-
"degree", "span", "confidenceMethod", "level", "confidence", "interval"
|
|
114
|
+
"degree", "span", "robustIterations", "confidenceMethod", "level", "confidence", "interval"
|
|
115
115
|
]) {
|
|
116
116
|
if (Object.hasOwn(args, key)) raw[key] = args[key];
|
|
117
117
|
}
|
|
118
118
|
const inheritsLevel = !Object.hasOwn(raw, "confidence");
|
|
119
119
|
if (method === previous.method && args.interval !== false) {
|
|
120
120
|
for (const key of [
|
|
121
|
-
"degree", "span", "confidenceMethod", "level", "interval"
|
|
121
|
+
"degree", "span", "robustIterations", "confidenceMethod", "level", "interval"
|
|
122
122
|
]) {
|
|
123
123
|
if (
|
|
124
124
|
!Object.hasOwn(raw, key) &&
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { requireStringValue as requireRegressionField } from "../../core/validation.js";
|
|
2
|
+
export { requireStringValue as requireRegressionField } from "../../core/validation.js";
|
|
1
3
|
import { isPlainObject } from "../../core/immutable.js";
|
|
2
4
|
import { findLayer } from "../../selectors/layers.js";
|
|
3
5
|
|
|
@@ -8,12 +10,6 @@ export function requireRegressionObject(value, label) {
|
|
|
8
10
|
return value;
|
|
9
11
|
}
|
|
10
12
|
|
|
11
|
-
export function requireRegressionField(value, label) {
|
|
12
|
-
if (typeof value !== "string" || value.length === 0) {
|
|
13
|
-
throw new TypeError(`${label} must be a non-empty string.`);
|
|
14
|
-
}
|
|
15
|
-
return value;
|
|
16
|
-
}
|
|
17
13
|
|
|
18
14
|
export function findRegressionPoint(program, requested) {
|
|
19
15
|
const eligible = program.semanticSpec.layers.filter(layer =>
|
|
@@ -5,6 +5,7 @@ import { normalizePositionScaleChannel } from "../../core/vocabulary.js";
|
|
|
5
5
|
import { findLayer } from "../../selectors/layers.js";
|
|
6
6
|
import { requireSemanticScale } from "../../selectors/scales.js";
|
|
7
7
|
import { findScaleConsumers } from "./consumers/index.js";
|
|
8
|
+
import { axisScaleBindings } from "../../materialization/coordinateBounds.js";
|
|
8
9
|
|
|
9
10
|
const DEFINITIONS = Object.freeze({
|
|
10
11
|
editXScale: Object.freeze({
|
|
@@ -41,7 +42,7 @@ const DEFINITIONS = Object.freeze({
|
|
|
41
42
|
channel: "color",
|
|
42
43
|
options: Object.freeze([
|
|
43
44
|
"type", "domain", "range", "emptyDomain", "clamp", "reverse", "palette",
|
|
44
|
-
"interpolate", "midpoint", "unknown"
|
|
45
|
+
"interpolate", "midpoint", "base", "constant", "unknown"
|
|
45
46
|
])
|
|
46
47
|
}),
|
|
47
48
|
editStrokeScale: Object.freeze({
|
|
@@ -49,7 +50,7 @@ const DEFINITIONS = Object.freeze({
|
|
|
49
50
|
targetOnly: true,
|
|
50
51
|
options: Object.freeze([
|
|
51
52
|
"type", "domain", "range", "emptyDomain", "clamp", "reverse", "palette",
|
|
52
|
-
"interpolate", "midpoint", "unknown"
|
|
53
|
+
"interpolate", "midpoint", "base", "constant", "unknown"
|
|
53
54
|
])
|
|
54
55
|
}),
|
|
55
56
|
editSizeScale: Object.freeze({
|
|
@@ -99,9 +100,11 @@ function allConsumers(program) {
|
|
|
99
100
|
}
|
|
100
101
|
|
|
101
102
|
function channelScaleIds(program, channel) {
|
|
102
|
-
return [...new Set(allConsumers(program)
|
|
103
|
+
return [...new Set([...allConsumers(program)
|
|
103
104
|
.filter(consumer => normalizePositionScaleChannel(consumer.channel) === channel)
|
|
104
|
-
.map(consumer => consumer.encoding.scale)
|
|
105
|
+
.map(consumer => consumer.encoding.scale),
|
|
106
|
+
...program.semanticSpec.scales.filter(scale => axisScaleBindings(program, scale.id)
|
|
107
|
+
.some(binding => binding.channel === channel)).map(scale => scale.id)])];
|
|
105
108
|
}
|
|
106
109
|
|
|
107
110
|
function requireSingleScale(ids, operation, scope) {
|
|
@@ -115,6 +118,8 @@ function requireSingleScale(ids, operation, scope) {
|
|
|
115
118
|
function validateScaleChannel(program, id, channel, operation) {
|
|
116
119
|
requireSemanticScale(program, id);
|
|
117
120
|
const consumers = findScaleConsumers(program, id);
|
|
121
|
+
const axes = axisScaleBindings(program, id);
|
|
122
|
+
if (consumers.length === 0 && axes.length === 1 && axes[0].channel === channel) return id;
|
|
118
123
|
if (consumers.length === 0) {
|
|
119
124
|
throw new Error(`${operation} requires a scale bound to the ${channel} channel.`);
|
|
120
125
|
}
|