@pushwoosh/kit-charts 0.0.5 → 0.0.7
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 +18 -8
- package/core/Chart/Axes.js +38 -25
- package/core/Chart/Chart.js +33 -18
- package/core/format/template.d.ts +5 -3
- package/core/format/template.js +8 -6
- package/core/format/tokens.d.ts +1 -1
- package/core/format/tokens.js +2 -2
- package/core/layout/node-text.d.ts +3 -0
- package/core/layout/node-text.js +14 -0
- package/core/model/build-model.d.ts +3 -0
- package/core/model/build-model.js +25 -10
- package/core/types/options.d.ts +2 -0
- package/package.json +1 -1
package/ai-documentation.md
CHANGED
|
@@ -94,8 +94,15 @@ never falls below 1.
|
|
|
94
94
|
|
|
95
95
|
`labels.allowOverlap` defaults to false on both axes and on data labels. A label
|
|
96
96
|
whose box would run into one already placed loses its text — Highcharts hides
|
|
97
|
-
the label and keeps the tick, and so do we.
|
|
98
|
-
|
|
97
|
+
the label and keeps the tick, and so do we.
|
|
98
|
+
|
|
99
|
+
An axis or data-label `formatter` may return a `ReactNode` and not just text; the
|
|
100
|
+
axis draws the node and measures the text inside it, so an edge label still turns
|
|
101
|
+
inwards and the y gutter still widens. A node the walk cannot read text out of —
|
|
102
|
+
a bare shape, a component — measures as a point: it is always drawn, and a text
|
|
103
|
+
label too close to it is dropped as it would be beside any other label. Inside an
|
|
104
|
+
axis label the node lands in an SVG `<text>`, so `<tspan>` and strings render and
|
|
105
|
+
an HTML element does not.
|
|
99
106
|
|
|
100
107
|
## Plot bands and lines
|
|
101
108
|
|
|
@@ -339,12 +346,15 @@ the value wrapped in `valuePrefix`/`valueSuffix`. `formatter` replaces the body
|
|
|
339
346
|
returns a `ReactNode`; `ctx.points[].prev`/`.next` carry the neighbouring points, so
|
|
340
347
|
a delta needs no extra lookup. `shared: true` collects every series at the hovered x.
|
|
341
348
|
|
|
342
|
-
`headerFormat` and `pointFormatter` are taken under their Highcharts
|
|
343
|
-
config that already carries them needs no rewrite: `headerFormat: ''`
|
|
344
|
-
|
|
345
|
-
`{
|
|
346
|
-
|
|
347
|
-
`
|
|
349
|
+
`headerFormat`, `pointFormat` and `pointFormatter` are taken under their Highcharts
|
|
350
|
+
names, so a config that already carries them needs no rewrite: `headerFormat: ''`
|
|
351
|
+
drops the header and `pointFormat` writes the row after the swatch. Both templates
|
|
352
|
+
fill `{point.key}`, `{point.x}`, `{point.y}`, `{point.value}` and `{series.name}`,
|
|
353
|
+
date an x through `{point.x:%b %d}` (a padded pattern is trimmed) and pass numbers
|
|
354
|
+
through the chart's `numberFormat`; markup in them is dropped, because the chrome
|
|
355
|
+
around a tooltip is the library's. `pointFormatter` is handed the point and returns
|
|
356
|
+
a `ReactNode`, and outranks `pointFormat`; `formatter` and `table` outrank all
|
|
357
|
+
three. Golden cases: `area/header-format`, `line/point-format-template`.
|
|
348
358
|
|
|
349
359
|
`ctx.points[].pointMeta` is the hovered point's own `meta` — the third type
|
|
350
360
|
parameter of `ChartOptions<TChartMeta, TSeriesMeta, TPointMeta>`, the same object
|
package/core/Chart/Axes.js
CHANGED
|
@@ -14,6 +14,13 @@ const ANCHOR = {
|
|
|
14
14
|
center: 'middle',
|
|
15
15
|
right: 'end'
|
|
16
16
|
};
|
|
17
|
+
/** What a tick draws: its own node if a formatter made one, its text otherwise. */
|
|
18
|
+
const tickContent = tick => {
|
|
19
|
+
if (tick.node !== undefined && tick.node !== null) {
|
|
20
|
+
return tick.node;
|
|
21
|
+
}
|
|
22
|
+
return tick.label === '' ? null : tick.label;
|
|
23
|
+
};
|
|
17
24
|
/** A tick label, wrapped in its explanation when the axis gave it one. */
|
|
18
25
|
function TickLabel({
|
|
19
26
|
hint,
|
|
@@ -125,23 +132,26 @@ export function XAxis({
|
|
|
125
132
|
y2: crisp(baseline, options.lineWidth ?? 1),
|
|
126
133
|
stroke: lineColor,
|
|
127
134
|
strokeWidth: options.lineWidth ?? 1
|
|
128
|
-
}), axis.ticks.map(tick =>
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
135
|
+
}), axis.ticks.map(tick => {
|
|
136
|
+
const content = tickContent(tick);
|
|
137
|
+
return _jsxs("g", {
|
|
138
|
+
children: [tickLength > 0 && tickWidth > 0 ? _jsx("line", {
|
|
139
|
+
x1: crisp(tick.position, tickWidth),
|
|
140
|
+
x2: crisp(tick.position, tickWidth),
|
|
141
|
+
y1: crisp(baseline, tickWidth),
|
|
142
|
+
y2: crisp(baseline + direction * tickLength, tickWidth),
|
|
143
|
+
stroke: options.tickColor ?? lineColor,
|
|
144
|
+
strokeWidth: tickWidth
|
|
145
|
+
}) : null, labelsEnabled && content !== null ? _jsx(TickLabel, {
|
|
146
|
+
hint: tick.hint,
|
|
147
|
+
x: tick.position + (options.labels?.x ?? 0),
|
|
148
|
+
y: labelY,
|
|
149
|
+
anchor: tick.anchor,
|
|
150
|
+
style: options.labels?.style,
|
|
151
|
+
children: content
|
|
152
|
+
}) : null]
|
|
153
|
+
}, `x-${tick.value}`);
|
|
154
|
+
}), _jsx(AxisTitle, {
|
|
145
155
|
title: options.title,
|
|
146
156
|
x: plot.x + plot.width / 2,
|
|
147
157
|
y: labelY + direction * ((options.title?.offset ?? 0) + X_AXIS_TITLE_GAP),
|
|
@@ -173,14 +183,17 @@ export function YAxis({
|
|
|
173
183
|
const titleShift = outwards ? Math.ceil(columnWidth * (anchor === 'middle' ? 0.5 : 1)) + AXIS_TITLE_GAP : 0;
|
|
174
184
|
const titleX = onRight ? edge + titleShift + (options.title?.offset ?? 0) : edge - titleShift - (options.title?.offset ?? 0);
|
|
175
185
|
return _jsxs("g", {
|
|
176
|
-
children: [labelsEnabled ? axis.ticks.map(tick =>
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
186
|
+
children: [labelsEnabled ? axis.ticks.map(tick => {
|
|
187
|
+
const content = tickContent(tick);
|
|
188
|
+
return content === null ? null : _jsx(TickLabel, {
|
|
189
|
+
x: edge + (options.labels?.x ?? 0),
|
|
190
|
+
y: tick.position + TEXT_BASELINE_SHIFT,
|
|
191
|
+
anchor: anchor,
|
|
192
|
+
hint: tick.hint,
|
|
193
|
+
style: options.labels?.style,
|
|
194
|
+
children: content
|
|
195
|
+
}, `y-${tick.value}`);
|
|
196
|
+
}) : null, _jsx(AxisTitle, {
|
|
184
197
|
title: options.title,
|
|
185
198
|
x: titleX,
|
|
186
199
|
y: titleY(options, plot),
|
package/core/Chart/Chart.js
CHANGED
|
@@ -33,32 +33,47 @@ const toCssSize = (value, fallback) => {
|
|
|
33
33
|
}
|
|
34
34
|
return typeof value === 'number' ? `${value}px` : value;
|
|
35
35
|
};
|
|
36
|
-
|
|
36
|
+
const templateContext = (point, x) => ({
|
|
37
|
+
x,
|
|
38
|
+
y: point?.y ?? null,
|
|
39
|
+
value: point?.value ?? null,
|
|
40
|
+
seriesName: point?.seriesName ?? ''
|
|
41
|
+
});
|
|
42
|
+
function tooltipHeader(ctx, headerFormat, formatX, numberFormat) {
|
|
37
43
|
if (headerFormat === undefined) {
|
|
38
44
|
return formatX(ctx.x);
|
|
39
45
|
}
|
|
40
|
-
return applyTooltipTemplate(headerFormat,
|
|
41
|
-
x: ctx.x,
|
|
42
|
-
y: ctx.points[0]?.y ?? null,
|
|
43
|
-
seriesName: ctx.points[0]?.seriesName ?? ''
|
|
44
|
-
});
|
|
46
|
+
return applyTooltipTemplate(headerFormat, templateContext(ctx.points[0], ctx.x), numberFormat);
|
|
45
47
|
}
|
|
46
|
-
function
|
|
47
|
-
const header = tooltipHeader(ctx, tooltip?.headerFormat, formatX);
|
|
48
|
+
function pointRow(point, tooltip, formatValue, numberFormat) {
|
|
48
49
|
const pointFormatter = tooltip?.pointFormatter;
|
|
50
|
+
if (pointFormatter) {
|
|
51
|
+
return pointFormatter(point);
|
|
52
|
+
}
|
|
53
|
+
if (tooltip?.pointFormat !== undefined) {
|
|
54
|
+
return _jsxs(_Fragment, {
|
|
55
|
+
children: [_jsx(TooltipSwatch, {
|
|
56
|
+
"$color": point.color
|
|
57
|
+
}), applyTooltipTemplate(tooltip.pointFormat, templateContext(point, point.x), numberFormat)]
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
return _jsxs(_Fragment, {
|
|
61
|
+
children: [_jsx(TooltipSwatch, {
|
|
62
|
+
"$color": point.color
|
|
63
|
+
}), _jsx(TooltipName, {
|
|
64
|
+
children: point.seriesName
|
|
65
|
+
}), _jsx(TooltipValue, {
|
|
66
|
+
children: formatValue(point.value ?? point.y)
|
|
67
|
+
})]
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
function defaultTooltip(ctx, tooltip, formatX, formatValue, numberFormat) {
|
|
71
|
+
const header = tooltipHeader(ctx, tooltip?.headerFormat, formatX, numberFormat);
|
|
49
72
|
return _jsxs("div", {
|
|
50
73
|
children: [header ? _jsx(TooltipTitle, {
|
|
51
74
|
children: header
|
|
52
75
|
}) : null, ctx.points.map(point => _jsx(TooltipRow, {
|
|
53
|
-
children:
|
|
54
|
-
children: [_jsx(TooltipSwatch, {
|
|
55
|
-
"$color": point.color
|
|
56
|
-
}), _jsx(TooltipName, {
|
|
57
|
-
children: point.seriesName
|
|
58
|
-
}), _jsx(TooltipValue, {
|
|
59
|
-
children: formatValue(point.value ?? point.y)
|
|
60
|
-
})]
|
|
61
|
-
})
|
|
76
|
+
children: pointRow(point, tooltip, formatValue, numberFormat)
|
|
62
77
|
}, `${point.seriesIndex}-${point.index}`))]
|
|
63
78
|
});
|
|
64
79
|
}
|
|
@@ -450,7 +465,7 @@ function ChartBody({
|
|
|
450
465
|
return formatter(ctx);
|
|
451
466
|
}
|
|
452
467
|
const table = options.tooltip?.table;
|
|
453
|
-
return table ? tableTooltip(ctx, table, formatX) : defaultTooltip(ctx, options.tooltip, formatX, formatValue);
|
|
468
|
+
return table ? tableTooltip(ctx, table, formatX) : defaultTooltip(ctx, options.tooltip, formatX, formatValue, model.numberFormat);
|
|
454
469
|
}, [hover, model, options.title?.text, options.meta, options.tooltip, formatX, formatValue]);
|
|
455
470
|
const hasData = model?.series.some(series => series.points.some(point => point.y !== null)) ?? false;
|
|
456
471
|
const plotBorderWidth = options.chart?.plotBorderWidth ?? 0;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import { type NumberFormatter } from '../types/renderer';
|
|
1
2
|
export interface TemplateContext {
|
|
2
3
|
x: number | string;
|
|
3
4
|
y: number | null;
|
|
5
|
+
value: number | null;
|
|
4
6
|
seriesName: string;
|
|
5
7
|
}
|
|
6
8
|
/**
|
|
7
|
-
* Fills a Highcharts header template. Markup in it is dropped rather than
|
|
8
|
-
* the chrome around a tooltip is the library's, and a `<span>`
|
|
9
|
+
* Fills a Highcharts header or point template. Markup in it is dropped rather than
|
|
10
|
+
* rendered: the chrome around a tooltip is the library's, and a `<span>` is styling.
|
|
9
11
|
*/
|
|
10
|
-
export declare function applyTooltipTemplate(format: string, ctx: TemplateContext): string;
|
|
12
|
+
export declare function applyTooltipTemplate(format: string, ctx: TemplateContext, numberFormat?: NumberFormatter): string;
|
package/core/format/template.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { formatTime } from './time';
|
|
2
2
|
import { fillTokens } from './tokens';
|
|
3
|
-
const substitute = (name, ctx) => {
|
|
3
|
+
const substitute = (name, ctx, numberFormat) => {
|
|
4
4
|
switch (name) {
|
|
5
5
|
case 'point.key':
|
|
6
6
|
case 'point.x':
|
|
7
7
|
return String(ctx.x);
|
|
8
8
|
case 'point.y':
|
|
9
|
-
return ctx.y === null ? '' :
|
|
9
|
+
return ctx.y === null ? '' : numberFormat(ctx.y);
|
|
10
|
+
case 'point.value':
|
|
11
|
+
return ctx.value === null ? '' : numberFormat(ctx.value);
|
|
10
12
|
case 'series.name':
|
|
11
13
|
return ctx.seriesName;
|
|
12
14
|
default:
|
|
@@ -14,13 +16,13 @@ const substitute = (name, ctx) => {
|
|
|
14
16
|
}
|
|
15
17
|
};
|
|
16
18
|
/**
|
|
17
|
-
* Fills a Highcharts header template. Markup in it is dropped rather than
|
|
18
|
-
* the chrome around a tooltip is the library's, and a `<span>`
|
|
19
|
+
* Fills a Highcharts header or point template. Markup in it is dropped rather than
|
|
20
|
+
* rendered: the chrome around a tooltip is the library's, and a `<span>` is styling.
|
|
19
21
|
*/
|
|
20
|
-
export function applyTooltipTemplate(format, ctx) {
|
|
22
|
+
export function applyTooltipTemplate(format, ctx, numberFormat = String) {
|
|
21
23
|
const stripped = format.replace(/<br\s*\/?>/gi, ' ').replace(/<[^>]*>/g, '');
|
|
22
24
|
return fillTokens(stripped, (name, pattern) => {
|
|
23
|
-
const value = substitute(name, ctx);
|
|
25
|
+
const value = substitute(name, ctx, numberFormat);
|
|
24
26
|
if (value === undefined) {
|
|
25
27
|
return undefined;
|
|
26
28
|
}
|
package/core/format/tokens.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export type TokenResolver = (name: string, pattern: string | undefined) => string | undefined;
|
|
2
|
-
/** Fills `{name}` and `{name:pattern}
|
|
2
|
+
/** Fills `{name}` and `{name:pattern}`; an unresolved token is left as it stands. */
|
|
3
3
|
export declare function fillTokens(format: string, resolve: TokenResolver): string;
|
package/core/format/tokens.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const TOKEN = /\{([a-z][a-z.]*)(?::([^}]*))?\}/gi;
|
|
2
|
-
/** Fills `{name}` and `{name:pattern}
|
|
2
|
+
/** Fills `{name}` and `{name:pattern}`; an unresolved token is left as it stands. */
|
|
3
3
|
export function fillTokens(format, resolve) {
|
|
4
|
-
return format.replace(TOKEN, (match, name, pattern) => resolve(name, pattern) ?? match);
|
|
4
|
+
return format.replace(TOKEN, (match, name, pattern) => resolve(name, pattern?.trim() || undefined) ?? match);
|
|
5
5
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { isValidElement } from 'react';
|
|
2
|
+
/** The text a node prints, for the layout that has to measure it. */
|
|
3
|
+
export function nodeText(node) {
|
|
4
|
+
if (typeof node === 'string' || typeof node === 'number') {
|
|
5
|
+
return String(node);
|
|
6
|
+
}
|
|
7
|
+
if (Array.isArray(node)) {
|
|
8
|
+
return node.map(child => nodeText(child)).join('');
|
|
9
|
+
}
|
|
10
|
+
if (isValidElement(node)) {
|
|
11
|
+
return nodeText(node.props.children);
|
|
12
|
+
}
|
|
13
|
+
return '';
|
|
14
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
1
2
|
import { type NormalizedPoint } from '../data/points';
|
|
2
3
|
import { type TitleEntry } from '../layout/title-layout';
|
|
3
4
|
import { type Rect, type Size, type SlotGeometry, type Spacing } from '../types/geometry';
|
|
@@ -24,6 +25,8 @@ export interface AxisTick {
|
|
|
24
25
|
value: number | string;
|
|
25
26
|
position: number;
|
|
26
27
|
label: string;
|
|
28
|
+
/** What the axis draws where a formatter returned something other than text. */
|
|
29
|
+
node?: ReactNode;
|
|
27
30
|
anchor: TickAnchor;
|
|
28
31
|
/** Sentence this tick explains itself with, opened from the label on hover. */
|
|
29
32
|
hint?: string;
|
|
@@ -4,6 +4,7 @@ import { applyValueFormat, formatTime } from '../format/time';
|
|
|
4
4
|
import { measureLegend } from '../layout/legend-layout';
|
|
5
5
|
import { measureText, textHeight } from '../layout/measure-text';
|
|
6
6
|
import { AXIS_GUTTER_PADDING, AXIS_LABEL_GAP, AXIS_TITLE_GAP, MARKER_REACH, VALUE_LABEL_HEADROOM, X_AXIS_LABEL_DROP } from '../layout/metrics';
|
|
7
|
+
import { nodeText } from '../layout/node-text';
|
|
7
8
|
import { computeSlots } from '../layout/point-width';
|
|
8
9
|
import { layoutTitles } from '../layout/title-layout';
|
|
9
10
|
import { resolveAxes, resolveSeries, isSeriesVisible } from '../options/normalize';
|
|
@@ -17,6 +18,10 @@ export const hiddenKey = (seriesIndex, pointIndex) => pointIndex === undefined ?
|
|
|
17
18
|
const DEFAULT_SPACING = [10, 10, 15, 10];
|
|
18
19
|
const isBand = scale => scale.kind === 'band';
|
|
19
20
|
const isContinuous = scale => scale.kind === 'linear' || scale.kind === 'time';
|
|
21
|
+
const asText = label => ({
|
|
22
|
+
label,
|
|
23
|
+
node: undefined
|
|
24
|
+
});
|
|
20
25
|
function tickLabel(value, axis, timePattern, index, count, kind, numberFormat) {
|
|
21
26
|
const formatter = axis.labels?.formatter;
|
|
22
27
|
if (formatter) {
|
|
@@ -32,17 +37,20 @@ function tickLabel(value, axis, timePattern, index, count, kind, numberFormat) {
|
|
|
32
37
|
}
|
|
33
38
|
});
|
|
34
39
|
if (typeof node === 'string' || typeof node === 'number') {
|
|
35
|
-
return String(node);
|
|
40
|
+
return asText(String(node));
|
|
36
41
|
}
|
|
37
|
-
return
|
|
42
|
+
return {
|
|
43
|
+
label: nodeText(node),
|
|
44
|
+
node
|
|
45
|
+
};
|
|
38
46
|
}
|
|
39
47
|
if (axis.labels?.format) {
|
|
40
|
-
return applyValueFormat(axis.labels.format, value);
|
|
48
|
+
return asText(applyValueFormat(axis.labels.format, value));
|
|
41
49
|
}
|
|
42
50
|
if (typeof value === 'number') {
|
|
43
|
-
return timePattern ? formatTime(timePattern, value) : numberFormat(value);
|
|
51
|
+
return asText(timePattern ? formatTime(timePattern, value) : numberFormat(value));
|
|
44
52
|
}
|
|
45
|
-
return String(value);
|
|
53
|
+
return asText(String(value));
|
|
46
54
|
}
|
|
47
55
|
function collectCategories(axis, series, numeric = false) {
|
|
48
56
|
if (axis.categories?.length) {
|
|
@@ -177,14 +185,17 @@ function dropOverlapping(ticks, axis, along) {
|
|
|
177
185
|
// rather than order keeps one rule for both directions.
|
|
178
186
|
let kept = null;
|
|
179
187
|
return ticks.map(tick => {
|
|
180
|
-
if (!tick.label) {
|
|
188
|
+
if (!tick.label && tick.node === undefined) {
|
|
181
189
|
return tick;
|
|
182
190
|
}
|
|
183
|
-
|
|
184
|
-
|
|
191
|
+
// A node with no text to read measures as a point rather than dropping out of
|
|
192
|
+
// the walk: it is always drawn, so a label after it still has to clear it.
|
|
193
|
+
const half = tick.label ? (along === 'x' ? measureText(tick.label, style) : textHeight(style)) / 2 : 0;
|
|
194
|
+
if (tick.label && kept && Math.abs(tick.position - kept.position) < half + kept.half + AXIS_LABEL_GAP) {
|
|
185
195
|
return {
|
|
186
196
|
...tick,
|
|
187
|
-
label: ''
|
|
197
|
+
label: '',
|
|
198
|
+
node: undefined
|
|
188
199
|
};
|
|
189
200
|
}
|
|
190
201
|
kept = {
|
|
@@ -212,11 +223,15 @@ function axisTicks(axis, scale, hint, timePattern, along, numberFormat, bounds)
|
|
|
212
223
|
const offset = isBand(scale) ? scale.bandwidth() / 2 : 0;
|
|
213
224
|
const built = values.map((value, index) => {
|
|
214
225
|
const position = (isBand(scale) ? scale.map(String(value)) : scale.map(Number(value))) + offset;
|
|
215
|
-
const
|
|
226
|
+
const {
|
|
227
|
+
label,
|
|
228
|
+
node
|
|
229
|
+
} = tickLabel(value, axis, timePattern, index, values.length, scale.kind, numberFormat);
|
|
216
230
|
return {
|
|
217
231
|
value,
|
|
218
232
|
position,
|
|
219
233
|
label,
|
|
234
|
+
node,
|
|
220
235
|
anchor: tickAnchor(label, position, axis, bounds),
|
|
221
236
|
hint: axis.labels?.hint?.({
|
|
222
237
|
value,
|
package/core/types/options.d.ts
CHANGED
|
@@ -207,6 +207,8 @@ export interface TooltipOptions<TChartMeta = unknown, TSeriesMeta = unknown, TPo
|
|
|
207
207
|
formatter?: TooltipFormatter<TChartMeta, TSeriesMeta, TPointMeta>;
|
|
208
208
|
/** Highcharts' header template: `''` drops the header, `{point.key}` and friends fill. */
|
|
209
209
|
headerFormat?: string;
|
|
210
|
+
/** Highcharts' per-point template, drawn after the swatch; `pointFormatter` wins. */
|
|
211
|
+
pointFormat?: string;
|
|
210
212
|
/** One row per point, in place of the default swatch, name and value. */
|
|
211
213
|
pointFormatter?: TooltipCellFormatter<TSeriesMeta, TPointMeta>;
|
|
212
214
|
dateFormat?: string;
|