ggaction 0.0.9 → 0.0.11
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 +84 -0
- package/README.md +5 -4
- package/knowledge/action-card.schema.json +1 -0
- package/knowledge/action-cards.json +1 -10941
- package/knowledge/action-cards.schema.json +1 -0
- package/knowledge/extension-authoring.md +248 -0
- package/knowledge/intent-taxonomy.json +1 -183
- package/knowledge/intent-taxonomy.schema.json +1 -0
- package/knowledge/mcp-resources.json +1 -95
- package/knowledge/mcp-resources.schema.json +1 -0
- package/knowledge/task-packet.schema.json +1 -159
- package/knowledge/task-resolver.js +1 -1230
- package/package.json +19 -2
- package/src/actions/boxPlots/edit.js +45 -298
- package/src/actions/boxPlots/options.js +46 -77
- package/src/actions/boxPlots/resolve.js +1 -1
- package/src/actions/charts/bar.js +3 -0
- package/src/actions/charts/heatmap.js +73 -83
- package/src/actions/charts/index.js +1 -1
- package/src/actions/charts/parallel.js +35 -1
- package/src/actions/charts/shared.js +20 -0
- package/src/actions/composition/actions.js +46 -49
- package/src/actions/data/bin2d.js +105 -2
- package/src/actions/data/box.js +17 -17
- package/src/actions/data/density.js +42 -78
- package/src/actions/data/gradientProfile.js +22 -52
- package/src/actions/data/horizon.js +16 -41
- package/src/actions/data/index.js +10 -10
- package/src/actions/data/interval.js +16 -38
- package/src/actions/data/intervalEdit.js +55 -0
- package/src/actions/data/regression.js +7 -17
- package/src/actions/data/shared.js +47 -0
- package/src/actions/data/timeUnit.js +14 -38
- package/src/actions/data/window.js +14 -38
- package/src/actions/distributions/revision.js +154 -34
- package/src/actions/encodings/angle.js +9 -10
- package/src/actions/encodings/appearance.js +17 -51
- package/src/actions/encodings/density/resolve.js +3 -1
- package/src/actions/encodings/density.js +1 -15
- package/src/actions/encodings/horizon/resolve.js +13 -3
- package/src/actions/encodings/horizon.js +8 -7
- package/src/actions/encodings/index.js +9 -9
- package/src/actions/encodings/offset.js +8 -24
- package/src/actions/encodings/parallel.js +9 -10
- package/src/actions/encodings/pathOrder.js +6 -13
- package/src/actions/encodings/position/policies/index.js +4 -4
- package/src/actions/encodings/position/policies/line.js +47 -17
- package/src/actions/encodings/position/resolve.js +24 -5
- package/src/actions/encodings/ranged.js +18 -23
- package/src/actions/encodings/remove.js +15 -25
- package/src/actions/encodings/ruleAppearance.js +9 -24
- package/src/actions/encodings/shared.js +37 -0
- package/src/actions/encodings/strokeDash.js +8 -23
- package/src/actions/encodings/text.js +9 -10
- package/src/actions/errorBands/create.js +105 -39
- package/src/actions/errorBands/edit.js +89 -93
- package/src/actions/errorBands/index.js +1 -2
- package/src/actions/errorBars/create.js +78 -27
- package/src/actions/errorBars/edit.js +81 -48
- package/src/actions/errorBars/index.js +1 -2
- package/src/actions/facets/actions.js +45 -69
- package/src/actions/facets/derive.js +38 -53
- package/src/actions/facets/replay.js +0 -1
- package/src/actions/gradientPlots/components.js +59 -48
- package/src/actions/gradientPlots/edit.js +35 -216
- package/src/actions/gradientPlots/index.js +11 -10
- package/src/actions/gradientPlots/materialize.js +21 -35
- package/src/actions/gradientPlots/options.js +5 -7
- package/src/actions/gradientPlots/paint.js +9 -6
- package/src/actions/gradientPlots/resolve.js +1 -1
- package/src/actions/guides/axes/index.js +3 -3
- package/src/actions/guides/axes/labels.js +68 -26
- package/src/actions/guides/axes/lines.js +22 -2
- package/src/actions/guides/axes/policy.js +28 -8
- package/src/actions/guides/axes/ticks.js +27 -9
- package/src/actions/guides/axes/titles.js +113 -65
- package/src/actions/guides/grids/resolve.js +14 -0
- package/src/actions/guides/guides.js +2 -4
- package/src/actions/guides/index.js +2 -2
- package/src/actions/guides/legends/categorical/actions.js +44 -68
- package/src/actions/guides/legends/categorical/components.js +6 -1
- package/src/actions/guides/legends/categorical/index.js +2 -2
- package/src/actions/guides/legends/categorical/layout.js +148 -175
- package/src/actions/guides/legends/categorical/recipes.js +5 -0
- package/src/actions/guides/legends/categorical/resolve.js +8 -1
- package/src/actions/guides/legends/continuous/common.js +117 -66
- package/src/actions/guides/legends/continuous/gradient.js +38 -37
- package/src/actions/guides/legends/continuous/interval.js +97 -76
- package/src/actions/guides/legends/continuous/opacity.js +28 -38
- package/src/actions/guides/legends/edit.js +14 -1
- package/src/actions/guides/legends/index.js +1 -1
- package/src/actions/guides/legends/lane.js +2 -1
- package/src/actions/guides/legends/remove.js +8 -0
- package/src/actions/guides/legends/size.js +36 -43
- package/src/actions/guides/legends/strokeWidth.js +20 -26
- package/src/actions/guides/polar/axes/labels.js +17 -1
- package/src/actions/guides/polar/axes/shared.js +7 -1
- package/src/actions/guides/polar/axes/titles.js +19 -2
- package/src/actions/guides/polar/grids.js +4 -0
- package/src/actions/guides/polar/resolve.js +6 -0
- package/src/actions/index.js +13 -13
- package/src/actions/intervals/resolve.js +87 -117
- package/src/actions/marks/arc/actions.js +31 -36
- package/src/actions/marks/area/actions.js +23 -31
- package/src/actions/marks/index.js +6 -6
- package/src/actions/marks/line/actions.js +33 -52
- package/src/actions/marks/line/materialize.js +7 -5
- package/src/actions/marks/point/materialize.js +5 -3
- package/src/actions/marks/remove.js +21 -0
- package/src/actions/marks/rule/actions.js +53 -53
- package/src/actions/marks/shared.js +8 -4
- package/src/actions/marks/text/actions.js +14 -6
- package/src/actions/marks/text/layout.js +21 -45
- package/src/actions/marks/tick/actions.js +22 -21
- package/src/actions/primitives/createGraphics.js +4 -0
- package/src/actions/primitives/editGraphics.js +4 -0
- package/src/actions/primitives/index.js +3 -3
- package/src/actions/primitives/semantic.js +1 -1
- package/src/actions/primitives/semanticValidation/index.js +2 -2
- package/src/actions/primitives/semanticValidation/layer.js +1 -1
- package/src/actions/regression/edit.js +3 -43
- package/src/actions/scales/consumers/families.js +24 -4
- package/src/actions/scales/consumers/index.js +1 -1
- package/src/actions/scales/consumers/seriesLayout.js +17 -0
- package/src/actions/scales/definitions.js +100 -103
- package/src/actions/scales/materialize.js +5 -0
- package/src/actions/selection/actions.js +79 -121
- package/src/actions/titles/actions.js +48 -94
- package/src/actions/titles/resolve.js +14 -27
- package/src/actions/violinPlots/create.js +37 -44
- package/src/core/action.js +20 -5
- package/src/core/extensionRegistry.js +104 -0
- package/src/core/textMetrics.js +74 -37
- package/src/core/validation.js +18 -0
- package/src/extension.js +8 -1
- package/src/grammar/aggregate.js +48 -28
- package/src/grammar/arcs.js +58 -11
- package/src/grammar/areaSeries.js +14 -16
- package/src/grammar/bin2d.js +37 -23
- package/src/grammar/boxPlot.js +14 -13
- package/src/grammar/categoricalDensity.js +14 -14
- package/src/grammar/categoryOrder.js +8 -14
- package/src/grammar/curveCommands.js +87 -20
- package/src/grammar/density.js +162 -141
- package/src/grammar/facets/index.js +14 -0
- package/src/grammar/facets/scales.js +7 -4
- package/src/grammar/gradientProfile.js +12 -15
- package/src/grammar/histogram.js +145 -83
- package/src/grammar/horizon.js +60 -23
- package/src/grammar/interval.js +14 -13
- package/src/grammar/lineSeries.js +91 -61
- package/src/grammar/numeric.js +290 -0
- package/src/grammar/paint.js +3 -4
- package/src/grammar/palettes.js +21 -32
- package/src/grammar/pathCommands.js +6 -0
- package/src/grammar/polarPaths.js +5 -5
- package/src/grammar/regression/derive.js +48 -9
- package/src/grammar/regression/models.js +248 -69
- package/src/grammar/regression/parameters.js +8 -0
- package/src/grammar/scales/color.js +35 -26
- package/src/grammar/scales/continuous.js +71 -24
- package/src/grammar/scales/definition.js +1 -1
- package/src/grammar/scales/discretized.js +42 -12
- package/src/grammar/scales/fields.js +21 -4
- package/src/grammar/scales/ordinal.js +117 -61
- package/src/grammar/scales/temporal.js +40 -11
- package/src/grammar/scales/transformed.js +199 -52
- package/src/grammar/schemas/graphicBounds.js +270 -79
- package/src/grammar/seriesLayout.js +50 -18
- package/src/grammar/ticks.js +123 -53
- package/src/grammar/timeUnit.js +18 -8
- package/src/grammar/transforms.js +4 -1
- package/src/grammar/window.js +70 -57
- package/src/index.js +1 -1
- package/src/layout/canvas.js +4 -8
- package/src/layout/composition.js +39 -40
- package/src/layout/facets.js +10 -14
- package/src/layout/labels.js +91 -15
- package/src/layout/legend.js +22 -20
- package/src/layout/legendLane.js +54 -40
- package/src/layout/title.js +7 -7
- package/src/materialization/bars/aggregate.js +14 -40
- package/src/materialization/bars/grouped.js +7 -18
- package/src/materialization/bars/histogram.js +2 -19
- package/src/materialization/bars/ranged.js +20 -15
- package/src/materialization/bars/resolve.js +21 -0
- package/src/materialization/facetGuides/legacyCategorical.js +2 -1
- package/src/materialization/facetGuides/placement.js +34 -19
- package/src/materialization/facetGuides/preparation.js +9 -5
- package/src/materialization/facets.js +39 -27
- package/src/materialization/guides/resources.js +2 -1
- package/src/materialization/marks/capabilities.js +5 -2
- package/src/materialization/marks/index.js +24 -4
- package/src/materialization/scales/policies/bar.js +61 -59
- package/src/materialization/scales/policies/binnedPosition.js +34 -0
- package/src/materialization/scales/policies/series.js +13 -7
- package/src/materialization/scales/resolve.js +4 -5
- package/src/materialization/selection/items/arc.js +16 -2
- package/src/materialization/selection/items/bar.js +4 -2
- package/src/materialization/selection/items/common.js +21 -1
- package/src/materialization/selection/items/path.js +19 -2
- package/src/materialization/selection/items/point.js +4 -16
- package/src/materialization/selection/items/tick.js +13 -21
- package/src/materialization/selection/point.js +11 -4
- package/src/materialization/selection/styles.js +19 -27
- package/src/materialization/text.js +4 -1
- package/src/mcp/adapter.js +8 -3
- package/src/renderers/canvas/fill.js +2 -2
- package/src/renderers/canvas/index.js +35 -2
- package/src/renderers/canvas/native.js +160 -0
- package/src/renderers/pdf.js +14 -0
- package/src/renderers/svg.js +37 -4
- package/types/extension.d.ts +19 -0
- package/types/index.d.ts +3 -0
- package/types/program.d.ts +509 -131
- package/types/svg.d.ts +1 -0
- package/src/actions/errorBands/components.js +0 -81
- package/src/actions/errorBands/options.js +0 -39
- package/src/actions/errorBands/resolve.js +0 -28
- package/src/actions/errorBars/components.js +0 -62
- package/src/actions/errorBars/options.js +0 -58
- package/src/actions/errorBars/resolve.js +0 -25
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { mapContinuousScaleValues, mapOrdinalPositionValues, mapOrdinalValues } from "../../grammar/scales/index.js";
|
|
2
2
|
import { resolveBarWidth } from "../../grammar/bars/geometry.js";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
DEFAULT_BAR_FILL,
|
|
5
|
+
DEFAULT_BAR_STROKE,
|
|
6
|
+
resolveBarAppearance
|
|
7
|
+
} from "./resolve.js";
|
|
4
8
|
|
|
5
9
|
export function deriveRangedRectangles(required, program, width) {
|
|
6
10
|
const { layer, dataset } = required;
|
|
@@ -29,25 +33,26 @@ export function deriveRangedRectangles(required, program, width) {
|
|
|
29
33
|
const fills = color === undefined
|
|
30
34
|
? dataset.values.map(() => appearance.fill ?? config.fill ?? DEFAULT_BAR_FILL)
|
|
31
35
|
: mapOrdinalValues(dataset.values.map(row => row[color.field]), program.resolvedScales[color.scale].domain, program.resolvedScales[color.scale].range);
|
|
36
|
+
const appearanceConfig = color === undefined
|
|
37
|
+
? config
|
|
38
|
+
: { ...config, stroke: undefined };
|
|
32
39
|
return dataset.values.map((_, index) => vertical ? {
|
|
33
40
|
x: centers[index] - band / 2, y: Math.min(first[index], second[index]), width: band,
|
|
34
41
|
height: Math.abs(second[index] - first[index]), fill: fills[index],
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
opacity: appearance.opacity ?? config.opacity ?? 1
|
|
42
|
+
...resolveBarAppearance(
|
|
43
|
+
appearanceConfig,
|
|
44
|
+
undefined,
|
|
45
|
+
color === undefined ? DEFAULT_BAR_STROKE : fills[index],
|
|
46
|
+
1
|
|
47
|
+
)
|
|
42
48
|
} : {
|
|
43
49
|
x: Math.min(first[index], second[index]), y: centers[index] - band / 2,
|
|
44
50
|
width: Math.abs(second[index] - first[index]), height: band, fill: fills[index],
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
opacity: appearance.opacity ?? config.opacity ?? 1
|
|
51
|
+
...resolveBarAppearance(
|
|
52
|
+
appearanceConfig,
|
|
53
|
+
undefined,
|
|
54
|
+
color === undefined ? DEFAULT_BAR_STROKE : fills[index],
|
|
55
|
+
1
|
|
56
|
+
)
|
|
52
57
|
});
|
|
53
58
|
}
|
|
@@ -8,6 +8,27 @@ export const DEFAULT_BAR_FILL = DEFAULT_COLORS.mark;
|
|
|
8
8
|
export const DEFAULT_BAR_STROKE = "white";
|
|
9
9
|
export const DEFAULT_BAR_STROKE_WIDTH = 0.5;
|
|
10
10
|
|
|
11
|
+
export function resolveBarAppearance(
|
|
12
|
+
config,
|
|
13
|
+
existing,
|
|
14
|
+
defaultStroke = DEFAULT_BAR_STROKE,
|
|
15
|
+
defaultOpacity
|
|
16
|
+
) {
|
|
17
|
+
const appearance = config.barAppearance ?? {};
|
|
18
|
+
const opacity = appearance.opacity ?? config.opacity ??
|
|
19
|
+
existing?.opacity ?? defaultOpacity;
|
|
20
|
+
return {
|
|
21
|
+
stroke: appearance.stroke === false
|
|
22
|
+
? "transparent"
|
|
23
|
+
: appearance.stroke ?? config.stroke ?? existing?.stroke ?? defaultStroke,
|
|
24
|
+
strokeWidth: appearance.stroke === false
|
|
25
|
+
? 0
|
|
26
|
+
: appearance.strokeWidth ?? config.strokeWidth ??
|
|
27
|
+
existing?.strokeWidth ?? DEFAULT_BAR_STROKE_WIDTH,
|
|
28
|
+
...(opacity === undefined ? {} : { opacity })
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
11
32
|
export function requireCompleteBar(program, id) {
|
|
12
33
|
const layer = findLayer(program, id);
|
|
13
34
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
resolveSharedFacetLegends
|
|
3
3
|
} from "../../grammar/facets/guides.js";
|
|
4
|
+
import { formatVisibleText } from "../../core/textMetrics.js";
|
|
4
5
|
import { mapOrdinalValues } from "../../grammar/scales/index.js";
|
|
5
6
|
import { DEFAULT_COLORS, DEFAULT_FONT_FAMILY } from "../../theme/defaults.js";
|
|
6
7
|
|
|
@@ -10,7 +11,7 @@ function textItem(text, x, y, options = {}) {
|
|
|
10
11
|
properties: {
|
|
11
12
|
x,
|
|
12
13
|
y,
|
|
13
|
-
text:
|
|
14
|
+
text: formatVisibleText(text),
|
|
14
15
|
fill: options.color ?? DEFAULT_COLORS.strongText,
|
|
15
16
|
fontSize: options.fontSize ?? 12,
|
|
16
17
|
fontFamily: options.fontFamily ?? DEFAULT_FONT_FAMILY,
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { isPlainObject } from "../../core/immutable.js";
|
|
2
|
+
import { resolveConcreteGraphicBounds } from
|
|
3
|
+
"../../grammar/schemas/graphicBounds.js";
|
|
2
4
|
import { planFacetGuideOwnership } from "../../grammar/facets/guides.js";
|
|
3
5
|
import {
|
|
4
6
|
axisGraphicIds,
|
|
@@ -30,25 +32,14 @@ function removeNamespacedGraphics(program, namespace, ids) {
|
|
|
30
32
|
return next;
|
|
31
33
|
}
|
|
32
34
|
|
|
33
|
-
function gradientAnchor(prepared, plot, layout) {
|
|
34
|
-
const strips = prepared.source.graphicSpec.objects.colorGradientStrips;
|
|
35
|
-
if (strips?.items?.[0] === undefined) return undefined;
|
|
36
|
-
const properties = strips.items[0].properties;
|
|
37
|
-
const length = prepared.source.guideConfigs.legend.gradient.gradient.length;
|
|
38
|
-
return {
|
|
39
|
-
sourceX: properties.x,
|
|
40
|
-
sourceY: properties.y,
|
|
41
|
-
targetX: layout.legend.x,
|
|
42
|
-
targetY: plot.y + (plot.height - length) / 2
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
|
|
46
35
|
function legendTranslation(prepared, plot, layout) {
|
|
47
|
-
const
|
|
48
|
-
if (
|
|
36
|
+
const strips = prepared.source.graphicSpec.objects.colorGradientStrips;
|
|
37
|
+
if (strips?.items?.[0] !== undefined) {
|
|
38
|
+
const properties = strips.items[0].properties;
|
|
39
|
+
const length = prepared.source.guideConfigs.legend.gradient.gradient.length;
|
|
49
40
|
return {
|
|
50
|
-
x:
|
|
51
|
-
y:
|
|
41
|
+
x: layout.legend.x - properties.x,
|
|
42
|
+
y: plot.y + (plot.height - length) / 2 - properties.y
|
|
52
43
|
};
|
|
53
44
|
}
|
|
54
45
|
const height = prepared.bounds.bottom - prepared.bounds.top;
|
|
@@ -79,6 +70,25 @@ function attachParentLegend(program, prepared, plot, layout) {
|
|
|
79
70
|
return next;
|
|
80
71
|
}
|
|
81
72
|
|
|
73
|
+
function assertSharedLegendFits(program, id, layout, plot) {
|
|
74
|
+
const bounds = resolveConcreteGraphicBounds(program.graphicSpec, id);
|
|
75
|
+
if (bounds === undefined) {
|
|
76
|
+
throw new Error("Facet shared legend requires measurable concrete bounds.");
|
|
77
|
+
}
|
|
78
|
+
const outside = bounds.left < 0 || bounds.right > layout.width ||
|
|
79
|
+
bounds.top < 0 || bounds.bottom > layout.height;
|
|
80
|
+
const overlapsPlot = Math.min(bounds.right, plot.x + plot.width) >
|
|
81
|
+
Math.max(bounds.left, plot.x) &&
|
|
82
|
+
Math.min(bounds.bottom, plot.y + plot.height) >
|
|
83
|
+
Math.max(bounds.top, plot.y);
|
|
84
|
+
if (outside || overlapsPlot) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
"Facet shared legend requires more space than the composed Canvas provides."
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
return program;
|
|
90
|
+
}
|
|
91
|
+
|
|
82
92
|
export function applyFacetGuideComposition(program, { layout, plot } = {}) {
|
|
83
93
|
if (!isPlainObject(layout) || !Array.isArray(layout.children)) {
|
|
84
94
|
throw new TypeError("Facet guide composition requires a resolved layout.");
|
|
@@ -119,8 +129,13 @@ export function applyFacetGuideComposition(program, { layout, plot } = {}) {
|
|
|
119
129
|
}
|
|
120
130
|
}
|
|
121
131
|
if (prepared === undefined) return next;
|
|
132
|
+
let id;
|
|
122
133
|
if (prepared.mode === "legacyCategorical") {
|
|
123
|
-
|
|
134
|
+
id = `${program.compositionSpec.id}-legend`;
|
|
135
|
+
next = materializeLegacyCategoricalLegend(next, prepared, layout);
|
|
136
|
+
} else {
|
|
137
|
+
id = `${program.compositionSpec.id}-shared-legend`;
|
|
138
|
+
next = attachParentLegend(next, prepared, plot, layout);
|
|
124
139
|
}
|
|
125
|
-
return
|
|
140
|
+
return assertSharedLegendFits(next, id, layout, plot);
|
|
126
141
|
}
|
|
@@ -9,6 +9,10 @@ import { findSemanticScale } from "../../selectors/scales.js";
|
|
|
9
9
|
import { allLegendGraphicIds } from "../guides/resources.js";
|
|
10
10
|
import { DEFAULT_COLORS } from "../../theme/defaults.js";
|
|
11
11
|
import { resolveLegacyCategoricalLegend } from "./legacyCategorical.js";
|
|
12
|
+
import {
|
|
13
|
+
formatDistinctNumericSamples,
|
|
14
|
+
sampleNumericRange
|
|
15
|
+
} from "../../grammar/numeric.js";
|
|
12
16
|
|
|
13
17
|
const LEGEND_SOURCE_CANVAS = Object.freeze({
|
|
14
18
|
width: 640,
|
|
@@ -71,10 +75,7 @@ function compactNumber(value) {
|
|
|
71
75
|
}
|
|
72
76
|
|
|
73
77
|
function continuousValues(domain, count) {
|
|
74
|
-
return
|
|
75
|
-
{ length: count },
|
|
76
|
-
(_, index) => domain[0] + index / (count - 1) * (domain[1] - domain[0])
|
|
77
|
-
);
|
|
78
|
+
return sampleNumericRange(domain[0], domain[1], count, "Facet legend domain");
|
|
78
79
|
}
|
|
79
80
|
|
|
80
81
|
function prepareAutoLegendSource(child) {
|
|
@@ -123,7 +124,10 @@ function prepareAutoLegendSource(child) {
|
|
|
123
124
|
.editGraphics({
|
|
124
125
|
target: "colorGradientLabels",
|
|
125
126
|
property: "text",
|
|
126
|
-
value:
|
|
127
|
+
value: formatDistinctNumericSamples(
|
|
128
|
+
continuousValues(resolved.domain, 5),
|
|
129
|
+
compactNumber
|
|
130
|
+
)
|
|
127
131
|
})
|
|
128
132
|
.editGraphics({
|
|
129
133
|
target: "colorGradientTitle",
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import { resolveGraphicBounds } from "../layout/canvas.js";
|
|
2
|
+
import {
|
|
3
|
+
formatVisibleText,
|
|
4
|
+
resolveTextBounds,
|
|
5
|
+
textBoundsIntersect
|
|
6
|
+
} from "../core/textMetrics.js";
|
|
2
7
|
import { resolvePlacedPlotBounds } from "../layout/composition.js";
|
|
3
8
|
import {
|
|
4
9
|
alignedTextAnchor,
|
|
@@ -12,9 +17,8 @@ import {
|
|
|
12
17
|
clearCompositionChildren,
|
|
13
18
|
compositionChildDescriptor
|
|
14
19
|
} from "./composition.js";
|
|
15
|
-
import { DEFAULT_COLORS, DEFAULT_FONT_FAMILY } from "../theme/defaults.js";
|
|
16
20
|
|
|
17
|
-
const ZERO_MARGIN =
|
|
21
|
+
const ZERO_MARGIN = { top: 0, right: 0, bottom: 0, left: 0 };
|
|
18
22
|
|
|
19
23
|
function facetConfig(program) {
|
|
20
24
|
const config = program.materializationConfigs.facets?.[program.compositionSpec.id];
|
|
@@ -46,29 +50,35 @@ function titleLayout(program) {
|
|
|
46
50
|
return { config, block, top, height: Math.ceil(top + block.height) };
|
|
47
51
|
}
|
|
48
52
|
|
|
49
|
-
function textItem(text, x, y, options = {}) {
|
|
50
|
-
return {
|
|
51
|
-
type: "text",
|
|
52
|
-
properties: {
|
|
53
|
-
x,
|
|
54
|
-
y,
|
|
55
|
-
text: String(text),
|
|
56
|
-
fill: options.color ?? DEFAULT_COLORS.strongText,
|
|
57
|
-
fontSize: options.fontSize ?? 12,
|
|
58
|
-
fontFamily: options.fontFamily ?? DEFAULT_FONT_FAMILY,
|
|
59
|
-
fontWeight: options.fontWeight ?? "normal",
|
|
60
|
-
textAlign: options.textAlign ?? "left",
|
|
61
|
-
textBaseline: options.textBaseline ?? "middle"
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
|
|
66
53
|
function collection(program, id, items) {
|
|
67
54
|
return program
|
|
68
55
|
.createGraphics({ id, type: "collection", parent: "canvas" })
|
|
69
56
|
.editGraphics({ target: id, property: "items", value: items });
|
|
70
57
|
}
|
|
71
58
|
|
|
59
|
+
function assertFacetHeadersFit(items, layout, plotById) {
|
|
60
|
+
const previous = [];
|
|
61
|
+
for (let index = 0; index < items.length; index += 1) {
|
|
62
|
+
const bounds = resolveTextBounds(items[index].properties);
|
|
63
|
+
const cell = layout.children[index];
|
|
64
|
+
const plot = plotById.get(cell.id);
|
|
65
|
+
if (bounds.left < 0 || bounds.right > layout.width ||
|
|
66
|
+
bounds.top < 0 || bounds.bottom > layout.height ||
|
|
67
|
+
previous.some(item => textBoundsIntersect(item, bounds)) ||
|
|
68
|
+
textBoundsIntersect(bounds, {
|
|
69
|
+
left: cell.x + plot.x,
|
|
70
|
+
right: cell.x + plot.x + plot.width,
|
|
71
|
+
top: cell.y + plot.y,
|
|
72
|
+
bottom: cell.y + plot.y + plot.height
|
|
73
|
+
})) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
"Facet headers require sufficient non-overlapping space above every child plot."
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
previous.push(bounds);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
72
82
|
function materializeHeaders(program, layout, plots, config) {
|
|
73
83
|
const plotById = new Map(plots.map(plot => [plot.id, plot]));
|
|
74
84
|
const items = layout.children.map(cell => {
|
|
@@ -76,20 +86,22 @@ function materializeHeaders(program, layout, plots, config) {
|
|
|
76
86
|
if (plot === undefined) {
|
|
77
87
|
throw new Error(`Facet header requires plot bounds for "${cell.id}".`);
|
|
78
88
|
}
|
|
79
|
-
return
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
89
|
+
return {
|
|
90
|
+
type: "text",
|
|
91
|
+
properties: {
|
|
92
|
+
x: cell.x + plot.x + plot.width / 2,
|
|
93
|
+
y: cell.y + config.offset,
|
|
94
|
+
text: formatVisibleText(cell.value),
|
|
95
|
+
fill: config.color,
|
|
85
96
|
fontSize: config.fontSize,
|
|
86
97
|
fontFamily: config.fontFamily,
|
|
87
98
|
fontWeight: config.fontWeight,
|
|
88
99
|
textAlign: "center",
|
|
89
|
-
textBaseline: "
|
|
100
|
+
textBaseline: "middle"
|
|
90
101
|
}
|
|
91
|
-
|
|
102
|
+
};
|
|
92
103
|
});
|
|
104
|
+
assertFacetHeadersFit(items, layout, plotById);
|
|
93
105
|
return collection(program, `${program.compositionSpec.id}-headers`, items);
|
|
94
106
|
}
|
|
95
107
|
|
|
@@ -44,7 +44,8 @@ const LEGEND_RESOURCE_POLICIES = Object.freeze({
|
|
|
44
44
|
family: "interval",
|
|
45
45
|
rematerializeOp: "rematerializeIntervalLegend",
|
|
46
46
|
graphicIds: Object.freeze([
|
|
47
|
-
"
|
|
47
|
+
"colorLegendBackground", "colorLegendSymbols", "colorLegendLabels",
|
|
48
|
+
"colorLegendTitle"
|
|
48
49
|
])
|
|
49
50
|
}),
|
|
50
51
|
opacity: Object.freeze({
|
|
@@ -59,7 +59,10 @@ export function canMaterializeLine(program, layer) {
|
|
|
59
59
|
return (
|
|
60
60
|
layer.mark?.type === "line" &&
|
|
61
61
|
hasCartesianPositionScales(layer) &&
|
|
62
|
-
(isAggregate(layer.encoding.y.aggregate)
|
|
62
|
+
((isAggregate(layer.encoding.y.aggregate) &&
|
|
63
|
+
(layer.encoding.x.fieldType === "temporal" ||
|
|
64
|
+
(layer.encoding.x.fieldType === "quantitative" &&
|
|
65
|
+
layer.encoding.x.bin !== undefined))) ||
|
|
63
66
|
(layer.encoding.x.fieldType === "quantitative" &&
|
|
64
67
|
layer.encoding.y.fieldType === "quantitative") ||
|
|
65
68
|
((interval || layer.encoding.y.aggregate === undefined) &&
|
|
@@ -159,7 +162,7 @@ export function canMaterializeText(program, layer) {
|
|
|
159
162
|
if (layer.source !== undefined) {
|
|
160
163
|
const source = findLayer(program, layer.source);
|
|
161
164
|
return source !== undefined &&
|
|
162
|
-
["point", "bar", "rule", "rect"].includes(source.mark?.type) &&
|
|
165
|
+
["point", "bar", "rule", "rect", "arc"].includes(source.mark?.type) &&
|
|
163
166
|
Array.isArray(program.graphicSpec.objects[source.id]?.items);
|
|
164
167
|
}
|
|
165
168
|
return hasCartesianPositionScales(layer);
|
|
@@ -42,14 +42,34 @@ export function getSourceDependentMarkSteps(program, sourceId) {
|
|
|
42
42
|
export function getPositionEncodingMaterializationSteps(program, layer, scaleId) {
|
|
43
43
|
const policy = getMarkMaterializationPolicy(layer);
|
|
44
44
|
if (policy === undefined) return [];
|
|
45
|
-
const scale = { op: "rematerializeScale", args: { id: scaleId } };
|
|
46
45
|
const complete = policy.canMaterialize(program, layer);
|
|
47
46
|
const mark = getMarkRematerializationStep(layer);
|
|
47
|
+
const sharedConsumerMarks = (program.semanticSpec.layers ?? [])
|
|
48
|
+
.filter(candidate =>
|
|
49
|
+
candidate.id !== layer.id &&
|
|
50
|
+
POSITION_CHANNELS.some(channel =>
|
|
51
|
+
candidate.encoding?.[channel]?.scale === scaleId
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
.map(candidate => getMarkMaterializationStep(program, candidate))
|
|
55
|
+
.filter(step => step !== undefined);
|
|
56
|
+
const scale = {
|
|
57
|
+
op: "rematerializeScale",
|
|
58
|
+
args: sharedConsumerMarks.length === 0
|
|
59
|
+
? { id: scaleId }
|
|
60
|
+
: { id: scaleId, marks: false }
|
|
61
|
+
};
|
|
48
62
|
if (!complete) {
|
|
49
|
-
if (policy.positionEncoding.incomplete === "scale")
|
|
50
|
-
|
|
63
|
+
if (policy.positionEncoding.incomplete === "scale") {
|
|
64
|
+
return [scale, ...sharedConsumerMarks];
|
|
65
|
+
}
|
|
66
|
+
return policy.positionEncoding.scaleFirst
|
|
67
|
+
? [scale, mark, ...sharedConsumerMarks]
|
|
68
|
+
: [mark, ...sharedConsumerMarks];
|
|
51
69
|
}
|
|
52
|
-
return policy.positionEncoding.scaleFirst
|
|
70
|
+
return policy.positionEncoding.scaleFirst
|
|
71
|
+
? [scale, mark, ...sharedConsumerMarks]
|
|
72
|
+
: [mark, ...sharedConsumerMarks];
|
|
53
73
|
}
|
|
54
74
|
|
|
55
75
|
export function getScaleConsumerMaterializationMode(layer, channel) {
|
|
@@ -4,41 +4,10 @@ import {
|
|
|
4
4
|
resolveBarChannels,
|
|
5
5
|
resolveBarGrain
|
|
6
6
|
} from "../../../grammar/bars/policy.js";
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
channel,
|
|
12
|
-
scale,
|
|
13
|
-
id,
|
|
14
|
-
allValues
|
|
15
|
-
}) {
|
|
16
|
-
const binnedBars = valuesByConsumer.filter(
|
|
17
|
-
({ consumer }) =>
|
|
18
|
-
consumer.layer.mark?.type === "bar" &&
|
|
19
|
-
consumer.encoding.bin !== undefined
|
|
20
|
-
);
|
|
21
|
-
if (binnedBars.length === 0) return undefined;
|
|
22
|
-
if (channel !== "x" || binnedBars.length !== valuesByConsumer.length) {
|
|
23
|
-
throw new Error(
|
|
24
|
-
`Binned scale "${id}" cannot be shared with an unbinned consumer.`
|
|
25
|
-
);
|
|
26
|
-
}
|
|
27
|
-
const binDefinitions = new Set(
|
|
28
|
-
binnedBars.map(({ consumer }) => JSON.stringify(consumer.encoding.bin))
|
|
29
|
-
);
|
|
30
|
-
if (binDefinitions.size !== 1) {
|
|
31
|
-
throw new Error(`Binned scale "${id}" requires one shared bin definition.`);
|
|
32
|
-
}
|
|
33
|
-
return resolveHistogramBins({
|
|
34
|
-
values: allValues,
|
|
35
|
-
bin: binnedBars[0].consumer.encoding.bin,
|
|
36
|
-
domain: scale.domain,
|
|
37
|
-
nice: scale.nice ?? true,
|
|
38
|
-
zero: scale.zero ?? false
|
|
39
|
-
}).domain;
|
|
40
|
-
}
|
|
41
|
-
|
|
7
|
+
import {
|
|
8
|
+
interpolateNumber,
|
|
9
|
+
inverseLerp
|
|
10
|
+
} from "../../../grammar/numeric.js";
|
|
42
11
|
export function resolveOffsetScalePolicy({
|
|
43
12
|
consumers,
|
|
44
13
|
resolvedScales,
|
|
@@ -81,8 +50,8 @@ export function resolveOffsetScalePolicy({
|
|
|
81
50
|
};
|
|
82
51
|
}
|
|
83
52
|
|
|
84
|
-
export function resolveTemporalBarBand(
|
|
85
|
-
const temporalBars =
|
|
53
|
+
export function resolveTemporalBarBand(valuesByConsumer, domain, range) {
|
|
54
|
+
const temporalBars = valuesByConsumer.filter(({ consumer }) => {
|
|
86
55
|
const channels = resolveBarChannels(consumer.layer);
|
|
87
56
|
return resolveBarGrain(consumer.layer) === BAR_GRAINS.aggregate &&
|
|
88
57
|
channels?.category === consumer.channel &&
|
|
@@ -90,43 +59,76 @@ export function resolveTemporalBarBand(consumers, domain, range, values) {
|
|
|
90
59
|
});
|
|
91
60
|
if (temporalBars.length === 0) return undefined;
|
|
92
61
|
const temporalFields = new Set(
|
|
93
|
-
temporalBars.map(consumer => consumer.encoding.field)
|
|
62
|
+
temporalBars.map(({ consumer }) => consumer.encoding.field)
|
|
94
63
|
);
|
|
95
|
-
const
|
|
96
|
-
consumer.layer.mark?.type
|
|
97
|
-
|
|
98
|
-
|
|
64
|
+
const compatibleConsumers = valuesByConsumer.filter(({ consumer }) =>
|
|
65
|
+
["line", "rule", "text"].includes(consumer.layer.mark?.type) &&
|
|
66
|
+
consumer.encoding.fieldType === "temporal" &&
|
|
67
|
+
temporalFields.has(consumer.encoding.field)
|
|
99
68
|
);
|
|
100
|
-
if (
|
|
69
|
+
if (
|
|
70
|
+
temporalBars.length + compatibleConsumers.length !==
|
|
71
|
+
valuesByConsumer.length
|
|
72
|
+
) {
|
|
101
73
|
throw new Error(
|
|
102
|
-
"A temporal bar position scale requires compatible bar or
|
|
74
|
+
"A temporal bar position scale requires compatible bar, line, rule, or text consumers of one field."
|
|
103
75
|
);
|
|
104
76
|
}
|
|
77
|
+
const values = temporalBars.flatMap(item => item.values);
|
|
105
78
|
const ordered = [...new Set(values)].sort((left, right) => left - right);
|
|
106
79
|
if (ordered.length < 2) {
|
|
107
80
|
throw new Error("Temporal bar position requires at least two distinct values.");
|
|
108
81
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
82
|
+
let minimumGap = Infinity;
|
|
83
|
+
for (let index = 1; index < ordered.length; index += 1) {
|
|
84
|
+
minimumGap = Math.min(minimumGap, ordered[index] - ordered[index - 1]);
|
|
85
|
+
}
|
|
112
86
|
const domainSpan = Math.abs(domain[1] - domain[0]);
|
|
113
87
|
if (!(minimumGap > 0) || !(domainSpan > 0)) {
|
|
114
88
|
throw new Error("Temporal bar position requires an increasing time domain.");
|
|
115
89
|
}
|
|
116
90
|
const direction = Math.sign(range[1] - range[0]) || 1;
|
|
117
|
-
const
|
|
91
|
+
const bandwidthFraction = minimumGap / (domainSpan + minimumGap);
|
|
92
|
+
const directBandwidth = Math.abs(range[1] - range[0]) * minimumGap /
|
|
118
93
|
(domainSpan + minimumGap);
|
|
119
|
-
const
|
|
120
|
-
range[0] + direction *
|
|
121
|
-
range[1] - direction *
|
|
94
|
+
const directRange = [
|
|
95
|
+
range[0] + direction * directBandwidth / 2,
|
|
96
|
+
range[1] - direction * directBandwidth / 2
|
|
122
97
|
];
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
98
|
+
const stable = !Number.isFinite(directBandwidth) ||
|
|
99
|
+
!directRange.every(Number.isFinite);
|
|
100
|
+
const estimatedBandwidth = stable
|
|
101
|
+
? Math.abs(
|
|
102
|
+
interpolateNumber(range[0], range[1], bandwidthFraction) - range[0]
|
|
103
|
+
)
|
|
104
|
+
: directBandwidth;
|
|
105
|
+
const resolvedRange = stable
|
|
106
|
+
? [
|
|
107
|
+
interpolateNumber(range[0], range[1], bandwidthFraction / 2),
|
|
108
|
+
interpolateNumber(range[0], range[1], 1 - bandwidthFraction / 2)
|
|
109
|
+
]
|
|
110
|
+
: directRange;
|
|
111
|
+
const resolvedSpan = resolvedRange[1] - resolvedRange[0];
|
|
112
|
+
const positions = ordered.map(value => {
|
|
113
|
+
const direct = resolvedRange[0] +
|
|
114
|
+
(value - domain[0]) / (domain[1] - domain[0]) * resolvedSpan;
|
|
115
|
+
return Number.isFinite(direct)
|
|
116
|
+
? direct
|
|
117
|
+
: interpolateNumber(
|
|
118
|
+
resolvedRange[0],
|
|
119
|
+
resolvedRange[1],
|
|
120
|
+
inverseLerp(value, domain[0], domain[1])
|
|
121
|
+
);
|
|
122
|
+
});
|
|
123
|
+
let bandwidth = Infinity;
|
|
124
|
+
for (let index = 1; index < positions.length; index += 1) {
|
|
125
|
+
bandwidth = Math.min(
|
|
126
|
+
bandwidth,
|
|
127
|
+
Math.abs(positions[index] - positions[index - 1])
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
if (![estimatedBandwidth, bandwidth, ...resolvedRange].every(Number.isFinite)) {
|
|
131
|
+
throw new RangeError("Temporal bar position exceeds the finite numeric range.");
|
|
132
|
+
}
|
|
131
133
|
return { bandwidth, range: resolvedRange };
|
|
132
134
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { resolveHistogramBins } from "../../../grammar/histogram.js";
|
|
2
|
+
|
|
3
|
+
export function resolveBinnedPositionDomain({
|
|
4
|
+
valuesByConsumer,
|
|
5
|
+
channel,
|
|
6
|
+
scale,
|
|
7
|
+
id,
|
|
8
|
+
allValues
|
|
9
|
+
}) {
|
|
10
|
+
const binnedPositions = valuesByConsumer.filter(
|
|
11
|
+
({ consumer }) =>
|
|
12
|
+
["bar", "line"].includes(consumer.layer.mark?.type) &&
|
|
13
|
+
consumer.encoding.bin !== undefined
|
|
14
|
+
);
|
|
15
|
+
if (binnedPositions.length === 0) return undefined;
|
|
16
|
+
if (channel !== "x" || binnedPositions.length !== valuesByConsumer.length) {
|
|
17
|
+
throw new Error(
|
|
18
|
+
`Binned scale "${id}" cannot be shared with an unbinned consumer.`
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
const binDefinitions = new Set(
|
|
22
|
+
binnedPositions.map(({ consumer }) => JSON.stringify(consumer.encoding.bin))
|
|
23
|
+
);
|
|
24
|
+
if (binDefinitions.size !== 1) {
|
|
25
|
+
throw new Error(`Binned scale "${id}" requires one shared bin definition.`);
|
|
26
|
+
}
|
|
27
|
+
return resolveHistogramBins({
|
|
28
|
+
values: allValues,
|
|
29
|
+
bin: binnedPositions[0].consumer.encoding.bin,
|
|
30
|
+
domain: scale.domain,
|
|
31
|
+
nice: scale.nice ?? true,
|
|
32
|
+
zero: scale.zero ?? false
|
|
33
|
+
}).domain;
|
|
34
|
+
}
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
resolveContinuousDomain,
|
|
4
4
|
resolveTransformedDomain
|
|
5
5
|
} from "../../../grammar/scales/index.js";
|
|
6
|
+
import { numericExtent } from "../../../grammar/numeric.js";
|
|
6
7
|
|
|
7
8
|
export function resolveSeriesLayoutDomain({
|
|
8
9
|
id,
|
|
@@ -25,9 +26,14 @@ export function resolveSeriesLayoutDomain({
|
|
|
25
26
|
const compatibleDirect = directConsumers.every(({ consumer }) =>
|
|
26
27
|
layoutConsumers.every(({ consumer: layoutConsumer }) => {
|
|
27
28
|
const aggregate = layoutConsumer.encoding.aggregate;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
const directEncoding = consumer.encoding;
|
|
30
|
+
return directEncoding.fieldType === "quantitative" && (
|
|
31
|
+
Object.hasOwn(directEncoding, "datum") ||
|
|
32
|
+
(
|
|
33
|
+
aggregate !== "count" &&
|
|
34
|
+
directEncoding.field === layoutConsumer.encoding.field
|
|
35
|
+
)
|
|
36
|
+
);
|
|
31
37
|
})
|
|
32
38
|
);
|
|
33
39
|
if (!compatibleDirect) {
|
|
@@ -41,9 +47,9 @@ export function resolveSeriesLayoutDomain({
|
|
|
41
47
|
...directConsumers.flatMap(item => item.values)
|
|
42
48
|
];
|
|
43
49
|
if (layout === "center" && scale.domain !== "auto") {
|
|
44
|
-
const minimum =
|
|
45
|
-
const
|
|
46
|
-
if (
|
|
50
|
+
const [minimum, maximum] = numericExtent(values);
|
|
51
|
+
const [domainMinimum, domainMaximum] = numericExtent(scale.domain);
|
|
52
|
+
if (domainMinimum > minimum || domainMaximum < maximum) {
|
|
47
53
|
throw new Error(
|
|
48
54
|
`Center layout scale "${id}" explicit domain must contain every centered bound.`
|
|
49
55
|
);
|
|
@@ -52,7 +58,7 @@ export function resolveSeriesLayoutDomain({
|
|
|
52
58
|
if (
|
|
53
59
|
["group", "overlay"].includes(layout) &&
|
|
54
60
|
scale.domain !== "auto" &&
|
|
55
|
-
(
|
|
61
|
+
(numericExtent(scale.domain)[0] > 0 || numericExtent(scale.domain)[1] < 0)
|
|
56
62
|
) {
|
|
57
63
|
throw new Error(
|
|
58
64
|
`Series layout scale "${id}" requires an explicit domain containing zero.`
|
|
@@ -28,10 +28,10 @@ import {
|
|
|
28
28
|
} from "../../grammar/scales/index.js";
|
|
29
29
|
import { resolveArcAutoPositionRange } from "./policies/arc.js";
|
|
30
30
|
import {
|
|
31
|
-
resolveBinnedBarDomain,
|
|
32
31
|
resolveOffsetScalePolicy,
|
|
33
32
|
resolveTemporalBarBand
|
|
34
33
|
} from "./policies/bar.js";
|
|
34
|
+
import { resolveBinnedPositionDomain } from "./policies/binnedPosition.js";
|
|
35
35
|
import { resolveSeriesLayoutDomain } from "./policies/series.js";
|
|
36
36
|
import { OFFSET_POSITION_CHANNELS } from "../../core/vocabulary.js";
|
|
37
37
|
|
|
@@ -223,7 +223,7 @@ export function resolveScaleMaterialization({
|
|
|
223
223
|
}
|
|
224
224
|
const binnedDomain = isDiscretizedColor
|
|
225
225
|
? undefined
|
|
226
|
-
:
|
|
226
|
+
: resolveBinnedPositionDomain({
|
|
227
227
|
valuesByConsumer,
|
|
228
228
|
channel,
|
|
229
229
|
scale,
|
|
@@ -331,10 +331,9 @@ export function resolveScaleMaterialization({
|
|
|
331
331
|
}
|
|
332
332
|
if (scale.type === "time" && ["x", "y"].includes(channel)) {
|
|
333
333
|
const temporalBarBand = resolveTemporalBarBand(
|
|
334
|
-
|
|
334
|
+
valuesByConsumer,
|
|
335
335
|
domain,
|
|
336
|
-
range
|
|
337
|
-
allValues
|
|
336
|
+
range
|
|
338
337
|
);
|
|
339
338
|
if (temporalBarBand !== undefined) {
|
|
340
339
|
resolvedScale = {
|