ggaction 0.0.12 → 0.0.15
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 +127 -0
- package/README.md +20 -11
- package/knowledge/action-card.schema.json +1 -1
- package/knowledge/action-cards.json +1 -1
- package/knowledge/action-cards.schema.json +1 -1
- package/knowledge/extension-authoring.md +2 -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 +10 -3
- package/src/actions/basic.js +4 -2
- package/src/actions/boxPlots/components.js +14 -4
- package/src/actions/boxPlots/edit.js +31 -5
- package/src/actions/boxPlots/materialize.js +13 -4
- package/src/actions/boxPlots/options.js +48 -16
- package/src/actions/canvas/actions.js +3 -1
- package/src/actions/canvas/fitting.js +211 -0
- package/src/actions/canvas/index.js +2 -0
- package/src/actions/categoryOrder/index.js +18 -6
- package/src/actions/charts/area.js +51 -0
- package/src/actions/charts/bar.js +13 -6
- package/src/actions/charts/beeswarm.js +70 -0
- package/src/actions/charts/density.js +48 -0
- package/src/actions/charts/ecdf.js +186 -0
- package/src/actions/charts/endpoints.js +473 -0
- package/src/actions/charts/heatmap.js +4 -3
- package/src/actions/charts/histogram.js +5 -4
- package/src/actions/charts/horizon.js +73 -0
- package/src/actions/charts/index.js +37 -0
- package/src/actions/charts/interval-regression.js +172 -0
- package/src/actions/charts/line.js +8 -6
- package/src/actions/charts/parallel.js +8 -2
- package/src/actions/charts/pie.js +52 -0
- package/src/actions/charts/polar.js +134 -0
- package/src/actions/charts/radar.js +305 -0
- package/src/actions/charts/radial.js +50 -0
- package/src/actions/charts/raincloud.js +422 -0
- package/src/actions/charts/rug-strip.js +283 -0
- package/src/actions/charts/scatter.js +11 -3
- package/src/actions/charts/shared.js +69 -10
- package/src/actions/composition/actions.js +129 -3
- package/src/actions/coordinates/actions.js +1 -1
- package/src/actions/coordinates/edit.js +76 -0
- package/src/actions/data/bin.js +29 -0
- package/src/actions/data/bin2d.js +57 -185
- package/src/actions/data/complete.js +26 -0
- package/src/actions/data/computed.js +24 -0
- package/src/actions/data/create.js +2 -2
- package/src/actions/data/density.js +12 -20
- package/src/actions/data/derived.js +111 -15
- package/src/actions/data/ecdf.js +38 -0
- package/src/actions/data/edit.js +539 -0
- package/src/actions/data/filter.js +226 -30
- package/src/actions/data/fold.js +24 -0
- package/src/actions/data/impute.js +26 -0
- package/src/actions/data/index.js +38 -0
- package/src/actions/data/interval.js +2 -0
- package/src/actions/data/intervalEdit.js +86 -4
- package/src/actions/data/normalize.js +27 -0
- package/src/actions/data/regression.js +19 -21
- package/src/actions/data/revisionBindings.js +145 -0
- package/src/actions/data/shared.js +20 -4
- package/src/actions/data/stack.js +26 -0
- package/src/actions/data/summary.js +26 -0
- package/src/actions/data/timeUnit.js +4 -1
- package/src/actions/data/window.js +1 -1
- package/src/actions/encodings/appearance.js +32 -13
- package/src/actions/encodings/barWidth.js +8 -0
- package/src/actions/encodings/basic.js +2 -0
- package/src/actions/encodings/channels.js +415 -0
- package/src/actions/encodings/color/continuous.js +19 -59
- package/src/actions/encodings/color/index.js +30 -23
- package/src/actions/encodings/color/layout.js +3 -97
- package/src/actions/encodings/color/policy.js +5 -8
- package/src/actions/encodings/density/resolve.js +37 -5
- package/src/actions/encodings/density.js +124 -34
- package/src/actions/encodings/histogram.js +3 -2
- package/src/actions/encodings/horizon/resolve.js +8 -4
- package/src/actions/encodings/horizon.js +18 -8
- package/src/actions/encodings/index.js +6 -0
- package/src/actions/encodings/offset.js +30 -29
- package/src/actions/encodings/parallel.js +2 -8
- package/src/actions/encodings/position/apply.js +63 -11
- package/src/actions/encodings/position/index.js +52 -6
- package/src/actions/encodings/position/policies/arc.js +14 -0
- package/src/actions/encodings/position/policies/area.js +4 -42
- package/src/actions/encodings/position/policies/bar.js +57 -13
- package/src/actions/encodings/position/resolve.js +88 -26
- package/src/actions/encodings/ranged.js +123 -97
- package/src/actions/encodings/remove.js +44 -34
- package/src/actions/encodings/ruleAppearance.js +24 -32
- package/src/actions/encodings/seriesLayout.js +105 -0
- package/src/actions/encodings/shared.js +107 -37
- package/src/actions/encodings/stroke.js +226 -0
- package/src/actions/encodings/strokeDash.js +9 -6
- package/src/actions/encodings/temporal.js +7 -0
- package/src/actions/encodings/text.js +30 -15
- package/src/actions/errorBands/create.js +41 -8
- package/src/actions/errorBands/edit.js +360 -25
- package/src/actions/errorBars/create.js +56 -17
- package/src/actions/errorBars/edit.js +372 -21
- package/src/actions/facets/actions.js +619 -41
- package/src/actions/facets/derive.js +139 -25
- package/src/actions/gradientPlots/components.js +43 -13
- package/src/actions/gradientPlots/edit.js +14 -4
- package/src/actions/gradientPlots/materialize.js +8 -8
- package/src/actions/gradientPlots/options.js +10 -2
- package/src/actions/guides/applicability.js +28 -19
- package/src/actions/guides/axes/axes.js +4 -3
- package/src/actions/guides/axes/axis.js +18 -18
- package/src/actions/guides/axes/components.js +43 -0
- package/src/actions/guides/axes/edit.js +16 -51
- package/src/actions/guides/axes/labels.js +184 -28
- package/src/actions/guides/axes/lines.js +13 -7
- package/src/actions/guides/axes/parallel/lifecycle.js +117 -0
- package/src/actions/guides/axes/parallel/policy.js +137 -0
- package/src/actions/guides/axes/parallel/resolve.js +50 -25
- package/src/actions/guides/axes/parallel.js +93 -217
- package/src/actions/guides/axes/policy.js +21 -67
- package/src/actions/guides/axes/tickGroups.js +15 -8
- package/src/actions/guides/axes/ticks.js +11 -6
- package/src/actions/guides/axes/titles.js +32 -11
- package/src/actions/guides/basic.js +2 -0
- package/src/actions/guides/facade.js +106 -0
- package/src/actions/guides/facadeAxes.js +200 -0
- package/src/actions/guides/facadeLegend.js +63 -0
- package/src/actions/guides/grids/grid.js +37 -33
- package/src/actions/guides/grids/resolve.js +17 -4
- package/src/actions/guides/guides.js +54 -31
- package/src/actions/guides/legends/blocks.js +426 -0
- package/src/actions/guides/legends/categorical/actions.js +309 -279
- package/src/actions/guides/legends/categorical/components.js +21 -17
- package/src/actions/guides/legends/categorical/index.js +37 -33
- package/src/actions/guides/legends/categorical/layout.js +123 -354
- package/src/actions/guides/legends/categorical/options.js +32 -16
- package/src/actions/guides/legends/categorical/recipes.js +114 -9
- package/src/actions/guides/legends/categorical/resolve.js +30 -15
- package/src/actions/guides/legends/categorical/symbols.js +87 -33
- package/src/actions/guides/legends/continuous/common.js +99 -26
- package/src/actions/guides/legends/continuous/gradient.js +120 -89
- package/src/actions/guides/legends/continuous/index.js +8 -2
- package/src/actions/guides/legends/continuous/interval.js +79 -95
- package/src/actions/guides/legends/continuous/opacity.js +113 -89
- package/src/actions/guides/legends/continuous/stroke.js +525 -0
- package/src/actions/guides/legends/creation.js +40 -0
- package/src/actions/guides/legends/edit.js +321 -90
- package/src/actions/guides/legends/focused.js +2 -2
- package/src/actions/guides/legends/index.js +2 -0
- package/src/actions/guides/legends/lane.js +149 -102
- package/src/actions/guides/legends/lifecycle.js +143 -0
- package/src/actions/guides/legends/remove.js +43 -78
- package/src/actions/guides/legends/sampling.js +140 -0
- package/src/actions/guides/legends/size.js +213 -116
- package/src/actions/guides/legends/strokeWidth.js +121 -73
- package/src/actions/guides/legends/target.js +123 -0
- package/src/actions/guides/legends/transition.js +192 -0
- package/src/actions/guides/polar/axes/facade.js +81 -66
- package/src/actions/guides/polar/axes/labels.js +28 -8
- package/src/actions/guides/polar/axes/lines.js +12 -5
- package/src/actions/guides/polar/axes/shared.js +22 -0
- package/src/actions/guides/polar/axes/ticks.js +6 -5
- package/src/actions/guides/polar/axes/titles.js +6 -5
- package/src/actions/guides/polar/grids.js +8 -8
- package/src/actions/guides/polar/resolve.js +12 -8
- package/src/actions/guides/reuse.js +44 -0
- package/src/actions/guides/tickValues.js +19 -21
- package/src/actions/index.js +15 -2
- package/src/actions/intervals/resolve.js +35 -15
- package/src/actions/marks/arc/actions.js +78 -20
- package/src/actions/marks/area/actions.js +51 -12
- package/src/actions/marks/area/materialize.js +22 -3
- package/src/actions/marks/bar/create.js +11 -2
- package/src/actions/marks/bar/edit.js +20 -3
- package/src/actions/marks/bar/materialize.js +33 -9
- package/src/actions/marks/index.js +14 -0
- package/src/actions/marks/line/actions.js +71 -113
- package/src/actions/marks/line/materialize.js +57 -56
- package/src/actions/marks/point/create.js +8 -2
- package/src/actions/marks/point/edit.js +29 -21
- package/src/actions/marks/point/index.js +1 -0
- package/src/actions/marks/point/jitter.js +3 -0
- package/src/actions/marks/point/materialize.js +154 -16
- package/src/actions/marks/point/packing.js +68 -0
- package/src/actions/marks/rect/actions.js +33 -12
- package/src/actions/marks/references.js +658 -0
- package/src/actions/marks/remove.js +87 -4
- package/src/actions/marks/rule/actions.js +77 -9
- package/src/actions/marks/rule/appearance.js +26 -0
- package/src/actions/marks/shared.js +13 -13
- package/src/actions/marks/text/actions.js +296 -13
- package/src/actions/marks/text/index.js +4 -0
- package/src/actions/marks/text/layout.js +156 -8
- package/src/actions/marks/text/removal.js +173 -0
- package/src/actions/marks/text/selection.js +73 -0
- package/src/actions/marks/tick/actions.js +22 -5
- package/src/actions/primitives/createGraphics.js +3 -1
- package/src/actions/primitives/editGraphics.js +26 -0
- package/src/actions/primitives/semantic.js +3 -2
- package/src/actions/primitives/semanticAction.js +120 -10
- package/src/actions/primitives/semanticValidation/guide.js +10 -0
- package/src/actions/primitives/semanticValidation/index.js +9 -1
- package/src/actions/primitives/semanticValidation/layer.js +23 -1
- package/src/actions/primitives/semanticValidation/scale.js +38 -8
- package/src/actions/regression/components.js +26 -11
- package/src/actions/regression/create.js +11 -3
- package/src/actions/regression/edit.js +40 -13
- package/src/actions/regression/resolve.js +1 -1
- package/src/actions/resources/index.js +7 -0
- package/src/actions/resources/remove.js +172 -0
- package/src/actions/scales/channels.js +212 -0
- package/src/actions/scales/consumers/common.js +21 -54
- package/src/actions/scales/consumers/families.js +28 -14
- package/src/actions/scales/consumers/index.js +42 -4
- package/src/actions/scales/consumers/seriesLayout.js +57 -12
- package/src/actions/scales/create.js +58 -26
- package/src/actions/scales/definitions.js +109 -39
- package/src/actions/scales/edit.js +101 -34
- package/src/actions/scales/editPolicy.js +95 -35
- package/src/actions/scales/index.js +6 -0
- package/src/actions/scales/materialize.js +14 -49
- package/src/actions/scales/offset.js +48 -0
- package/src/actions/scales/parallel.js +71 -0
- package/src/actions/scales/patch.js +1 -1
- package/src/actions/scales/preview.js +140 -0
- package/src/actions/scales/quantitativeColor.js +9 -5
- package/src/actions/selection/actions.js +154 -30
- package/src/actions/selection/index.js +3 -0
- package/src/actions/theme/actions.js +98 -0
- package/src/actions/theme/composition.js +238 -0
- package/src/actions/theme/index.js +16 -0
- package/src/actions/theme/reconcile.js +1130 -0
- package/src/actions/theme/state.js +160 -0
- package/src/actions/titles/actions.js +11 -10
- package/src/actions/titles/resolve.js +1 -40
- package/src/actions/violinPlots/create.js +90 -43
- package/src/actions/violinPlots/edit.js +192 -0
- package/src/actions/violinPlots/index.js +2 -0
- package/src/core/ChartProgram.js +7 -0
- package/src/core/action.js +32 -2
- package/src/core/compositionState.js +141 -8
- package/src/core/programState.js +20 -0
- package/src/core/resourceReferences.js +332 -0
- package/src/core/vocabulary.js +16 -3
- package/src/grammar/aggregate.js +66 -23
- package/src/grammar/arcs.js +58 -1
- package/src/grammar/areaEndpoints.js +46 -0
- package/src/grammar/areaSeries.js +87 -41
- package/src/grammar/bars/aggregate.js +37 -13
- package/src/grammar/bars/geometry.js +44 -0
- package/src/grammar/bars/policy.js +9 -7
- package/src/grammar/bin.js +226 -0
- package/src/grammar/categoryOrder.js +34 -0
- package/src/grammar/complete.js +279 -0
- package/src/grammar/computed.js +351 -0
- package/src/grammar/coordinates.js +37 -0
- package/src/grammar/density.js +239 -37
- package/src/grammar/displayLabels.js +57 -0
- package/src/grammar/ecdf.js +198 -0
- package/src/grammar/facets/dependencies.js +133 -1
- package/src/grammar/facets/guides.js +3 -0
- package/src/grammar/facets/index.js +123 -52
- package/src/grammar/facets/scales.js +15 -18
- package/src/grammar/fold.js +123 -0
- package/src/grammar/horizon.js +6 -4
- package/src/grammar/impute.js +340 -0
- package/src/grammar/interval.js +37 -20
- package/src/grammar/lineSeries.js +20 -63
- package/src/grammar/markFilter.js +22 -4
- package/src/grammar/markLabelSelection.js +57 -0
- package/src/grammar/markLabels.js +110 -0
- package/src/grammar/normalize.js +366 -0
- package/src/grammar/parallelAxisTitles.js +16 -0
- package/src/grammar/pathSeries.js +109 -0
- package/src/grammar/pointPacking.js +183 -0
- package/src/grammar/pointShapes.js +13 -4
- package/src/grammar/polar.js +90 -8
- package/src/grammar/positionDatum.js +12 -0
- package/src/grammar/rects.js +9 -1
- package/src/grammar/regression/derive.js +4 -0
- package/src/grammar/regression/index.js +8 -2
- package/src/grammar/regression/models.js +22 -7
- package/src/grammar/regression/parameters.js +63 -13
- package/src/grammar/rotation.js +29 -0
- package/src/grammar/roundedRect.js +180 -0
- package/src/grammar/rules.js +8 -21
- package/src/grammar/scales/color.js +23 -1
- package/src/grammar/scales/colorConsumers.js +57 -0
- package/src/grammar/scales/continuous.js +2 -2
- package/src/grammar/scales/definition.js +42 -5
- package/src/grammar/scales/discretized.js +11 -5
- package/src/grammar/scales/fields.js +38 -8
- package/src/grammar/scales/index.js +1 -0
- package/src/grammar/scales/mapping.js +2 -0
- package/src/grammar/scales/ordinal.js +12 -3
- package/src/grammar/scales/policies.js +2 -2
- package/src/grammar/scales/radial.js +55 -0
- package/src/grammar/scales/size.js +456 -0
- package/src/grammar/scales/types.js +1 -0
- package/src/grammar/schemas/concreteGraphic.js +14 -2
- package/src/grammar/schemas/graphic.js +15 -0
- package/src/grammar/schemas/graphicBounds.js +179 -30
- package/src/grammar/schemas/semanticPath.js +23 -4
- package/src/grammar/stack.js +170 -0
- package/src/grammar/statisticalReference.js +103 -0
- package/src/grammar/statistics/confidenceInterval.js +101 -0
- package/src/grammar/strokeStyle.js +77 -0
- package/src/grammar/summary.js +245 -0
- package/src/grammar/text.js +22 -16
- package/src/grammar/timeUnit.js +234 -13
- package/src/grammar/transformTopology.js +10 -0
- package/src/grammar/transforms.js +418 -16
- package/src/grammar/valueFormat.js +112 -0
- package/src/grammar/weightedStatistics.js +374 -0
- package/src/grammar/window.js +232 -21
- package/src/layout/aspect.js +80 -0
- package/src/layout/facets.js +100 -12
- package/src/layout/guideCollisions.js +12 -0
- package/src/layout/labels.js +343 -0
- package/src/layout/legend.js +2 -10
- package/src/layout/legendItems.js +102 -0
- package/src/layout/legendLane.js +100 -11
- package/src/materialization/bars/aggregate.js +56 -147
- package/src/materialization/bars/histogram.js +104 -20
- package/src/materialization/bars/ranged.js +19 -3
- package/src/materialization/bars/resolve.js +7 -4
- package/src/materialization/categorySlotOffset.js +40 -0
- package/src/materialization/composition.js +3 -1
- package/src/materialization/compositionSnapshot.js +1 -1
- package/src/materialization/coordinateBounds.js +204 -0
- package/src/materialization/dependencies.js +93 -16
- package/src/materialization/encodings.js +1 -1
- package/src/materialization/facetGuides/legacyCategorical.js +3 -0
- package/src/materialization/facetGuides/placement.js +8 -2
- package/src/materialization/facetGuides/preparation.js +12 -7
- package/src/materialization/facetHeaders.js +346 -0
- package/src/materialization/facets.js +23 -66
- package/src/materialization/guides/layout.js +57 -0
- package/src/materialization/guides/resources.js +28 -2
- package/src/materialization/legends.js +11 -2
- package/src/materialization/marks/capabilities.js +20 -5
- package/src/materialization/marks/grouping.js +52 -0
- package/src/materialization/marks/index.js +68 -27
- package/src/materialization/marks/policies.js +4 -1
- package/src/materialization/rect.js +55 -42
- package/src/materialization/rectConfig.js +5 -1
- package/src/materialization/rowEncoding.js +13 -13
- package/src/materialization/scaleGuideDependencies.js +8 -5
- package/src/materialization/scales/map.js +6 -3
- package/src/materialization/scales/policies/arc.js +42 -0
- package/src/materialization/scales/policies/binnedPosition.js +7 -4
- package/src/materialization/scales/policies/offset.js +50 -8
- package/src/materialization/scales/policies/series.js +5 -1
- package/src/materialization/scales/resolve.js +65 -17
- package/src/materialization/selection/compatibility.js +11 -0
- package/src/materialization/selection/items/arc.js +28 -4
- package/src/materialization/selection/items/bar.js +42 -71
- package/src/materialization/selection/items/common.js +27 -6
- package/src/materialization/selection/items/path.js +3 -13
- package/src/materialization/selection/items/rect.js +35 -8
- package/src/materialization/selection/path.js +1 -1
- package/src/materialization/selection/point.js +3 -1
- package/src/materialization/text.js +324 -14
- package/src/mcp/adapter.js +2 -2
- package/src/renderers/canvas/circle.js +2 -3
- 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/svg.js +19 -0
- package/src/selectors/datasets.js +44 -0
- package/src/selectors/layers.js +7 -2
- package/src/selectors/scales.js +6 -0
- package/src/theme/defaults.js +140 -0
- package/types/basic.d.ts +17 -2
- package/types/index.d.ts +211 -0
- package/types/program.d.ts +2182 -204
- package/src/materialization/bars/grouped.js +0 -144
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { normalizeOffsetScalePolicy } from "../../../grammar/bars/geometry.js";
|
|
2
2
|
|
|
3
3
|
function resolveParentSlot(consumer, resolvedScales, parentChannel) {
|
|
4
4
|
if (parentChannel === "x" && consumer.layer.encoding?.x?.bin !== undefined) {
|
|
@@ -16,6 +16,7 @@ function resolveParentSlot(consumer, resolvedScales, parentChannel) {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
export function resolveOffsetScalePolicy({
|
|
19
|
+
scale,
|
|
19
20
|
consumers,
|
|
20
21
|
resolvedScales,
|
|
21
22
|
markConfigs,
|
|
@@ -34,21 +35,62 @@ export function resolveOffsetScalePolicy({
|
|
|
34
35
|
`${channel} scale "${id}" requires one shared resolved ${parentChannel} categorical slot.`
|
|
35
36
|
);
|
|
36
37
|
}
|
|
37
|
-
|
|
38
|
+
return {
|
|
39
|
+
parentBandwidth: slots[0],
|
|
40
|
+
...resolveRequestedOffsetPolicy({
|
|
41
|
+
scale,
|
|
42
|
+
consumers,
|
|
43
|
+
markConfigs,
|
|
44
|
+
id,
|
|
45
|
+
channel
|
|
46
|
+
})
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Resolve the requested offset policy independently from its parent slot.
|
|
52
|
+
* Semantic scale fields are canonical. Consistent legacy mark-owned padding is
|
|
53
|
+
* retained as a fallback until the next successful scale/encoding edit migrates
|
|
54
|
+
* it into the semantic scale.
|
|
55
|
+
*/
|
|
56
|
+
export function resolveRequestedOffsetPolicy({
|
|
57
|
+
scale,
|
|
58
|
+
consumers = [],
|
|
59
|
+
markConfigs = {},
|
|
60
|
+
id = scale?.id ?? channel,
|
|
61
|
+
channel = "xOffset"
|
|
62
|
+
}) {
|
|
63
|
+
const semanticPatch = scale === undefined
|
|
64
|
+
? {}
|
|
65
|
+
: Object.fromEntries(
|
|
66
|
+
["padding", "paddingInner", "paddingOuter", "align"]
|
|
67
|
+
.filter(property => Object.hasOwn(scale, property))
|
|
68
|
+
.map(property => [property, scale[property]])
|
|
69
|
+
);
|
|
70
|
+
if (
|
|
71
|
+
Object.hasOwn(semanticPatch, "padding") ||
|
|
72
|
+
["paddingInner", "paddingOuter", "align"].every(
|
|
73
|
+
property => Object.hasOwn(semanticPatch, property)
|
|
74
|
+
)
|
|
75
|
+
) {
|
|
76
|
+
return normalizeOffsetScalePolicy(semanticPatch, undefined, channel);
|
|
77
|
+
}
|
|
78
|
+
const legacyPolicies = consumers.map(consumer => normalizeOffsetScalePolicy(
|
|
38
79
|
markConfigs[consumer.layer.id]?.[channel],
|
|
39
80
|
undefined,
|
|
40
81
|
channel
|
|
41
82
|
));
|
|
42
83
|
const signatures = new Set(
|
|
43
|
-
|
|
84
|
+
legacyPolicies.map(policy => JSON.stringify(policy))
|
|
44
85
|
);
|
|
45
|
-
if (signatures.size
|
|
86
|
+
if (signatures.size > 1) {
|
|
46
87
|
throw new Error(
|
|
47
88
|
`${channel} scale "${id}" requires one shared padding policy.`
|
|
48
89
|
);
|
|
49
90
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
91
|
+
const legacy = legacyPolicies[0] ?? normalizeOffsetScalePolicy(
|
|
92
|
+
{}, undefined, channel
|
|
93
|
+
);
|
|
94
|
+
if (scale === undefined) return legacy;
|
|
95
|
+
return normalizeOffsetScalePolicy(semanticPatch, legacy, channel);
|
|
54
96
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isSourceOwnedText } from "../../../grammar/text.js";
|
|
1
2
|
import {
|
|
2
3
|
isTransformedScaleType,
|
|
3
4
|
resolveContinuousDomain,
|
|
@@ -21,7 +22,10 @@ export function resolveSeriesLayoutDomain({
|
|
|
21
22
|
(_, index) => seriesLayouts[index] !== undefined
|
|
22
23
|
);
|
|
23
24
|
const directConsumers = valuesByConsumer.filter(
|
|
24
|
-
(
|
|
25
|
+
({ consumer, scalePolicy }, index) =>
|
|
26
|
+
scalePolicy !== false &&
|
|
27
|
+
seriesLayouts[index] === undefined &&
|
|
28
|
+
!isSourceOwnedText(consumer.layer)
|
|
25
29
|
);
|
|
26
30
|
const compatibleDirect = directConsumers.every(({ consumer }) =>
|
|
27
31
|
layoutConsumers.every(({ consumer: layoutConsumer }) => {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { validateContinuousColorConsumer } from "../../grammar/scales/colorConsumers.js";
|
|
1
2
|
import {
|
|
2
3
|
isContinuousColorScaleType,
|
|
3
4
|
isDiscretePositionScaleType,
|
|
@@ -16,17 +17,19 @@ import {
|
|
|
16
17
|
resolveScaleRange,
|
|
17
18
|
resolveSequentialColorStops,
|
|
18
19
|
resolveShapeRange,
|
|
20
|
+
resolveSizeScale,
|
|
19
21
|
resolveSizeRange,
|
|
20
22
|
resolveStrokeDashRange,
|
|
21
23
|
resolveStrokeWidthRange,
|
|
22
24
|
resolveTransformedDomain,
|
|
23
25
|
SCALE_ROLES,
|
|
26
|
+
validateSequentialMidpoint,
|
|
24
27
|
validateLinearScaleType,
|
|
25
28
|
validateOrdinalScaleType,
|
|
26
29
|
validateScaleTypeForRole,
|
|
27
30
|
validateTimeScaleType
|
|
28
31
|
} from "../../grammar/scales/index.js";
|
|
29
|
-
import { resolveArcAutoPositionRange } from "./policies/arc.js";
|
|
32
|
+
import { resolveArcAutoPositionRange, resolveMeasuredRadiusDomain, validateMeasuredRadiusConsumers } from "./policies/arc.js";
|
|
30
33
|
import { resolveTemporalBarBand } from "./policies/bar.js";
|
|
31
34
|
import { resolveOffsetScalePolicy } from "./policies/offset.js";
|
|
32
35
|
import { resolveBinnedPositionDomain } from "./policies/binnedPosition.js";
|
|
@@ -94,7 +97,8 @@ function resolveRange({
|
|
|
94
97
|
isSequentialColor,
|
|
95
98
|
isDiscretizedColor,
|
|
96
99
|
isOrdinalOffset,
|
|
97
|
-
discretizedScale
|
|
100
|
+
discretizedScale,
|
|
101
|
+
polarFrame
|
|
98
102
|
}) {
|
|
99
103
|
if (channel === "color") {
|
|
100
104
|
if (isDiscretizedColor) return discretizedScale.range;
|
|
@@ -112,7 +116,7 @@ function resolveRange({
|
|
|
112
116
|
scale,
|
|
113
117
|
channel,
|
|
114
118
|
domain,
|
|
115
|
-
range: resolveScaleRange(scale.range, channel, bounds),
|
|
119
|
+
range: resolveScaleRange(scale.range, channel, bounds, polarFrame),
|
|
116
120
|
markConfigs
|
|
117
121
|
});
|
|
118
122
|
}
|
|
@@ -139,7 +143,9 @@ function resolveContinuousScale({
|
|
|
139
143
|
: validateLinearScaleType(scale.type),
|
|
140
144
|
domain,
|
|
141
145
|
range,
|
|
146
|
+
...(scale.midpoint === undefined ? {} : { midpoint: scale.midpoint }),
|
|
142
147
|
...(scale.clamp === undefined ? {} : { clamp: scale.clamp }),
|
|
148
|
+
...(scale.radialMapping === undefined ? {} : { radialMapping: scale.radialMapping }),
|
|
143
149
|
...(scale.type === "log"
|
|
144
150
|
? {
|
|
145
151
|
base: normalizeTransformParameters("log", {
|
|
@@ -182,19 +188,43 @@ function reverseResolvedScale(scale) {
|
|
|
182
188
|
};
|
|
183
189
|
}
|
|
184
190
|
|
|
185
|
-
export function resolveScaleMaterialization({
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
191
|
+
export function resolveScaleMaterialization(options) {
|
|
192
|
+
const {
|
|
193
|
+
id,
|
|
194
|
+
scale,
|
|
195
|
+
channel: requestedChannel,
|
|
196
|
+
consumers,
|
|
197
|
+
valuesByConsumer,
|
|
198
|
+
bounds,
|
|
199
|
+
resolvedScales,
|
|
200
|
+
markConfigs,
|
|
201
|
+
thetaScales,
|
|
202
|
+
polarFrame
|
|
203
|
+
} = options;
|
|
204
|
+
const channel = requestedChannel === "stroke" ? "color" : requestedChannel;
|
|
195
205
|
const allValues = valuesByConsumer
|
|
196
206
|
.flatMap(item => item.values)
|
|
197
207
|
.filter(value => value !== undefined);
|
|
208
|
+
const preserved = resolvedScales[id]?.domain;
|
|
209
|
+
if (
|
|
210
|
+
scale.domain === "auto" &&
|
|
211
|
+
Array.isArray(preserved) &&
|
|
212
|
+
consumers.some(({ layer }) =>
|
|
213
|
+
markConfigs?.[layer.id]?.markFilter?.empty === true
|
|
214
|
+
)
|
|
215
|
+
) {
|
|
216
|
+
return resolveScaleMaterialization({
|
|
217
|
+
...options,
|
|
218
|
+
scale: { ...scale, domain: preserved },
|
|
219
|
+
valuesByConsumer: valuesByConsumer.map(item => ({
|
|
220
|
+
...item,
|
|
221
|
+
categoryOrder: undefined
|
|
222
|
+
}))
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
if (channel === "size") {
|
|
226
|
+
return resolveSizeScale({ ...scale, values: allValues });
|
|
227
|
+
}
|
|
198
228
|
const isSequentialColor = channel === "color" &&
|
|
199
229
|
isContinuousColorScaleType(scale.type);
|
|
200
230
|
const isDiscretizedColor = channel === "color" &&
|
|
@@ -210,6 +240,16 @@ export function resolveScaleMaterialization({
|
|
|
210
240
|
!isOrdinalOffset &&
|
|
211
241
|
isDiscretePositionScaleType(scale.type);
|
|
212
242
|
|
|
243
|
+
if (isSequentialColor || isDiscretizedColor) {
|
|
244
|
+
for (const consumer of consumers) {
|
|
245
|
+
validateContinuousColorConsumer(
|
|
246
|
+
consumer.layer,
|
|
247
|
+
consumer.encoding,
|
|
248
|
+
scale,
|
|
249
|
+
{ channel: consumer.channel }
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
213
253
|
let discretizedScale;
|
|
214
254
|
if (isDiscretizedColor) {
|
|
215
255
|
discretizedScale = resolveDiscretizedColorScale({
|
|
@@ -225,8 +265,7 @@ export function resolveScaleMaterialization({
|
|
|
225
265
|
valuesByConsumer,
|
|
226
266
|
channel,
|
|
227
267
|
scale,
|
|
228
|
-
id
|
|
229
|
-
allValues
|
|
268
|
+
id
|
|
230
269
|
});
|
|
231
270
|
const seriesLayouts = valuesByConsumer.map(item => item.seriesLayout);
|
|
232
271
|
const seriesDomain = isDiscretizedColor || binnedDomain !== undefined
|
|
@@ -247,7 +286,7 @@ export function resolveScaleMaterialization({
|
|
|
247
286
|
}
|
|
248
287
|
const domain = isDiscretizedColor
|
|
249
288
|
? discretizedScale.domain
|
|
250
|
-
: categoryOrderDomain ?? binnedDomain ?? seriesDomain ?? resolveDefaultDomain({
|
|
289
|
+
: resolveMeasuredRadiusDomain({ scale, channel, allValues }) ?? categoryOrderDomain ?? binnedDomain ?? seriesDomain ?? resolveDefaultDomain({
|
|
251
290
|
scale,
|
|
252
291
|
allValues,
|
|
253
292
|
isOrdinalAppearance,
|
|
@@ -265,8 +304,16 @@ export function resolveScaleMaterialization({
|
|
|
265
304
|
isSequentialColor,
|
|
266
305
|
isDiscretizedColor,
|
|
267
306
|
isOrdinalOffset,
|
|
268
|
-
discretizedScale
|
|
307
|
+
discretizedScale,
|
|
308
|
+
polarFrame
|
|
269
309
|
});
|
|
310
|
+
validateSequentialMidpoint(scale.midpoint, scale.type, domain);
|
|
311
|
+
if (scale.midpoint !== undefined && (channel !== "color" || consumers.some(
|
|
312
|
+
consumer => consumer.encoding.fieldType !== "quantitative"
|
|
313
|
+
))) {
|
|
314
|
+
throw new Error("Scale midpoint requires quantitative color consumers.");
|
|
315
|
+
}
|
|
316
|
+
validateMeasuredRadiusConsumers({ scale, domain, range, consumers, markConfigs, thetaScales });
|
|
270
317
|
if (channel === "shape" && domain.length > range.length) {
|
|
271
318
|
throw new Error(
|
|
272
319
|
`Shape scale "${id}" requires at least one distinct shape per domain value.`
|
|
@@ -281,6 +328,7 @@ export function resolveScaleMaterialization({
|
|
|
281
328
|
values: allValues,
|
|
282
329
|
range: scale.range,
|
|
283
330
|
...resolveOffsetScalePolicy({
|
|
331
|
+
scale,
|
|
284
332
|
consumers,
|
|
285
333
|
resolvedScales,
|
|
286
334
|
markConfigs,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function assertEncodingSelectionCompatibility(program, target, channels) {
|
|
2
|
+
for (const [id, selection] of Object.entries(
|
|
3
|
+
program.materializationConfigs.selections ?? {}
|
|
4
|
+
)) {
|
|
5
|
+
if (selection.target === target && channels.includes(selection.selector?.channel)) {
|
|
6
|
+
throw new Error(
|
|
7
|
+
`Cannot remove ${selection.selector.channel} encoding while selection "${id}" references that channel.`
|
|
8
|
+
);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { deriveArcSectors } from "../../../grammar/arcs.js";
|
|
2
2
|
import { resolvePolarPoint } from "../../../grammar/polarPaths.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { readScaleField } from "../../../grammar/scales/index.js";
|
|
4
|
+
import { resolveCoordinatePolarFrame } from "../../coordinateBounds.js";
|
|
5
5
|
import {
|
|
6
6
|
concreteProperties,
|
|
7
7
|
finalizeItems,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
export function resolveArcItems(program, layer, dataset) {
|
|
12
12
|
const thetaScale = program.resolvedScales[layer.encoding?.theta?.scale];
|
|
13
13
|
const radiusScale = program.resolvedScales[layer.encoding?.radius?.scale];
|
|
14
|
-
const frame =
|
|
14
|
+
const frame = resolveCoordinatePolarFrame(program, layer.coordinate);
|
|
15
15
|
const derived = deriveArcSectors(dataset.values, layer, {
|
|
16
16
|
thetaScale,
|
|
17
17
|
...(radiusScale === undefined ? {} : { radiusScale }),
|
|
@@ -19,14 +19,25 @@ export function resolveArcItems(program, layer, dataset) {
|
|
|
19
19
|
innerRadiusRatio: program.markConfigs[layer.id]?.innerRadius ?? 0
|
|
20
20
|
});
|
|
21
21
|
const graphic = program.graphicSpec.objects[layer.id];
|
|
22
|
+
const stroke = layer.encoding?.stroke;
|
|
22
23
|
const definitions = derived.sectors.map((sector, index) => {
|
|
23
24
|
const members = sector.sourceIndices.map(index => dataset.values[index]);
|
|
25
|
+
const strokeValues = stroke === undefined ? undefined : readScaleField(
|
|
26
|
+
members,
|
|
27
|
+
stroke.field,
|
|
28
|
+
stroke.fieldType,
|
|
29
|
+
{ temporalUnit: stroke.temporalUnit }
|
|
30
|
+
);
|
|
31
|
+
if (strokeValues !== undefined && new Set(strokeValues).size !== 1) {
|
|
32
|
+
throw new Error("Arc stroke requires one value within each sector.");
|
|
33
|
+
}
|
|
24
34
|
return {
|
|
25
35
|
fields: uniqueFields(members),
|
|
26
36
|
channels: {
|
|
27
37
|
theta: sector.theta,
|
|
28
38
|
...(sector.radius === undefined ? {} : { radius: sector.radius }),
|
|
29
|
-
...(sector.color === undefined ? {} : { color: sector.color })
|
|
39
|
+
...(sector.color === undefined ? {} : { color: sector.color }),
|
|
40
|
+
...(strokeValues === undefined ? {} : { stroke: strokeValues[0] })
|
|
30
41
|
},
|
|
31
42
|
properties: {
|
|
32
43
|
...concreteProperties(graphic.items[index]?.properties),
|
|
@@ -36,6 +47,19 @@ export function resolveArcItems(program, layer, dataset) {
|
|
|
36
47
|
(sector.innerRadius + sector.outerRadius) / 2
|
|
37
48
|
)
|
|
38
49
|
},
|
|
50
|
+
geometry: {
|
|
51
|
+
kind: "arc",
|
|
52
|
+
centerX: frame.centerX,
|
|
53
|
+
centerY: frame.centerY,
|
|
54
|
+
startTheta: sector.startTheta +
|
|
55
|
+
Math.sign(sector.endTheta - sector.startTheta) *
|
|
56
|
+
(program.markConfigs[layer.id]?.padAngle ?? 0) / 2,
|
|
57
|
+
endTheta: sector.endTheta -
|
|
58
|
+
Math.sign(sector.endTheta - sector.startTheta) *
|
|
59
|
+
(program.markConfigs[layer.id]?.padAngle ?? 0) / 2,
|
|
60
|
+
innerRadius: sector.innerRadius,
|
|
61
|
+
outerRadius: sector.outerRadius
|
|
62
|
+
},
|
|
39
63
|
members
|
|
40
64
|
};
|
|
41
65
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { deriveSeriesBarCells } from "../../../grammar/bars/aggregate.js";
|
|
2
|
+
import { readSeriesIdentity } from "../../../grammar/pathSeries.js";
|
|
2
3
|
import {
|
|
3
4
|
BAR_GRAINS,
|
|
4
5
|
resolveBarChannels,
|
|
@@ -7,9 +8,9 @@ import {
|
|
|
7
8
|
resolveBarGrain
|
|
8
9
|
} from "../../../grammar/bars/policy.js";
|
|
9
10
|
import { numericExtent } from "../../../grammar/numeric.js";
|
|
10
|
-
import { layoutSeriesPartition } from "../../../grammar/seriesLayout.js";
|
|
11
11
|
import {
|
|
12
12
|
readNominalField,
|
|
13
|
+
readScaleField,
|
|
13
14
|
readTemporalField
|
|
14
15
|
} from "../../../grammar/scales/index.js";
|
|
15
16
|
import { findSemanticScale } from "../../../selectors/scales.js";
|
|
@@ -24,7 +25,7 @@ import {
|
|
|
24
25
|
|
|
25
26
|
function categoryValues(rows, encoding) {
|
|
26
27
|
return encoding.fieldType === "temporal"
|
|
27
|
-
? readTemporalField(rows, encoding.field)
|
|
28
|
+
? readTemporalField(rows, encoding.field, encoding.temporalUnit)
|
|
28
29
|
: readNominalField(rows, encoding.field);
|
|
29
30
|
}
|
|
30
31
|
|
|
@@ -36,28 +37,35 @@ function aggregateMembers(rows, layer, cell, channels) {
|
|
|
36
37
|
const colors = !["nominal", "ordinal"].includes(color?.fieldType)
|
|
37
38
|
? undefined
|
|
38
39
|
: readNominalField(rows, color.field);
|
|
40
|
+
const identity = layer.encoding?.group === undefined ? undefined : readSeriesIdentity(rows, layer);
|
|
39
41
|
return rows.filter((_, index) =>
|
|
40
42
|
categories[index] === categoryValue &&
|
|
41
|
-
(colors === undefined || colors[index] === cell.color)
|
|
43
|
+
(identity === undefined ? colors === undefined || colors[index] === cell.color : identity.values[index] === cell.series)
|
|
42
44
|
);
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
function aggregateCellDefinitions(program, layer, dataset) {
|
|
46
48
|
const channels = resolveBarChannels(layer);
|
|
47
|
-
const derived = deriveBarAggregates(dataset.values, layer).values;
|
|
48
49
|
const categoryEncoding = layer.encoding[channels.category];
|
|
49
50
|
const categoryScale = program.resolvedScales[categoryEncoding.scale];
|
|
50
|
-
const measureScale = program.resolvedScales[
|
|
51
|
-
layer.encoding[channels.measure].scale
|
|
52
|
-
];
|
|
53
51
|
const colorEncoding = layer.encoding?.color;
|
|
54
52
|
const colorScale = program.resolvedScales[colorEncoding?.scale];
|
|
55
53
|
const offsetChannel = resolveBarOffsetChannel(layer);
|
|
56
54
|
const offsetScale = program.resolvedScales[layer.encoding?.[offsetChannel]?.scale];
|
|
57
55
|
const layout = resolveBarColorLayout(layer);
|
|
56
|
+
const stroke = layer.encoding?.stroke;
|
|
58
57
|
|
|
59
58
|
function definition(cell, start, end) {
|
|
60
59
|
const members = aggregateMembers(dataset.values, layer, cell, channels);
|
|
60
|
+
const strokeValues = stroke === undefined ? undefined : readScaleField(
|
|
61
|
+
members,
|
|
62
|
+
stroke.field,
|
|
63
|
+
stroke.fieldType,
|
|
64
|
+
{ temporalUnit: stroke.temporalUnit }
|
|
65
|
+
);
|
|
66
|
+
if (strokeValues !== undefined && new Set(strokeValues).size !== 1) {
|
|
67
|
+
throw new Error("Bar stroke requires one value within each aggregate cell.");
|
|
68
|
+
}
|
|
61
69
|
return {
|
|
62
70
|
fields: uniqueFields(members),
|
|
63
71
|
channels: {
|
|
@@ -65,72 +73,20 @@ function aggregateCellDefinitions(program, layer, dataset) {
|
|
|
65
73
|
[channels.measure]: start,
|
|
66
74
|
[`${channels.measure}2`]: end,
|
|
67
75
|
...(cell.color === undefined ? {} : { color: cell.color }),
|
|
76
|
+
...(strokeValues === undefined ? {} : { stroke: strokeValues[0] }),
|
|
68
77
|
...(layer.encoding?.[offsetChannel] === undefined
|
|
69
78
|
? {}
|
|
70
|
-
: { [offsetChannel]: cell.color })
|
|
79
|
+
: { [offsetChannel]: cell.series ?? cell.color })
|
|
71
80
|
},
|
|
72
81
|
members
|
|
73
82
|
};
|
|
74
83
|
}
|
|
75
84
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const categories = ["ordinal", "band", "point"].includes(categoryScale.type)
|
|
82
|
-
? categoryScale.domain
|
|
83
|
-
: [...new Set(derived.map(cell => cell[channels.category]))]
|
|
84
|
-
.sort((left, right) => left - right);
|
|
85
|
-
return categories.flatMap(category => {
|
|
86
|
-
const cell = cells.get(category);
|
|
87
|
-
return cell === undefined
|
|
88
|
-
? []
|
|
89
|
-
: [definition(cell, measureScale.domain[0], cell[channels.measure])];
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (layout === "group" && offsetScale !== undefined) {
|
|
94
|
-
const categoryIndex = new Map(
|
|
95
|
-
categoryScale.domain.map((value, index) => [value, index])
|
|
96
|
-
);
|
|
97
|
-
const offsetIndex = new Map(
|
|
98
|
-
offsetScale.domain.map((value, index) => [value, index])
|
|
99
|
-
);
|
|
100
|
-
const cells = [...derived].sort((left, right) =>
|
|
101
|
-
categoryIndex.get(left[channels.category]) -
|
|
102
|
-
categoryIndex.get(right[channels.category]) ||
|
|
103
|
-
offsetIndex.get(left.color) - offsetIndex.get(right.color)
|
|
104
|
-
);
|
|
105
|
-
return cells.map(cell => definition(
|
|
106
|
-
cell,
|
|
107
|
-
measureScale.domain[0],
|
|
108
|
-
cell[channels.measure]
|
|
109
|
-
));
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const categories = ["ordinal", "band", "point"].includes(categoryScale.type)
|
|
113
|
-
? categoryScale.domain
|
|
114
|
-
: [...new Set(derived.map(cell => cell[channels.category]))]
|
|
115
|
-
.sort((left, right) => left - right);
|
|
116
|
-
const series = colorScale?.domain ?? [undefined];
|
|
117
|
-
const lookup = new Map(derived.map(cell => [
|
|
118
|
-
JSON.stringify([cell[channels.category], cell.color]),
|
|
119
|
-
cell
|
|
120
|
-
]));
|
|
121
|
-
const baseline = layout === "overlay" ? measureScale.domain[0] : 0;
|
|
122
|
-
return categories.flatMap(category => {
|
|
123
|
-
const cells = series.map(color =>
|
|
124
|
-
lookup.get(JSON.stringify([category, color]))
|
|
125
|
-
);
|
|
126
|
-
const values = cells.map(cell => cell?.[channels.measure] ?? 0);
|
|
127
|
-
return layoutSeriesPartition(values, layout, { baseline }).flatMap(segment => {
|
|
128
|
-
const cell = cells[segment.index];
|
|
129
|
-
return cell === undefined
|
|
130
|
-
? []
|
|
131
|
-
: [definition(cell, segment.start, segment.end)];
|
|
132
|
-
});
|
|
133
|
-
});
|
|
85
|
+
const categories = categoryScale.type === "time" ? undefined : categoryScale.domain;
|
|
86
|
+
const series = layout === "group" ? offsetScale?.domain
|
|
87
|
+
: layer.encoding.group === undefined && colorEncoding?.fieldType !== "quantitative" ? colorScale?.domain : undefined;
|
|
88
|
+
return deriveSeriesBarCells(dataset.values, layer, categories, series)
|
|
89
|
+
.map(({ cell, start, end }) => definition(cell, start, end));
|
|
134
90
|
}
|
|
135
91
|
|
|
136
92
|
function histogramDefinitions(program, layer, dataset) {
|
|
@@ -144,8 +100,18 @@ function histogramDefinitions(program, layer, dataset) {
|
|
|
144
100
|
resolvedScales: program.resolvedScales
|
|
145
101
|
});
|
|
146
102
|
const colorScale = program.resolvedScales[colorEncoding?.scale];
|
|
103
|
+
const stroke = layer.encoding?.stroke;
|
|
147
104
|
return segments.map(segment => {
|
|
148
|
-
const colorValue = colorScale?.domain[segment.category];
|
|
105
|
+
const colorValue = segment.colorValue ?? colorScale?.domain[segment.category];
|
|
106
|
+
const strokeValues = stroke === undefined ? undefined : readScaleField(
|
|
107
|
+
segment.members,
|
|
108
|
+
stroke.field,
|
|
109
|
+
stroke.fieldType,
|
|
110
|
+
{ temporalUnit: stroke.temporalUnit }
|
|
111
|
+
);
|
|
112
|
+
if (strokeValues !== undefined && new Set(strokeValues).size !== 1) {
|
|
113
|
+
throw new Error("Histogram stroke requires one value within each bin/series cell.");
|
|
114
|
+
}
|
|
149
115
|
return {
|
|
150
116
|
fields: uniqueFields(segment.members),
|
|
151
117
|
channels: {
|
|
@@ -153,7 +119,8 @@ function histogramDefinitions(program, layer, dataset) {
|
|
|
153
119
|
x2: segment.end,
|
|
154
120
|
y: segment.stackStart,
|
|
155
121
|
y2: segment.stackEnd,
|
|
156
|
-
...(colorValue === undefined ? {} : { color: colorValue })
|
|
122
|
+
...(colorValue === undefined ? {} : { color: colorValue }),
|
|
123
|
+
...(strokeValues === undefined ? {} : { stroke: strokeValues[0] })
|
|
157
124
|
},
|
|
158
125
|
members: segment.members
|
|
159
126
|
};
|
|
@@ -244,14 +211,18 @@ export function resolveBarItems(program, layer, dataset, selectionGrain) {
|
|
|
244
211
|
if (definitions === undefined) {
|
|
245
212
|
throw new Error(`Bar mark "${layer.id}" is incomplete for selection.`);
|
|
246
213
|
}
|
|
214
|
+
const graphicTypes = Object.hasOwn(
|
|
215
|
+
program.markConfigs[layer.id]?.barAppearance ?? {},
|
|
216
|
+
"cornerRadius"
|
|
217
|
+
) ? ["rect", "path"] : "rect";
|
|
247
218
|
if (selectionGrain === "stack") {
|
|
248
219
|
return finalizeItems(
|
|
249
220
|
program,
|
|
250
221
|
layer,
|
|
251
222
|
"stack",
|
|
252
223
|
barStackDefinitions(layer, grain, definitions),
|
|
253
|
-
|
|
224
|
+
graphicTypes
|
|
254
225
|
);
|
|
255
226
|
}
|
|
256
|
-
return finalizeItems(program, layer, grain, definitions,
|
|
227
|
+
return finalizeItems(program, layer, grain, definitions, graphicTypes);
|
|
257
228
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { normalizeTemporalValue } from "../../../grammar/scales/fields.js";
|
|
1
2
|
import { cloneAndFreeze, isPlainObject } from "../../../core/immutable.js";
|
|
2
3
|
import {
|
|
3
4
|
MARK_GRAPHIC_PROPERTIES,
|
|
@@ -5,6 +6,7 @@ import {
|
|
|
5
6
|
} from "../../../grammar/markSelection.js";
|
|
6
7
|
import { unionConcreteGraphicBounds } from
|
|
7
8
|
"../../../grammar/schemas/graphicBounds.js";
|
|
9
|
+
import { readRectItemGeometry } from "../../../grammar/roundedRect.js";
|
|
8
10
|
|
|
9
11
|
export function itemKey(layer, grain, index) {
|
|
10
12
|
return `${layer.id}/${grain}/${index}`;
|
|
@@ -16,8 +18,13 @@ function graphicId(layer, index) {
|
|
|
16
18
|
|
|
17
19
|
function encodingValue(row, encoding) {
|
|
18
20
|
if (encoding === undefined) return undefined;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
const value = Object.hasOwn(encoding, "field") ? row?.[encoding.field] : encoding.datum;
|
|
22
|
+
if (encoding.fieldType !== "temporal") return value;
|
|
23
|
+
try {
|
|
24
|
+
return normalizeTemporalValue(value, encoding.field, 0, encoding.temporalUnit);
|
|
25
|
+
} catch {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
21
28
|
}
|
|
22
29
|
|
|
23
30
|
export function channelMapFromRow(row, layer) {
|
|
@@ -48,14 +55,15 @@ export function uniqueFields(rows) {
|
|
|
48
55
|
|
|
49
56
|
function requireResolvedGraphic(program, layer, type) {
|
|
50
57
|
const graphic = program.graphicSpec.objects[layer.id];
|
|
58
|
+
const types = Array.isArray(type) ? type : [type];
|
|
51
59
|
const compatibleCollection = graphic?.type === "collection" &&
|
|
52
|
-
graphic.items?.every(child => child.type
|
|
60
|
+
graphic.items?.every(child => types.includes(child.type));
|
|
53
61
|
if (
|
|
54
|
-
(graphic?.type
|
|
62
|
+
(!types.includes(graphic?.type) && !compatibleCollection) ||
|
|
55
63
|
!Array.isArray(graphic.items)
|
|
56
64
|
) {
|
|
57
65
|
throw new Error(
|
|
58
|
-
`Mark "${layer.id}" requires a materialized ${
|
|
66
|
+
`Mark "${layer.id}" requires a materialized ${types.join("/")} collection for selection.`
|
|
59
67
|
);
|
|
60
68
|
}
|
|
61
69
|
return graphic;
|
|
@@ -72,6 +80,13 @@ export function concreteProperties(properties) {
|
|
|
72
80
|
);
|
|
73
81
|
}
|
|
74
82
|
|
|
83
|
+
function selectableGraphicProperties(item) {
|
|
84
|
+
return {
|
|
85
|
+
...concreteProperties(item?.properties),
|
|
86
|
+
...(readRectItemGeometry(item) ?? {})
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
75
90
|
function sharedConcreteProperties(items) {
|
|
76
91
|
if (items.length === 0) return {};
|
|
77
92
|
const shared = concreteProperties(items[0].properties);
|
|
@@ -112,7 +127,10 @@ export function finalizeItems(program, layer, grain, definitions, graphicType) {
|
|
|
112
127
|
const items = indices.map(childIndex => graphic.items[childIndex]);
|
|
113
128
|
const properties = definition.properties ?? (
|
|
114
129
|
items.length === 1
|
|
115
|
-
?
|
|
130
|
+
? selectableGraphicProperties({
|
|
131
|
+
type: items[0].type ?? graphic.type,
|
|
132
|
+
properties: items[0].properties
|
|
133
|
+
})
|
|
116
134
|
: {
|
|
117
135
|
...sharedConcreteProperties(items),
|
|
118
136
|
...collectionBounds(program, items)
|
|
@@ -125,6 +143,9 @@ export function finalizeItems(program, layer, grain, definitions, graphicType) {
|
|
|
125
143
|
fields: cloneAndFreeze(definition.fields),
|
|
126
144
|
channels: cloneAndFreeze(definition.channels),
|
|
127
145
|
properties: cloneAndFreeze(properties),
|
|
146
|
+
...(definition.geometry === undefined
|
|
147
|
+
? {}
|
|
148
|
+
: { geometry: cloneAndFreeze(definition.geometry) }),
|
|
128
149
|
members: Object.freeze([...definition.members]),
|
|
129
150
|
graphicIds: Object.freeze(items.map((child, childOffset) =>
|
|
130
151
|
child.id ?? graphicId(layer, indices[childOffset])
|
|
@@ -25,20 +25,10 @@ function rowsForSeries(rows, key) {
|
|
|
25
25
|
|
|
26
26
|
function seriesDefinitions(layer, rows, series) {
|
|
27
27
|
return series.map(item => {
|
|
28
|
-
const members = rowsForSeries(rows, item.key)
|
|
28
|
+
const members = item.sourceIndices === undefined ? rowsForSeries(rows, item.key)
|
|
29
|
+
: item.sourceIndices.map(index => rows[index]);
|
|
29
30
|
const fields = { ...uniqueFields(members), ...item.key };
|
|
30
|
-
const channels =
|
|
31
|
-
Object.entries(layer.encoding ?? {}).flatMap(([channel, encoding]) => {
|
|
32
|
-
if (encoding.field === undefined) {
|
|
33
|
-
return Object.hasOwn(encoding, "datum")
|
|
34
|
-
? [[channel, encoding.datum]]
|
|
35
|
-
: [];
|
|
36
|
-
}
|
|
37
|
-
return Object.hasOwn(fields, encoding.field)
|
|
38
|
-
? [[channel, fields[encoding.field]]]
|
|
39
|
-
: [];
|
|
40
|
-
})
|
|
41
|
-
);
|
|
31
|
+
const channels = channelMapFromRow(fields, layer);
|
|
42
32
|
return { fields, channels, members };
|
|
43
33
|
});
|
|
44
34
|
}
|