@pushwoosh/kit-charts 0.0.1 → 0.0.3
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/ai-documentation.md +19 -0
- package/core/Chart/Axes.js +38 -51
- package/core/Chart/Chart.js +45 -53
- package/core/Chart/Hinted.js +1 -2
- package/core/Chart/Legend.js +14 -15
- package/core/Chart/PlotDecorations.js +63 -36
- package/core/Chart/StackLabels.js +6 -7
- package/core/Chart/Titles.js +14 -17
- package/core/Chart/Tooltip.js +12 -13
- package/core/data/points.js +1 -1
- package/core/geometry/path.js +1 -2
- package/core/layout/legend-layout.js +12 -12
- package/core/layout/measure-text.d.ts +5 -0
- package/core/layout/measure-text.js +8 -3
- package/core/layout/metrics.d.ts +7 -0
- package/core/layout/metrics.js +7 -0
- package/core/layout/title-layout.js +9 -15
- package/core/model/build-model.js +44 -69
- package/core/model/palette.d.ts +8 -0
- package/core/model/palette.js +31 -0
- package/core/options/normalize.js +1 -2
- package/core/registry/registry.js +1 -1
- package/core/types/options.d.ts +4 -0
- package/package.json +2 -2
- package/series/area/area.js +2 -3
- package/series/column/column.js +4 -8
- package/series/heatmap/heatmap.js +5 -8
- package/series/line/line.js +2 -3
- package/series/pie/pie.js +6 -8
- package/series/shared/DataLabels.js +8 -8
- package/series/shared/Markers.js +8 -8
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { DEFAULT_FONT_SIZE, textHeight } from '../layout/measure-text';
|
|
3
|
-
import { crisp, PLOT_LABEL_GAP } from '../layout/metrics';
|
|
4
|
-
import { DEFAULT_AXIS_COLOR, DEFAULT_MUTED_TEXT_COLOR, DEFAULT_SURFACE_COLOR } from '../model/palette';
|
|
2
|
+
import { DEFAULT_FONT_SIZE, measureText, textAscent, textHeight } from '../layout/measure-text';
|
|
3
|
+
import { crisp, PLOT_BADGE_PADDING_X, PLOT_BADGE_PADDING_Y, PLOT_BADGE_RADIUS, PLOT_LABEL_GAP } from '../layout/metrics';
|
|
4
|
+
import { DEFAULT_AXIS_COLOR, DEFAULT_MUTED_TEXT_COLOR, DEFAULT_SURFACE_COLOR, textColorOn } from '../model/palette';
|
|
5
5
|
import { ABOVE_SERIES_Z_INDEX } from '../types/options';
|
|
6
6
|
const DASH = {
|
|
7
7
|
solid: undefined,
|
|
@@ -28,51 +28,70 @@ function project(scale, value, edge) {
|
|
|
28
28
|
const at = scale.map(numeric);
|
|
29
29
|
return typeof at === 'number' ? at : null;
|
|
30
30
|
}
|
|
31
|
-
const textProps = style => ({
|
|
32
|
-
fill:
|
|
33
|
-
fontFamily: style
|
|
34
|
-
fontSize:
|
|
35
|
-
fontWeight: style
|
|
31
|
+
const textProps = (style, onFill) => ({
|
|
32
|
+
fill: style?.color ?? (onFill ? textColorOn(onFill) : DEFAULT_MUTED_TEXT_COLOR),
|
|
33
|
+
fontFamily: style?.fontFamily,
|
|
34
|
+
fontSize: style?.fontSize ?? DEFAULT_FONT_SIZE,
|
|
35
|
+
fontWeight: style?.fontWeight
|
|
36
36
|
});
|
|
37
37
|
const ANCHOR = {
|
|
38
38
|
left: 'start',
|
|
39
39
|
center: 'middle',
|
|
40
40
|
right: 'end'
|
|
41
41
|
};
|
|
42
|
+
/**
|
|
43
|
+
* `align` is horizontal and `verticalAlign` vertical whichever way the
|
|
44
|
+
* decoration runs, as in Highcharts — a horizontal line takes its label to the
|
|
45
|
+
* left edge of the plot and sits it on the rule.
|
|
46
|
+
*/
|
|
42
47
|
function PlotLabel({
|
|
43
48
|
label,
|
|
44
|
-
|
|
45
|
-
span,
|
|
49
|
+
box,
|
|
46
50
|
along
|
|
47
51
|
}) {
|
|
48
|
-
if (!
|
|
52
|
+
if (!label?.text) {
|
|
49
53
|
return null;
|
|
50
54
|
}
|
|
51
|
-
const [low, high] = span[0] <= span[1] ? span : [span[1], span[0]];
|
|
52
55
|
const align = label.align ?? (along === 'x' ? 'center' : 'left');
|
|
53
56
|
const verticalAlign = label.verticalAlign ?? (along === 'x' ? 'top' : 'middle');
|
|
54
|
-
const
|
|
55
|
-
const
|
|
56
|
-
const
|
|
57
|
-
left:
|
|
58
|
-
center:
|
|
59
|
-
right:
|
|
60
|
-
}[align];
|
|
61
|
-
const
|
|
62
|
-
top:
|
|
63
|
-
middle:
|
|
64
|
-
bottom:
|
|
65
|
-
}[verticalAlign];
|
|
66
|
-
const x = (along === 'x' ? alongAt : acrossAt) + (label.x ?? 0);
|
|
67
|
-
const y = (along === 'x' ? acrossAt : alongAt) + (label.y ?? 0);
|
|
57
|
+
const line = textHeight(label.style);
|
|
58
|
+
const ascent = textAscent(label.style);
|
|
59
|
+
const x = {
|
|
60
|
+
left: box.x + PLOT_LABEL_GAP,
|
|
61
|
+
center: box.x + box.width / 2,
|
|
62
|
+
right: box.x + box.width - PLOT_LABEL_GAP
|
|
63
|
+
}[align] + (label.x ?? 0);
|
|
64
|
+
const y = {
|
|
65
|
+
top: box.y + PLOT_LABEL_GAP + ascent,
|
|
66
|
+
middle: box.y + box.height / 2 + ascent - line / 2,
|
|
67
|
+
bottom: box.y + box.height - PLOT_LABEL_GAP - (line - ascent)
|
|
68
|
+
}[verticalAlign] + (label.y ?? 0);
|
|
68
69
|
const rotation = label.rotation ?? 0;
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
const fill = label.backgroundColor;
|
|
71
|
+
const padX = label.padding ?? PLOT_BADGE_PADDING_X;
|
|
72
|
+
const padY = label.padding ?? PLOT_BADGE_PADDING_Y;
|
|
73
|
+
const width = measureText(label.text, label.style) + padX * 2;
|
|
74
|
+
const height = line + padY * 2;
|
|
75
|
+
return _jsxs("g", {
|
|
73
76
|
transform: rotation === 0 ? undefined : `rotate(${rotation} ${x} ${y})`,
|
|
74
|
-
|
|
75
|
-
|
|
77
|
+
children: [fill && _jsx("rect", {
|
|
78
|
+
x: {
|
|
79
|
+
left: x - padX,
|
|
80
|
+
center: x - width / 2,
|
|
81
|
+
right: x - width + padX
|
|
82
|
+
}[align],
|
|
83
|
+
y: y - ascent - padY,
|
|
84
|
+
width: width,
|
|
85
|
+
height: height,
|
|
86
|
+
rx: label.borderRadius ?? PLOT_BADGE_RADIUS,
|
|
87
|
+
fill: fill
|
|
88
|
+
}), _jsx("text", {
|
|
89
|
+
x: x,
|
|
90
|
+
y: y,
|
|
91
|
+
textAnchor: ANCHOR[align],
|
|
92
|
+
...textProps(label.style, fill),
|
|
93
|
+
children: label.text
|
|
94
|
+
})]
|
|
76
95
|
});
|
|
77
96
|
}
|
|
78
97
|
/**
|
|
@@ -124,8 +143,7 @@ export function PlotDecorations({
|
|
|
124
143
|
strokeWidth: options_.borderWidth ?? 0
|
|
125
144
|
}), _jsx(PlotLabel, {
|
|
126
145
|
label: options_.label,
|
|
127
|
-
|
|
128
|
-
span: [low, high],
|
|
146
|
+
box: box,
|
|
129
147
|
along: along
|
|
130
148
|
})]
|
|
131
149
|
}, `band-${index}`);
|
|
@@ -159,8 +177,17 @@ export function PlotDecorations({
|
|
|
159
177
|
strokeDasharray: DASH[options_.dashStyle ?? 'solid']
|
|
160
178
|
}), _jsx(PlotLabel, {
|
|
161
179
|
label: options_.label,
|
|
162
|
-
|
|
163
|
-
|
|
180
|
+
box: along === 'x' ? {
|
|
181
|
+
x: on,
|
|
182
|
+
y: plot.y,
|
|
183
|
+
width: 0,
|
|
184
|
+
height: plot.height
|
|
185
|
+
} : {
|
|
186
|
+
x: plot.x,
|
|
187
|
+
y: on,
|
|
188
|
+
width: plot.width,
|
|
189
|
+
height: 0
|
|
190
|
+
},
|
|
164
191
|
along: along
|
|
165
192
|
})]
|
|
166
193
|
}, `line-${index}`);
|
|
@@ -6,7 +6,6 @@ import { DEFAULT_TEXT_COLOR } from '../model/palette';
|
|
|
6
6
|
function collectTotals(model, axisIndex) {
|
|
7
7
|
const totals = new Map();
|
|
8
8
|
model.series.forEach(series => {
|
|
9
|
-
var _model$yAxes$axisInde;
|
|
10
9
|
if ((series.options.yAxis ?? 0) !== axisIndex) {
|
|
11
10
|
return;
|
|
12
11
|
}
|
|
@@ -14,7 +13,7 @@ function collectTotals(model, axisIndex) {
|
|
|
14
13
|
if (!stacked) {
|
|
15
14
|
return;
|
|
16
15
|
}
|
|
17
|
-
const yScale =
|
|
16
|
+
const yScale = model.yAxes[axisIndex]?.scale;
|
|
18
17
|
if (!yScale || yScale.kind === 'band' || yScale.kind === 'color') {
|
|
19
18
|
return;
|
|
20
19
|
}
|
|
@@ -47,7 +46,7 @@ export function StackLabels({
|
|
|
47
46
|
}) {
|
|
48
47
|
const labels = model.yAxes.flatMap((axis, axisIndex) => {
|
|
49
48
|
const options = axis.options.stackLabels;
|
|
50
|
-
if (!
|
|
49
|
+
if (!options?.enabled) {
|
|
51
50
|
return [];
|
|
52
51
|
}
|
|
53
52
|
const style = options.style;
|
|
@@ -68,10 +67,10 @@ export function StackLabels({
|
|
|
68
67
|
x: total.px,
|
|
69
68
|
y: total.py - VALUE_LABEL_GAP,
|
|
70
69
|
textAnchor: "middle",
|
|
71
|
-
fill:
|
|
72
|
-
fontFamily: style
|
|
73
|
-
fontSize:
|
|
74
|
-
fontWeight:
|
|
70
|
+
fill: style?.color ?? DEFAULT_TEXT_COLOR,
|
|
71
|
+
fontFamily: style?.fontFamily,
|
|
72
|
+
fontSize: style?.fontSize ?? DEFAULT_FONT_SIZE,
|
|
73
|
+
fontWeight: style?.fontWeight ?? '600',
|
|
75
74
|
children: content
|
|
76
75
|
}, `sl-${axis.id}-${total.key}`);
|
|
77
76
|
});
|
package/core/Chart/Titles.js
CHANGED
|
@@ -46,22 +46,19 @@ export function Titles({
|
|
|
46
46
|
return null;
|
|
47
47
|
}
|
|
48
48
|
return _jsx("g", {
|
|
49
|
-
children: entries.map(entry => {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}) : null]
|
|
64
|
-
}, entry.key);
|
|
65
|
-
})
|
|
49
|
+
children: entries.map(entry => _jsxs("g", {
|
|
50
|
+
children: [_jsx("text", {
|
|
51
|
+
x: entry.x,
|
|
52
|
+
y: entry.y,
|
|
53
|
+
textAnchor: entry.anchor,
|
|
54
|
+
fill: entry.style?.color ?? COLOR[entry.key],
|
|
55
|
+
fontFamily: entry.style?.fontFamily,
|
|
56
|
+
fontSize: entry.fontSize ?? DEFAULT_FONT_SIZE,
|
|
57
|
+
fontWeight: entry.style?.fontWeight ?? WEIGHT[entry.key],
|
|
58
|
+
children: entry.text
|
|
59
|
+
}), entry.hint ? _jsx(Hint, {
|
|
60
|
+
hint: entry.hint
|
|
61
|
+
}) : null]
|
|
62
|
+
}, entry.key))
|
|
66
63
|
});
|
|
67
64
|
}
|
package/core/Chart/Tooltip.js
CHANGED
|
@@ -11,10 +11,9 @@ export function Tooltip({
|
|
|
11
11
|
pointer,
|
|
12
12
|
children
|
|
13
13
|
}) {
|
|
14
|
-
var _options$style, _options$style2, _options$style3, _options$style4;
|
|
15
14
|
const ref = useRef(null);
|
|
16
|
-
const callout = (
|
|
17
|
-
const target = options
|
|
15
|
+
const callout = (options?.shape ?? 'callout') === 'callout' && !options?.followPointer;
|
|
16
|
+
const target = options?.followPointer ? pointer : at;
|
|
18
17
|
const [placement, setPlacement] = useState({
|
|
19
18
|
x: target.x + TOOLTIP_CURSOR_OFFSET,
|
|
20
19
|
y: target.y + TOOLTIP_CURSOR_OFFSET,
|
|
@@ -57,25 +56,25 @@ export function Tooltip({
|
|
|
57
56
|
if (typeof document === 'undefined') {
|
|
58
57
|
return null;
|
|
59
58
|
}
|
|
60
|
-
const shadow = options
|
|
59
|
+
const shadow = options?.shadow;
|
|
61
60
|
// A table needs room and columns; the dark hint chrome reads badly at that size,
|
|
62
61
|
// so the tabular tooltip is a light card unless the caller says otherwise.
|
|
63
|
-
const tabular = Boolean(options
|
|
64
|
-
const background =
|
|
65
|
-
const color =
|
|
62
|
+
const tabular = Boolean(options?.table);
|
|
63
|
+
const background = options?.backgroundColor ?? (tabular ? DEFAULTS.surface : DEFAULTS.tooltipSurface);
|
|
64
|
+
const color = options?.style?.color ?? (tabular ? DEFAULTS.text : DEFAULTS.tooltipText);
|
|
66
65
|
return createPortal(_jsxs(TooltipBox, {
|
|
67
66
|
ref: ref,
|
|
68
67
|
role: "tooltip",
|
|
69
68
|
"$x": placement.x,
|
|
70
69
|
"$y": placement.y,
|
|
71
|
-
"$padding":
|
|
72
|
-
"$radius":
|
|
73
|
-
"$borderColor":
|
|
70
|
+
"$padding": options?.padding ?? TOOLTIP_PADDING,
|
|
71
|
+
"$radius": options?.borderRadius ?? TOOLTIP_RADIUS,
|
|
72
|
+
"$borderColor": options?.borderColor ?? (tabular ? DEFAULTS.border : 'transparent'),
|
|
74
73
|
"$background": background,
|
|
75
74
|
"$color": color,
|
|
76
|
-
"$fontFamily":
|
|
77
|
-
"$fontSize":
|
|
78
|
-
"$whiteSpace":
|
|
75
|
+
"$fontFamily": options?.style?.fontFamily ?? DEFAULTS.fontFamily,
|
|
76
|
+
"$fontSize": options?.style?.fontSize ?? DEFAULTS.fontSize,
|
|
77
|
+
"$whiteSpace": options?.style?.whiteSpace ?? 'nowrap',
|
|
79
78
|
"$shadow": shadow ? `${shadow.offsetX ?? 0}px ${shadow.offsetY ?? 2}px ${shadow.width ?? 8}px ` + `${shadow.color ?? SHADOW_COLOR}` : DEFAULT_SHADOW,
|
|
80
79
|
children: [children, placement.tailX === null ? null : _jsx(Tail, {
|
|
81
80
|
"$left": placement.tailX,
|
package/core/data/points.js
CHANGED
|
@@ -15,7 +15,7 @@ const isPointObject = point => typeof point === 'object' && point !== null && !A
|
|
|
15
15
|
/** What a hit or a tooltip should show for the point's x: its category, else its x. */
|
|
16
16
|
export const pointKey = point => point.band ?? point.name ?? point.x;
|
|
17
17
|
export function normalizePoints(data, seriesName = 'series') {
|
|
18
|
-
if (!
|
|
18
|
+
if (!data?.length) {
|
|
19
19
|
return [];
|
|
20
20
|
}
|
|
21
21
|
return data.map((point, index) => {
|
package/core/geometry/path.js
CHANGED
|
@@ -24,8 +24,7 @@ export function projectSeries(ctx) {
|
|
|
24
24
|
return [];
|
|
25
25
|
}
|
|
26
26
|
return ctx.points.map((point, index) => {
|
|
27
|
-
|
|
28
|
-
const stacked = (_ctx$stacked = ctx.stacked) === null || _ctx$stacked === void 0 ? void 0 : _ctx$stacked[index];
|
|
27
|
+
const stacked = ctx.stacked?.[index];
|
|
29
28
|
if (point.y === null || ctx.hiddenPoints.has(point.index) || ctx.stacked && !stacked) {
|
|
30
29
|
return null;
|
|
31
30
|
}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { measureText, textHeight } from './measure-text';
|
|
2
2
|
import { LEGEND_BLOCK_GAP, LEGEND_ROW_GAP } from './metrics';
|
|
3
3
|
const itemWidth = (item, options) => {
|
|
4
|
-
const symbolSize =
|
|
5
|
-
return symbolSize + (
|
|
4
|
+
const symbolSize = options?.symbolHeight ?? 8;
|
|
5
|
+
return symbolSize + (options?.symbolPadding ?? 6) + measureText(item.label, options?.itemStyle);
|
|
6
6
|
};
|
|
7
7
|
export function measureLegend(items, options) {
|
|
8
|
-
const rowHeight = textHeight(options
|
|
9
|
-
const vertical = (
|
|
8
|
+
const rowHeight = textHeight(options?.itemStyle);
|
|
9
|
+
const vertical = (options?.layout ?? 'horizontal') === 'vertical';
|
|
10
10
|
// A middle-aligned legend has nowhere to go above or below the plot, so it
|
|
11
11
|
// takes a column at the side instead — the same trade Highcharts makes.
|
|
12
|
-
const beside = (
|
|
13
|
-
if (!items.length ||
|
|
12
|
+
const beside = (options?.verticalAlign ?? 'bottom') === 'middle';
|
|
13
|
+
if (!items.length || options?.enabled === false) {
|
|
14
14
|
return {
|
|
15
15
|
width: 0,
|
|
16
16
|
height: 0,
|
|
@@ -43,10 +43,10 @@ export function measureLegend(items, options) {
|
|
|
43
43
|
*/
|
|
44
44
|
export function layoutLegend(items, options, plot, size, spacing) {
|
|
45
45
|
const metrics = measureLegend(items, options);
|
|
46
|
-
const distance =
|
|
47
|
-
const align =
|
|
48
|
-
const shiftX =
|
|
49
|
-
const shiftY =
|
|
46
|
+
const distance = options?.itemDistance ?? 16;
|
|
47
|
+
const align = options?.align ?? 'left';
|
|
48
|
+
const shiftX = options?.x ?? 0;
|
|
49
|
+
const shiftY = options?.y ?? 0;
|
|
50
50
|
const widths = items.map(item => itemWidth(item, options));
|
|
51
51
|
const rowStep = metrics.rowHeight + LEGEND_ROW_GAP;
|
|
52
52
|
if (metrics.vertical || metrics.beside) {
|
|
@@ -65,7 +65,7 @@ export function layoutLegend(items, options, plot, size, spacing) {
|
|
|
65
65
|
if (metrics.beside) {
|
|
66
66
|
return plot.y + (plot.height - block) / 2 + metrics.rowHeight / 2;
|
|
67
67
|
}
|
|
68
|
-
return (
|
|
68
|
+
return (options?.verticalAlign ?? 'bottom') === 'top' ? plot.y - metrics.height + metrics.rowHeight : plot.y + plot.height + metrics.rowHeight + LEGEND_ROW_GAP;
|
|
69
69
|
})();
|
|
70
70
|
return {
|
|
71
71
|
metrics,
|
|
@@ -88,7 +88,7 @@ export function layoutLegend(items, options, plot, size, spacing) {
|
|
|
88
88
|
// Sit on the last row inside the chart's bottom spacing, not on the canvas
|
|
89
89
|
// edge: flush with the edge, the row's own descenders and its focus ring are
|
|
90
90
|
// cut off by the viewport.
|
|
91
|
-
const y = (
|
|
91
|
+
const y = (options?.verticalAlign ?? 'bottom') === 'top' ? plot.y - metrics.height + metrics.rowHeight : size.height - spacing[2] - metrics.rowHeight / 2;
|
|
92
92
|
const offsets = widths.reduce((acc, _width, index) => {
|
|
93
93
|
acc.push(index === 0 ? startX : acc[index - 1] + widths[index - 1] + distance);
|
|
94
94
|
return acc;
|
|
@@ -6,4 +6,9 @@ export declare const DEFAULT_SUBTITLE_FONT_SIZE: string;
|
|
|
6
6
|
export declare const toCssFont: (style?: TextStyle) => string;
|
|
7
7
|
export declare function measureText(text: string, style?: TextStyle): number;
|
|
8
8
|
export declare const textHeight: (style?: TextStyle) => number;
|
|
9
|
+
/**
|
|
10
|
+
* How much of a line box stands above the baseline. Drawing a box around text
|
|
11
|
+
* needs the split, not just the height: SVG places text by its baseline.
|
|
12
|
+
*/
|
|
13
|
+
export declare const textAscent: (style?: TextStyle) => number;
|
|
9
14
|
export declare function clearTextMeasurementCache(): void;
|
|
@@ -17,9 +17,9 @@ export const DEFAULT_FONT_FAMILY = FontFamily.MAIN;
|
|
|
17
17
|
export const DEFAULT_TITLE_FONT_SIZE = FontSize.HEADING2;
|
|
18
18
|
export const DEFAULT_SUBTITLE_FONT_SIZE = FontSize.SMALL;
|
|
19
19
|
export const toCssFont = style => {
|
|
20
|
-
const weight =
|
|
21
|
-
const size =
|
|
22
|
-
const family =
|
|
20
|
+
const weight = style?.fontWeight ?? 'normal';
|
|
21
|
+
const size = style?.fontSize ?? DEFAULT_FONT_SIZE;
|
|
22
|
+
const family = style?.fontFamily ?? DEFAULT_FONT_FAMILY;
|
|
23
23
|
return `${weight} ${size} ${family}`;
|
|
24
24
|
};
|
|
25
25
|
const parseFontSize = font => {
|
|
@@ -51,6 +51,11 @@ export function measureText(text, style) {
|
|
|
51
51
|
return width;
|
|
52
52
|
}
|
|
53
53
|
export const textHeight = style => Math.ceil(parseFontSize(toCssFont(style)) * 1.2);
|
|
54
|
+
/**
|
|
55
|
+
* How much of a line box stands above the baseline. Drawing a box around text
|
|
56
|
+
* needs the split, not just the height: SVG places text by its baseline.
|
|
57
|
+
*/
|
|
58
|
+
export const textAscent = style => Math.round(textHeight(style) * 0.8);
|
|
54
59
|
export function clearTextMeasurementCache() {
|
|
55
60
|
cache.clear();
|
|
56
61
|
context = undefined;
|
package/core/layout/metrics.d.ts
CHANGED
|
@@ -48,6 +48,13 @@ export declare const TOOLTIP_PADDING = 8;
|
|
|
48
48
|
export declare const TOOLTIP_RADIUS = 6;
|
|
49
49
|
/** Distance from a plot band or plot line to its own label. */
|
|
50
50
|
export declare const PLOT_LABEL_GAP = 6;
|
|
51
|
+
/**
|
|
52
|
+
* A plot label drawn on a fill: its padding and corner radius. Wider than it is
|
|
53
|
+
* tall, because a pill reads as a pill only when the text has room either side.
|
|
54
|
+
*/
|
|
55
|
+
export declare const PLOT_BADGE_PADDING_X = 6;
|
|
56
|
+
export declare const PLOT_BADGE_PADDING_Y = 2;
|
|
57
|
+
export declare const PLOT_BADGE_RADIUS = 4;
|
|
51
58
|
/**
|
|
52
59
|
* Puts a stroke where the device can draw it in one row of pixels: half a stroke
|
|
53
60
|
* either side of the position has to land on a pixel boundary, so an odd stroke
|
package/core/layout/metrics.js
CHANGED
|
@@ -48,6 +48,13 @@ export const TOOLTIP_PADDING = 8;
|
|
|
48
48
|
export const TOOLTIP_RADIUS = 6;
|
|
49
49
|
/** Distance from a plot band or plot line to its own label. */
|
|
50
50
|
export const PLOT_LABEL_GAP = 6;
|
|
51
|
+
/**
|
|
52
|
+
* A plot label drawn on a fill: its padding and corner radius. Wider than it is
|
|
53
|
+
* tall, because a pill reads as a pill only when the text has room either side.
|
|
54
|
+
*/
|
|
55
|
+
export const PLOT_BADGE_PADDING_X = 6;
|
|
56
|
+
export const PLOT_BADGE_PADDING_Y = 2;
|
|
57
|
+
export const PLOT_BADGE_RADIUS = 4;
|
|
51
58
|
/**
|
|
52
59
|
* Puts a stroke where the device can draw it in one row of pixels: half a stroke
|
|
53
60
|
* either side of the position has to land on a pixel boundary, so an odd stroke
|
|
@@ -5,14 +5,8 @@ const ANCHOR = {
|
|
|
5
5
|
center: 'middle',
|
|
6
6
|
right: 'end'
|
|
7
7
|
};
|
|
8
|
-
const shown = title =>
|
|
9
|
-
|
|
10
|
-
return Boolean(title === null || title === void 0 ? void 0 : title.text) && (title === null || title === void 0 || (_title$style = title.style) === null || _title$style === void 0 ? void 0 : _title$style.display) !== 'none';
|
|
11
|
-
};
|
|
12
|
-
const sizeOf = (title, fallback) => {
|
|
13
|
-
var _title$style2;
|
|
14
|
-
return (title === null || title === void 0 || (_title$style2 = title.style) === null || _title$style2 === void 0 ? void 0 : _title$style2.fontSize) ?? fallback;
|
|
15
|
-
};
|
|
8
|
+
const shown = title => Boolean(title?.text) && title?.style?.display !== 'none';
|
|
9
|
+
const sizeOf = (title, fallback) => title?.style?.fontSize ?? fallback;
|
|
16
10
|
export function layoutTitles(options, size, spacing) {
|
|
17
11
|
const [spaceTop, spaceRight,, spaceLeft] = spacing;
|
|
18
12
|
const {
|
|
@@ -22,13 +16,13 @@ export function layoutTitles(options, size, spacing) {
|
|
|
22
16
|
const entries = [];
|
|
23
17
|
const push = (key, source, fontSize, top) => {
|
|
24
18
|
const height = textHeight({
|
|
25
|
-
...
|
|
19
|
+
...source?.style,
|
|
26
20
|
fontSize
|
|
27
21
|
});
|
|
28
22
|
if (!shown(source)) {
|
|
29
23
|
return 0;
|
|
30
24
|
}
|
|
31
|
-
const align =
|
|
25
|
+
const align = source?.align ?? 'left';
|
|
32
26
|
const x = (() => {
|
|
33
27
|
if (align === 'center') {
|
|
34
28
|
return size.width / 2;
|
|
@@ -38,10 +32,10 @@ export function layoutTitles(options, size, spacing) {
|
|
|
38
32
|
}
|
|
39
33
|
return spaceLeft;
|
|
40
34
|
})();
|
|
41
|
-
const text =
|
|
35
|
+
const text = source?.text ?? '';
|
|
42
36
|
const baseline = top + height;
|
|
43
37
|
const width = measureText(text, {
|
|
44
|
-
...
|
|
38
|
+
...source?.style,
|
|
45
39
|
fontSize
|
|
46
40
|
});
|
|
47
41
|
// The icon follows the text, wherever the anchor put it.
|
|
@@ -61,9 +55,9 @@ export function layoutTitles(options, size, spacing) {
|
|
|
61
55
|
// `y` is a baseline, so the first line starts one line height down.
|
|
62
56
|
y: baseline,
|
|
63
57
|
anchor: ANCHOR[align],
|
|
64
|
-
style: source
|
|
58
|
+
style: source?.style,
|
|
65
59
|
fontSize,
|
|
66
|
-
hint: source
|
|
60
|
+
hint: source?.hint ? {
|
|
67
61
|
text: source.hint,
|
|
68
62
|
x: textEnd + HINT_ICON_GAP + HINT_ICON_SIZE / 2,
|
|
69
63
|
y: baseline - textHeight({
|
|
@@ -76,7 +70,7 @@ export function layoutTitles(options, size, spacing) {
|
|
|
76
70
|
const titleHeight = push('title', title, sizeOf(title, DEFAULT_TITLE_FONT_SIZE), spaceTop);
|
|
77
71
|
// The caption shares the title's row, pushed to the far edge: the note about
|
|
78
72
|
// what the figures cover belongs beside the name, not under it.
|
|
79
|
-
if (title
|
|
73
|
+
if (title?.caption) {
|
|
80
74
|
push('caption', {
|
|
81
75
|
text: title.caption,
|
|
82
76
|
align: 'right',
|