ggaction 0.0.6 → 0.0.8
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 +43 -0
- package/README.md +77 -14
- package/package.json +19 -2
- package/src/BasicChartProgram.js +10 -0
- package/src/actions/basic.js +21 -0
- package/src/actions/canvas/actions.js +42 -0
- package/src/actions/canvas/index.js +5 -1
- package/src/actions/categoryOrder/index.js +111 -0
- package/src/actions/charts/basic.js +13 -0
- package/src/actions/data/basic.js +11 -0
- package/src/actions/data/basicBin2d.js +43 -0
- package/src/actions/data/bin2d.js +1 -26
- package/src/actions/data/bin2dMaterialize.js +29 -0
- package/src/actions/data/index.js +6 -0
- package/src/actions/data/timeUnit.js +48 -0
- package/src/actions/encodings/angle.js +77 -0
- package/src/actions/encodings/appearance.js +6 -2
- package/src/actions/encodings/basic.js +21 -0
- package/src/actions/encodings/color/index.js +17 -13
- package/src/actions/encodings/color/layout.js +2 -0
- package/src/actions/encodings/color/policy.js +3 -0
- package/src/actions/encodings/index.js +2 -0
- package/src/actions/encodings/position/index.js +5 -1
- package/src/actions/encodings/position/policies/area.js +40 -2
- package/src/actions/encodings/position/policies/bar.js +6 -0
- package/src/actions/encodings/position/policies/index.js +2 -0
- package/src/actions/encodings/position/policies/tick.js +19 -0
- package/src/actions/encodings/ranged.js +6 -2
- package/src/actions/encodings/remove.js +1 -1
- package/src/actions/guides/axes/index.js +10 -2
- package/src/actions/guides/basic.js +19 -0
- package/src/actions/guides/grids/grid.js +7 -3
- package/src/actions/guides/grids/index.js +1 -1
- package/src/actions/guides/legends/categorical/actions.js +23 -6
- package/src/actions/guides/legends/categorical/index.js +14 -2
- package/src/actions/guides/legends/continuous/common.js +17 -5
- package/src/actions/guides/legends/continuous/gradient.js +12 -8
- package/src/actions/guides/legends/continuous/index.js +6 -2
- package/src/actions/guides/legends/continuous/opacity.js +70 -17
- package/src/actions/guides/legends/edit.js +13 -8
- package/src/actions/guides/legends/lane.js +468 -0
- package/src/actions/guides/legends/remove.js +3 -7
- package/src/actions/index.js +2 -0
- package/src/actions/marks/area/actions.js +3 -1
- package/src/actions/marks/area/materialize.js +12 -3
- package/src/actions/marks/bar/index.js +5 -1
- package/src/actions/marks/basic.js +11 -0
- package/src/actions/marks/index.js +2 -0
- package/src/actions/marks/line/actions.js +5 -1
- package/src/actions/marks/line/index.js +4 -1
- package/src/actions/marks/point/index.js +5 -1
- package/src/actions/marks/point/materialize.js +17 -5
- package/src/actions/marks/rect/actions.js +5 -1
- package/src/actions/marks/rect/index.js +4 -1
- package/src/actions/marks/tick/actions.js +225 -0
- package/src/actions/marks/tick/index.js +1 -0
- package/src/actions/primitives/semanticValidation/layer.js +2 -0
- package/src/actions/scales/consumers/index.js +8 -0
- package/src/actions/scales/consumers/seriesLayout.js +22 -2
- package/src/actions/scales/index.js +5 -1
- package/src/actions/scales/materialize.js +2 -0
- package/src/actions/selection/actions.js +5 -2
- package/src/basic.js +2 -0
- package/src/core/vocabulary.js +7 -3
- package/src/grammar/areaSeries.js +112 -2
- package/src/grammar/categoryOrder.js +138 -0
- package/src/grammar/direction.js +46 -0
- package/src/grammar/facets/index.js +1 -1
- package/src/grammar/pointShapes.js +34 -6
- package/src/grammar/positionCompatibility.js +4 -0
- package/src/grammar/schemas/semanticPath.js +2 -1
- package/src/grammar/seriesLayout.js +10 -2
- package/src/grammar/timeUnit.js +108 -0
- package/src/grammar/transforms.js +6 -0
- package/src/grammar/window.js +73 -7
- package/src/layout/legendLane.js +338 -0
- package/src/materialization/marks/capabilities.js +9 -0
- package/src/materialization/marks/index.js +2 -1
- package/src/materialization/marks/policies.js +13 -0
- package/src/materialization/scales/policies/series.js +9 -0
- package/src/materialization/scales/resolve.js +25 -3
- package/src/materialization/selection/items/index.js +1 -0
- package/src/materialization/selection/items/path.js +4 -1
- package/src/materialization/selection/items/tick.js +42 -0
- package/src/materialization/selection/policies/index.js +2 -0
- package/src/materialization/selection/policies/tick.js +10 -0
- package/src/renderers/canvas/index.js +96 -60
- package/src/renderers/canvas/text.js +3 -14
- package/src/renderers/pdf.js +112 -0
- package/src/renderers/svg.js +559 -0
- package/src/renderers/text.js +11 -0
- package/types/basic.d.ts +107 -0
- package/types/index.d.ts +10 -0
- package/types/pdf.d.ts +26 -0
- package/types/program.d.ts +96 -3
- package/types/svg.d.ts +11 -0
|
@@ -39,12 +39,18 @@ function resolveGradientLayout(program, config, scale) {
|
|
|
39
39
|
: config.align === "right" ? plot.x + plot.width - length
|
|
40
40
|
: plot.x + (plot.width - length) / 2;
|
|
41
41
|
y = config.position === "top"
|
|
42
|
-
? plot.y - config.offset - thickness
|
|
43
|
-
|
|
42
|
+
? plot.y - config.offset - thickness - config.labels.offset -
|
|
43
|
+
config.labels.fontSize
|
|
44
|
+
: plot.y + plot.height + config.offset +
|
|
45
|
+
config.titleStyle.fontSize + 12;
|
|
44
46
|
}
|
|
45
47
|
const title = vertical
|
|
46
48
|
? { x, y: plot.y + 20, align: "left" }
|
|
47
|
-
: {
|
|
49
|
+
: {
|
|
50
|
+
x: x + length / 2,
|
|
51
|
+
y: y - 12 - config.titleStyle.fontSize / 2,
|
|
52
|
+
align: "center"
|
|
53
|
+
};
|
|
48
54
|
const values = sampleContinuousValues(scale.domain, config.count);
|
|
49
55
|
const fractions = values.map((_, index) => index / (values.length - 1));
|
|
50
56
|
const labelOffset = config.labels.offset;
|
|
@@ -58,9 +64,7 @@ function resolveGradientLayout(program, config, scale) {
|
|
|
58
64
|
}))
|
|
59
65
|
: fractions.map(fraction => ({
|
|
60
66
|
x: x + length * fraction,
|
|
61
|
-
y: config.
|
|
62
|
-
? y - labelOffset
|
|
63
|
-
: y + thickness + labelOffset,
|
|
67
|
+
y: y + thickness + labelOffset + config.labels.fontSize / 2,
|
|
64
68
|
align: "center"
|
|
65
69
|
}));
|
|
66
70
|
const ticks = vertical
|
|
@@ -72,9 +76,9 @@ function resolveGradientLayout(program, config, scale) {
|
|
|
72
76
|
}))
|
|
73
77
|
: labels.map(label => ({
|
|
74
78
|
x1: label.x,
|
|
75
|
-
y1:
|
|
79
|
+
y1: y + thickness,
|
|
76
80
|
x2: label.x,
|
|
77
|
-
y2:
|
|
81
|
+
y2: y + thickness + 6
|
|
78
82
|
}));
|
|
79
83
|
assertLegendInsideCanvas([
|
|
80
84
|
title,
|
|
@@ -15,11 +15,15 @@ export {
|
|
|
15
15
|
export { createIntervalLegend, rematerializeIntervalLegend } from "./interval.js";
|
|
16
16
|
|
|
17
17
|
export function registerContinuousLegendActions(ProgramClass) {
|
|
18
|
-
ProgramClass
|
|
19
|
-
ProgramClass.prototype.rematerializeGradientLegend = rematerializeGradientLegend;
|
|
18
|
+
registerGradientLegendActions(ProgramClass);
|
|
20
19
|
ProgramClass.prototype.createOpacityLegend = createOpacityLegend;
|
|
21
20
|
ProgramClass.prototype.rematerializeOpacityLegend = rematerializeOpacityLegend;
|
|
22
21
|
ProgramClass.prototype.removeOpacityLegend = removeOpacityLegend;
|
|
23
22
|
ProgramClass.prototype.createIntervalLegend = createIntervalLegend;
|
|
24
23
|
ProgramClass.prototype.rematerializeIntervalLegend = rematerializeIntervalLegend;
|
|
25
24
|
}
|
|
25
|
+
|
|
26
|
+
export function registerGradientLegendActions(ProgramClass) {
|
|
27
|
+
ProgramClass.prototype.createGradientLegend = createGradientLegend;
|
|
28
|
+
ProgramClass.prototype.rematerializeGradientLegend = rematerializeGradientLegend;
|
|
29
|
+
}
|
|
@@ -2,6 +2,7 @@ import { action } from "../../../../core/action.js";
|
|
|
2
2
|
import { isPlainObject } from "../../../../core/immutable.js";
|
|
3
3
|
import { validateKeys } from "../../../../core/validation.js";
|
|
4
4
|
import { mapLinearValues } from "../../../../grammar/scales/index.js";
|
|
5
|
+
import { measureTextWidth } from "../../../../core/textMetrics.js";
|
|
5
6
|
import { DEFAULT_COLORS } from "../../../../theme/defaults.js";
|
|
6
7
|
import {
|
|
7
8
|
assertLegendInsideCanvas,
|
|
@@ -84,6 +85,7 @@ function resolveOpacityLayout(program, config, scale) {
|
|
|
84
85
|
const { plot, canvas } = resolveContinuousBounds(program);
|
|
85
86
|
const vertical = ["right", "left"].includes(config.position);
|
|
86
87
|
const values = sampleContinuousValues(scale.domain, config.count);
|
|
88
|
+
const texts = formatContinuousValues(values, scale.domain, "quantitative");
|
|
87
89
|
const radius = config.symbol.radius;
|
|
88
90
|
let symbols;
|
|
89
91
|
let labels;
|
|
@@ -109,29 +111,81 @@ function resolveOpacityLayout(program, config, scale) {
|
|
|
109
111
|
y: plot.y + 20,
|
|
110
112
|
align: config.position === "right" ? "left" : "right"
|
|
111
113
|
};
|
|
114
|
+
} else if (config.titlePosition === "left") {
|
|
115
|
+
const titleWidth = measureTextWidth(config.title, config.titleStyle);
|
|
116
|
+
const labelWidths = texts.map(text =>
|
|
117
|
+
measureTextWidth(text, config.labels)
|
|
118
|
+
);
|
|
119
|
+
const samplesWidth = labelWidths.reduce(
|
|
120
|
+
(sum, width) => sum + radius * 2 + config.labels.offset + width,
|
|
121
|
+
0
|
|
122
|
+
) + config.itemGap * (values.length - 1);
|
|
123
|
+
const width = titleWidth + 20 + samplesWidth;
|
|
124
|
+
const startX = config.align === "left" ? plot.x
|
|
125
|
+
: config.align === "right" ? plot.x + plot.width - width
|
|
126
|
+
: plot.x + (plot.width - width) / 2;
|
|
127
|
+
const height = Math.max(
|
|
128
|
+
radius * 2,
|
|
129
|
+
config.labels.fontSize,
|
|
130
|
+
config.titleStyle.fontSize
|
|
131
|
+
);
|
|
132
|
+
const centerY = config.position === "top"
|
|
133
|
+
? plot.y - config.offset - height / 2
|
|
134
|
+
: plot.y + plot.height + config.offset + height / 2;
|
|
135
|
+
title = { x: startX, y: centerY, align: "left" };
|
|
136
|
+
let cursor = startX + titleWidth + 20;
|
|
137
|
+
symbols = [];
|
|
138
|
+
labels = [];
|
|
139
|
+
for (let index = 0; index < values.length; index += 1) {
|
|
140
|
+
const symbolX = cursor + radius;
|
|
141
|
+
symbols.push({ x: symbolX, y: centerY });
|
|
142
|
+
const labelX = symbolX + radius + config.labels.offset;
|
|
143
|
+
labels.push({ x: labelX, y: centerY, align: "left" });
|
|
144
|
+
cursor = labelX + labelWidths[index] + config.itemGap;
|
|
145
|
+
}
|
|
112
146
|
} else {
|
|
113
147
|
const width = (values.length - 1) * Math.max(56, config.itemGap * 2);
|
|
114
148
|
const startX = config.align === "left" ? plot.x
|
|
115
149
|
: config.align === "right" ? plot.x + plot.width - width
|
|
116
150
|
: plot.x + (plot.width - width) / 2;
|
|
151
|
+
const titleHalf = config.titleStyle.fontSize / 2;
|
|
152
|
+
const labelHalf = config.labels.fontSize / 2;
|
|
117
153
|
const y = config.position === "top"
|
|
118
|
-
? plot.y - config.offset -
|
|
119
|
-
|
|
154
|
+
? plot.y - config.offset - config.labels.fontSize -
|
|
155
|
+
config.labels.offset - radius
|
|
156
|
+
: plot.y + plot.height + config.offset +
|
|
157
|
+
config.titleStyle.fontSize + 12 + radius;
|
|
120
158
|
symbols = values.map((_, index) => ({
|
|
121
159
|
x: startX + index * width / (values.length - 1),
|
|
122
160
|
y
|
|
123
161
|
}));
|
|
124
162
|
labels = symbols.map(symbol => ({
|
|
125
163
|
x: symbol.x,
|
|
126
|
-
y: config.
|
|
127
|
-
? symbol.y - radius - config.labels.offset
|
|
128
|
-
: symbol.y + radius + config.labels.offset,
|
|
164
|
+
y: symbol.y + radius + config.labels.offset + labelHalf,
|
|
129
165
|
align: "center"
|
|
130
166
|
}));
|
|
131
|
-
title = {
|
|
167
|
+
title = {
|
|
168
|
+
x: startX + width / 2,
|
|
169
|
+
y: y - radius - 12 - titleHalf,
|
|
170
|
+
align: "center"
|
|
171
|
+
};
|
|
132
172
|
}
|
|
173
|
+
const titleWidth = measureTextWidth(config.title, config.titleStyle);
|
|
174
|
+
const titleEdge = {
|
|
175
|
+
x: title.x + (title.align === "left" ? titleWidth : -titleWidth),
|
|
176
|
+
y: title.y
|
|
177
|
+
};
|
|
178
|
+
const labelEdges = labels.map((label, index) => {
|
|
179
|
+
const width = measureTextWidth(texts[index], config.labels);
|
|
180
|
+
return {
|
|
181
|
+
x: label.x + (label.align === "left"
|
|
182
|
+
? width
|
|
183
|
+
: label.align === "right" ? -width : width / 2),
|
|
184
|
+
y: label.y
|
|
185
|
+
};
|
|
186
|
+
});
|
|
133
187
|
assertLegendInsideCanvas(
|
|
134
|
-
[title, ...symbols, ...labels],
|
|
188
|
+
[title, titleEdge, ...symbols, ...labels, ...labelEdges],
|
|
135
189
|
canvas,
|
|
136
190
|
"Opacity legend layout"
|
|
137
191
|
);
|
|
@@ -145,14 +199,17 @@ function resolveOpacityLayout(program, config, scale) {
|
|
|
145
199
|
x: symbol.x - radius,
|
|
146
200
|
y: symbol.y - radius
|
|
147
201
|
})),
|
|
148
|
-
...labels.map(label => ({
|
|
149
|
-
x: label.x + (
|
|
150
|
-
|
|
151
|
-
|
|
202
|
+
...labels.map((label, index) => ({
|
|
203
|
+
x: label.x + (label.align === "left"
|
|
204
|
+
? measureTextWidth(
|
|
205
|
+
texts[index],
|
|
206
|
+
config.labels
|
|
207
|
+
)
|
|
208
|
+
: label.align === "right" ? -42 : 0),
|
|
152
209
|
y: label.y
|
|
153
210
|
}))
|
|
154
211
|
], config.border, canvas, "Opacity legend");
|
|
155
|
-
return { values, symbols, labels, title, background };
|
|
212
|
+
return { values, texts, symbols, labels, title, background };
|
|
156
213
|
}
|
|
157
214
|
|
|
158
215
|
export const rematerializeOpacityLegend = action(
|
|
@@ -229,11 +286,7 @@ export const rematerializeOpacityLegend = action(
|
|
|
229
286
|
.editGraphics({
|
|
230
287
|
target: "opacityLegendLabels",
|
|
231
288
|
property: "text",
|
|
232
|
-
value:
|
|
233
|
-
layout.values,
|
|
234
|
-
scale.domain,
|
|
235
|
-
"quantitative"
|
|
236
|
-
)
|
|
289
|
+
value: layout.texts
|
|
237
290
|
});
|
|
238
291
|
next = editLegendBackground(
|
|
239
292
|
next,
|
|
@@ -54,7 +54,7 @@ function editContinuous(program, kind, previous, args) {
|
|
|
54
54
|
? ["target", "position", "align", "offset", "title", "labels",
|
|
55
55
|
"titleStyle", "border", "count", "gradient"]
|
|
56
56
|
: ["target", "position", "align", "offset", "title", "symbol", "labels",
|
|
57
|
-
"titleStyle", "itemGap", "border", "count"];
|
|
57
|
+
"titleStyle", "titlePosition", "itemGap", "border", "count"];
|
|
58
58
|
for (const key of Object.keys(args)) {
|
|
59
59
|
if (!allowed.includes(key)) throw new Error(`${kind} legend does not accept ${key}.`);
|
|
60
60
|
}
|
|
@@ -69,18 +69,25 @@ function editContinuous(program, kind, previous, args) {
|
|
|
69
69
|
const title = titleMode === "auto"
|
|
70
70
|
? inferred
|
|
71
71
|
: typeof titleMode === "string" ? titleMode : previous.title;
|
|
72
|
+
const titlePosition = args.titlePosition ?? previous.titlePosition;
|
|
73
|
+
const enteringInline = kind === "opacity" &&
|
|
74
|
+
titlePosition === "left" && previous.titlePosition !== "left";
|
|
75
|
+
const labels = mergeObject(previous.labels, args.labels);
|
|
72
76
|
const normalized = normalizeContinuousLegend({
|
|
73
77
|
target: previous.target,
|
|
74
78
|
position: args.position ?? previous.position,
|
|
75
79
|
align: args.align ?? previous.align,
|
|
76
80
|
offset: args.offset ?? previous.offset,
|
|
77
81
|
count: args.count ?? previous.count,
|
|
82
|
+
titlePosition,
|
|
78
83
|
title,
|
|
79
|
-
labels:
|
|
84
|
+
labels: enteringInline && args.labels?.offset === undefined
|
|
85
|
+
? { ...labels, offset: 8 }
|
|
86
|
+
: labels,
|
|
80
87
|
titleStyle: mergeObject(previous.titleStyle, args.titleStyle),
|
|
81
88
|
border: mergeBorder(previous.border, args.border),
|
|
82
89
|
...(kind === "opacity" ? {
|
|
83
|
-
itemGap: args.itemGap ?? previous.itemGap,
|
|
90
|
+
itemGap: args.itemGap ?? (enteringInline ? 20 : previous.itemGap),
|
|
84
91
|
symbol: args.symbol ?? previous.symbol
|
|
85
92
|
} : {})
|
|
86
93
|
}, kind);
|
|
@@ -112,9 +119,7 @@ function editContinuous(program, kind, previous, args) {
|
|
|
112
119
|
before: kind === "gradient" ? "colorGradientStrips" : "opacityLegendSymbols"
|
|
113
120
|
});
|
|
114
121
|
next = reconcileGraphic(next, `${prefix}Title`, titleVisible, { type: "text" });
|
|
115
|
-
return
|
|
116
|
-
? next.rematerializeGradientLegend()
|
|
117
|
-
: next.rematerializeOpacityLegend();
|
|
122
|
+
return next.rematerializeLegend();
|
|
118
123
|
}
|
|
119
124
|
|
|
120
125
|
function editInterval(program, previous, args) {
|
|
@@ -165,7 +170,7 @@ function editInterval(program, previous, args) {
|
|
|
165
170
|
next = reconcileGraphic(next, "colorLegendTitle", titleVisible, {
|
|
166
171
|
type: "text"
|
|
167
172
|
});
|
|
168
|
-
return next.
|
|
173
|
+
return next.rematerializeLegend();
|
|
169
174
|
}
|
|
170
175
|
|
|
171
176
|
function editStrokeWidth(program, previous, args) {
|
|
@@ -220,7 +225,7 @@ function editStrokeWidth(program, previous, args) {
|
|
|
220
225
|
next = reconcileGraphic(next, "strokeWidthLegendTitle", titleVisible, {
|
|
221
226
|
type: "text"
|
|
222
227
|
});
|
|
223
|
-
return next.
|
|
228
|
+
return next.rematerializeLegend();
|
|
224
229
|
}
|
|
225
230
|
|
|
226
231
|
function categoricalSymbolIds(config) {
|