ggaction 0.0.17 → 0.0.19
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 +96 -0
- package/README.md +6 -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 +20 -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 +8 -7
- 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/derived.js +11 -1
- package/src/actions/data/edit.js +3 -1
- package/src/actions/data/index.js +4 -0
- package/src/actions/data/regression.js +25 -16
- package/src/actions/data/regressionStatistics.js +73 -0
- package/src/actions/data/shared.js +8 -4
- package/src/actions/data/stack.js +3 -1
- package/src/actions/distributions/revision.js +24 -18
- 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 +14 -8
- package/src/actions/encodings/seriesLayout.js +1 -0
- package/src/actions/encodings/shared.js +26 -24
- 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 +176 -247
- package/src/actions/facets/guides.js +2 -1
- package/src/actions/gradientPlots/resolve.js +2 -17
- package/src/actions/guides/applicability.js +1 -1
- package/src/actions/guides/axes/axes.js +30 -11
- package/src/actions/guides/axes/axis.js +72 -10
- package/src/actions/guides/axes/components.js +13 -13
- package/src/actions/guides/axes/edit.js +36 -24
- package/src/actions/guides/axes/labels.js +155 -71
- package/src/actions/guides/axes/lines.js +27 -24
- 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 +13 -8
- package/src/actions/guides/axes/tickGroups.js +11 -7
- package/src/actions/guides/axes/ticks.js +36 -24
- package/src/actions/guides/axes/titles.js +107 -69
- package/src/actions/guides/basic.js +2 -0
- package/src/actions/guides/facadeAxes.js +33 -24
- package/src/actions/guides/facadeLegend.js +12 -4
- package/src/actions/guides/grids/grid.js +8 -2
- package/src/actions/guides/grids/resolve.js +102 -58
- package/src/actions/guides/legends/blocks.js +46 -20
- package/src/actions/guides/legends/categorical/actions.js +95 -19
- package/src/actions/guides/legends/categorical/components.js +15 -18
- package/src/actions/guides/legends/categorical/index.js +8 -0
- package/src/actions/guides/legends/categorical/layout.js +45 -20
- package/src/actions/guides/legends/categorical/options.js +39 -22
- package/src/actions/guides/legends/categorical/recipes.js +7 -3
- package/src/actions/guides/legends/categorical/resolve.js +11 -4
- package/src/actions/guides/legends/categorical/symbols.js +38 -18
- package/src/actions/guides/legends/categorical/validation.js +1 -6
- package/src/actions/guides/legends/colorSize.js +110 -0
- package/src/actions/guides/legends/combine.js +37 -0
- 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 +73 -46
- package/src/actions/guides/legends/continuous/interval.js +4 -4
- package/src/actions/guides/legends/continuous/opacity.js +33 -21
- package/src/actions/guides/legends/creation.js +2 -0
- package/src/actions/guides/legends/edit.js +1 -604
- package/src/actions/guides/legends/focused.js +8 -7
- package/src/actions/guides/legends/group.js +1 -0
- package/src/actions/guides/legends/lane.js +1 -516
- package/src/actions/guides/legends/lifecycle.js +2 -1
- package/src/actions/guides/legends/remove.js +9 -3
- package/src/actions/guides/legends/sampling.js +1 -1
- package/src/actions/guides/legends/size.js +65 -47
- package/src/actions/guides/legends/strokeWidth.js +13 -29
- package/src/actions/guides/legends/target.js +2 -79
- package/src/actions/guides/legends/transition.js +44 -11
- 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/guides/tickValues.js +8 -5
- 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 +29 -14
- package/src/actions/marks/bar/materialize.js +15 -7
- 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/point/materialize.js +5 -1
- package/src/actions/marks/rect/actions.js +3 -3
- package/src/actions/marks/remove.js +12 -10
- 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/createGraphics.js +3 -2
- package/src/actions/primitives/editGraphics.js +15 -11
- package/src/actions/primitives/graphicProperties.js +1 -6
- package/src/actions/primitives/semanticAction.js +27 -1
- package/src/actions/primitives/semanticValidation/guide.js +11 -1
- package/src/actions/primitives/semanticValidation/layer.js +8 -0
- package/src/actions/regression/create.js +1 -1
- package/src/actions/regression/edit.js +18 -6
- 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 +10 -5
- package/src/actions/scales/definitions.js +1 -425
- package/src/actions/scales/edit.js +3 -3
- package/src/actions/scales/editPolicy.js +12 -3
- package/src/actions/scales/materialize.js +1 -1
- package/src/actions/scales/patch.js +1 -1
- package/src/actions/scales/preview.js +21 -1
- package/src/actions/scales/quantitativeColor.js +8 -3
- package/src/actions/selection/actions.js +1 -13
- package/src/actions/theme/reconcile.js +126 -45
- 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 +6 -3
- package/src/core/font.js +9 -2
- package/src/core/graphicProperties.js +1 -0
- package/src/core/optionRequirements.js +1 -0
- package/src/core/programState.js +4 -0
- package/src/core/resourceReferences.js +5 -2
- package/src/core/textMetricProfile.js +3 -2
- package/src/core/textMetrics.js +41 -4
- package/src/core/validation.js +35 -0
- package/src/core/vocabulary.js +7 -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/axisTextPlacement.js +1 -0
- 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/colorSizeLegend.js +39 -0
- package/src/grammar/complete.js +15 -19
- package/src/grammar/computed.js +115 -26
- package/src/grammar/curveCommands.js +29 -16
- package/src/grammar/datasetSchema.js +13 -9
- package/src/grammar/displayLabels.js +36 -0
- package/src/grammar/ecdf.js +2 -9
- package/src/grammar/facets/dependencies.js +6 -2
- package/src/grammar/facets/guides.js +58 -30
- package/src/grammar/facets/index.js +2 -5
- package/src/grammar/facets/legendIntent.js +65 -0
- package/src/grammar/fold.js +2 -12
- package/src/grammar/gradientProfile.js +1 -6
- package/src/grammar/guideDash.js +1 -0
- package/src/grammar/impute.js +21 -48
- package/src/grammar/interval.js +2 -9
- package/src/grammar/legendBlocks.js +1 -0
- package/src/grammar/legendBorder.js +9 -0
- package/src/grammar/legendClip.js +19 -0
- package/src/grammar/legendCombinations.js +45 -0
- package/src/grammar/legendGradient.js +27 -0
- package/src/grammar/legendGroups.js +1 -0
- package/src/grammar/legendOpacity.js +6 -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/pointShapes.js +16 -2
- package/src/grammar/polar.js +1 -184
- package/src/grammar/positionCompatibility.js +3 -3
- package/src/grammar/regression/derive.js +16 -2
- package/src/grammar/regression/models.js +142 -54
- package/src/grammar/regression/parameters.js +23 -4
- package/src/grammar/regression/statistics.js +64 -0
- 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 +14 -0
- package/src/grammar/schemas/graphic.js +6 -0
- package/src/grammar/schemas/graphicBounds.js +31 -7
- package/src/grammar/schemas/semanticPath.js +19 -3
- package/src/grammar/stack.js +2 -10
- package/src/grammar/statisticalReference.js +1 -6
- package/src/grammar/summary.js +4 -27
- package/src/grammar/text.js +19 -0
- package/src/grammar/timeUnit.js +22 -11
- package/src/grammar/transformKeys.js +47 -0
- package/src/grammar/transformTopology.js +1 -0
- package/src/grammar/transforms.js +15 -2
- 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/guideCollisions.js +9 -1
- 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/compositionSnapshot.js +2 -7
- package/src/materialization/coordinateBounds.js +21 -0
- package/src/materialization/dependencies.js +4 -44
- package/src/materialization/facetGuides/edit.js +30 -0
- package/src/materialization/facetGuides/layout.js +1 -0
- package/src/materialization/facetGuides/owner.js +125 -0
- package/src/materialization/facetGuides/ownerLayout.js +71 -0
- package/src/materialization/facetGuides/placement.js +71 -14
- package/src/materialization/facetGuides/preparation.js +22 -7
- package/src/materialization/facetHeaders.js +1 -347
- package/src/materialization/facets.js +1 -185
- package/src/materialization/graphicHierarchy.js +5 -1
- package/src/materialization/graphicTranslation.js +73 -0
- 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/legendBorder.js +28 -0
- package/src/materialization/legendClip.js +76 -0
- package/src/materialization/legendGroups.js +1 -0
- package/src/materialization/legendText.js +1 -0
- package/src/materialization/legends.js +2 -2
- package/src/materialization/marks/capabilities.js +5 -6
- package/src/materialization/rect.js +1 -1
- package/src/materialization/scaleGuideDependencies.js +14 -15
- package/src/materialization/scales/map.js +6 -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 +7 -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/materialization/typography.js +10 -2
- package/src/persistence/codec.js +37 -6
- package/src/persistence/validation.js +220 -0
- package/src/persistence.js +19 -6
- package/src/renderers/canvas/circle.js +6 -3
- package/src/renderers/canvas/fill.js +7 -1
- package/src/renderers/canvas/index.js +22 -11
- package/src/renderers/canvas/isolated.js +166 -0
- package/src/renderers/canvas/limits.js +3 -0
- 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/pdf.js +2 -2
- package/src/renderers/png.js +2 -2
- package/src/renderers/svg.js +14 -4
- package/src/selectors/axes.js +1 -0
- package/src/selectors/graphicCapacity.js +38 -0
- 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 +3 -0
- package/types/index.d.ts +12 -0
- package/types/program.d.ts +536 -129
|
@@ -1,19 +1,27 @@
|
|
|
1
|
+
import { AXIS_RESOURCE_CHANNELS } from "../../../core/vocabulary.js";
|
|
2
|
+
import { cartesianAxisOwners, axisTargetArgs, assertCartesianAxisSideAvailable } from "../../../selectors/axes.js";
|
|
3
|
+
import { axisGraphicIds } from "../../../materialization/guides/resources.js";
|
|
4
|
+
import { defaultAxisPosition, validateAxisPosition } from "./policy.js";
|
|
1
5
|
import { isSourceOwnedText } from "../../../grammar/text.js";
|
|
2
6
|
import { withGuideLayoutValidation } from "../../../materialization/guides/layout.js";
|
|
3
7
|
import { action } from "../../../core/action.js";
|
|
4
8
|
import { isPlainObject } from "../../../core/immutable.js";
|
|
5
9
|
import { validateUserId } from "../../../core/identifiers.js";
|
|
6
|
-
import { validateKeys } from "../../../core/validation.js";
|
|
10
|
+
import { validateKeys, validateNonNegativeFinite } from "../../../core/validation.js";
|
|
7
11
|
import { hasCoordinate } from "../../../selectors/index.js";
|
|
8
12
|
import { validateEnabledAxisComponents } from "./components.js";
|
|
13
|
+
import { requireSemanticScale } from "../../../selectors/scales.js";
|
|
14
|
+
import { requireCoordinate } from "../../../selectors/coordinates.js";
|
|
9
15
|
|
|
10
16
|
const TOP_OPTIONS = Object.freeze([
|
|
17
|
+
"id",
|
|
11
18
|
"scale",
|
|
12
19
|
"coordinate",
|
|
13
20
|
"position",
|
|
14
21
|
"line",
|
|
15
22
|
"ticksAndLabels",
|
|
16
|
-
"title"
|
|
23
|
+
"title",
|
|
24
|
+
"offset"
|
|
17
25
|
]);
|
|
18
26
|
const LINE_OPTIONS = Object.freeze(["color", "lineWidth"]);
|
|
19
27
|
const TICK_GROUP_OPTIONS = Object.freeze([
|
|
@@ -30,7 +38,8 @@ const TITLE_OPTIONS = Object.freeze([
|
|
|
30
38
|
"color",
|
|
31
39
|
"fontSize",
|
|
32
40
|
"fontFamily",
|
|
33
|
-
"fontWeight"
|
|
41
|
+
"fontWeight",
|
|
42
|
+
"alongOffset", "align", "baseline"
|
|
34
43
|
]);
|
|
35
44
|
|
|
36
45
|
function validateNested(value, supported, label) {
|
|
@@ -43,6 +52,7 @@ function validateNested(value, supported, label) {
|
|
|
43
52
|
|
|
44
53
|
export function validateAxisArgs(args, operation) {
|
|
45
54
|
validateKeys(args, TOP_OPTIONS, operation);
|
|
55
|
+
if (Object.hasOwn(args, "offset")) validateNonNegativeFinite(args.offset, "Axis frame offset");
|
|
46
56
|
|
|
47
57
|
validateEnabledAxisComponents(args, operation);
|
|
48
58
|
if (Object.hasOwn(args, "line") && args.line !== false) {
|
|
@@ -82,10 +92,52 @@ function makeCreateAxis(channel) {
|
|
|
82
92
|
},
|
|
83
93
|
withGuideLayoutValidation(function (args = {}) {
|
|
84
94
|
validateAxisArgs(args, operation.create);
|
|
85
|
-
const
|
|
95
|
+
const axisId = validateUserId(args.id ?? channel, "Axis id");
|
|
96
|
+
if (AXIS_RESOURCE_CHANNELS.includes(axisId) && axisId !== channel) {
|
|
97
|
+
throw new Error(`Axis id "${axisId}" is reserved for another axis channel.`);
|
|
98
|
+
}
|
|
99
|
+
const named = axisId !== channel;
|
|
100
|
+
let source = this;
|
|
101
|
+
if (named) {
|
|
102
|
+
if (cartesianAxisOwners(this).some(owner => owner.id === axisId) ||
|
|
103
|
+
axisGraphicIds(channel, axisId).some(id => this.graphicSpec.objects[id] !== undefined)) {
|
|
104
|
+
throw new Error(`Axis "${axisId}" requires a missing resource and missing concrete components.`);
|
|
105
|
+
}
|
|
106
|
+
const layers = this.semanticSpec.layers.filter(layer => !isSourceOwnedText(layer) &&
|
|
107
|
+
layer.encoding?.[channel]?.scale !== undefined &&
|
|
108
|
+
(args.coordinate === undefined || layer.coordinate === args.coordinate) &&
|
|
109
|
+
(args.scale === undefined || layer.encoding[channel].scale === args.scale));
|
|
110
|
+
const scales = [...new Set(layers.map(layer => layer.encoding[channel].scale))];
|
|
111
|
+
if (args.scale === undefined && scales.length !== 1) throw new Error("Named axes require an explicit scale when the channel binding is ambiguous.");
|
|
112
|
+
const scale = args.scale ?? scales[0];
|
|
113
|
+
const coordinates = [...new Set(layers.map(layer => layer.coordinate))];
|
|
114
|
+
if (args.coordinate === undefined && coordinates.length !== 1) throw new Error("Named axes require an explicit coordinate when the binding is ambiguous.");
|
|
115
|
+
const coordinate = args.coordinate ?? coordinates[0];
|
|
116
|
+
if (requireCoordinate(this, coordinate).type !== "cartesian") throw new Error("Named Cartesian axes require a Cartesian coordinate.");
|
|
117
|
+
const position = validateAxisPosition(channel, args.position ?? defaultAxisPosition(channel));
|
|
118
|
+
assertCartesianAxisSideAvailable(this, { id: axisId, channel, coordinate, position });
|
|
119
|
+
args = { ...args, scale, coordinate, position };
|
|
120
|
+
source = source.editSemantic({ property: `guide.axis.${axisId}.channel`, value: channel })
|
|
121
|
+
.editSemantic({ property: `guide.axis.${axisId}.position`, value: position })
|
|
122
|
+
.editSemantic({ property: `guide.axis.${axisId}.scale`, value: scale })
|
|
123
|
+
.editSemantic({ property: `guide.axis.${axisId}.coordinate`, value: coordinate });
|
|
124
|
+
} else if (cartesianAxisOwners(this).some(owner => owner.named)) {
|
|
125
|
+
const scale = args.scale ?? channel;
|
|
126
|
+
const coordinates = [...new Set(this.semanticSpec.layers.filter(layer =>
|
|
127
|
+
layer.encoding?.[channel]?.scale === scale).map(layer => layer.coordinate))];
|
|
128
|
+
const coordinate = args.coordinate ?? (coordinates.length === 1 ? coordinates[0] : undefined);
|
|
129
|
+
assertCartesianAxisSideAvailable(this, { id: axisId, channel, coordinate,
|
|
130
|
+
position: validateAxisPosition(channel, args.position ?? defaultAxisPosition(channel)) });
|
|
131
|
+
if (!cartesianAxisOwners(this).some(owner => owner.id === channel)) {
|
|
132
|
+
source = source.editSemantic({ property: `guide.axis.${axisId}.scale`, value: scale });
|
|
133
|
+
if (coordinate !== undefined) source = source.editSemantic({ property: `guide.axis.${axisId}.coordinate`, value: coordinate });
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const shared = axisTargetArgs(axisId, channel);
|
|
86
137
|
if (Object.hasOwn(args, "scale")) shared.scale = args.scale;
|
|
87
138
|
if (Object.hasOwn(args, "position")) shared.position = args.position;
|
|
88
|
-
let next =
|
|
139
|
+
let next = Object.hasOwn(args, "offset")
|
|
140
|
+
? source._withGuideConfig(axisId, "frame", { offset: args.offset }) : source;
|
|
89
141
|
|
|
90
142
|
if (Object.hasOwn(args, "coordinate")) {
|
|
91
143
|
const coordinate = validateUserId(args.coordinate, "Coordinate id");
|
|
@@ -100,16 +152,26 @@ function makeCreateAxis(channel) {
|
|
|
100
152
|
if (!exists) {
|
|
101
153
|
throw new Error(`Unknown coordinate "${coordinate}".`);
|
|
102
154
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
);
|
|
155
|
+
const standalone = !hasConsumer;
|
|
156
|
+
if (standalone && (!Object.hasOwn(args, "scale") ||
|
|
157
|
+
next.semanticSpec.layers.some(layer => Object.values(layer.encoding ?? {}).some(encoding => encoding?.scale === scale)))) {
|
|
158
|
+
throw new Error(`${operation.create} found no ${channel} encoding for coordinate "${coordinate}" and scale "${scale}"; standalone binding requires an explicit unused scale.`);
|
|
159
|
+
}
|
|
160
|
+
if (standalone) {
|
|
161
|
+
const definition = requireSemanticScale(next, scale);
|
|
162
|
+
if (!Array.isArray(definition.domain) || definition.domain.length === 0 ||
|
|
163
|
+
requireCoordinate(next, coordinate).type !== "cartesian") {
|
|
164
|
+
throw new Error("Standalone axes require a Cartesian coordinate and explicit nonempty scale domain.");
|
|
165
|
+
}
|
|
107
166
|
}
|
|
108
167
|
|
|
109
168
|
next = next.editSemantic({
|
|
110
|
-
property: `guide.axis.${
|
|
169
|
+
property: `guide.axis.${axisId}.coordinate`,
|
|
111
170
|
value: coordinate
|
|
112
171
|
});
|
|
172
|
+
if (standalone) next = next._withGuideConfig(axisId, "binding", { standalone: true })
|
|
173
|
+
.editSemantic({ property: `guide.axis.${axisId}.scale`, value: scale })
|
|
174
|
+
.rematerializeScale({ id: scale, guides: false, marks: false });
|
|
113
175
|
}
|
|
114
176
|
|
|
115
177
|
for (const component of ["line", "ticksAndLabels", "title"]) {
|
|
@@ -2,8 +2,8 @@ import { axisGraphicIds } from "../../../materialization/guides/resources.js";
|
|
|
2
2
|
|
|
3
3
|
export const AXIS_COMPONENTS = Object.freeze(["line", "ticks", "labels", "title"]);
|
|
4
4
|
|
|
5
|
-
export function axisComponentId(channel, component) {
|
|
6
|
-
return axisGraphicIds(channel)[AXIS_COMPONENTS.indexOf(component)];
|
|
5
|
+
export function axisComponentId(channel, component, axisId = channel) {
|
|
6
|
+
return axisGraphicIds(channel, axisId)[AXIS_COMPONENTS.indexOf(component)];
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export function axisComponentDisabled(args, component) {
|
|
@@ -17,27 +17,27 @@ export function validateEnabledAxisComponents(args, operation) {
|
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
export function hasAxisComponent(program, channel, component) {
|
|
21
|
-
return program.graphicSpec.objects[axisComponentId(channel, component)] !== undefined ||
|
|
22
|
-
program.guideConfigs.axis?.[
|
|
20
|
+
export function hasAxisComponent(program, channel, component, axisId = channel) {
|
|
21
|
+
return program.graphicSpec.objects[axisComponentId(channel, component, axisId)] !== undefined ||
|
|
22
|
+
program.guideConfigs.axis?.[axisId]?.[component] !== undefined ||
|
|
23
23
|
(component === "title" &&
|
|
24
|
-
program.semanticSpec.guides.axis?.[
|
|
24
|
+
program.semanticSpec.guides.axis?.[axisId]?.title !== undefined);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
export function assertRemovableAxisComponent(program, channel, component, operation) {
|
|
28
|
-
if (!hasAxisComponent(program, channel, component)) {
|
|
27
|
+
export function assertRemovableAxisComponent(program, channel, component, operation, axisId = channel) {
|
|
28
|
+
if (!hasAxisComponent(program, channel, component, axisId)) {
|
|
29
29
|
throw new Error(`${operation}.${component} requires an existing ${channel}-axis ${component}.`);
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
export function removeAxisComponent(program, channel, component) {
|
|
34
|
-
const id = axisComponentId(channel, component);
|
|
33
|
+
export function removeAxisComponent(program, channel, component, axisId = channel) {
|
|
34
|
+
const id = axisComponentId(channel, component, axisId);
|
|
35
35
|
let next = program;
|
|
36
|
-
if (component === "title" && next.semanticSpec.guides.axis?.[
|
|
37
|
-
next = next.editSemantic({ property: `guide.axis.${
|
|
36
|
+
if (component === "title" && next.semanticSpec.guides.axis?.[axisId]?.title !== undefined) {
|
|
37
|
+
next = next.editSemantic({ property: `guide.axis.${axisId}.title`, remove: true });
|
|
38
38
|
}
|
|
39
39
|
if (next.graphicSpec.objects[id] !== undefined) {
|
|
40
40
|
next = next.editGraphics({ target: id, remove: true });
|
|
41
41
|
}
|
|
42
|
-
return next._withoutMaterializationConfig(["guides", "axis",
|
|
42
|
+
return next._withoutMaterializationConfig(["guides", "axis", axisId, component]);
|
|
43
43
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { selectCartesianAxis, axisTargetArgs, assertCartesianAxisSideAvailable } from "../../../selectors/axes.js";
|
|
2
|
+
import { validateAxisPosition } from "./policy.js";
|
|
1
3
|
import { withGuideLayoutValidation } from "../../../materialization/guides/layout.js";
|
|
2
4
|
import { action } from "../../../core/action.js";
|
|
3
5
|
import { isPlainObject } from "../../../core/immutable.js";
|
|
4
|
-
import { validateKeys, validateOptionObject } from "../../../core/validation.js";
|
|
6
|
+
import { validateKeys, validateOptionObject, validateNonNegativeFinite } from "../../../core/validation.js";
|
|
5
7
|
|
|
6
8
|
import {
|
|
7
9
|
AXIS_COMPONENTS,
|
|
@@ -12,21 +14,21 @@ import {
|
|
|
12
14
|
} from "./components.js";
|
|
13
15
|
|
|
14
16
|
const OPTIONS = Object.freeze([
|
|
15
|
-
"position", "line", "ticks", "labels", "ticksAndLabels", "title"
|
|
17
|
+
"target", "position", "line", "ticks", "labels", "ticksAndLabels", "title", "offset"
|
|
16
18
|
]);
|
|
17
19
|
const LINE_OPTIONS = Object.freeze(["color", "lineWidth"]);
|
|
18
20
|
const TICK_OPTIONS = Object.freeze([
|
|
19
|
-
"count", "values", "length", "color", "lineWidth"
|
|
21
|
+
"count", "values", "length", "color", "lineWidth", "alongOffset", "strokeDash", "strokeDashMap"
|
|
20
22
|
]);
|
|
21
23
|
const LABEL_OPTIONS = Object.freeze([
|
|
22
24
|
"count", "values", "offset", "format", "color", "fontSize",
|
|
23
25
|
"fontFamily", "fontWeight", "rotation", "maxWidth", "wrap",
|
|
24
|
-
"lineHeight", "overlap"
|
|
26
|
+
"lineHeight", "overlap", "alongOffset", "labelMap", "flush", "colorMap", "align"
|
|
25
27
|
]);
|
|
26
28
|
const GROUP_OPTIONS = Object.freeze(["count", "values", "ticks", "labels"]);
|
|
27
29
|
const TITLE_OPTIONS = Object.freeze([
|
|
28
30
|
"text", "at", "offset", "rotation", "color", "fontSize",
|
|
29
|
-
"fontFamily", "fontWeight"
|
|
31
|
+
"fontFamily", "fontWeight", "alongOffset", "align", "baseline"
|
|
30
32
|
]);
|
|
31
33
|
|
|
32
34
|
function validateNested(args, key, options, operation, { allowFalse = false } = {}) {
|
|
@@ -40,6 +42,7 @@ function validateNested(args, key, options, operation, { allowFalse = false } =
|
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
function validateArgs(args, operation) {
|
|
45
|
+
if (Object.hasOwn(args, "offset")) validateNonNegativeFinite(args.offset, "Axis frame offset");
|
|
43
46
|
validateOptionObject(args, OPTIONS, operation, {
|
|
44
47
|
allowEmpty: false,
|
|
45
48
|
emptyMessage: `${operation} requires at least one axis change.`,
|
|
@@ -59,12 +62,12 @@ function validateArgs(args, operation) {
|
|
|
59
62
|
);
|
|
60
63
|
}
|
|
61
64
|
if (args.ticksAndLabels !== false && args.ticksAndLabels?.ticks !== undefined) {
|
|
62
|
-
validateNested(args.ticksAndLabels, "ticks", ["length", "color", "lineWidth"], `${operation}.ticksAndLabels`);
|
|
65
|
+
validateNested(args.ticksAndLabels, "ticks", ["length", "color", "lineWidth", "alongOffset", "strokeDash", "strokeDashMap"], `${operation}.ticksAndLabels`);
|
|
63
66
|
}
|
|
64
67
|
if (args.ticksAndLabels !== false && args.ticksAndLabels?.labels !== undefined) {
|
|
65
68
|
validateNested(args.ticksAndLabels, "labels", [
|
|
66
69
|
"offset", "format", "color", "fontSize", "fontFamily", "fontWeight",
|
|
67
|
-
"rotation", "maxWidth", "wrap", "lineHeight", "overlap"
|
|
70
|
+
"rotation", "maxWidth", "wrap", "lineHeight", "overlap", "alongOffset", "labelMap", "flush", "colorMap", "align"
|
|
68
71
|
], `${operation}.ticksAndLabels`);
|
|
69
72
|
}
|
|
70
73
|
}
|
|
@@ -82,27 +85,26 @@ function names(channel) {
|
|
|
82
85
|
};
|
|
83
86
|
}
|
|
84
87
|
|
|
85
|
-
function buildPlan(program, channel, args, operation) {
|
|
86
|
-
const shared =
|
|
87
|
-
? { position: args.position }
|
|
88
|
-
: {};
|
|
88
|
+
function buildPlan(program, channel, args, operation, axisId = channel) {
|
|
89
|
+
const shared = { ...axisTargetArgs(axisId, channel),
|
|
90
|
+
...(Object.hasOwn(args, "position") ? { position: args.position } : {}) };
|
|
89
91
|
const plan = [];
|
|
90
92
|
const addLeaf = (component, edit) => {
|
|
91
93
|
const value = args[component];
|
|
92
94
|
if (value === false) {
|
|
93
|
-
assertRemovable(program, channel, component, operation.operation);
|
|
95
|
+
assertRemovable(program, channel, component, operation.operation, axisId);
|
|
94
96
|
plan.push({ kind: "remove", component });
|
|
95
97
|
} else if (value !== undefined) {
|
|
96
98
|
plan.push({ kind: "edit", operation: edit, args: { ...shared, ...value } });
|
|
97
|
-
} else if (args.position !== undefined && hasComponent(program, channel, component)) {
|
|
99
|
+
} else if ((args.position !== undefined || Object.hasOwn(args, "offset")) && hasComponent(program, channel, component, axisId)) {
|
|
98
100
|
plan.push({ kind: "edit", operation: edit, args: shared });
|
|
99
101
|
}
|
|
100
102
|
};
|
|
101
103
|
|
|
102
104
|
addLeaf("line", operation.line);
|
|
103
105
|
if (args.ticksAndLabels === false) {
|
|
104
|
-
assertRemovable(program, channel, "ticks", operation.operation);
|
|
105
|
-
assertRemovable(program, channel, "labels", operation.operation);
|
|
106
|
+
assertRemovable(program, channel, "ticks", operation.operation, axisId);
|
|
107
|
+
assertRemovable(program, channel, "labels", operation.operation, axisId);
|
|
106
108
|
plan.push(
|
|
107
109
|
{ kind: "remove", component: "ticks" },
|
|
108
110
|
{ kind: "remove", component: "labels" }
|
|
@@ -125,22 +127,22 @@ function buildPlan(program, channel, args, operation) {
|
|
|
125
127
|
return plan;
|
|
126
128
|
}
|
|
127
129
|
|
|
128
|
-
function applyPlan(program, channel, plan, operation) {
|
|
130
|
+
function applyPlan(program, channel, plan, operation, axisId = channel) {
|
|
129
131
|
let next = program;
|
|
130
132
|
for (const step of plan) {
|
|
131
133
|
next = step.kind === "remove"
|
|
132
|
-
? removeComponent(next, channel, step.component)
|
|
134
|
+
? removeComponent(next, channel, step.component, axisId)
|
|
133
135
|
: next[step.operation](step.args);
|
|
134
136
|
}
|
|
135
137
|
|
|
136
138
|
const hasRetainedGraphic = AXIS_COMPONENTS.some(
|
|
137
|
-
component => next.graphicSpec.objects[graphicId(channel, component)] !== undefined
|
|
139
|
+
component => next.graphicSpec.objects[graphicId(channel, component, axisId)] !== undefined
|
|
138
140
|
);
|
|
139
141
|
if (!hasRetainedGraphic && (
|
|
140
|
-
next.semanticSpec.guides.axis?.[
|
|
141
|
-
next.guideConfigs.axis?.[
|
|
142
|
+
next.semanticSpec.guides.axis?.[axisId] !== undefined ||
|
|
143
|
+
next.guideConfigs.axis?.[axisId] !== undefined
|
|
142
144
|
)) {
|
|
143
|
-
next = next[operation.remove]();
|
|
145
|
+
next = next[operation.remove](axisId === channel ? undefined : axisTargetArgs(axisId, channel));
|
|
144
146
|
}
|
|
145
147
|
return next;
|
|
146
148
|
}
|
|
@@ -154,12 +156,22 @@ function makeEditAxis(channel) {
|
|
|
154
156
|
},
|
|
155
157
|
withGuideLayoutValidation(function (args = {}) {
|
|
156
158
|
validateArgs(args, operation.operation);
|
|
157
|
-
const
|
|
159
|
+
const selected = selectCartesianAxis(this, channel, args, { whole: true });
|
|
160
|
+
const axisId = selected.id; args = selected.options;
|
|
161
|
+
let source = this;
|
|
162
|
+
if (selected.owner?.named && args.position !== undefined) {
|
|
163
|
+
const position = validateAxisPosition(channel, args.position);
|
|
164
|
+
assertCartesianAxisSideAvailable(this, { id: axisId, channel,
|
|
165
|
+
coordinate: selected.owner.coordinate, position });
|
|
166
|
+
source = source.editSemantic({ property: `guide.axis.${axisId}.position`, value: position });
|
|
167
|
+
}
|
|
168
|
+
if (Object.hasOwn(args, "offset")) source = source._withGuideConfig(axisId, "frame", { offset: args.offset });
|
|
169
|
+
const plan = buildPlan(source, channel, args, operation, axisId);
|
|
158
170
|
|
|
159
171
|
// Run the complete proposal against an immutable speculative branch so a
|
|
160
172
|
// later leaf failure cannot begin the returned action trace or state.
|
|
161
|
-
applyPlan(
|
|
162
|
-
return applyPlan(
|
|
173
|
+
applyPlan(source, channel, plan, operation, axisId);
|
|
174
|
+
return applyPlan(source, channel, plan, operation, axisId);
|
|
163
175
|
})
|
|
164
176
|
);
|
|
165
177
|
}
|