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,3 +1,4 @@
|
|
|
1
|
+
import { cartesianAxisOwners, axisTargetArgs } from "../../selectors/axes.js";
|
|
1
2
|
import { ownedChildren, ownership } from "../../selectors/markOwners.js";
|
|
2
3
|
import { isSourceOwnedText } from "../../grammar/text.js";
|
|
3
4
|
import { closedAction } from "../../core/action.js";
|
|
@@ -163,6 +164,7 @@ export function removeOwnedMark(program, id, partial = false) {
|
|
|
163
164
|
next = next.editGraphics({ target: id, remove: true });
|
|
164
165
|
}
|
|
165
166
|
return next
|
|
167
|
+
._withoutMaterializationConfig(["guides", "legendGroups", id])
|
|
166
168
|
._withoutMaterializationConfig(["marks", id])
|
|
167
169
|
._withContext({
|
|
168
170
|
...(program.context.currentMark === id ? { currentMark: undefined } : {}),
|
|
@@ -182,16 +184,15 @@ function cleanupPositionGuides(program, scales) {
|
|
|
182
184
|
let next = program;
|
|
183
185
|
for (const channel of POSITION_CHANNELS) {
|
|
184
186
|
const definition = getPositionChannelDefinition(channel);
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
});
|
|
187
|
+
const owners = ["x", "y"].includes(channel)
|
|
188
|
+
? cartesianAxisOwners(next).filter(owner => owner.channel === channel)
|
|
189
|
+
: [{ id: channel, channel, semantic: next.semanticSpec.guides.axis?.[channel] }];
|
|
190
|
+
for (const owner of owners) {
|
|
191
|
+
const axis = owner.semantic;
|
|
192
|
+
if (axis !== undefined && scales[channel].has(axis.scale) && !hasScaleConsumer(next, channel, axis.scale)) {
|
|
193
|
+
next = next[REMOVE_AXIS[channel]]({ ...axisTargetArgs(owner.id, channel), scale: axis.scale,
|
|
194
|
+
...(axis.coordinate === undefined ? {} : { coordinate: axis.coordinate }) });
|
|
195
|
+
}
|
|
195
196
|
}
|
|
196
197
|
const grid = next.semanticSpec.guides.grid?.[definition.gridDirection];
|
|
197
198
|
if (
|
|
@@ -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,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { validateGraphicItemLimit } from "../../core/validation.js";
|
|
1
2
|
import { closedAction } from "../../core/action.js";
|
|
2
3
|
import { validateUserId } from "../../core/identifiers.js";
|
|
3
4
|
import { freezeOwned } from "../../core/immutable.js";
|
|
4
|
-
import {
|
|
5
|
+
import { resolveGraphicItemCapacity } from "../../selectors/graphicCapacity.js";
|
|
5
6
|
import {
|
|
6
7
|
isDrawableGraphicType,
|
|
7
8
|
isGraphicContainerType,
|
|
@@ -118,7 +119,7 @@ const createGraphics = /* @__PURE__ */ closedAction(
|
|
|
118
119
|
throw new TypeError("createGraphics length must be a non-negative integer.");
|
|
119
120
|
}
|
|
120
121
|
if (length !== undefined) {
|
|
121
|
-
|
|
122
|
+
validateGraphicItemLimit(length, resolveGraphicItemCapacity(this, { id, label: "createGraphics length", type: validatedType }));
|
|
122
123
|
}
|
|
123
124
|
if (validatedType === "collection" && length !== undefined) {
|
|
124
125
|
throw new Error("Heterogeneous collections use editGraphics items instead of length.");
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { validateGraphicItemLimit } from "../../core/validation.js";
|
|
1
2
|
import { closedAction } from "../../core/action.js";
|
|
2
3
|
import { cloneAndFreeze, freezeOwned, isPlainObject } from "../../core/immutable.js";
|
|
3
|
-
import {
|
|
4
|
+
import { resolveGraphicItemCapacity } from "../../selectors/graphicCapacity.js";
|
|
4
5
|
import {
|
|
5
6
|
isPrimitiveDrawableGraphicType,
|
|
6
7
|
isStructuralGraphicType,
|
|
@@ -43,14 +44,14 @@ function validateCollectionItem(item, ownerId, index) {
|
|
|
43
44
|
});
|
|
44
45
|
}
|
|
45
46
|
|
|
46
|
-
function replaceCollectionItems(value, id) {
|
|
47
|
+
function replaceCollectionItems(value, id, program) {
|
|
47
48
|
if (!Array.isArray(value)) throw new TypeError("collection.items must be an array.");
|
|
48
|
-
|
|
49
|
+
validateGraphicItemLimit(value.length, resolveGraphicItemCapacity(program, { id, label: "collection.items length" }));
|
|
49
50
|
return freezeOwned(value.map((item, index) => validateCollectionItem(item, id, index)));
|
|
50
51
|
}
|
|
51
52
|
|
|
52
|
-
function replaceDrawableItems(value, id, type) {
|
|
53
|
-
const items = replaceCollectionItems(value, id);
|
|
53
|
+
function replaceDrawableItems(value, id, type, program) {
|
|
54
|
+
const items = replaceCollectionItems(value, id, program);
|
|
54
55
|
if (!items.every(item => item.type === type)) {
|
|
55
56
|
return freezeOwned({ type: "collection", items });
|
|
56
57
|
}
|
|
@@ -102,15 +103,15 @@ function editGeneratedItem(owner, itemIndex, property, value) {
|
|
|
102
103
|
return freezeOwned({ ...owner, items: freezeOwned(items) });
|
|
103
104
|
}
|
|
104
105
|
|
|
105
|
-
function editItems(owner, property, value, id) {
|
|
106
|
+
function editItems(owner, property, value, id, program) {
|
|
106
107
|
if (owner.type === "collection" && property === "items") {
|
|
107
|
-
return freezeOwned({ ...owner, items: replaceCollectionItems(value, id) });
|
|
108
|
+
return freezeOwned({ ...owner, items: replaceCollectionItems(value, id, program) });
|
|
108
109
|
}
|
|
109
110
|
if (property === "length") {
|
|
110
111
|
if (!Number.isInteger(value) || value < 0) {
|
|
111
112
|
throw new TypeError("Graphic collection length must be a non-negative integer.");
|
|
112
113
|
}
|
|
113
|
-
|
|
114
|
+
validateGraphicItemLimit(value, resolveGraphicItemCapacity(program, { id, label: "Graphic collection length" }));
|
|
114
115
|
const items = owner.items.slice(0, value);
|
|
115
116
|
for (let index = items.length; index < value; index += 1) {
|
|
116
117
|
if (owner.type === "collection") {
|
|
@@ -189,6 +190,9 @@ const editGraphics = /* @__PURE__ */ closedAction(
|
|
|
189
190
|
|
|
190
191
|
const ownerId = found.kind === "item" ? found.ownerId : found.id;
|
|
191
192
|
const owner = found.kind === "item" ? found.owner : found.object;
|
|
193
|
+
if (owner.items !== undefined) validateGraphicItemLimit(owner.items.length, resolveGraphicItemCapacity(this, {
|
|
194
|
+
id: ownerId, label: "Graphic collection length"
|
|
195
|
+
}));
|
|
192
196
|
const replacesDrawableItems =
|
|
193
197
|
found.kind === "object" && property === "items" &&
|
|
194
198
|
owner.items !== undefined && owner.type !== "collection";
|
|
@@ -199,7 +203,7 @@ const editGraphics = /* @__PURE__ */ closedAction(
|
|
|
199
203
|
: owner.type, property);
|
|
200
204
|
let updated;
|
|
201
205
|
if (replacesDrawableItems) {
|
|
202
|
-
updated = replaceDrawableItems(value, ownerId, owner.type);
|
|
206
|
+
updated = replaceDrawableItems(value, ownerId, owner.type, this);
|
|
203
207
|
} else if (found.kind === "item") {
|
|
204
208
|
if (validatedProperty === "length" || validatedProperty === "items") {
|
|
205
209
|
throw new Error(`${validatedProperty} can only target an owning graphic.`);
|
|
@@ -212,12 +216,12 @@ const editGraphics = /* @__PURE__ */ closedAction(
|
|
|
212
216
|
properties: setGraphicProperty(owner.properties, validatedProperty, value)
|
|
213
217
|
});
|
|
214
218
|
} else if (owner.items !== undefined) {
|
|
215
|
-
updated = editItems(owner, validatedProperty, value, ownerId);
|
|
219
|
+
updated = editItems(owner, validatedProperty, value, ownerId, this);
|
|
216
220
|
} else if (validatedProperty === "length") {
|
|
217
221
|
if (!Number.isInteger(value) || value < 0) {
|
|
218
222
|
throw new TypeError("Graphic collection length must be a non-negative integer.");
|
|
219
223
|
}
|
|
220
|
-
|
|
224
|
+
validateGraphicItemLimit(value, resolveGraphicItemCapacity(this, { id: ownerId, label: "Graphic collection length" }));
|
|
221
225
|
updated = freezeOwned({
|
|
222
226
|
type: owner.type,
|
|
223
227
|
items: freezeOwned(Array.from({ length: value }, (_, index) => freezeOwned({
|
|
@@ -1,6 +1 @@
|
|
|
1
|
-
export
|
|
2
|
-
for (const [property, value] of Object.entries(properties)) {
|
|
3
|
-
program = program.editGraphics({ target, property, value });
|
|
4
|
-
}
|
|
5
|
-
return program;
|
|
6
|
-
}
|
|
1
|
+
export { editGraphicProperties } from "../../core/graphicProperties.js";
|
|
@@ -57,7 +57,7 @@ function updateEntity(spec, parsed, value) {
|
|
|
57
57
|
source.schema,
|
|
58
58
|
value[0],
|
|
59
59
|
undefined,
|
|
60
|
-
{ sourceId: source.id, ownerId: parsed.id }
|
|
60
|
+
{ sourceId: source.id, ownerId: parsed.id, sourceTransform: source.transform?.[0] }
|
|
61
61
|
));
|
|
62
62
|
}
|
|
63
63
|
}
|
|
@@ -72,6 +72,7 @@ function updateEntity(spec, parsed, value) {
|
|
|
72
72
|
const schema = source?.schema !== undefined && current.transform?.length === 1
|
|
73
73
|
? deriveTransformSchema(source.schema, current.transform[0], value, {
|
|
74
74
|
sourceId: source.id,
|
|
75
|
+
sourceTransform: source.transform?.[0],
|
|
75
76
|
ownerId: parsed.id
|
|
76
77
|
})
|
|
77
78
|
: inferDatasetSchema(value);
|
|
@@ -225,6 +226,31 @@ export function withoutPreviewLayerEncodings(program, { id, channels }) {
|
|
|
225
226
|
return program._clone({ semanticSpec });
|
|
226
227
|
}
|
|
227
228
|
|
|
229
|
+
// Composition materialization may retain this native two-scale guide meaning.
|
|
230
|
+
// Use the existing semantic primitives on a private unit adapter; this does not
|
|
231
|
+
// relax the public composition guard or accept arbitrary semantic paths.
|
|
232
|
+
export function synchronizeFacetColorSizeMeaning(program, meaning) {
|
|
233
|
+
if (program.compositionSpec?.type !== "facet") {
|
|
234
|
+
throw new Error("Color-size parent meaning requires a facet composition.");
|
|
235
|
+
}
|
|
236
|
+
if (meaning !== undefined && (!isPlainObject(meaning) ||
|
|
237
|
+
Object.keys(meaning).some(key => !["colorScale", "sizeScale", "title"].includes(key)))) {
|
|
238
|
+
throw new Error("Color-size parent meaning requires its native scale bindings and title.");
|
|
239
|
+
}
|
|
240
|
+
let unit = new program.constructor({ semanticSpec: program.semanticSpec, graphicSpec: program.graphicSpec,
|
|
241
|
+
resolvedScales: program.resolvedScales, materializationConfigs: program.materializationConfigs,
|
|
242
|
+
context: program.context, trace: program.trace, actionStack: program.actionStack,
|
|
243
|
+
actionSequence: program._actionSequence });
|
|
244
|
+
if (unit.semanticSpec.guides.legend?.colorSize !== undefined) {
|
|
245
|
+
unit = unit.editSemantic({ property: "guide.legend.colorSize", remove: true });
|
|
246
|
+
}
|
|
247
|
+
if (meaning !== undefined) for (const [property, value] of Object.entries(meaning)) {
|
|
248
|
+
unit = unit.editSemantic({ property: `guide.legend.colorSize.${property}`, value });
|
|
249
|
+
}
|
|
250
|
+
return program._clone({ semanticSpec: unit.semanticSpec, context: unit.context,
|
|
251
|
+
trace: unit.trace, actionStack: unit.actionStack, actionSequence: unit._actionSequence });
|
|
252
|
+
}
|
|
253
|
+
|
|
228
254
|
export function createSemanticPrimitiveAction(validateSemanticValue, planNamedRemoval) {
|
|
229
255
|
return closedAction(
|
|
230
256
|
{
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import {isSizeShapeChannels} from "../../../grammar/legendCombinations.js";
|
|
1
2
|
import { normalizeLegendOrder } from "../../../grammar/categoryOrder.js";
|
|
2
3
|
import { validateUserId } from "../../../core/identifiers.js";
|
|
3
4
|
import { CATEGORICAL_LEGEND_CHANNELS } from "../../../core/vocabulary.js";
|
|
@@ -16,7 +17,7 @@ function validateLegend(property, value) {
|
|
|
16
17
|
throw new Error(`Legend ${property} must not contain duplicates.`);
|
|
17
18
|
}
|
|
18
19
|
if (property === "channels") {
|
|
19
|
-
if (!value.every(channel => CATEGORICAL_LEGEND_CHANNELS.includes(channel))) {
|
|
20
|
+
if (!isSizeShapeChannels(value) && !value.every(channel => CATEGORICAL_LEGEND_CHANNELS.includes(channel))) {
|
|
20
21
|
throw new Error("Legend channels support only color, strokeDash, and shape.");
|
|
21
22
|
}
|
|
22
23
|
return;
|
|
@@ -26,6 +27,11 @@ function validateLegend(property, value) {
|
|
|
26
27
|
|
|
27
28
|
export function validateGuideSemanticValue(_program, parsed, value) {
|
|
28
29
|
const property = parsed.path.at(-1);
|
|
30
|
+
if (parsed.id === "legend.colorSize") {
|
|
31
|
+
if(property === "title") validateNonEmptySemanticString(value,"Legend title");
|
|
32
|
+
else validateUserId(value,"Legend scale id");
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
29
35
|
if (parsed.id === "axis.parallel" && property === "titles") {
|
|
30
36
|
validateParallelAxisTitles(value);
|
|
31
37
|
return;
|
|
@@ -44,6 +50,10 @@ export function validateGuideSemanticValue(_program, parsed, value) {
|
|
|
44
50
|
throw new TypeError("Parallel axis scales must contain at least two ids.");
|
|
45
51
|
}
|
|
46
52
|
value.forEach(id => validateUserId(id, "Parallel axis scale id"));
|
|
53
|
+
} else if (parsed.id.startsWith("axis.") && property === "channel") {
|
|
54
|
+
if (!["x", "y"].includes(value)) throw new Error("Named Cartesian axis channel requires x or y.");
|
|
55
|
+
} else if (parsed.id.startsWith("axis.") && property === "position") {
|
|
56
|
+
if (!["top", "bottom", "left", "right"].includes(value)) throw new Error("Named Cartesian axis position requires a physical side.");
|
|
47
57
|
} else if (property === "title") {
|
|
48
58
|
validateNonEmptySemanticString(value, "Axis title");
|
|
49
59
|
} else {
|
|
@@ -66,6 +66,14 @@ 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
|
+
}
|
|
73
|
+
if (property === "mark.blend" &&
|
|
74
|
+
(findLayer(program, parsed.id)?.mark?.type !== "bar" || value !== "difference")) {
|
|
75
|
+
throw new Error("Bar blend must be difference; remove the optional intent for normal painting.");
|
|
76
|
+
}
|
|
69
77
|
if (property === "derivedBindings.regression") {
|
|
70
78
|
if (!value || typeof value !== "object" || Array.isArray(value) ||
|
|
71
79
|
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
|
]);
|
|
@@ -19,9 +19,10 @@ import { findLayer } from "../../selectors/layers.js";
|
|
|
19
19
|
import { removeOwnedMark } from "../marks/remove.js";
|
|
20
20
|
import { removeOwnedColorLegends } from "../guides/legends/remove.js";
|
|
21
21
|
import { requireRegressionField } from "./resolve.js";
|
|
22
|
+
import { buildSourceRevisionPlan, applyRevisionPlan } from "../data/edit.js";
|
|
22
23
|
|
|
23
24
|
const OPTIONS = Object.freeze([
|
|
24
|
-
"target", "data", "x", "y", "groupBy", "method", "degree", "span",
|
|
25
|
+
"target", "data", "x", "y", "groupBy", "method", "degree", "span", "robustIterations",
|
|
25
26
|
"confidenceMethod", "level", "confidence", "interval", "band", "line"
|
|
26
27
|
, "predict", "sourceBinding", "missing"
|
|
27
28
|
]);
|
|
@@ -111,14 +112,14 @@ function resolveParameters(previous, args) {
|
|
|
111
112
|
const method = args.method ?? previous.method;
|
|
112
113
|
const raw = { method };
|
|
113
114
|
for (const key of [
|
|
114
|
-
"degree", "span", "confidenceMethod", "level", "confidence", "interval"
|
|
115
|
+
"degree", "span", "robustIterations", "confidenceMethod", "level", "confidence", "interval"
|
|
115
116
|
]) {
|
|
116
117
|
if (Object.hasOwn(args, key)) raw[key] = args[key];
|
|
117
118
|
}
|
|
118
119
|
const inheritsLevel = !Object.hasOwn(raw, "confidence");
|
|
119
120
|
if (method === previous.method && args.interval !== false) {
|
|
120
121
|
for (const key of [
|
|
121
|
-
"degree", "span", "confidenceMethod", "level", "interval"
|
|
122
|
+
"degree", "span", "robustIterations", "confidenceMethod", "level", "interval"
|
|
122
123
|
]) {
|
|
123
124
|
if (
|
|
124
125
|
!Object.hasOwn(raw, key) &&
|
|
@@ -128,7 +129,7 @@ function resolveParameters(previous, args) {
|
|
|
128
129
|
raw[key] = previous[key];
|
|
129
130
|
}
|
|
130
131
|
}
|
|
131
|
-
} else if (
|
|
132
|
+
} else if (!["loess", "exp"].includes(method) && !["loess", "exp"].includes(previous.method) && args.interval !== false) {
|
|
132
133
|
for (const key of ["confidenceMethod", "level", "interval"]) {
|
|
133
134
|
if (
|
|
134
135
|
!Object.hasOwn(raw, key) &&
|
|
@@ -295,6 +296,11 @@ export const editRegression = /* @__PURE__ */ closedAction(
|
|
|
295
296
|
const changesParameters = JSON.stringify(parameters) !==
|
|
296
297
|
JSON.stringify(current.parameters);
|
|
297
298
|
const changesStatistics = changesProvenance || changesParameters;
|
|
299
|
+
const hasStatistics = this.semanticSpec.datasets.some(dataset =>
|
|
300
|
+
dataset.source === current.dataId && dataset.transform?.[0]?.type === "regressionStatistics");
|
|
301
|
+
if (hasStatistics && parameters.method !== "linear") {
|
|
302
|
+
throw new Error("Remove dependent regression statistics before changing the linear fit method.");
|
|
303
|
+
}
|
|
298
304
|
const bandPatch = Object.hasOwn(args, "band")
|
|
299
305
|
? validateBandPatch(args.band)
|
|
300
306
|
: undefined;
|
|
@@ -302,14 +308,16 @@ export const editRegression = /* @__PURE__ */ closedAction(
|
|
|
302
308
|
? validateLinePatch(args.line)
|
|
303
309
|
: undefined;
|
|
304
310
|
if ((parameters.method === "loess" || parameters.interval === false) && bandPatch !== undefined && bandPatch !== false) {
|
|
305
|
-
throw new Error(
|
|
311
|
+
throw new Error(parameters.method === "loess"
|
|
312
|
+
? "LOESS regression does not support a band object."
|
|
313
|
+
: "Regression interval false cannot include a band object.");
|
|
306
314
|
}
|
|
307
315
|
|
|
308
316
|
const hadBand = current.bandId !== undefined;
|
|
309
317
|
const wantsBand = parameters.method !== "loess" && parameters.interval !== false &&
|
|
310
318
|
(bandPatch === false
|
|
311
319
|
? false
|
|
312
|
-
: hadBand || bandPatch !== undefined || current.parameters.method
|
|
320
|
+
: hadBand || bandPatch !== undefined || ["loess", "exp"].includes(current.parameters.method));
|
|
313
321
|
const revision = changesStatistics
|
|
314
322
|
? planDerivedDataRevision(this, {
|
|
315
323
|
owner: owner.id,
|
|
@@ -321,6 +329,8 @@ export const editRegression = /* @__PURE__ */ closedAction(
|
|
|
321
329
|
]
|
|
322
330
|
})
|
|
323
331
|
: undefined;
|
|
332
|
+
const statisticsRevision = changesStatistics && hasStatistics
|
|
333
|
+
? buildSourceRevisionPlan(this, current.dataId, revision.id) : undefined;
|
|
324
334
|
|
|
325
335
|
const applyEdit = program => {
|
|
326
336
|
let dataId = current.dataId;
|
|
@@ -412,6 +422,8 @@ export const editRegression = /* @__PURE__ */ closedAction(
|
|
|
412
422
|
}
|
|
413
423
|
});
|
|
414
424
|
if (changesStatistics) {
|
|
425
|
+
if (statisticsRevision !== undefined) next = applyRevisionPlan(next, undefined,
|
|
426
|
+
statisticsRevision, { retain: new Set([current.dataId]) });
|
|
415
427
|
next = next.releaseDerivedData(revision.release);
|
|
416
428
|
}
|
|
417
429
|
const binding = findLayer(next, owner.id)?.derivedBindings?.regression;
|
|
@@ -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", "whitePoint", "gamma", "midpoint", "base", "exponent", "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", "whitePoint", "gamma", "midpoint", "base", "exponent", "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
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { isPendingMeasuredRadiusConsumer } from "../../../materialization/scales/policies/arc.js";
|
|
2
|
-
import { deriveMeasuredArcValues } from "../../../grammar/arcs.js";
|
|
2
|
+
import { deriveMeasuredArcValues, deriveProportionalArcRadiusValues } from "../../../grammar/arcs.js";
|
|
3
3
|
import { deriveBarAggregates } from "../../../grammar/bars/aggregate.js";
|
|
4
4
|
import {
|
|
5
5
|
BAR_GRAINS,
|
|
6
|
+
resolveBarChannels,
|
|
6
7
|
resolveBarGrain
|
|
7
8
|
} from "../../../grammar/bars/policy.js";
|
|
8
9
|
import {
|
|
@@ -28,9 +29,15 @@ function lineDerivationOptions(program, consumer, dataset) {
|
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
export function resolveMarkFamilyConsumerValues(program, consumer, dataset) {
|
|
31
|
-
if (consumer.layer.mark?.type === "arc" && consumer.channel === "radius"
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
if (consumer.layer.mark?.type === "arc" && consumer.channel === "radius") {
|
|
33
|
+
let derive;
|
|
34
|
+
if (findScale(program, consumer.encoding.scale).radialMapping !== undefined) derive = deriveMeasuredArcValues;
|
|
35
|
+
else if (consumer.layer.encoding?.theta?.fieldType === "quantitative" ||
|
|
36
|
+
consumer.layer.encoding?.theta?.aggregate !== undefined || consumer.encoding.aggregate !== undefined) {
|
|
37
|
+
derive = deriveProportionalArcRadiusValues;
|
|
38
|
+
}
|
|
39
|
+
if (derive !== undefined) return { matched: true, values: isPendingMeasuredRadiusConsumer(consumer)
|
|
40
|
+
? [] : derive(dataset.values, consumer.layer).map(item => item.radius) };
|
|
34
41
|
}
|
|
35
42
|
|
|
36
43
|
if (consumer.layer.mark?.type === "rect") {
|
|
@@ -44,6 +51,10 @@ export function resolveMarkFamilyConsumerValues(program, consumer, dataset) {
|
|
|
44
51
|
};
|
|
45
52
|
}
|
|
46
53
|
const { layer, channel } = consumer;
|
|
54
|
+
if (resolveBarGrain(layer) === BAR_GRAINS.centered &&
|
|
55
|
+
channel === resolveBarChannels(layer).measure && layer.encoding[`${channel}2`] === undefined) {
|
|
56
|
+
return { matched: true, values: [0, ...dataset.values.map(row => row[consumer.encoding.field])] };
|
|
57
|
+
}
|
|
47
58
|
const encoding = layer.encoding ?? {};
|
|
48
59
|
const appearance = ["stroke", "strokeWidth", "opacity"].includes(channel);
|
|
49
60
|
if (
|
|
@@ -75,7 +86,7 @@ export function resolveMarkFamilyConsumerValues(program, consumer, dataset) {
|
|
|
75
86
|
: channel === "x" ? derived.xValues : derived.yValues
|
|
76
87
|
};
|
|
77
88
|
}
|
|
78
|
-
if (resolveBarGrain(consumer.layer) === BAR_GRAINS.aggregate) {
|
|
89
|
+
if (resolveBarGrain(consumer.layer) === BAR_GRAINS.aggregate && ["x", "y", "color"].includes(consumer.channel)) {
|
|
79
90
|
const derived = deriveBarAggregates(dataset.values, consumer.layer);
|
|
80
91
|
if (consumer.channel === "color") {
|
|
81
92
|
return {
|
|
@@ -38,7 +38,7 @@ const CREATE_SCALE_OPTIONS = Object.freeze([
|
|
|
38
38
|
"padding",
|
|
39
39
|
"align",
|
|
40
40
|
"palette",
|
|
41
|
-
"interpolate", "midpoint",
|
|
41
|
+
"interpolate", "whitePoint", "gamma", "midpoint",
|
|
42
42
|
"radialMapping", "unknown"
|
|
43
43
|
]);
|
|
44
44
|
|
|
@@ -87,14 +87,18 @@ export const createScale = /* @__PURE__ */ action(
|
|
|
87
87
|
validateOptions(args);
|
|
88
88
|
const id = validateUserId(args.id, "Scale id");
|
|
89
89
|
const type = validateScaleType(args.type ?? "linear");
|
|
90
|
-
const colorType = isColorScaleType(type)
|
|
90
|
+
const colorType = isColorScaleType(type) && (
|
|
91
|
+
!["log", "pow", "sqrt", "symlog"].includes(type) || args.palette !== undefined ||
|
|
92
|
+
args.interpolate !== undefined || (Array.isArray(args.range) && typeof args.range[0] === "string") ||
|
|
93
|
+
args.range?.palette !== undefined
|
|
94
|
+
);
|
|
91
95
|
if (args.palette !== undefined && args.range !== undefined) {
|
|
92
96
|
throw new Error("Color scale cannot specify both palette and range.");
|
|
93
97
|
}
|
|
94
98
|
if (args.palette !== undefined && !colorType) {
|
|
95
99
|
throw new Error(`Scale type "${type}" does not support palette.`);
|
|
96
100
|
}
|
|
97
|
-
if (args.interpolate !== undefined &&
|
|
101
|
+
if (args.interpolate !== undefined && !colorType) {
|
|
98
102
|
throw new Error(`Scale type "${type}" does not support interpolate.`);
|
|
99
103
|
}
|
|
100
104
|
const requestedRange = args.palette === undefined
|
|
@@ -129,6 +133,7 @@ export const createScale = /* @__PURE__ */ action(
|
|
|
129
133
|
})
|
|
130
134
|
: normalizeScaleDefinition({
|
|
131
135
|
type,
|
|
136
|
+
color: colorType,
|
|
132
137
|
patch: {
|
|
133
138
|
...args,
|
|
134
139
|
...(requestedRange === undefined ? {} : { range: requestedRange })
|
|
@@ -143,7 +148,7 @@ export const createScale = /* @__PURE__ */ action(
|
|
|
143
148
|
validateRange: (scaleType, value) =>
|
|
144
149
|
isDiscretizedColorScaleType(scaleType)
|
|
145
150
|
? validateDiscretizedColorRange(value)
|
|
146
|
-
: isContinuousColorScaleType(scaleType)
|
|
151
|
+
: colorType && isContinuousColorScaleType(scaleType)
|
|
147
152
|
? validateSequentialColorRange(value)
|
|
148
153
|
: scaleType === "ordinal"
|
|
149
154
|
? validateOrdinalRange(value)
|
|
@@ -166,7 +171,7 @@ export const createScale = /* @__PURE__ */ action(
|
|
|
166
171
|
|
|
167
172
|
for (const property of [
|
|
168
173
|
"emptyDomain", "nice", "zero", "clamp", "reverse", "base", "exponent", "constant",
|
|
169
|
-
"paddingInner", "paddingOuter", "padding", "align", "interpolate", "midpoint",
|
|
174
|
+
"paddingInner", "paddingOuter", "padding", "align", "interpolate", "whitePoint", "gamma", "midpoint",
|
|
170
175
|
"radialMapping", "unknown"
|
|
171
176
|
]) {
|
|
172
177
|
if (definition[property] === undefined) continue;
|