ggaction 0.0.13 → 0.0.16
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 +146 -0
- package/README.md +41 -12
- package/knowledge/action-cards.json +1 -1
- package/knowledge/intent-taxonomy.json +1 -1
- package/knowledge/mcp-resources.json +1 -1
- package/knowledge/task-packet.schema.json +1 -1
- package/knowledge/task-resolver.js +1 -1
- package/package.json +39 -7
- package/src/BasicChartProgram.js +3 -1
- package/src/ChartProgram.js +2 -0
- package/src/accessibility.js +157 -0
- package/src/actions/basic.js +2 -2
- package/src/actions/boxPlots/components.js +20 -12
- package/src/actions/boxPlots/create.js +4 -5
- package/src/actions/boxPlots/edit.js +34 -9
- package/src/actions/boxPlots/materialize.js +12 -4
- package/src/actions/boxPlots/options.js +48 -16
- package/src/actions/canvas/actions.js +3 -3
- package/src/actions/canvas/fitting.js +1 -1
- package/src/actions/categoryOrder/index.js +21 -12
- package/src/actions/charts/area.js +6 -2
- package/src/actions/charts/bar.js +3 -2
- package/src/actions/charts/beeswarm.js +1 -1
- package/src/actions/charts/density.js +7 -3
- package/src/actions/charts/ecdf.js +6 -3
- package/src/actions/charts/endpoints.js +29 -7
- package/src/actions/charts/heatmap.js +3 -2
- package/src/actions/charts/histogram.js +5 -4
- package/src/actions/charts/horizon.js +4 -2
- package/src/actions/charts/interval-regression.js +7 -4
- package/src/actions/charts/line.js +4 -2
- package/src/actions/charts/parallel.js +9 -3
- package/src/actions/charts/pie.js +11 -3
- package/src/actions/charts/polar.js +17 -6
- package/src/actions/charts/radar.js +9 -3
- package/src/actions/charts/radial.js +12 -4
- package/src/actions/charts/raincloud.js +2 -2
- package/src/actions/charts/rug-strip.js +8 -4
- package/src/actions/charts/scatter.js +4 -2
- package/src/actions/composition/actions.js +29 -24
- package/src/actions/coordinates/actions.js +2 -2
- package/src/actions/coordinates/edit.js +76 -0
- package/src/actions/data/basicBin2d.js +4 -5
- package/src/actions/data/bin.js +3 -3
- package/src/actions/data/bin2d.js +63 -193
- package/src/actions/data/bin2dMaterialize.js +4 -5
- package/src/actions/data/box.js +4 -4
- package/src/actions/data/complete.js +26 -0
- package/src/actions/data/computed.js +4 -4
- package/src/actions/data/create.js +7 -14
- package/src/actions/data/density.js +15 -23
- package/src/actions/data/derived.js +57 -31
- package/src/actions/data/ecdf.js +2 -2
- package/src/actions/data/edit.js +551 -0
- package/src/actions/data/filter.js +28 -22
- package/src/actions/data/fold.js +2 -2
- package/src/actions/data/gradientProfile.js +2 -2
- package/src/actions/data/horizon.js +2 -2
- package/src/actions/data/impute.js +26 -0
- package/src/actions/data/index.js +62 -47
- package/src/actions/data/interval.js +2 -2
- package/src/actions/data/intervalEdit.js +26 -18
- package/src/actions/data/normalize.js +27 -0
- package/src/actions/data/regression.js +21 -27
- package/src/actions/data/revise.js +48 -0
- package/src/actions/data/revisionBindings.js +125 -0
- package/src/actions/data/shared.js +20 -4
- package/src/actions/data/stack.js +2 -2
- package/src/actions/data/summary.js +3 -3
- package/src/actions/data/timeUnit.js +6 -3
- package/src/actions/data/window.js +3 -3
- package/src/actions/encodings/angle.js +1 -1
- package/src/actions/encodings/appearance.js +4 -4
- package/src/actions/encodings/barWidth.js +1 -1
- package/src/actions/encodings/channels.js +415 -0
- package/src/actions/encodings/color/index.js +3 -2
- package/src/actions/encodings/density.js +7 -4
- package/src/actions/encodings/histogram.js +4 -3
- package/src/actions/encodings/horizon.js +6 -8
- package/src/actions/encodings/index.js +4 -0
- package/src/actions/encodings/offset.js +22 -15
- package/src/actions/encodings/parallel.js +4 -5
- package/src/actions/encodings/pathOrder.js +6 -11
- package/src/actions/encodings/position/apply.js +53 -7
- package/src/actions/encodings/position/index.js +14 -6
- package/src/actions/encodings/position/policies/bar.js +9 -0
- package/src/actions/encodings/position/resolve.js +24 -7
- package/src/actions/encodings/ranged.js +5 -5
- package/src/actions/encodings/remove.js +13 -9
- package/src/actions/encodings/ruleAppearance.js +2 -24
- package/src/actions/encodings/seriesLayout.js +2 -1
- package/src/actions/encodings/shared.js +82 -14
- package/src/actions/encodings/stroke.js +226 -0
- package/src/actions/encodings/strokeDash.js +2 -2
- package/src/actions/encodings/text.js +1 -1
- package/src/actions/errorBands/create.js +34 -20
- package/src/actions/errorBands/edit.js +34 -137
- package/src/actions/errorBars/create.js +43 -20
- package/src/actions/errorBars/edit.js +29 -125
- package/src/actions/facets/actions.js +267 -73
- package/src/actions/facets/derive.js +99 -28
- package/src/actions/facets/guides.js +4 -5
- package/src/actions/facets/replay.js +4 -5
- package/src/actions/gradientPlots/components.js +15 -8
- package/src/actions/gradientPlots/create.js +4 -5
- package/src/actions/gradientPlots/edit.js +18 -9
- package/src/actions/gradientPlots/materialize.js +5 -3
- package/src/actions/gradientPlots/options.js +10 -2
- package/src/actions/gradientPlots/rebind.js +4 -5
- package/src/actions/guides/applicability.js +8 -2
- package/src/actions/guides/axes/axes.js +23 -4
- package/src/actions/guides/axes/axis.js +2 -2
- package/src/actions/guides/axes/edit.js +2 -2
- package/src/actions/guides/axes/labels.js +67 -21
- package/src/actions/guides/axes/lines.js +15 -9
- package/src/actions/guides/axes/parallel/lifecycle.js +13 -13
- package/src/actions/guides/axes/parallel/resolve.js +6 -2
- package/src/actions/guides/axes/parallel.js +5 -5
- package/src/actions/guides/axes/remove.js +4 -4
- package/src/actions/guides/axes/tickGroups.js +6 -5
- package/src/actions/guides/axes/ticks.js +19 -6
- package/src/actions/guides/axes/titles.js +21 -13
- package/src/actions/guides/grids/grid.js +18 -29
- package/src/actions/guides/grids/resolve.js +7 -2
- package/src/actions/guides/guides.js +20 -4
- package/src/actions/guides/legends/blocks.js +426 -0
- package/src/actions/guides/legends/categorical/actions.js +138 -52
- package/src/actions/guides/legends/categorical/components.js +55 -45
- package/src/actions/guides/legends/categorical/index.js +37 -33
- package/src/actions/guides/legends/categorical/layout.js +98 -16
- package/src/actions/guides/legends/categorical/options.js +2 -2
- package/src/actions/guides/legends/categorical/recipes.js +111 -20
- package/src/actions/guides/legends/categorical/resolve.js +12 -8
- package/src/actions/guides/legends/categorical/symbols.js +104 -52
- package/src/actions/guides/legends/continuous/common.js +76 -20
- package/src/actions/guides/legends/continuous/gradient.js +95 -158
- package/src/actions/guides/legends/continuous/index.js +2 -0
- package/src/actions/guides/legends/continuous/interval.js +21 -16
- package/src/actions/guides/legends/continuous/opacity.js +37 -83
- package/src/actions/guides/legends/continuous/stroke.js +225 -0
- package/src/actions/guides/legends/creation.js +14 -1
- package/src/actions/guides/legends/edit.js +159 -60
- package/src/actions/guides/legends/focused.js +5 -5
- package/src/actions/guides/legends/index.js +2 -0
- package/src/actions/guides/legends/lane.js +59 -24
- package/src/actions/guides/legends/lifecycle.js +19 -8
- package/src/actions/guides/legends/remove.js +17 -8
- package/src/actions/guides/legends/sampling.js +140 -0
- package/src/actions/guides/legends/size.js +100 -108
- package/src/actions/guides/legends/strokeWidth.js +38 -92
- package/src/actions/guides/legends/target.js +112 -1
- package/src/actions/guides/legends/transition.js +165 -16
- package/src/actions/guides/polar/axes/facade.js +8 -8
- package/src/actions/guides/polar/axes/labels.js +29 -10
- package/src/actions/guides/polar/axes/lines.js +19 -19
- package/src/actions/guides/polar/axes/shared.js +16 -0
- package/src/actions/guides/polar/axes/ticks.js +10 -16
- package/src/actions/guides/polar/axes/titles.js +6 -6
- package/src/actions/guides/polar/grids.js +12 -14
- package/src/actions/guides/polar/resolve.js +12 -8
- package/src/actions/index.js +16 -3
- package/src/actions/intervals/revision.js +126 -0
- package/src/actions/marks/arc/actions.js +80 -22
- package/src/actions/marks/area/actions.js +38 -13
- package/src/actions/marks/area/materialize.js +17 -1
- package/src/actions/marks/bar/create.js +12 -3
- package/src/actions/marks/bar/edit.js +21 -4
- package/src/actions/marks/bar/materialize.js +34 -40
- package/src/actions/marks/index.js +10 -1
- package/src/actions/marks/line/actions.js +40 -17
- package/src/actions/marks/line/materialize.js +29 -10
- package/src/actions/marks/point/create.js +9 -3
- package/src/actions/marks/point/edit.js +19 -7
- package/src/actions/marks/point/jitter.js +6 -8
- package/src/actions/marks/point/materialize.js +48 -22
- package/src/actions/marks/point/packing.js +6 -8
- package/src/actions/marks/rect/actions.js +36 -15
- package/src/actions/marks/references.js +602 -33
- package/src/actions/marks/remove.js +67 -41
- package/src/actions/marks/rule/actions.js +51 -14
- package/src/actions/marks/shared.js +1 -1
- package/src/actions/marks/text/actions.js +79 -14
- package/src/actions/marks/text/index.js +4 -0
- package/src/actions/marks/text/layout.js +157 -17
- package/src/actions/marks/text/removal.js +173 -0
- package/src/actions/marks/text/selection.js +72 -0
- package/src/actions/marks/tick/actions.js +25 -8
- package/src/actions/primitives/createGraphics.js +5 -4
- package/src/actions/primitives/editGraphics.js +31 -4
- package/src/actions/primitives/graphicProperties.js +6 -0
- package/src/actions/primitives/semantic.js +11 -3
- package/src/actions/primitives/semanticAction.js +90 -14
- package/src/actions/primitives/semanticValidation/dataset.js +2 -4
- package/src/actions/primitives/semanticValidation/guide.js +1 -1
- package/src/actions/primitives/semanticValidation/index.js +9 -1
- package/src/actions/primitives/semanticValidation/layer.js +5 -0
- package/src/actions/primitives/semanticValidation/scale.js +28 -7
- package/src/actions/regression/components.js +36 -25
- package/src/actions/regression/create.js +13 -6
- package/src/actions/regression/edit.js +24 -11
- package/src/actions/resources/index.js +7 -0
- package/src/actions/resources/remove.js +173 -0
- package/src/actions/scales/channels.js +212 -0
- package/src/actions/scales/consumers/common.js +1 -1
- package/src/actions/scales/consumers/families.js +3 -2
- package/src/actions/scales/consumers/index.js +19 -2
- package/src/actions/scales/consumers/seriesLayout.js +28 -14
- package/src/actions/scales/create.js +55 -23
- package/src/actions/scales/definitions.js +94 -40
- package/src/actions/scales/edit.js +67 -12
- package/src/actions/scales/editPolicy.js +81 -11
- package/src/actions/scales/index.js +6 -0
- package/src/actions/scales/materialize.js +5 -4
- package/src/actions/scales/offset.js +48 -0
- package/src/actions/scales/parallel.js +71 -0
- package/src/actions/scales/preview.js +111 -10
- package/src/actions/scales/quantitativeColor.js +4 -5
- package/src/actions/selection/actions.js +179 -66
- package/src/actions/selection/index.js +3 -0
- package/src/actions/textMetrics/index.js +56 -0
- package/src/actions/theme/actions.js +70 -15
- package/src/actions/theme/composition.js +238 -0
- package/src/actions/theme/index.js +9 -2
- package/src/actions/theme/reconcile.js +313 -121
- package/src/actions/theme/state.js +160 -0
- package/src/actions/titles/actions.js +8 -8
- package/src/actions/titles/resolve.js +3 -3
- package/src/actions/violinPlots/create.js +6 -3
- package/src/actions/violinPlots/edit.js +2 -1
- package/src/core/action.js +117 -35
- package/src/core/compositionState.js +6 -1
- package/src/core/diagnostics.js +16 -0
- package/src/core/extensionRegistry.js +27 -0
- package/src/core/font.js +18 -0
- package/src/core/identifiers.js +5 -4
- package/src/core/immutable.js +10 -3
- package/src/core/optionRequirements.js +8 -0
- package/src/core/programState.js +21 -1
- package/src/core/resourceReferences.js +341 -0
- package/src/core/textMetricProfile.js +28 -0
- package/src/core/textMetrics.js +24 -3
- package/src/core/validation.js +22 -10
- package/src/core/vocabulary.js +16 -3
- package/src/diagnostics.js +1 -0
- package/src/grammar/bars/aggregate.js +4 -1
- package/src/grammar/bars/geometry.js +44 -0
- package/src/grammar/bin.js +54 -9
- package/src/grammar/complete.js +279 -0
- package/src/grammar/computed.js +265 -64
- package/src/grammar/coordinates.js +37 -0
- package/src/grammar/density.js +239 -37
- package/src/grammar/displayLabels.js +57 -0
- package/src/grammar/facets/dependencies.js +133 -1
- package/src/grammar/facets/guides.js +3 -0
- package/src/grammar/facets/index.js +7 -47
- package/src/grammar/facets/scales.js +15 -18
- package/src/grammar/impute.js +341 -0
- package/src/grammar/markLabelSelection.js +57 -0
- package/src/grammar/markLabels.js +18 -1
- package/src/grammar/normalize.js +366 -0
- package/src/grammar/pathSeries.js +26 -8
- package/src/grammar/pointShapes.js +13 -4
- package/src/grammar/polar.js +90 -8
- package/src/grammar/regression/index.js +1 -0
- package/src/grammar/regression/parameters.js +23 -0
- package/src/grammar/roundedRect.js +180 -0
- package/src/grammar/rules.js +1 -0
- package/src/grammar/scales/colorConsumers.js +20 -1
- package/src/grammar/scales/continuous.js +2 -2
- package/src/grammar/scales/definition.js +27 -4
- package/src/grammar/scales/index.js +1 -0
- package/src/grammar/scales/ordinal.js +12 -3
- package/src/grammar/scales/policies.js +2 -2
- package/src/grammar/scales/size.js +456 -0
- package/src/grammar/scales/types.js +8 -0
- package/src/grammar/schemas/concreteGraphic.js +14 -2
- package/src/grammar/schemas/graphic.js +15 -0
- package/src/grammar/schemas/graphicBounds.js +190 -41
- package/src/grammar/schemas/semanticPath.js +7 -2
- package/src/grammar/statisticalReference.js +103 -0
- package/src/grammar/strokeStyle.js +77 -0
- package/src/grammar/summary.js +78 -16
- package/src/grammar/timeUnit.js +216 -5
- package/src/grammar/transformTopology.js +4 -0
- package/src/grammar/transforms.js +394 -28
- package/src/grammar/weightedStatistics.js +374 -0
- package/src/grammar/window.js +221 -16
- package/src/layout/aspect.js +80 -0
- package/src/layout/facets.js +59 -5
- package/src/layout/labels.js +351 -7
- package/src/layout/legend.js +4 -4
- package/src/layout/legendItems.js +6 -6
- package/src/layout/text.js +10 -9
- package/src/layout/title.js +9 -9
- package/src/materialization/bars/aggregate.js +25 -2
- package/src/materialization/bars/histogram.js +84 -24
- package/src/materialization/bars/ranged.js +19 -3
- package/src/materialization/bars/resolve.js +7 -4
- package/src/materialization/composition.js +3 -1
- package/src/materialization/coordinateBounds.js +204 -0
- package/src/materialization/dependencies.js +36 -0
- package/src/materialization/facetGuides/legacyCategorical.js +3 -0
- package/src/materialization/facetGuides/placement.js +9 -3
- package/src/materialization/facetGuides/preparation.js +13 -8
- package/src/materialization/facetHeaders.js +347 -0
- package/src/materialization/facets.js +15 -65
- package/src/materialization/guides/layout.js +3 -3
- package/src/materialization/guides/resources.js +26 -0
- package/src/materialization/legends.js +1 -1
- package/src/materialization/marks/index.js +11 -5
- package/src/materialization/marks/policies.js +2 -1
- package/src/materialization/rect.js +25 -12
- package/src/materialization/rectConfig.js +5 -1
- package/src/materialization/scaleGuideDependencies.js +10 -0
- package/src/materialization/scales/map.js +5 -3
- package/src/materialization/scales/policies/binnedPosition.js +2 -2
- package/src/materialization/scales/policies/offset.js +50 -8
- package/src/materialization/scales/policies/series.js +4 -1
- package/src/materialization/scales/resolve.js +22 -6
- package/src/materialization/selection/items/arc.js +28 -4
- package/src/materialization/selection/items/bar.js +30 -3
- package/src/materialization/selection/items/common.js +20 -5
- package/src/materialization/selection/items/rect.js +28 -4
- package/src/materialization/selection/path.js +1 -1
- package/src/materialization/selection/point.js +3 -1
- package/src/materialization/text.js +265 -14
- package/src/materialization/typography.js +31 -0
- package/src/mcp/cli.js +5 -3
- package/src/persistence/codec.js +65 -0
- package/src/persistence/validation.js +185 -0
- package/src/persistence.js +74 -0
- package/src/renderers/canvas/circle.js +2 -3
- package/src/renderers/canvas/index.js +5 -4
- package/src/renderers/canvas/line.js +2 -3
- package/src/renderers/canvas/native.js +4 -4
- package/src/renderers/canvas/path.js +2 -3
- package/src/renderers/canvas/rect.js +2 -3
- package/src/renderers/canvas/stroke.js +11 -0
- package/src/renderers/nativeBackend.js +10 -0
- package/src/renderers/options.js +13 -0
- package/src/renderers/pdf.js +25 -54
- package/src/renderers/png.js +22 -17
- package/src/renderers/svg.js +29 -14
- package/src/renderers/text.js +1 -11
- package/src/selectors/coordinates.js +3 -1
- package/src/selectors/datasets.js +38 -5
- package/src/selectors/layers.js +10 -4
- package/src/selectors/markOwners.js +46 -0
- package/src/selectors/scales.js +6 -4
- package/src/theme/defaults.js +103 -1
- package/src/version.js +2 -0
- package/types/accessibility.d.ts +37 -0
- package/types/basic.d.ts +10 -2
- package/types/diagnostics.d.ts +15 -0
- package/types/index.d.ts +90 -0
- package/types/pdf.d.ts +9 -0
- package/types/persistence.d.ts +7 -0
- package/types/png.d.ts +9 -0
- package/types/program.d.ts +944 -118
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { closedAction } from "../../core/action.js";
|
|
2
2
|
import { isPlainObject } from "../../core/immutable.js";
|
|
3
3
|
import { validateUserId } from "../../core/identifiers.js";
|
|
4
4
|
import {
|
|
@@ -9,6 +9,10 @@ import {
|
|
|
9
9
|
} from "../../core/validation.js";
|
|
10
10
|
import { normalizeRegressionParameters } from "../../grammar/regression/index.js";
|
|
11
11
|
import { validateCurveInterpolation } from "../../grammar/curveCommands.js";
|
|
12
|
+
import {
|
|
13
|
+
requestedStrokeDetails,
|
|
14
|
+
STROKE_STYLE_PROPERTIES
|
|
15
|
+
} from "../../grammar/strokeStyle.js";
|
|
12
16
|
import { planDerivedDataRevision } from
|
|
13
17
|
"../../materialization/dataProvenance.js";
|
|
14
18
|
import { findLayer } from "../../selectors/layers.js";
|
|
@@ -21,9 +25,12 @@ const OPTIONS = Object.freeze([
|
|
|
21
25
|
"confidenceMethod", "level", "confidence", "interval", "band", "line"
|
|
22
26
|
]);
|
|
23
27
|
const BAND_OPTIONS = Object.freeze([
|
|
24
|
-
"color", "opacity", "stroke", "strokeWidth", "curve"
|
|
28
|
+
"color", "opacity", "stroke", "strokeWidth", "curve",
|
|
29
|
+
...STROKE_STYLE_PROPERTIES
|
|
30
|
+
]);
|
|
31
|
+
const LINE_OPTIONS = Object.freeze([
|
|
32
|
+
"strokeWidth", "curve", ...STROKE_STYLE_PROPERTIES
|
|
25
33
|
]);
|
|
26
|
-
const LINE_OPTIONS = Object.freeze(["strokeWidth", "curve"]);
|
|
27
34
|
|
|
28
35
|
function resolveRegressionOwner(program, requested) {
|
|
29
36
|
const eligible = program.semanticSpec.layers.filter(
|
|
@@ -80,6 +87,7 @@ function validateBandPatch(value) {
|
|
|
80
87
|
if (Object.hasOwn(patch, "curve")) {
|
|
81
88
|
validateCurveInterpolation(patch.curve);
|
|
82
89
|
}
|
|
90
|
+
requestedStrokeDetails(patch, "editRegression band");
|
|
83
91
|
return patch;
|
|
84
92
|
}
|
|
85
93
|
|
|
@@ -94,6 +102,7 @@ function validateLinePatch(value) {
|
|
|
94
102
|
if (Object.hasOwn(patch, "curve")) {
|
|
95
103
|
validateCurveInterpolation(patch.curve);
|
|
96
104
|
}
|
|
105
|
+
requestedStrokeDetails(patch, "editRegression line");
|
|
97
106
|
return patch;
|
|
98
107
|
}
|
|
99
108
|
|
|
@@ -225,13 +234,12 @@ function updateRegressionBandSemantics(program, bandId, { x, groupBy }) {
|
|
|
225
234
|
});
|
|
226
235
|
}
|
|
227
236
|
|
|
228
|
-
export const editRegression =
|
|
237
|
+
export const editRegression = /* @__PURE__ */ closedAction(
|
|
229
238
|
{
|
|
230
239
|
op: "editRegression",
|
|
231
240
|
description: "Revise a regression model and its owned visible components."
|
|
232
|
-
},
|
|
241
|
+
}, OPTIONS,
|
|
233
242
|
function (args = {}) {
|
|
234
|
-
validateKeys(args, OPTIONS, "editRegression");
|
|
235
243
|
if (!OPTIONS.slice(1).some(key => Object.hasOwn(args, key))) {
|
|
236
244
|
throw new Error("editRegression requires a statistical or component option.");
|
|
237
245
|
}
|
|
@@ -325,7 +333,11 @@ export const editRegression = action(
|
|
|
325
333
|
const bandConfig = next.markConfigs[bandId];
|
|
326
334
|
next = next._withMarkConfig(bandId, {
|
|
327
335
|
...bandConfig,
|
|
328
|
-
errorBand: {
|
|
336
|
+
errorBand: {
|
|
337
|
+
...bandConfig.errorBand,
|
|
338
|
+
source: dataId,
|
|
339
|
+
data: dataId
|
|
340
|
+
}
|
|
329
341
|
});
|
|
330
342
|
}
|
|
331
343
|
|
|
@@ -356,10 +368,7 @@ export const editRegression = action(
|
|
|
356
368
|
next = next.rematerializeLineMark({ id: current.lineId });
|
|
357
369
|
}
|
|
358
370
|
|
|
359
|
-
|
|
360
|
-
next = next.releaseDerivedData(revision.release);
|
|
361
|
-
}
|
|
362
|
-
return next._withMarkConfig(owner.id, {
|
|
371
|
+
next = next._withMarkConfig(owner.id, {
|
|
363
372
|
...next.markConfigs[owner.id],
|
|
364
373
|
regression: {
|
|
365
374
|
...current,
|
|
@@ -373,6 +382,10 @@ export const editRegression = action(
|
|
|
373
382
|
parameters
|
|
374
383
|
}
|
|
375
384
|
});
|
|
385
|
+
if (changesStatistics) {
|
|
386
|
+
next = next.releaseDerivedData(revision.release);
|
|
387
|
+
}
|
|
388
|
+
return next;
|
|
376
389
|
};
|
|
377
390
|
return applyEdit(this);
|
|
378
391
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { removeCoordinate, removeData, removeScale } from "./remove.js";
|
|
2
|
+
|
|
3
|
+
export function registerResourceActions(ProgramClass) {
|
|
4
|
+
ProgramClass.prototype.removeData = removeData;
|
|
5
|
+
ProgramClass.prototype.removeScale = removeScale;
|
|
6
|
+
ProgramClass.prototype.removeCoordinate = removeCoordinate;
|
|
7
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { annotateError } from "../../core/diagnostics.js";
|
|
2
|
+
import { action } from "../../core/action.js";
|
|
3
|
+
import { validateUserId } from "../../core/identifiers.js";
|
|
4
|
+
import { validateOptionObject } from "../../core/validation.js";
|
|
5
|
+
import {
|
|
6
|
+
canonicalResourcePath,
|
|
7
|
+
collectResourceReferences
|
|
8
|
+
} from "../../core/resourceReferences.js";
|
|
9
|
+
import { releaseNamedResourceOwnership } from "../../core/programState.js";
|
|
10
|
+
import { findCoordinate } from "../../selectors/coordinates.js";
|
|
11
|
+
import { findDataset } from "../../selectors/datasets.js";
|
|
12
|
+
import { findLayer } from "../../selectors/layers.js";
|
|
13
|
+
import { findSemanticScale } from "../../selectors/scales.js";
|
|
14
|
+
|
|
15
|
+
const OPTIONS = Object.freeze(["id"]);
|
|
16
|
+
|
|
17
|
+
function dataOwners(program) {
|
|
18
|
+
return Object.entries(program.materializationConfigs.data ?? {}).flatMap(
|
|
19
|
+
([family, owners]) => Object.entries(owners ?? {}).flatMap(([owner, config]) =>
|
|
20
|
+
typeof config?.current === "string"
|
|
21
|
+
? [{ family, owner, current: config.current }]
|
|
22
|
+
: []
|
|
23
|
+
)
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function ownedGeneratedDatasetIds(program) {
|
|
28
|
+
const result = new Set();
|
|
29
|
+
for (const [id, config] of Object.entries(program.markConfigs)) {
|
|
30
|
+
const layer = findLayer(program, id);
|
|
31
|
+
for (const value of [
|
|
32
|
+
config.errorBar?.data,
|
|
33
|
+
config.errorBand?.data,
|
|
34
|
+
config.regression?.dataId,
|
|
35
|
+
config.boxPlot?.summaryId,
|
|
36
|
+
config.boxPlot?.outlierDataId,
|
|
37
|
+
config.gradientPlot?.profileId,
|
|
38
|
+
config.violinPlot?.materialized === true ? layer?.data : undefined,
|
|
39
|
+
config.ecdfPlot?.data,
|
|
40
|
+
config.endpointPlot?.data,
|
|
41
|
+
config.statisticalReference?.dataId
|
|
42
|
+
]) {
|
|
43
|
+
if (typeof value === "string") result.add(value);
|
|
44
|
+
}
|
|
45
|
+
const dataset = findDataset(program, layer?.data);
|
|
46
|
+
if (dataset?.transform?.[0]?.type === "markFilter") result.add(dataset.id);
|
|
47
|
+
}
|
|
48
|
+
return Object.freeze([...result].sort());
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function exactResource(program, kind, id) {
|
|
52
|
+
if (kind === "data") return findDataset(program, id);
|
|
53
|
+
if (kind === "scale") return findSemanticScale(program, id);
|
|
54
|
+
return findCoordinate(program, id);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function existingKind(program, id) {
|
|
58
|
+
if (findDataset(program, id) !== undefined ||
|
|
59
|
+
dataOwners(program).some(owner => owner.owner === id)) return "data";
|
|
60
|
+
if (findSemanticScale(program, id) !== undefined) return "scale";
|
|
61
|
+
if (findCoordinate(program, id) !== undefined) {
|
|
62
|
+
return "coordinate";
|
|
63
|
+
}
|
|
64
|
+
if (findLayer(program, id) !== undefined) return "mark";
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function resolveResourceOwnership(program, { kind, id }) {
|
|
69
|
+
const resource = exactResource(program, kind, id);
|
|
70
|
+
if (kind !== "data") {
|
|
71
|
+
if (resource !== undefined) return { ownership: "named", resource };
|
|
72
|
+
const other = existingKind(program, id);
|
|
73
|
+
if (other !== undefined) {
|
|
74
|
+
throw annotateError(new Error(`Resource "${id}" exists as ${other}, not ${kind}.`), { code: "incompatible-resource", resourceId: id });
|
|
75
|
+
}
|
|
76
|
+
throw annotateError(new Error(`Unknown ${kind} "${id}".`), { code: "missing-resource", resourceId: id });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const owners = dataOwners(program).filter(owner => owner.owner === id);
|
|
80
|
+
if (owners.length > 1) {
|
|
81
|
+
throw annotateError(new Error(`Data resource "${id}" has ambiguous logical ownership.`), { code: "ambiguous-resource", resourceId: id });
|
|
82
|
+
}
|
|
83
|
+
if (owners.length === 1) {
|
|
84
|
+
const owner = owners[0];
|
|
85
|
+
const current = exactResource(program, "data", owner.current);
|
|
86
|
+
if (current === undefined) {
|
|
87
|
+
throw annotateError(new Error(`Data owner "${id}" has no current dataset.`), { code: "missing-resource", resourceId: id });
|
|
88
|
+
}
|
|
89
|
+
return { ownership: "standalone", resource: current, dataOwner: owner };
|
|
90
|
+
}
|
|
91
|
+
if (resource !== undefined) {
|
|
92
|
+
if (ownedGeneratedDatasetIds(program).includes(id) ||
|
|
93
|
+
dataOwners(program).some(owner => owner.current === id)) {
|
|
94
|
+
throw new Error(
|
|
95
|
+
`Dataset "${id}" is chart-owned; use its owning resource action.`
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
return { ownership: "named", resource };
|
|
99
|
+
}
|
|
100
|
+
const other = existingKind(program, id);
|
|
101
|
+
if (other !== undefined) {
|
|
102
|
+
throw annotateError(new Error(`Resource "${id}" exists as ${other}, not data.`), { code: "incompatible-resource", resourceId: id });
|
|
103
|
+
}
|
|
104
|
+
throw annotateError(new Error(`Unknown data "${id}".`), { code: "missing-resource", resourceId: id });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function assertSupportedProgram(program, operation) {
|
|
108
|
+
if (program.compositionSpec !== undefined && program.compositionSpec.type !== "facet") {
|
|
109
|
+
throw new Error(`${operation} supports unit and facet composition programs.`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function formatReference(value) {
|
|
114
|
+
return `${value.ownerKind} "${value.ownerId}" at ` +
|
|
115
|
+
canonicalResourcePath(value.path);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function planResourceRemoval(program, { kind, id, operation }) {
|
|
119
|
+
assertSupportedProgram(program, operation);
|
|
120
|
+
const resolved = resolveResourceOwnership(program, { kind, id });
|
|
121
|
+
const actualId = resolved.resource.id;
|
|
122
|
+
const references = collectResourceReferences(program, { kind, id: actualId });
|
|
123
|
+
const live = references.filter(value => value.strength === "live" && !(
|
|
124
|
+
resolved.ownership === "standalone" &&
|
|
125
|
+
value.ownerKind === "dataOwner" &&
|
|
126
|
+
value.ownerId === resolved.dataOwner.owner &&
|
|
127
|
+
value.path.at(-1) === "current"
|
|
128
|
+
));
|
|
129
|
+
if (live.length > 0) {
|
|
130
|
+
throw annotateError(new Error(
|
|
131
|
+
`Cannot remove ${kind} "${id}"; live references: ` +
|
|
132
|
+
live.map(formatReference).join("; ")
|
|
133
|
+
), { code: "resource-in-use", resourceId: id, candidates: [...new Set(live.map(value => value.ownerId))] });
|
|
134
|
+
}
|
|
135
|
+
return Object.freeze({
|
|
136
|
+
kind,
|
|
137
|
+
id,
|
|
138
|
+
semanticIds: Object.freeze([actualId]),
|
|
139
|
+
...(resolved.dataOwner === undefined
|
|
140
|
+
? {}
|
|
141
|
+
: { dataOwner: Object.freeze({ ...resolved.dataOwner }) })
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function removeResource(kind, operation, description) {
|
|
146
|
+
return action(
|
|
147
|
+
{ op: operation, description, scope: "any" },
|
|
148
|
+
function (args = {}) {
|
|
149
|
+
validateOptionObject(args, OPTIONS, operation);
|
|
150
|
+
const id = validateUserId(args.id, `${operation} id`);
|
|
151
|
+
const plan = planResourceRemoval(this, { kind, id, operation });
|
|
152
|
+
// Release a validated logical owner before removing its physical revision.
|
|
153
|
+
// The primitive then checks the remaining consumers without mistaking the
|
|
154
|
+
// owner's own replay entry for an independently chart-owned resource.
|
|
155
|
+
let next = releaseNamedResourceOwnership(this, plan);
|
|
156
|
+
const selector = kind === "data" ? "dataset" : kind;
|
|
157
|
+
for (const semanticId of plan.semanticIds) {
|
|
158
|
+
next = next.editSemantic({ property: `${selector}[${semanticId}]`, remove: true });
|
|
159
|
+
}
|
|
160
|
+
return next;
|
|
161
|
+
}
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export const removeData = /* @__PURE__ */ removeResource(
|
|
166
|
+
"data", "removeData", "Remove one unreferenced named dataset."
|
|
167
|
+
);
|
|
168
|
+
export const removeScale = /* @__PURE__ */ removeResource(
|
|
169
|
+
"scale", "removeScale", "Remove one unreferenced named scale."
|
|
170
|
+
);
|
|
171
|
+
export const removeCoordinate = /* @__PURE__ */ removeResource(
|
|
172
|
+
"coordinate", "removeCoordinate", "Remove one unreferenced named coordinate."
|
|
173
|
+
);
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { action } from "../../core/action.js";
|
|
2
|
+
import { validateUserId } from "../../core/identifiers.js";
|
|
3
|
+
import { validateOptionObject } from "../../core/validation.js";
|
|
4
|
+
import { normalizePositionScaleChannel } from "../../core/vocabulary.js";
|
|
5
|
+
import { findLayer } from "../../selectors/layers.js";
|
|
6
|
+
import { requireSemanticScale } from "../../selectors/scales.js";
|
|
7
|
+
import { findScaleConsumers } from "./consumers/index.js";
|
|
8
|
+
|
|
9
|
+
const DEFINITIONS = Object.freeze({
|
|
10
|
+
editXScale: Object.freeze({
|
|
11
|
+
channel: "x",
|
|
12
|
+
options: Object.freeze([
|
|
13
|
+
"type", "domain", "range", "nice", "zero", "clamp", "reverse",
|
|
14
|
+
"base", "exponent", "constant", "paddingInner", "paddingOuter",
|
|
15
|
+
"padding", "align", "unknown"
|
|
16
|
+
])
|
|
17
|
+
}),
|
|
18
|
+
editYScale: Object.freeze({
|
|
19
|
+
channel: "y",
|
|
20
|
+
options: Object.freeze([
|
|
21
|
+
"type", "domain", "range", "nice", "zero", "clamp", "reverse",
|
|
22
|
+
"base", "exponent", "constant", "paddingInner", "paddingOuter",
|
|
23
|
+
"padding", "align", "unknown"
|
|
24
|
+
])
|
|
25
|
+
}),
|
|
26
|
+
editThetaScale: Object.freeze({
|
|
27
|
+
channel: "theta",
|
|
28
|
+
options: Object.freeze([
|
|
29
|
+
"type", "domain", "range", "nice", "zero", "clamp", "reverse",
|
|
30
|
+
"paddingInner", "paddingOuter", "padding", "align"
|
|
31
|
+
])
|
|
32
|
+
}),
|
|
33
|
+
editRScale: Object.freeze({
|
|
34
|
+
channel: "radius",
|
|
35
|
+
options: Object.freeze([
|
|
36
|
+
"type", "domain", "range", "nice", "zero", "clamp", "reverse",
|
|
37
|
+
"base", "exponent", "constant", "radialMapping"
|
|
38
|
+
])
|
|
39
|
+
}),
|
|
40
|
+
editColorScale: Object.freeze({
|
|
41
|
+
channel: "color",
|
|
42
|
+
options: Object.freeze([
|
|
43
|
+
"type", "domain", "range", "clamp", "reverse", "palette",
|
|
44
|
+
"interpolate", "midpoint", "unknown"
|
|
45
|
+
])
|
|
46
|
+
}),
|
|
47
|
+
editStrokeScale: Object.freeze({
|
|
48
|
+
channel: "stroke",
|
|
49
|
+
targetOnly: true,
|
|
50
|
+
options: Object.freeze([
|
|
51
|
+
"type", "domain", "range", "clamp", "reverse", "palette",
|
|
52
|
+
"interpolate", "midpoint", "unknown"
|
|
53
|
+
])
|
|
54
|
+
}),
|
|
55
|
+
editSizeScale: Object.freeze({
|
|
56
|
+
channel: "size",
|
|
57
|
+
options: Object.freeze([
|
|
58
|
+
"type", "domain", "range", "unknown", "clamp", "reverse", "base",
|
|
59
|
+
"exponent"
|
|
60
|
+
])
|
|
61
|
+
}),
|
|
62
|
+
editOpacityScale: Object.freeze({
|
|
63
|
+
channel: "opacity",
|
|
64
|
+
options: Object.freeze([
|
|
65
|
+
"type", "domain", "range", "nice", "zero", "clamp", "reverse", "unknown"
|
|
66
|
+
])
|
|
67
|
+
}),
|
|
68
|
+
editShapeScale: Object.freeze({
|
|
69
|
+
channel: "shape",
|
|
70
|
+
options: Object.freeze(["type", "domain", "range", "unknown"])
|
|
71
|
+
}),
|
|
72
|
+
editStrokeWidthScale: Object.freeze({
|
|
73
|
+
channel: "strokeWidth",
|
|
74
|
+
options: Object.freeze([
|
|
75
|
+
"type", "domain", "range", "nice", "zero", "clamp", "reverse",
|
|
76
|
+
"base", "exponent", "constant"
|
|
77
|
+
])
|
|
78
|
+
}),
|
|
79
|
+
editStrokeDashScale: Object.freeze({
|
|
80
|
+
channel: "strokeDash",
|
|
81
|
+
options: Object.freeze(["type", "domain", "range"])
|
|
82
|
+
})
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
function scaleIdsForLayer(program, layer, channel) {
|
|
86
|
+
if (layer === undefined) return [];
|
|
87
|
+
return [...new Set(allConsumers(program)
|
|
88
|
+
.filter(consumer =>
|
|
89
|
+
consumer.layer.id === layer.id &&
|
|
90
|
+
normalizePositionScaleChannel(consumer.channel) === channel
|
|
91
|
+
)
|
|
92
|
+
.map(consumer => consumer.encoding.scale))];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function allConsumers(program) {
|
|
96
|
+
return program.semanticSpec.scales.flatMap(scale =>
|
|
97
|
+
findScaleConsumers(program, scale.id)
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function channelScaleIds(program, channel) {
|
|
102
|
+
return [...new Set(allConsumers(program)
|
|
103
|
+
.filter(consumer => normalizePositionScaleChannel(consumer.channel) === channel)
|
|
104
|
+
.map(consumer => consumer.encoding.scale))];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function requireSingleScale(ids, operation, scope) {
|
|
108
|
+
if (ids.length === 1) return ids[0];
|
|
109
|
+
if (ids.length === 0) {
|
|
110
|
+
throw new Error(`${operation} could not find a ${scope} scale.`);
|
|
111
|
+
}
|
|
112
|
+
throw new Error(`${operation} ${scope} scale is ambiguous; provide id or target.`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function validateScaleChannel(program, id, channel, operation) {
|
|
116
|
+
requireSemanticScale(program, id);
|
|
117
|
+
const consumers = findScaleConsumers(program, id);
|
|
118
|
+
if (consumers.length === 0) {
|
|
119
|
+
throw new Error(`${operation} requires a scale bound to the ${channel} channel.`);
|
|
120
|
+
}
|
|
121
|
+
const compatible = ["color", "stroke"].includes(channel)
|
|
122
|
+
? new Set(["color", "stroke"])
|
|
123
|
+
: new Set([channel]);
|
|
124
|
+
if (!consumers.some(consumer =>
|
|
125
|
+
normalizePositionScaleChannel(consumer.channel) === channel
|
|
126
|
+
) || consumers.some(consumer =>
|
|
127
|
+
!compatible.has(normalizePositionScaleChannel(consumer.channel))
|
|
128
|
+
)) {
|
|
129
|
+
throw new Error(
|
|
130
|
+
`Scale "${id}" must include a ${channel} channel consumer and no incompatible channel consumers.`
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
return id;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function resolveScaleId(program, args, channel, operation) {
|
|
137
|
+
const explicitId = args.id === undefined
|
|
138
|
+
? undefined
|
|
139
|
+
: validateUserId(args.id, "Scale id");
|
|
140
|
+
const target = args.target === undefined
|
|
141
|
+
? undefined
|
|
142
|
+
: validateUserId(args.target, "Mark id");
|
|
143
|
+
const targetLayer = target === undefined ? undefined : findLayer(program, target);
|
|
144
|
+
if (target !== undefined && targetLayer === undefined) {
|
|
145
|
+
throw new Error(`Unknown mark target "${target}".`);
|
|
146
|
+
}
|
|
147
|
+
const targetId = targetLayer === undefined
|
|
148
|
+
? undefined
|
|
149
|
+
: requireSingleScale(scaleIdsForLayer(program, targetLayer, channel), operation, `target ${channel}`);
|
|
150
|
+
if (explicitId !== undefined && targetId !== undefined && explicitId !== targetId) {
|
|
151
|
+
throw new Error(`${operation} id "${explicitId}" does not match target "${target}" scale "${targetId}".`);
|
|
152
|
+
}
|
|
153
|
+
if (explicitId !== undefined) {
|
|
154
|
+
return validateScaleChannel(program, explicitId, channel, operation);
|
|
155
|
+
}
|
|
156
|
+
if (targetId !== undefined) return validateScaleChannel(program, targetId, channel, operation);
|
|
157
|
+
|
|
158
|
+
const current = findLayer(program, program.context.currentMark);
|
|
159
|
+
const currentIds = scaleIdsForLayer(program, current, channel);
|
|
160
|
+
if (currentIds.length > 1) {
|
|
161
|
+
throw new Error(`${operation} current-mark ${channel} scale is ambiguous; provide id or target.`);
|
|
162
|
+
}
|
|
163
|
+
if (currentIds.length === 1) {
|
|
164
|
+
return validateScaleChannel(program, currentIds[0], channel, operation);
|
|
165
|
+
}
|
|
166
|
+
return validateScaleChannel(
|
|
167
|
+
program,
|
|
168
|
+
requireSingleScale(channelScaleIds(program, channel), operation, channel),
|
|
169
|
+
channel,
|
|
170
|
+
operation
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function createChannelScaleEditor(operation, definition) {
|
|
175
|
+
return action(
|
|
176
|
+
{
|
|
177
|
+
op: operation,
|
|
178
|
+
description: `Edit the scale bound to the ${definition.channel} channel.`
|
|
179
|
+
},
|
|
180
|
+
function (args = {}) {
|
|
181
|
+
validateOptionObject(
|
|
182
|
+
args,
|
|
183
|
+
[...(definition.targetOnly === true ? [] : ["id"]), "target", ...definition.options],
|
|
184
|
+
operation
|
|
185
|
+
);
|
|
186
|
+
if (definition.targetOnly === true && args.target === undefined) {
|
|
187
|
+
throw new Error(`${operation} requires target.`);
|
|
188
|
+
}
|
|
189
|
+
if (!definition.options.some(property => Object.hasOwn(args, property))) {
|
|
190
|
+
throw new Error(`${operation} requires at least one editable property.`);
|
|
191
|
+
}
|
|
192
|
+
const id = resolveScaleId(this, args, definition.channel, operation);
|
|
193
|
+
const patch = Object.fromEntries(
|
|
194
|
+
Object.entries(args).filter(([property]) => property !== "target")
|
|
195
|
+
);
|
|
196
|
+
return this.editScale({ ...patch, id });
|
|
197
|
+
}
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export const channelScaleEditors = Object.freeze(Object.fromEntries(
|
|
202
|
+
Object.entries(DEFINITIONS).map(([operation, definition]) => [
|
|
203
|
+
operation,
|
|
204
|
+
createChannelScaleEditor(operation, definition)
|
|
205
|
+
])
|
|
206
|
+
));
|
|
207
|
+
|
|
208
|
+
export function registerChannelScaleActions(ProgramClass) {
|
|
209
|
+
for (const [operation, editor] of Object.entries(channelScaleEditors)) {
|
|
210
|
+
ProgramClass.prototype[operation] = editor;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
@@ -43,7 +43,7 @@ export function requireConsumerDataset(program, consumer) {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
export function isDirectCategoricalConsumer(consumer) {
|
|
46
|
-
return ["color", "strokeDash", "xOffset", "yOffset", "shape"].includes(
|
|
46
|
+
return ["color", "stroke", "strokeDash", "xOffset", "yOffset", "shape"].includes(
|
|
47
47
|
consumer.channel
|
|
48
48
|
) && ["nominal", "ordinal"].includes(consumer.encoding.fieldType);
|
|
49
49
|
}
|
|
@@ -45,7 +45,7 @@ export function resolveMarkFamilyConsumerValues(program, consumer, dataset) {
|
|
|
45
45
|
}
|
|
46
46
|
const { layer, channel } = consumer;
|
|
47
47
|
const encoding = layer.encoding ?? {};
|
|
48
|
-
const appearance = ["strokeWidth", "opacity"].includes(channel);
|
|
48
|
+
const appearance = ["stroke", "strokeWidth", "opacity"].includes(channel);
|
|
49
49
|
if (
|
|
50
50
|
layer.mark?.type === "line" &&
|
|
51
51
|
((["x", "y"].includes(channel) && encoding.x !== undefined &&
|
|
@@ -69,7 +69,8 @@ export function resolveMarkFamilyConsumerValues(program, consumer, dataset) {
|
|
|
69
69
|
dataset.values,
|
|
70
70
|
derived.series,
|
|
71
71
|
consumer.encoding.field,
|
|
72
|
-
channel
|
|
72
|
+
channel,
|
|
73
|
+
consumer.encoding
|
|
73
74
|
)
|
|
74
75
|
: channel === "x" ? derived.xValues : derived.yValues
|
|
75
76
|
};
|
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
} from "./common.js";
|
|
10
10
|
import { resolveMarkFamilyConsumerValues } from "./families.js";
|
|
11
11
|
import { CATEGORY_ORDER_CHANNELS, resolveCategoryOrder } from "../../../grammar/categoryOrder.js";
|
|
12
|
+
import { resolveHistogramInput } from
|
|
13
|
+
"../../../materialization/bars/histogram.js";
|
|
12
14
|
|
|
13
15
|
export { findScale, findScaleConsumers } from "./common.js";
|
|
14
16
|
export {
|
|
@@ -16,8 +18,7 @@ export {
|
|
|
16
18
|
resolveSeriesLayoutScaleValues
|
|
17
19
|
} from "./seriesLayout.js";
|
|
18
20
|
|
|
19
|
-
export function
|
|
20
|
-
const dataset = requireConsumerDataset(program, consumer);
|
|
21
|
+
export function resolveConsumerValuesForDataset(program, consumer, dataset) {
|
|
21
22
|
if (consumer.layer.mark.type === "rect") return resolveMarkFamilyConsumerValues(program, consumer, dataset).values;
|
|
22
23
|
if (Object.hasOwn(consumer.encoding, "datum")) {
|
|
23
24
|
if (consumer.layer.mark.type === "area") {
|
|
@@ -49,6 +50,14 @@ export function resolveConsumerValues(program, consumer) {
|
|
|
49
50
|
);
|
|
50
51
|
}
|
|
51
52
|
const scale = findScale(program, consumer.encoding.scale);
|
|
53
|
+
if (
|
|
54
|
+
consumer.layer.mark.type === "bar" &&
|
|
55
|
+
consumer.channel === "x" &&
|
|
56
|
+
consumer.encoding.bin !== undefined &&
|
|
57
|
+
consumer.encoding.weight !== undefined
|
|
58
|
+
) {
|
|
59
|
+
return resolveHistogramInput(dataset, consumer.encoding).values;
|
|
60
|
+
}
|
|
52
61
|
if (isDirectCategoricalConsumer(consumer)) {
|
|
53
62
|
return readConsumerFieldValues(program, consumer, dataset, scale);
|
|
54
63
|
}
|
|
@@ -58,6 +67,14 @@ export function resolveConsumerValues(program, consumer) {
|
|
|
58
67
|
: readConsumerFieldValues(program, consumer, dataset, scale);
|
|
59
68
|
}
|
|
60
69
|
|
|
70
|
+
export function resolveConsumerValues(program, consumer) {
|
|
71
|
+
return resolveConsumerValuesForDataset(
|
|
72
|
+
program,
|
|
73
|
+
consumer,
|
|
74
|
+
requireConsumerDataset(program, consumer)
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
61
78
|
export function resolveConsumerCategoryOrder(program, consumer) {
|
|
62
79
|
const order = consumer.encoding.categoryOrder;
|
|
63
80
|
if (order === undefined) return undefined;
|
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
resolveBarGrain
|
|
10
10
|
} from "../../../grammar/bars/policy.js";
|
|
11
11
|
import {
|
|
12
|
-
countHistogramBins,
|
|
13
12
|
findHistogramBinIndex,
|
|
14
13
|
resolveHistogramBins
|
|
15
14
|
} from "../../../grammar/histogram.js";
|
|
@@ -22,11 +21,12 @@ import {
|
|
|
22
21
|
} from "../../../grammar/seriesLayout.js";
|
|
23
22
|
import {
|
|
24
23
|
readNominalField,
|
|
25
|
-
readQuantitativeField,
|
|
26
24
|
resolveOrdinalDomain
|
|
27
25
|
} from "../../../grammar/scales/index.js";
|
|
28
26
|
import { findDataset } from "../../../selectors/datasets.js";
|
|
29
27
|
import { findScale } from "./common.js";
|
|
28
|
+
import { resolveHistogramInput } from
|
|
29
|
+
"../../../materialization/bars/histogram.js";
|
|
30
30
|
|
|
31
31
|
export function resolveHistogramCountValues(program, consumer) {
|
|
32
32
|
const xEncoding = consumer.layer.encoding?.x;
|
|
@@ -44,7 +44,8 @@ export function resolveHistogramCountValues(program, consumer) {
|
|
|
44
44
|
`Histogram mark "${consumer.layer.id}" requires an existing dataset.`
|
|
45
45
|
);
|
|
46
46
|
}
|
|
47
|
-
const
|
|
47
|
+
const input = resolveHistogramInput(dataset, xEncoding);
|
|
48
|
+
const xValues = input.values;
|
|
48
49
|
const bins = resolveHistogramBins({
|
|
49
50
|
values: xValues,
|
|
50
51
|
bin: xEncoding.bin,
|
|
@@ -52,7 +53,12 @@ export function resolveHistogramCountValues(program, consumer) {
|
|
|
52
53
|
nice: xScale.nice ?? true,
|
|
53
54
|
zero: xScale.zero ?? false
|
|
54
55
|
});
|
|
55
|
-
|
|
56
|
+
const cells = bins.boundaries.slice(0, -1).map(() => []);
|
|
57
|
+
xValues.forEach((value, index) => {
|
|
58
|
+
const bin = findHistogramBinIndex(value, bins.boundaries);
|
|
59
|
+
if (bin !== -1) cells[bin].push(input.entries[index]);
|
|
60
|
+
});
|
|
61
|
+
return cells.map(input.mass);
|
|
56
62
|
}
|
|
57
63
|
|
|
58
64
|
function resolveHistogramPartitions(program, consumer) {
|
|
@@ -60,7 +66,8 @@ function resolveHistogramPartitions(program, consumer) {
|
|
|
60
66
|
const dataset = findDataset(program, layer.data);
|
|
61
67
|
const xEncoding = layer.encoding.x;
|
|
62
68
|
const xScale = findScale(program, xEncoding.scale);
|
|
63
|
-
const
|
|
69
|
+
const input = resolveHistogramInput(dataset, xEncoding);
|
|
70
|
+
const xValues = input.values;
|
|
64
71
|
const bins = resolveHistogramBins({
|
|
65
72
|
values: xValues,
|
|
66
73
|
bin: xEncoding.bin,
|
|
@@ -70,19 +77,24 @@ function resolveHistogramPartitions(program, consumer) {
|
|
|
70
77
|
});
|
|
71
78
|
const colorEncoding = layer.encoding?.color;
|
|
72
79
|
if (layer.encoding?.group !== undefined) {
|
|
73
|
-
const identity = readSeriesIdentity(
|
|
80
|
+
const identity = readSeriesIdentity(input.rows, layer);
|
|
74
81
|
validateGeneratedItemLimit((bins.boundaries.length - 1) * identity.domain.length, "Histogram layout cell count");
|
|
75
|
-
const
|
|
82
|
+
const cells = bins.boundaries.slice(0, -1).map(() => identity.domain.map(() => []));
|
|
76
83
|
const groups = new Map(identity.domain.map((value, index) => [value, index]));
|
|
77
84
|
xValues.forEach((value, index) => { const bin = findHistogramBinIndex(value, bins.boundaries);
|
|
78
|
-
if (bin !== -1)
|
|
79
|
-
return
|
|
85
|
+
if (bin !== -1) cells[bin][groups.get(identity.values[index])].push(input.entries[index]); });
|
|
86
|
+
return cells.map(partition => partition.map(input.mass));
|
|
80
87
|
}
|
|
81
88
|
if (colorEncoding?.scale === undefined) {
|
|
82
|
-
|
|
89
|
+
const cells = bins.boundaries.slice(0, -1).map(() => []);
|
|
90
|
+
xValues.forEach((value, index) => {
|
|
91
|
+
const bin = findHistogramBinIndex(value, bins.boundaries);
|
|
92
|
+
if (bin !== -1) cells[bin].push(input.entries[index]);
|
|
93
|
+
});
|
|
94
|
+
return cells.map(entries => [input.mass(entries)]);
|
|
83
95
|
}
|
|
84
96
|
const colorScale = findScale(program, colorEncoding.scale);
|
|
85
|
-
const colorValues = readNominalField(
|
|
97
|
+
const colorValues = readNominalField(input.rows, colorEncoding.field);
|
|
86
98
|
const colorDomain = resolveOrdinalDomain(colorScale.domain, colorValues);
|
|
87
99
|
validateGeneratedItemLimit(
|
|
88
100
|
(bins.boundaries.length - 1) * colorDomain.length,
|
|
@@ -90,14 +102,16 @@ function resolveHistogramPartitions(program, consumer) {
|
|
|
90
102
|
);
|
|
91
103
|
const colorIndex = new Map(colorDomain.map((value, index) => [value, index]));
|
|
92
104
|
const partitions = bins.boundaries.slice(0, -1).map(() =>
|
|
93
|
-
colorDomain.map(() =>
|
|
105
|
+
colorDomain.map(() => [])
|
|
94
106
|
);
|
|
95
107
|
for (let index = 0; index < xValues.length; index += 1) {
|
|
96
108
|
const bin = findHistogramBinIndex(xValues[index], bins.boundaries);
|
|
97
109
|
const color = colorIndex.get(colorValues[index]);
|
|
98
|
-
if (bin !== -1 && color !== undefined)
|
|
110
|
+
if (bin !== -1 && color !== undefined) {
|
|
111
|
+
partitions[bin][color].push(input.entries[index]);
|
|
112
|
+
}
|
|
99
113
|
}
|
|
100
|
-
return partitions;
|
|
114
|
+
return partitions.map(partition => partition.map(input.mass));
|
|
101
115
|
}
|
|
102
116
|
|
|
103
117
|
function resolveAggregatePartitions(program, consumer) {
|