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
package/src/layout/facets.js
CHANGED
|
@@ -59,24 +59,20 @@ export function resolveFacetLayout({
|
|
|
59
59
|
"Facet shared legend width"
|
|
60
60
|
);
|
|
61
61
|
const rowCount = Math.ceil(resolvedChildren.length / resolvedColumns);
|
|
62
|
-
const columnWidths = Array
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
Math.max(
|
|
69
|
-
|
|
70
|
-
.map(child => child.height))
|
|
71
|
-
);
|
|
62
|
+
const columnWidths = Array(resolvedColumns).fill(-Infinity);
|
|
63
|
+
const rowHeights = Array(rowCount).fill(-Infinity);
|
|
64
|
+
resolvedChildren.forEach((child, index) => {
|
|
65
|
+
const column = index % resolvedColumns;
|
|
66
|
+
const row = Math.floor(index / resolvedColumns);
|
|
67
|
+
columnWidths[column] = Math.max(columnWidths[column], child.width);
|
|
68
|
+
rowHeights[row] = Math.max(rowHeights[row], child.height);
|
|
69
|
+
});
|
|
72
70
|
const columnStarts = columnWidths.map((_, column) =>
|
|
73
|
-
resolvedPadding.left + columnWidths
|
|
74
|
-
.slice(0, column)
|
|
71
|
+
resolvedPadding.left + columnWidths.slice(0, column)
|
|
75
72
|
.reduce((sum, width) => sum + width, 0) + resolvedGap * column
|
|
76
73
|
);
|
|
77
74
|
const rowStarts = rowHeights.map((_, row) =>
|
|
78
|
-
resolvedTitleHeight + resolvedPadding.top + rowHeights
|
|
79
|
-
.slice(0, row)
|
|
75
|
+
resolvedTitleHeight + resolvedPadding.top + rowHeights.slice(0, row)
|
|
80
76
|
.reduce((sum, height) => sum + height, 0) + resolvedGap * row
|
|
81
77
|
);
|
|
82
78
|
const placements = resolvedChildren.map((child, index) => {
|
package/src/layout/labels.js
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { cloneAndFreeze, isPlainObject } from "../core/immutable.js";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
resolveTextBounds,
|
|
4
|
+
textBoundsFitCanvas,
|
|
5
|
+
textBoundsIntersect
|
|
6
|
+
} from "../core/textMetrics.js";
|
|
3
7
|
import {
|
|
4
8
|
validateNonNegativeFinite,
|
|
5
|
-
validateOptionObject
|
|
9
|
+
validateOptionObject,
|
|
10
|
+
validateWorkLimit
|
|
6
11
|
} from "../core/validation.js";
|
|
7
12
|
|
|
8
13
|
const AXES = new Set(["x", "y", "both"]);
|
|
@@ -10,6 +15,10 @@ const BOUNDS = new Set(["plot", "canvas"]);
|
|
|
10
15
|
const POLICY_OPTIONS = Object.freeze([
|
|
11
16
|
"axis", "padding", "maxDisplacement", "bounds"
|
|
12
17
|
]);
|
|
18
|
+
const MAX_EXHAUSTIVE_OFFSET_STEPS = 28;
|
|
19
|
+
const MAX_SEARCH_DISPLACEMENT = 1_000_000;
|
|
20
|
+
const DISTANT_RING_COUNT = 16;
|
|
21
|
+
const DISTANT_ANGLE_COUNT = 32;
|
|
13
22
|
|
|
14
23
|
export const DEFAULT_LABEL_LAYOUT_GEOMETRY = cloneAndFreeze({
|
|
15
24
|
axis: "both",
|
|
@@ -98,16 +107,46 @@ function overflow(bounds, boundary) {
|
|
|
98
107
|
export function enumerateLabelOffsets({ axis, padding, maxDisplacement }) {
|
|
99
108
|
const policy = normalizeLabelLayoutGeometry({ axis, padding, maxDisplacement });
|
|
100
109
|
const step = Math.max(2, policy.padding);
|
|
101
|
-
const
|
|
110
|
+
const displacement = Math.min(
|
|
111
|
+
policy.maxDisplacement,
|
|
112
|
+
MAX_SEARCH_DISPLACEMENT
|
|
113
|
+
);
|
|
114
|
+
const limit = Math.ceil(displacement / step);
|
|
115
|
+
const localLimit = Math.min(limit, MAX_EXHAUSTIVE_OFFSET_STEPS);
|
|
102
116
|
const candidates = [];
|
|
103
|
-
|
|
104
|
-
|
|
117
|
+
const addCandidate = (x, y) => {
|
|
118
|
+
if (![x, y].every(Number.isFinite)) return;
|
|
119
|
+
const distanceSquared = x ** 2 + y ** 2;
|
|
120
|
+
if (Math.sqrt(distanceSquared) > displacement + 1e-9) return;
|
|
121
|
+
candidates.push({ x, y, distanceSquared });
|
|
122
|
+
};
|
|
123
|
+
for (let x = -localLimit; x <= localLimit; x += 1) {
|
|
124
|
+
for (let y = -localLimit; y <= localLimit; y += 1) {
|
|
105
125
|
if (policy.axis === "x" && y !== 0) continue;
|
|
106
126
|
if (policy.axis === "y" && x !== 0) continue;
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
127
|
+
addCandidate(x * step, y * step);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (limit > localLimit) {
|
|
131
|
+
const localRadius = localLimit * step;
|
|
132
|
+
for (let ring = 1; ring <= DISTANT_RING_COUNT; ring += 1) {
|
|
133
|
+
const radius = localRadius + (displacement - localRadius) *
|
|
134
|
+
ring / DISTANT_RING_COUNT;
|
|
135
|
+
if (policy.axis === "x" || policy.axis === "y") {
|
|
136
|
+
addCandidate(
|
|
137
|
+
policy.axis === "x" ? radius : 0,
|
|
138
|
+
policy.axis === "y" ? radius : 0
|
|
139
|
+
);
|
|
140
|
+
addCandidate(
|
|
141
|
+
policy.axis === "x" ? -radius : 0,
|
|
142
|
+
policy.axis === "y" ? -radius : 0
|
|
143
|
+
);
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
for (let angle = 0; angle < DISTANT_ANGLE_COUNT; angle += 1) {
|
|
147
|
+
const radians = angle / DISTANT_ANGLE_COUNT * Math.PI * 2;
|
|
148
|
+
addCandidate(radius * Math.cos(radians), radius * Math.sin(radians));
|
|
149
|
+
}
|
|
111
150
|
}
|
|
112
151
|
}
|
|
113
152
|
candidates.sort((first, second) =>
|
|
@@ -125,6 +164,8 @@ function textBounds(item, offset = { x: 0, y: 0 }) {
|
|
|
125
164
|
y: item.y + offset.y,
|
|
126
165
|
text: item.text,
|
|
127
166
|
fontSize: item.fontSize,
|
|
167
|
+
fontFamily: item.fontFamily,
|
|
168
|
+
fontWeight: item.fontWeight,
|
|
128
169
|
textAlign: item.textAlign,
|
|
129
170
|
textBaseline: item.textBaseline,
|
|
130
171
|
rotation: item.rotation
|
|
@@ -177,6 +218,10 @@ export function resolveLabelLayout({ items, bounds, ...options } = {}) {
|
|
|
177
218
|
const ids = new Set();
|
|
178
219
|
for (const item of items) validateItem(item, ids);
|
|
179
220
|
const candidates = enumerateLabelOffsets(policy);
|
|
221
|
+
validateWorkLimit(
|
|
222
|
+
items.length ** 2 * candidates.length,
|
|
223
|
+
"Label layout work"
|
|
224
|
+
);
|
|
180
225
|
const base = items.map(item => ({
|
|
181
226
|
id: item.id,
|
|
182
227
|
collisionBounds: expanded(textBounds(item), policy.padding)
|
|
@@ -231,9 +276,33 @@ export function resolveLabelLayout({ items, bounds, ...options } = {}) {
|
|
|
231
276
|
});
|
|
232
277
|
}
|
|
233
278
|
|
|
234
|
-
function
|
|
235
|
-
|
|
236
|
-
|
|
279
|
+
export function assertPolarTextLayout({ canvas, items, label }) {
|
|
280
|
+
const dimensions = canvas?.properties;
|
|
281
|
+
if (
|
|
282
|
+
![dimensions?.width, dimensions?.height].every(Number.isFinite) ||
|
|
283
|
+
dimensions.width <= 0 || dimensions.height <= 0
|
|
284
|
+
) {
|
|
285
|
+
throw new Error("Polar text layout requires finite Canvas dimensions.");
|
|
286
|
+
}
|
|
287
|
+
const bounds = items.map(resolveTextBounds);
|
|
288
|
+
const outside = bounds.some(item => !textBoundsFitCanvas(item, dimensions));
|
|
289
|
+
let overlap = false;
|
|
290
|
+
for (let first = 0; first < bounds.length && !overlap; first += 1) {
|
|
291
|
+
for (let second = first + 1; second < bounds.length; second += 1) {
|
|
292
|
+
if (
|
|
293
|
+
(Math.abs(items[first].x - items[second].x) > 1e-9 ||
|
|
294
|
+
Math.abs(items[first].y - items[second].y) > 1e-9) &&
|
|
295
|
+
textBoundsIntersect(bounds[first], bounds[second])
|
|
296
|
+
) {
|
|
297
|
+
overlap = true;
|
|
298
|
+
break;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
if (outside || overlap) {
|
|
303
|
+
const verb = label.endsWith("labels") ? "require" : "requires";
|
|
304
|
+
throw new Error(`${label} ${verb} sufficient non-overlapping Canvas space.`);
|
|
305
|
+
}
|
|
237
306
|
}
|
|
238
307
|
|
|
239
308
|
export function resolveLabelLeader(item) {
|
|
@@ -242,10 +311,17 @@ export function resolveLabelLeader(item) {
|
|
|
242
311
|
}
|
|
243
312
|
if (item.dx === 0 && item.dy === 0) return undefined;
|
|
244
313
|
const source = { x: item.sourceX, y: item.sourceY };
|
|
245
|
-
if (
|
|
314
|
+
if (
|
|
315
|
+
source.x >= item.bounds.left && source.x <= item.bounds.right &&
|
|
316
|
+
source.y >= item.bounds.top && source.y <= item.bounds.bottom
|
|
317
|
+
) return undefined;
|
|
318
|
+
const midpoint = (start, end) => {
|
|
319
|
+
const sum = start + end;
|
|
320
|
+
return Number.isFinite(sum) ? sum / 2 : start / 2 + end / 2;
|
|
321
|
+
};
|
|
246
322
|
const center = {
|
|
247
|
-
x: (item.bounds.left
|
|
248
|
-
y: (item.bounds.top
|
|
323
|
+
x: midpoint(item.bounds.left, item.bounds.right),
|
|
324
|
+
y: midpoint(item.bounds.top, item.bounds.bottom)
|
|
249
325
|
};
|
|
250
326
|
const dx = center.x - source.x;
|
|
251
327
|
const dy = center.y - source.y;
|
package/src/layout/legend.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import {
|
|
2
|
+
formatVisibleText,
|
|
3
|
+
measureTextWidth
|
|
4
|
+
} from "../core/textMetrics.js";
|
|
5
|
+
|
|
1
6
|
export function measureLegendSymbolHeight(config) {
|
|
2
7
|
return Math.max(...config.symbol.layers.map(layer => {
|
|
3
8
|
if (layer.type === "swatch") return layer.height;
|
|
@@ -6,41 +11,38 @@ export function measureLegendSymbolHeight(config) {
|
|
|
6
11
|
}));
|
|
7
12
|
}
|
|
8
13
|
|
|
9
|
-
export function measureLegendTextWidth(value) {
|
|
10
|
-
return
|
|
14
|
+
export function measureLegendTextWidth(value, style = { fontSize: 12 }) {
|
|
15
|
+
return measureTextWidth(formatVisibleText(value), style);
|
|
11
16
|
}
|
|
12
17
|
|
|
13
18
|
export function resolveLegendGrid(config, width, count) {
|
|
14
|
-
const labels = config.domain.map(
|
|
19
|
+
const labels = config.domain.map(formatVisibleText);
|
|
15
20
|
const itemWidths = labels.map(
|
|
16
|
-
label => width + config.labels.offset +
|
|
21
|
+
label => width + config.labels.offset +
|
|
22
|
+
measureLegendTextWidth(label, config.labels)
|
|
17
23
|
);
|
|
18
24
|
const columnCount = Math.min(config.columns ?? count, count);
|
|
19
25
|
const rowCount = Math.ceil(count / columnCount);
|
|
26
|
+
const columnWidths = [];
|
|
20
27
|
const cells = Array.from({ length: count }, (_, index) => {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
column
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
const cell = config.direction === "horizontal"
|
|
29
|
+
? { column: index % columnCount, row: Math.floor(index / columnCount) }
|
|
30
|
+
: { column: Math.floor(index / rowCount), row: index % rowCount };
|
|
31
|
+
columnWidths[cell.column] = Math.max(
|
|
32
|
+
columnWidths[cell.column] ?? 0,
|
|
33
|
+
itemWidths[index]
|
|
34
|
+
);
|
|
35
|
+
return cell;
|
|
28
36
|
});
|
|
29
|
-
const actualColumns =
|
|
30
|
-
const actualRows = Math.max(...cells.map(cell => cell.row)) + 1;
|
|
31
|
-
const columnWidths = Array.from({ length: actualColumns }, (_, column) =>
|
|
32
|
-
Math.max(...cells.map((cell, index) =>
|
|
33
|
-
cell.column === column ? itemWidths[index] : 0
|
|
34
|
-
))
|
|
35
|
-
);
|
|
37
|
+
const actualColumns = columnWidths.length;
|
|
36
38
|
const gridWidth = columnWidths.reduce((sum, value) => sum + value, 0) +
|
|
37
39
|
config.itemGap * Math.max(0, actualColumns - 1);
|
|
38
40
|
const rowHeight = Math.max(
|
|
39
41
|
config.labels.fontSize,
|
|
40
42
|
measureLegendSymbolHeight(config)
|
|
41
43
|
);
|
|
42
|
-
const gridHeight = rowHeight *
|
|
43
|
-
config.itemGap * Math.max(0,
|
|
44
|
+
const gridHeight = rowHeight * rowCount +
|
|
45
|
+
config.itemGap * Math.max(0, rowCount - 1);
|
|
44
46
|
return { cells, columnWidths, gridWidth, gridHeight, rowHeight };
|
|
45
47
|
}
|
|
46
48
|
|
package/src/layout/legendLane.js
CHANGED
|
@@ -4,13 +4,19 @@ export const SIDE_LEGEND_SYMBOL_CENTER = 16;
|
|
|
4
4
|
export const SIDE_LEGEND_LABEL_START = 44;
|
|
5
5
|
export const HORIZONTAL_LEGEND_TITLE_ELEMENT_GAP = 12;
|
|
6
6
|
|
|
7
|
+
function midpoint(start, end) {
|
|
8
|
+
const sum = start + end;
|
|
9
|
+
const result = Number.isFinite(sum) ? sum / 2 : start / 2 + end / 2;
|
|
10
|
+
return result === 0 ? 0 : result;
|
|
11
|
+
}
|
|
12
|
+
|
|
7
13
|
function unionBounds(bounds) {
|
|
8
|
-
return {
|
|
9
|
-
left: Math.min(
|
|
10
|
-
right: Math.max(
|
|
11
|
-
top: Math.min(
|
|
12
|
-
bottom: Math.max(
|
|
13
|
-
};
|
|
14
|
+
return bounds.reduce((union, item) => ({
|
|
15
|
+
left: Math.min(union.left, item.left),
|
|
16
|
+
right: Math.max(union.right, item.right),
|
|
17
|
+
top: Math.min(union.top, item.top),
|
|
18
|
+
bottom: Math.max(union.bottom, item.bottom)
|
|
19
|
+
}), { left: Infinity, right: -Infinity, top: Infinity, bottom: -Infinity });
|
|
14
20
|
}
|
|
15
21
|
|
|
16
22
|
function decoration(border) {
|
|
@@ -20,19 +26,20 @@ function decoration(border) {
|
|
|
20
26
|
}
|
|
21
27
|
|
|
22
28
|
function horizontalExtent(blocks) {
|
|
23
|
-
|
|
24
|
-
|
|
29
|
+
let left = 0;
|
|
30
|
+
let right = 0;
|
|
25
31
|
for (const block of blocks) {
|
|
26
|
-
if (block.title !== undefined) right.
|
|
27
|
-
left.
|
|
32
|
+
if (block.title !== undefined) right = Math.max(right, block.title.width);
|
|
33
|
+
left = Math.min(left,
|
|
28
34
|
SIDE_LEGEND_SYMBOL_CENTER + block.symbol.left - block.symbol.centerX
|
|
29
35
|
);
|
|
30
|
-
right.
|
|
31
|
-
|
|
36
|
+
right = Math.max(
|
|
37
|
+
right,
|
|
38
|
+
SIDE_LEGEND_SYMBOL_CENTER + block.symbol.right - block.symbol.centerX,
|
|
39
|
+
SIDE_LEGEND_LABEL_START + block.labels.width
|
|
32
40
|
);
|
|
33
|
-
right.push(SIDE_LEGEND_LABEL_START + block.labels.width);
|
|
34
41
|
}
|
|
35
|
-
return { left
|
|
42
|
+
return { left, right };
|
|
36
43
|
}
|
|
37
44
|
|
|
38
45
|
function placeBlock(block, cursor) {
|
|
@@ -81,7 +88,10 @@ export function resolveSideLegendLane({
|
|
|
81
88
|
const blocks = groups.flatMap(group => group.blocks);
|
|
82
89
|
if (blocks.length < 2) return undefined;
|
|
83
90
|
const laneExtent = horizontalExtent(blocks);
|
|
84
|
-
const offset =
|
|
91
|
+
const offset = blocks.reduce(
|
|
92
|
+
(maximum, block) => Math.max(maximum, block.offset),
|
|
93
|
+
-Infinity
|
|
94
|
+
);
|
|
85
95
|
const titleStartX = side === "right"
|
|
86
96
|
? plot.x + plot.width + offset
|
|
87
97
|
: plot.x - offset - laneExtent.right;
|
|
@@ -160,21 +170,12 @@ export function resolveSideLegendLane({
|
|
|
160
170
|
};
|
|
161
171
|
}
|
|
162
172
|
|
|
163
|
-
function translateBounds(bounds, dy) {
|
|
164
|
-
return {
|
|
165
|
-
left: bounds.left,
|
|
166
|
-
right: bounds.right,
|
|
167
|
-
top: bounds.top + dy,
|
|
168
|
-
bottom: bounds.bottom + dy
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function translateBoundsX(bounds, dx) {
|
|
173
|
+
function translateBounds(bounds, dx, dy) {
|
|
173
174
|
return {
|
|
174
175
|
left: bounds.left + dx,
|
|
175
176
|
right: bounds.right + dx,
|
|
176
|
-
top: bounds.top,
|
|
177
|
-
bottom: bounds.bottom
|
|
177
|
+
top: bounds.top + dy,
|
|
178
|
+
bottom: bounds.bottom + dy
|
|
178
179
|
};
|
|
179
180
|
}
|
|
180
181
|
|
|
@@ -198,13 +199,13 @@ function packHorizontalRows(groups, plot) {
|
|
|
198
199
|
throw new Error("Horizontal legend block requires more plot width.");
|
|
199
200
|
}
|
|
200
201
|
let dx = cursor - interval.left;
|
|
201
|
-
let placed =
|
|
202
|
+
let placed = translateBounds(interval, dx, 0);
|
|
202
203
|
if (row.length > 0 && placed.right > plot.x + plot.width) {
|
|
203
204
|
rows.push(row);
|
|
204
205
|
row = [];
|
|
205
206
|
cursor = plot.x;
|
|
206
207
|
dx = cursor - interval.left;
|
|
207
|
-
placed =
|
|
208
|
+
placed = translateBounds(interval, dx, 0);
|
|
208
209
|
}
|
|
209
210
|
row.push({ group, interval: placed, dx });
|
|
210
211
|
cursor = placed.right + HORIZONTAL_LEGEND_BLOCK_GAP;
|
|
@@ -220,15 +221,22 @@ function normalizeHorizontalRow(entries) {
|
|
|
220
221
|
const commonTitleY = stacked[0]?.group.title.y;
|
|
221
222
|
const titleDescent = stacked.length === 0
|
|
222
223
|
? 0
|
|
223
|
-
: Math.max(
|
|
224
|
-
|
|
225
|
-
|
|
224
|
+
: stacked.reduce((maximum, entry) => Math.max(
|
|
225
|
+
maximum,
|
|
226
|
+
entry.group.title.bounds.bottom - entry.group.title.y
|
|
227
|
+
), -Infinity);
|
|
226
228
|
const elementAnchor = commonTitleY === undefined
|
|
227
|
-
? (
|
|
229
|
+
? midpoint(
|
|
230
|
+
entries[0].group.element.top,
|
|
231
|
+
entries[0].group.element.bottom
|
|
232
|
+
)
|
|
228
233
|
: commonTitleY + titleDescent + HORIZONTAL_LEGEND_TITLE_ELEMENT_GAP;
|
|
229
234
|
return entries.map(({ group, dx }) => {
|
|
230
235
|
const contentDy = commonTitleY === undefined
|
|
231
|
-
? elementAnchor - (
|
|
236
|
+
? elementAnchor - midpoint(
|
|
237
|
+
group.element.top,
|
|
238
|
+
group.element.bottom
|
|
239
|
+
)
|
|
232
240
|
: elementAnchor - group.element.top;
|
|
233
241
|
const titleDy = group.title === undefined
|
|
234
242
|
? 0
|
|
@@ -238,8 +246,8 @@ function normalizeHorizontalRow(entries) {
|
|
|
238
246
|
const foreground = unionBounds([
|
|
239
247
|
...(group.title === undefined
|
|
240
248
|
? []
|
|
241
|
-
: [
|
|
242
|
-
|
|
249
|
+
: [translateBounds(group.title.bounds, dx, titleDy)]),
|
|
250
|
+
translateBounds(group.content, dx, contentDy)
|
|
243
251
|
]);
|
|
244
252
|
return {
|
|
245
253
|
id: group.id,
|
|
@@ -259,8 +267,8 @@ function translateHorizontalPlacement(placement, dy) {
|
|
|
259
267
|
...placement,
|
|
260
268
|
titleDy: placement.titleDy + dy,
|
|
261
269
|
contentDy: placement.contentDy + dy,
|
|
262
|
-
foreground: translateBounds(placement.foreground, dy),
|
|
263
|
-
occupied: translateBounds(placement.occupied, dy)
|
|
270
|
+
foreground: translateBounds(placement.foreground, 0, dy),
|
|
271
|
+
occupied: translateBounds(placement.occupied, 0, dy)
|
|
264
272
|
};
|
|
265
273
|
}
|
|
266
274
|
|
|
@@ -285,8 +293,14 @@ export function resolveHorizontalLegendLane({
|
|
|
285
293
|
let dy;
|
|
286
294
|
if (index === 0) {
|
|
287
295
|
const anchor = edge === "top"
|
|
288
|
-
?
|
|
289
|
-
|
|
296
|
+
? packed[index].reduce((maximum, item) => Math.max(
|
|
297
|
+
maximum,
|
|
298
|
+
item.group.horizontal.bottom
|
|
299
|
+
), -Infinity)
|
|
300
|
+
: packed[index].reduce((minimum, item) => Math.min(
|
|
301
|
+
minimum,
|
|
302
|
+
item.group.horizontal.top
|
|
303
|
+
), Infinity);
|
|
290
304
|
dy = edge === "top"
|
|
291
305
|
? anchor - rowBounds.bottom
|
|
292
306
|
: anchor - rowBounds.top;
|
package/src/layout/title.js
CHANGED
|
@@ -44,7 +44,7 @@ export function buildTitleReadingBlock({ text, subtitle }, config) {
|
|
|
44
44
|
subtitleLines,
|
|
45
45
|
titleCenters,
|
|
46
46
|
subtitleCenters,
|
|
47
|
-
width: Math.max(
|
|
47
|
+
width: widths.reduce((maximum, width) => Math.max(maximum, width), -Infinity),
|
|
48
48
|
height
|
|
49
49
|
};
|
|
50
50
|
}
|
|
@@ -74,12 +74,12 @@ function axisAlignedTextBounds({ x, y, text, style, align, rotation }) {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
function unionBounds(bounds) {
|
|
77
|
-
return {
|
|
78
|
-
left: Math.min(
|
|
79
|
-
right: Math.max(
|
|
80
|
-
top: Math.min(
|
|
81
|
-
bottom: Math.max(
|
|
82
|
-
};
|
|
77
|
+
return bounds.reduce((union, item) => ({
|
|
78
|
+
left: Math.min(union.left, item.left),
|
|
79
|
+
right: Math.max(union.right, item.right),
|
|
80
|
+
top: Math.min(union.top, item.top),
|
|
81
|
+
bottom: Math.max(union.bottom, item.bottom)
|
|
82
|
+
}), { left: Infinity, right: -Infinity, top: Infinity, bottom: -Infinity });
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
export function resolveTitleComponentBounds(component, style) {
|
|
@@ -11,13 +11,12 @@ import {
|
|
|
11
11
|
import {
|
|
12
12
|
isDiscretePositionScaleType,
|
|
13
13
|
mapContinuousScaleValues,
|
|
14
|
-
mapOrdinalPositionValues
|
|
15
|
-
mapSequentialColors
|
|
14
|
+
mapOrdinalPositionValues
|
|
16
15
|
} from "../../grammar/scales/index.js";
|
|
16
|
+
import { mapScaleConsumerValues } from "../scales/map.js";
|
|
17
17
|
import {
|
|
18
18
|
DEFAULT_BAR_FILL,
|
|
19
|
-
|
|
20
|
-
DEFAULT_BAR_STROKE_WIDTH
|
|
19
|
+
resolveBarAppearance
|
|
21
20
|
} from "./resolve.js";
|
|
22
21
|
import { deriveGroupedRectangles } from "./grouped.js";
|
|
23
22
|
import { resolveBarWidth } from "../../grammar/bars/geometry.js";
|
|
@@ -72,14 +71,8 @@ export function deriveAggregateRectangles(required, resolved, widthConfig) {
|
|
|
72
71
|
const baseline = DEFAULT_SERIES_BASELINE;
|
|
73
72
|
const continuousColor = colorEncoding?.fieldType === "quantitative";
|
|
74
73
|
if (continuousColor) {
|
|
75
|
-
const mappedColors =
|
|
76
|
-
cells.map(cell => cell.color),
|
|
77
|
-
colorScale.domain,
|
|
78
|
-
colorScale.range,
|
|
79
|
-
{
|
|
80
|
-
interpolation: colorScale.interpolate,
|
|
81
|
-
clamp: colorScale.clamp ?? false
|
|
82
|
-
}
|
|
74
|
+
const mappedColors = mapScaleConsumerValues(
|
|
75
|
+
cells.map(cell => cell.color), colorScale, "color"
|
|
83
76
|
);
|
|
84
77
|
const cellByCategory = new Map(
|
|
85
78
|
cells.map((cell, index) => [cell[channels.category], { cell, index }])
|
|
@@ -97,8 +90,14 @@ export function deriveAggregateRectangles(required, resolved, widthConfig) {
|
|
|
97
90
|
[categoryValue],
|
|
98
91
|
categoryScale
|
|
99
92
|
)[0];
|
|
93
|
+
const [segment] = layoutSeriesPartition(
|
|
94
|
+
[cell[channels.measure]],
|
|
95
|
+
layout,
|
|
96
|
+
{ baseline }
|
|
97
|
+
);
|
|
98
|
+
if (segment === undefined) return [];
|
|
100
99
|
const [start, end] = mapContinuousScaleValues(
|
|
101
|
-
[
|
|
100
|
+
[segment.start, segment.end],
|
|
102
101
|
measureScale
|
|
103
102
|
);
|
|
104
103
|
return [{
|
|
@@ -107,19 +106,7 @@ export function deriveAggregateRectangles(required, resolved, widthConfig) {
|
|
|
107
106
|
width: vertical ? thickness : Math.abs(start - end),
|
|
108
107
|
height: vertical ? Math.abs(start - end) : thickness,
|
|
109
108
|
fill: mappedColors[index],
|
|
110
|
-
|
|
111
|
-
? "transparent"
|
|
112
|
-
: appearance.stroke ?? config.stroke ??
|
|
113
|
-
existing[index]?.properties.stroke ?? DEFAULT_BAR_STROKE,
|
|
114
|
-
strokeWidth: appearance.stroke === false
|
|
115
|
-
? 0
|
|
116
|
-
: appearance.strokeWidth ?? config.strokeWidth ??
|
|
117
|
-
existing[index]?.properties.strokeWidth ?? DEFAULT_BAR_STROKE_WIDTH,
|
|
118
|
-
...((appearance.opacity ?? config.opacity) === undefined
|
|
119
|
-
? (existing[index]?.properties.opacity === undefined
|
|
120
|
-
? {}
|
|
121
|
-
: { opacity: existing[index].properties.opacity })
|
|
122
|
-
: { opacity: appearance.opacity ?? config.opacity })
|
|
109
|
+
...resolveBarAppearance(config, existing[index]?.properties)
|
|
123
110
|
}];
|
|
124
111
|
});
|
|
125
112
|
}
|
|
@@ -168,20 +155,7 @@ export function deriveAggregateRectangles(required, resolved, widthConfig) {
|
|
|
168
155
|
fill: segment.color === undefined
|
|
169
156
|
? appearance.fill ?? config.fill ?? colors.get(segment.color)
|
|
170
157
|
: colors.get(segment.color),
|
|
171
|
-
|
|
172
|
-
? "transparent"
|
|
173
|
-
: appearance.stroke ?? config.stroke ?? existing[index]?.properties.stroke ?? DEFAULT_BAR_STROKE,
|
|
174
|
-
strokeWidth:
|
|
175
|
-
appearance.stroke === false
|
|
176
|
-
? 0
|
|
177
|
-
: appearance.strokeWidth ?? config.strokeWidth ??
|
|
178
|
-
existing[index]?.properties.strokeWidth ??
|
|
179
|
-
DEFAULT_BAR_STROKE_WIDTH,
|
|
180
|
-
...((appearance.opacity ?? config.opacity) === undefined
|
|
181
|
-
? (existing[index]?.properties.opacity === undefined
|
|
182
|
-
? {}
|
|
183
|
-
: { opacity: existing[index].properties.opacity })
|
|
184
|
-
: { opacity: appearance.opacity ?? config.opacity })
|
|
158
|
+
...resolveBarAppearance(config, existing[index]?.properties)
|
|
185
159
|
};
|
|
186
160
|
});
|
|
187
161
|
}
|
|
@@ -5,11 +5,11 @@ import {
|
|
|
5
5
|
} from "../../grammar/scales/index.js";
|
|
6
6
|
import { sameOrderedValues } from "../../core/validation.js";
|
|
7
7
|
import {
|
|
8
|
-
|
|
9
|
-
DEFAULT_BAR_STROKE_WIDTH
|
|
8
|
+
resolveBarAppearance
|
|
10
9
|
} from "./resolve.js";
|
|
11
10
|
import { resolveBarWidth } from "../../grammar/bars/geometry.js";
|
|
12
11
|
import { DEFAULT_SERIES_BASELINE } from "../../grammar/seriesLayout.js";
|
|
12
|
+
import { finiteMidpoint } from "../../grammar/numeric.js";
|
|
13
13
|
import {
|
|
14
14
|
BAR_ORIENTATIONS,
|
|
15
15
|
resolveBarChannels,
|
|
@@ -58,7 +58,10 @@ export function deriveGroupedRectangles(required, resolved, widthConfig) {
|
|
|
58
58
|
colorScale.range[index % colorScale.range.length]
|
|
59
59
|
])
|
|
60
60
|
);
|
|
61
|
-
const offsetMidpoint = (
|
|
61
|
+
const offsetMidpoint = finiteMidpoint(
|
|
62
|
+
offsetScale.range[0],
|
|
63
|
+
offsetScale.range[1]
|
|
64
|
+
);
|
|
62
65
|
const categoryDirection = Math.sign(
|
|
63
66
|
categoryScale.step ?? categoryScale.range[1] - categoryScale.range[0]
|
|
64
67
|
) || 1;
|
|
@@ -78,7 +81,6 @@ export function deriveGroupedRectangles(required, resolved, widthConfig) {
|
|
|
78
81
|
);
|
|
79
82
|
const existing = resolved.graphicSpec.objects[layer.id].items;
|
|
80
83
|
const config = resolved.markConfigs[layer.id] ?? {};
|
|
81
|
-
const appearance = config.barAppearance ?? {};
|
|
82
84
|
|
|
83
85
|
return cells.map((cell, index) => {
|
|
84
86
|
const categoryValue = cell[channels.category];
|
|
@@ -136,20 +138,7 @@ export function deriveGroupedRectangles(required, resolved, widthConfig) {
|
|
|
136
138
|
return {
|
|
137
139
|
...geometry,
|
|
138
140
|
fill: colors.get(cell.color),
|
|
139
|
-
|
|
140
|
-
? "transparent"
|
|
141
|
-
: appearance.stroke ?? config.stroke ?? existing[index]?.properties.stroke ?? DEFAULT_BAR_STROKE,
|
|
142
|
-
strokeWidth:
|
|
143
|
-
appearance.stroke === false
|
|
144
|
-
? 0
|
|
145
|
-
: appearance.strokeWidth ?? config.strokeWidth ??
|
|
146
|
-
existing[index]?.properties.strokeWidth ??
|
|
147
|
-
DEFAULT_BAR_STROKE_WIDTH,
|
|
148
|
-
...((appearance.opacity ?? config.opacity) === undefined
|
|
149
|
-
? (existing[index]?.properties.opacity === undefined
|
|
150
|
-
? {}
|
|
151
|
-
: { opacity: existing[index].properties.opacity })
|
|
152
|
-
: { opacity: appearance.opacity ?? config.opacity })
|
|
141
|
+
...resolveBarAppearance(config, existing[index]?.properties)
|
|
153
142
|
};
|
|
154
143
|
});
|
|
155
144
|
}
|
|
@@ -13,8 +13,7 @@ import { resolveBarColorLayout } from "../../grammar/bars/policy.js";
|
|
|
13
13
|
import { layoutSeriesPartition } from "../../grammar/seriesLayout.js";
|
|
14
14
|
import {
|
|
15
15
|
DEFAULT_BAR_FILL,
|
|
16
|
-
|
|
17
|
-
DEFAULT_BAR_STROKE_WIDTH
|
|
16
|
+
resolveBarAppearance
|
|
18
17
|
} from "./resolve.js";
|
|
19
18
|
|
|
20
19
|
export function deriveHistogramSegments({
|
|
@@ -141,23 +140,7 @@ export function deriveHistogramRectangles(required, resolved) {
|
|
|
141
140
|
config.fill ??
|
|
142
141
|
existing[index]?.properties.fill ??
|
|
143
142
|
DEFAULT_BAR_FILL,
|
|
144
|
-
|
|
145
|
-
appearance.stroke === false
|
|
146
|
-
? "transparent"
|
|
147
|
-
: appearance.stroke ?? config.stroke ??
|
|
148
|
-
existing[index]?.properties.stroke ??
|
|
149
|
-
DEFAULT_BAR_STROKE,
|
|
150
|
-
strokeWidth:
|
|
151
|
-
appearance.stroke === false
|
|
152
|
-
? 0
|
|
153
|
-
: appearance.strokeWidth ?? config.strokeWidth ??
|
|
154
|
-
existing[index]?.properties.strokeWidth ??
|
|
155
|
-
DEFAULT_BAR_STROKE_WIDTH,
|
|
156
|
-
...((appearance.opacity ?? config.opacity) === undefined
|
|
157
|
-
? (existing[index]?.properties.opacity === undefined
|
|
158
|
-
? {}
|
|
159
|
-
: { opacity: existing[index].properties.opacity })
|
|
160
|
-
: { opacity: appearance.opacity ?? config.opacity })
|
|
143
|
+
...resolveBarAppearance(config, existing[index]?.properties)
|
|
161
144
|
};
|
|
162
145
|
});
|
|
163
146
|
}
|