@pushwoosh/kit-charts 0.0.3 → 0.0.5
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 +41 -1
- package/core/Chart/Chart.d.ts +4 -4
- package/core/Chart/Chart.js +31 -14
- package/core/Chart/Chart.styles.d.ts +2 -0
- package/core/Chart/Chart.styles.js +3 -2
- package/core/format/template.d.ts +10 -0
- package/core/format/template.js +30 -0
- package/core/format/time.js +5 -4
- package/core/format/tokens.d.ts +3 -0
- package/core/format/tokens.js +5 -0
- package/core/types/formatter.d.ts +7 -5
- package/core/types/options.d.ts +20 -10
- package/package.json +1 -1
- package/presets/AreaChart.d.ts +1 -1
- package/presets/ColumnChart.d.ts +1 -1
- package/presets/LineChart.d.ts +1 -1
- package/presets/PieChart.d.ts +1 -1
- package/presets/StackedColumnChart.d.ts +2 -2
- package/series/area/area.js +1 -1
- package/series/column/column.js +2 -3
- package/series/heatmap/heatmap.js +2 -3
- package/series/shared/color.d.ts +6 -0
- package/series/shared/color.js +9 -0
package/ai-documentation.md
CHANGED
|
@@ -62,7 +62,8 @@ Deliberate differences from Highcharts:
|
|
|
62
62
|
`ChartOptions<TChartMeta, TSeriesMeta, TPointMeta>`.
|
|
63
63
|
2. Formatters take a typed context **argument**; there is no `this`.
|
|
64
64
|
3. Formatters return a `ReactNode`, never an HTML string. There is no `useHTML`.
|
|
65
|
-
4. `TooltipPoint` exposes `prev` and `next` neighbours directly
|
|
65
|
+
4. `TooltipPoint` exposes `prev` and `next` neighbours directly, and carries the
|
|
66
|
+
point's own `meta` as `pointMeta` beside the series' `meta`.
|
|
66
67
|
5. Formatters see `ctx.chart.title` and `ctx.chart.meta`, not the option tree.
|
|
67
68
|
6. No `credits` option.
|
|
68
69
|
7. No `reflow`; resize is observed automatically.
|
|
@@ -338,6 +339,21 @@ the value wrapped in `valuePrefix`/`valueSuffix`. `formatter` replaces the body
|
|
|
338
339
|
returns a `ReactNode`; `ctx.points[].prev`/`.next` carry the neighbouring points, so
|
|
339
340
|
a delta needs no extra lookup. `shared: true` collects every series at the hovered x.
|
|
340
341
|
|
|
342
|
+
`headerFormat` and `pointFormatter` are taken under their Highcharts names, so a
|
|
343
|
+
config that already carries them needs no rewrite: `headerFormat: ''` drops the
|
|
344
|
+
header, a template fills `{point.key}`, `{point.x}`, `{point.y}` and
|
|
345
|
+
`{series.name}` (markup in it is dropped — the chrome is the library's), and
|
|
346
|
+
`pointFormatter` is handed the point and returns a `ReactNode`. `formatter` and
|
|
347
|
+
`table` both outrank them. Golden case: `area/header-format`.
|
|
348
|
+
|
|
349
|
+
`ctx.points[].pointMeta` is the hovered point's own `meta` — the third type
|
|
350
|
+
parameter of `ChartOptions<TChartMeta, TSeriesMeta, TPointMeta>`, the same object
|
|
351
|
+
`onPointClick` reports. `meta` beside it stays the series'. A bar whose height is a
|
|
352
|
+
rate and whose tooltip has to name the two counts behind it needs the per-point
|
|
353
|
+
one; nothing else reaches it, because a formatter sees no option tree.
|
|
354
|
+
|
|
355
|
+
Golden case: `tooltip/point-meta`.
|
|
356
|
+
|
|
341
357
|
Golden cases: `tooltip/callout`, `tooltip/shared-formatter`.
|
|
342
358
|
|
|
343
359
|
## Styling
|
|
@@ -358,10 +374,34 @@ With `fillOpacity` on the body and `states.hover.fillOpacity` to deepen it, that
|
|
|
358
374
|
is the funnel look: the series keeps its real colour, so the legend swatch and
|
|
359
375
|
the tooltip dot stay readable, and only the bar is washed out.
|
|
360
376
|
|
|
377
|
+
`states.hover.color` swaps the fill on hover instead of lightening it, for a body
|
|
378
|
+
too pale for `brightness` to show on; it also wins over a per-point colour, since
|
|
379
|
+
one colour on hover is the point of asking. `column` and `heatmap` read it.
|
|
380
|
+
Golden case: `column/hover-color`.
|
|
381
|
+
|
|
361
382
|
A data label whose text carries `\n` is drawn as stacked lines, styled by
|
|
362
383
|
`style` for the first and `secondaryStyle` for the rest. It is one label: it
|
|
363
384
|
moves, measures and hides as one.
|
|
364
385
|
|
|
386
|
+
## Series opacity
|
|
387
|
+
|
|
388
|
+
`series.opacity` fades a whole series layer — body, border and data labels — where
|
|
389
|
+
`fillOpacity` reaches only the fill a renderer paints. A hover's
|
|
390
|
+
`states.inactive.opacity` overrides it while it lasts. Golden case:
|
|
391
|
+
`column/series-opacity`.
|
|
392
|
+
|
|
393
|
+
## Areas
|
|
394
|
+
|
|
395
|
+
`fillColor` paints the body under the line, where the fill is a token of its own
|
|
396
|
+
rather than a wash of the series colour — `color` still keys the legend, the
|
|
397
|
+
markers and the tooltip swatch. Left unset the body is the series colour at
|
|
398
|
+
`fillOpacity`. Golden case: `area/fill-color`.
|
|
399
|
+
|
|
400
|
+
Both live on `SeriesOptionsBase`, as every series option does, but a renderer
|
|
401
|
+
reads only what it draws with: `fillColor` is read by `area` alone, `fillOpacity`
|
|
402
|
+
by `area` and `column`. On a pie or a heatmap they are typed and inert — the same
|
|
403
|
+
deal as `innerSize` on a line.
|
|
404
|
+
|
|
365
405
|
## The device pixel grid
|
|
366
406
|
|
|
367
407
|
`useSize` floors the measured box, so the canvas is always a whole number of
|
package/core/Chart/Chart.d.ts
CHANGED
|
@@ -12,8 +12,8 @@ export interface ClickedPoint<TPointMeta = unknown> {
|
|
|
12
12
|
color: string;
|
|
13
13
|
meta: TPointMeta;
|
|
14
14
|
}
|
|
15
|
-
export interface ChartProps<TChartMeta = unknown, TSeriesMeta = unknown> {
|
|
16
|
-
options: ChartOptions<TChartMeta, TSeriesMeta>;
|
|
15
|
+
export interface ChartProps<TChartMeta = unknown, TSeriesMeta = unknown, TPointMeta = unknown> {
|
|
16
|
+
options: ChartOptions<TChartMeta, TSeriesMeta, TPointMeta>;
|
|
17
17
|
renderers?: readonly AnySeriesRenderer[];
|
|
18
18
|
isLoading?: boolean;
|
|
19
19
|
empty?: ReactNode;
|
|
@@ -28,6 +28,6 @@ export interface ChartProps<TChartMeta = unknown, TSeriesMeta = unknown> {
|
|
|
28
28
|
*/
|
|
29
29
|
onSeriesToggle?: (seriesIndex: number, visible: boolean, pointIndex?: number) => void;
|
|
30
30
|
/** Called with the point nearest a click inside the plot; drill-down hangs off this. */
|
|
31
|
-
onPointClick?: (point: ClickedPoint
|
|
31
|
+
onPointClick?: (point: ClickedPoint<TPointMeta>, event: ReactMouseEvent<SVGSVGElement>) => void;
|
|
32
32
|
}
|
|
33
|
-
export declare function Chart<TChartMeta = unknown, TSeriesMeta = unknown>(props: ChartProps<TChartMeta, TSeriesMeta>): ReactElement;
|
|
33
|
+
export declare function Chart<TChartMeta = unknown, TSeriesMeta = unknown, TPointMeta = unknown>(props: ChartProps<TChartMeta, TSeriesMeta, TPointMeta>): ReactElement;
|
package/core/Chart/Chart.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx,
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
3
|
import { Grid, XAxis, YAxis } from './Axes';
|
|
4
4
|
import { Canvas, Centred, Container, PinnedAxes, ResetZoom, ScrollBox, SeriesLayer, TooltipCell, TooltipDivider, TooltipHeadCell, TooltipName, TooltipRow, TooltipSeriesCell, TooltipSeriesInner, TooltipSwatch, TooltipTable, TooltipTitle, TooltipValue, ZoomBand } from './Chart.styles';
|
|
@@ -10,6 +10,7 @@ import { Titles } from './Titles';
|
|
|
10
10
|
import { Tooltip } from './Tooltip';
|
|
11
11
|
import { useSize } from './useSize';
|
|
12
12
|
import { MOTION_MS } from '../animation/motion';
|
|
13
|
+
import { applyTooltipTemplate } from '../format/template';
|
|
13
14
|
import { formatTime } from '../format/time';
|
|
14
15
|
import { DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE } from '../layout/measure-text';
|
|
15
16
|
import { buildChartModel, hiddenKey, isContinuous } from '../model/build-model';
|
|
@@ -32,18 +33,32 @@ const toCssSize = (value, fallback) => {
|
|
|
32
33
|
}
|
|
33
34
|
return typeof value === 'number' ? `${value}px` : value;
|
|
34
35
|
};
|
|
35
|
-
function
|
|
36
|
+
function tooltipHeader(ctx, headerFormat, formatX) {
|
|
37
|
+
if (headerFormat === undefined) {
|
|
38
|
+
return formatX(ctx.x);
|
|
39
|
+
}
|
|
40
|
+
return applyTooltipTemplate(headerFormat, {
|
|
41
|
+
x: ctx.x,
|
|
42
|
+
y: ctx.points[0]?.y ?? null,
|
|
43
|
+
seriesName: ctx.points[0]?.seriesName ?? ''
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function defaultTooltip(ctx, tooltip, formatX, formatValue) {
|
|
47
|
+
const header = tooltipHeader(ctx, tooltip?.headerFormat, formatX);
|
|
48
|
+
const pointFormatter = tooltip?.pointFormatter;
|
|
36
49
|
return _jsxs("div", {
|
|
37
|
-
children: [_jsx(TooltipTitle, {
|
|
38
|
-
children:
|
|
39
|
-
}), ctx.points.map(point =>
|
|
40
|
-
children:
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
50
|
+
children: [header ? _jsx(TooltipTitle, {
|
|
51
|
+
children: header
|
|
52
|
+
}) : null, ctx.points.map(point => _jsx(TooltipRow, {
|
|
53
|
+
children: pointFormatter ? pointFormatter(point) : _jsxs(_Fragment, {
|
|
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
|
+
})
|
|
47
62
|
}, `${point.seriesIndex}-${point.index}`))]
|
|
48
63
|
});
|
|
49
64
|
}
|
|
@@ -409,6 +424,7 @@ function ChartBody({
|
|
|
409
424
|
value: hit.value,
|
|
410
425
|
index: hit.pointIndex,
|
|
411
426
|
meta: series?.options.meta,
|
|
427
|
+
pointMeta: neighbours[hit.pointIndex]?.meta,
|
|
412
428
|
prev: previous ? {
|
|
413
429
|
x: previous.x,
|
|
414
430
|
y: previous.y,
|
|
@@ -434,8 +450,8 @@ function ChartBody({
|
|
|
434
450
|
return formatter(ctx);
|
|
435
451
|
}
|
|
436
452
|
const table = options.tooltip?.table;
|
|
437
|
-
return table ? tableTooltip(ctx, table, formatX) : defaultTooltip(ctx, formatX, formatValue);
|
|
438
|
-
}, [hover, model, options.title?.text, options.meta, options.tooltip
|
|
453
|
+
return table ? tableTooltip(ctx, table, formatX) : defaultTooltip(ctx, options.tooltip, formatX, formatValue);
|
|
454
|
+
}, [hover, model, options.title?.text, options.meta, options.tooltip, formatX, formatValue]);
|
|
439
455
|
const hasData = model?.series.some(series => series.points.some(point => point.y !== null)) ?? false;
|
|
440
456
|
const plotBorderWidth = options.chart?.plotBorderWidth ?? 0;
|
|
441
457
|
const scrolls = canvas.width > size.width || canvas.height > size.height;
|
|
@@ -536,6 +552,7 @@ function ChartBody({
|
|
|
536
552
|
"$collapse": series.renderer.exit === 'collapse',
|
|
537
553
|
"$selfAnimated": series.renderer.animatesGeometry === true,
|
|
538
554
|
"$inactive": inactiveOpacity(series),
|
|
555
|
+
"$opacity": series.options.opacity,
|
|
539
556
|
"$motionMs": motionOf(series),
|
|
540
557
|
"$cursor": series.options.cursor ?? 'default',
|
|
541
558
|
"aria-hidden": series.hidden,
|
|
@@ -33,6 +33,8 @@ export declare const SeriesLayer: import("styled-components").StyledComponent<"g
|
|
|
33
33
|
$selfAnimated: boolean;
|
|
34
34
|
/** Opacity while another series is hovered, from `states.inactive`. */
|
|
35
35
|
$inactive?: number;
|
|
36
|
+
/** `series.opacity`: the whole series faded, border and labels included. */
|
|
37
|
+
$opacity?: number;
|
|
36
38
|
/** `series.animation`; the chart clock unless this series asked for its own. */
|
|
37
39
|
$motionMs: number;
|
|
38
40
|
$cursor: string;
|
|
@@ -56,12 +56,13 @@ export const SeriesLayer = styled.g.withConfig({
|
|
|
56
56
|
componentId: "sc-1pit1b9-4"
|
|
57
57
|
})(["opacity:", ";pointer-events:", ";cursor:", ";transition:opacity ", "ms ", ";", " @media (prefers-reduced-motion:reduce){transition:none;path,rect,circle,text{transition:none;}}"], ({
|
|
58
58
|
$hidden,
|
|
59
|
-
$inactive
|
|
59
|
+
$inactive,
|
|
60
|
+
$opacity
|
|
60
61
|
}) => {
|
|
61
62
|
if ($hidden) {
|
|
62
63
|
return 0;
|
|
63
64
|
}
|
|
64
|
-
return $inactive ?? 1;
|
|
65
|
+
return $inactive ?? $opacity ?? 1;
|
|
65
66
|
}, ({
|
|
66
67
|
$hidden
|
|
67
68
|
}) => $hidden ? 'none' : 'auto', ({
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface TemplateContext {
|
|
2
|
+
x: number | string;
|
|
3
|
+
y: number | null;
|
|
4
|
+
seriesName: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Fills a Highcharts header template. Markup in it is dropped rather than rendered:
|
|
8
|
+
* the chrome around a tooltip is the library's, and a `<span>` in a config is styling.
|
|
9
|
+
*/
|
|
10
|
+
export declare function applyTooltipTemplate(format: string, ctx: TemplateContext): string;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { formatTime } from './time';
|
|
2
|
+
import { fillTokens } from './tokens';
|
|
3
|
+
const substitute = (name, ctx) => {
|
|
4
|
+
switch (name) {
|
|
5
|
+
case 'point.key':
|
|
6
|
+
case 'point.x':
|
|
7
|
+
return String(ctx.x);
|
|
8
|
+
case 'point.y':
|
|
9
|
+
return ctx.y === null ? '' : String(ctx.y);
|
|
10
|
+
case 'series.name':
|
|
11
|
+
return ctx.seriesName;
|
|
12
|
+
default:
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Fills a Highcharts header template. Markup in it is dropped rather than rendered:
|
|
18
|
+
* the chrome around a tooltip is the library's, and a `<span>` in a config is styling.
|
|
19
|
+
*/
|
|
20
|
+
export function applyTooltipTemplate(format, ctx) {
|
|
21
|
+
const stripped = format.replace(/<br\s*\/?>/gi, ' ').replace(/<[^>]*>/g, '');
|
|
22
|
+
return fillTokens(stripped, (name, pattern) => {
|
|
23
|
+
const value = substitute(name, ctx);
|
|
24
|
+
if (value === undefined) {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
const dates = name === 'point.key' || name === 'point.x';
|
|
28
|
+
return pattern && dates && typeof ctx.x === 'number' ? formatTime(pattern, ctx.x) : value;
|
|
29
|
+
}).trim();
|
|
30
|
+
}
|
package/core/format/time.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { fillTokens } from './tokens';
|
|
1
2
|
const MONTHS_SHORT = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
|
2
3
|
const MONTHS_LONG = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
|
|
3
4
|
const DAYS_SHORT = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
|
@@ -29,10 +30,10 @@ export function formatTime(pattern, timestamp) {
|
|
|
29
30
|
});
|
|
30
31
|
}
|
|
31
32
|
export function applyValueFormat(format, value) {
|
|
32
|
-
return format
|
|
33
|
-
if (
|
|
34
|
-
return
|
|
33
|
+
return fillTokens(format, (name, pattern) => {
|
|
34
|
+
if (name !== 'value') {
|
|
35
|
+
return undefined;
|
|
35
36
|
}
|
|
36
|
-
return String(value);
|
|
37
|
+
return pattern && typeof value === 'number' ? formatTime(pattern, value) : String(value);
|
|
37
38
|
});
|
|
38
39
|
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export type TokenResolver = (name: string, pattern: string | undefined) => string | undefined;
|
|
2
|
+
/** Fills `{name}` and `{name:pattern}` placeholders; an unresolved one is left as it stands. */
|
|
3
|
+
export declare function fillTokens(format: string, resolve: TokenResolver): string;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
const TOKEN = /\{([a-z][a-z.]*)(?::([^}]*))?\}/gi;
|
|
2
|
+
/** Fills `{name}` and `{name:pattern}` placeholders; an unresolved one is left as it stands. */
|
|
3
|
+
export function fillTokens(format, resolve) {
|
|
4
|
+
return format.replace(TOKEN, (match, name, pattern) => resolve(name, pattern) ?? match);
|
|
5
|
+
}
|
|
@@ -35,7 +35,7 @@ export interface TooltipNeighbour {
|
|
|
35
35
|
y: number | null;
|
|
36
36
|
index: number;
|
|
37
37
|
}
|
|
38
|
-
export interface TooltipPoint<TSeriesMeta = unknown> {
|
|
38
|
+
export interface TooltipPoint<TSeriesMeta = unknown, TPointMeta = unknown> {
|
|
39
39
|
seriesIndex: number;
|
|
40
40
|
seriesName: string;
|
|
41
41
|
color: string;
|
|
@@ -44,16 +44,18 @@ export interface TooltipPoint<TSeriesMeta = unknown> {
|
|
|
44
44
|
value: number | null;
|
|
45
45
|
index: number;
|
|
46
46
|
meta: TSeriesMeta | undefined;
|
|
47
|
+
/** The hovered point's own `meta` — what `onPointClick` reports, at hover time. */
|
|
48
|
+
pointMeta: TPointMeta | undefined;
|
|
47
49
|
prev: TooltipNeighbour | undefined;
|
|
48
50
|
next: TooltipNeighbour | undefined;
|
|
49
51
|
}
|
|
50
|
-
export interface TooltipContext<TChartMeta = unknown, TSeriesMeta = unknown> {
|
|
52
|
+
export interface TooltipContext<TChartMeta = unknown, TSeriesMeta = unknown, TPointMeta = unknown> {
|
|
51
53
|
x: number | string;
|
|
52
|
-
points: readonly TooltipPoint<TSeriesMeta>[];
|
|
54
|
+
points: readonly TooltipPoint<TSeriesMeta, TPointMeta>[];
|
|
53
55
|
chart: {
|
|
54
56
|
title: string | undefined;
|
|
55
57
|
meta: TChartMeta | undefined;
|
|
56
58
|
};
|
|
57
59
|
}
|
|
58
|
-
export type TooltipFormatter<TChartMeta = unknown, TSeriesMeta = unknown> = (ctx: TooltipContext<TChartMeta, TSeriesMeta>) => ReactNode;
|
|
59
|
-
export type TooltipCellFormatter<TSeriesMeta = unknown> = (point: TooltipPoint<TSeriesMeta>) => ReactNode;
|
|
60
|
+
export type TooltipFormatter<TChartMeta = unknown, TSeriesMeta = unknown, TPointMeta = unknown> = (ctx: TooltipContext<TChartMeta, TSeriesMeta, TPointMeta>) => ReactNode;
|
|
61
|
+
export type TooltipCellFormatter<TSeriesMeta = unknown, TPointMeta = unknown> = (point: TooltipPoint<TSeriesMeta, TPointMeta>) => ReactNode;
|
package/core/types/options.d.ts
CHANGED
|
@@ -175,36 +175,40 @@ export interface LegendOptions<TSeriesMeta = unknown> {
|
|
|
175
175
|
symbolPadding?: number;
|
|
176
176
|
labelFormatter?: LegendLabelFormatter<TSeriesMeta>;
|
|
177
177
|
}
|
|
178
|
-
export interface TooltipTableColumn<TChartMeta = unknown, TSeriesMeta = unknown> {
|
|
178
|
+
export interface TooltipTableColumn<TChartMeta = unknown, TSeriesMeta = unknown, TPointMeta = unknown> {
|
|
179
179
|
/**
|
|
180
180
|
* A function is resolved per hover, and one that returns `null` drops the whole
|
|
181
181
|
* column: that is how a "vs the previous point" column names the date it
|
|
182
182
|
* compares against and disappears on the first point, where there is none.
|
|
183
183
|
*/
|
|
184
|
-
title?: string | TooltipFormatter<TChartMeta, TSeriesMeta>;
|
|
184
|
+
title?: string | TooltipFormatter<TChartMeta, TSeriesMeta, TPointMeta>;
|
|
185
185
|
align?: 'left' | 'right';
|
|
186
|
-
cell: TooltipCellFormatter<TSeriesMeta>;
|
|
186
|
+
cell: TooltipCellFormatter<TSeriesMeta, TPointMeta>;
|
|
187
187
|
}
|
|
188
188
|
/**
|
|
189
189
|
* Lays the tooltip out as a table: a header line, a heading row, and one row per
|
|
190
190
|
* series with its swatch. Use it when a point carries more than one figure —
|
|
191
191
|
* a count and its share of the whole — and a single line would run them together.
|
|
192
192
|
*/
|
|
193
|
-
export interface TooltipTableOptions<TChartMeta = unknown, TSeriesMeta = unknown> {
|
|
193
|
+
export interface TooltipTableOptions<TChartMeta = unknown, TSeriesMeta = unknown, TPointMeta = unknown> {
|
|
194
194
|
/** The line above the table. Defaults to the formatted x. */
|
|
195
|
-
header?: TooltipFormatter<TChartMeta, TSeriesMeta>;
|
|
195
|
+
header?: TooltipFormatter<TChartMeta, TSeriesMeta, TPointMeta>;
|
|
196
196
|
/** Heading over the series-name column. */
|
|
197
197
|
seriesTitle?: string;
|
|
198
|
-
columns: readonly TooltipTableColumn<TChartMeta, TSeriesMeta>[];
|
|
198
|
+
columns: readonly TooltipTableColumn<TChartMeta, TSeriesMeta, TPointMeta>[];
|
|
199
199
|
}
|
|
200
|
-
export interface TooltipOptions<TChartMeta = unknown, TSeriesMeta = unknown> {
|
|
200
|
+
export interface TooltipOptions<TChartMeta = unknown, TSeriesMeta = unknown, TPointMeta = unknown> {
|
|
201
201
|
enabled?: boolean;
|
|
202
202
|
shared?: boolean;
|
|
203
|
-
table?: TooltipTableOptions<TChartMeta, TSeriesMeta>;
|
|
203
|
+
table?: TooltipTableOptions<TChartMeta, TSeriesMeta, TPointMeta>;
|
|
204
204
|
/** `callout` anchors a beaked bubble to the point; `box` floats near the cursor. */
|
|
205
205
|
shape?: 'callout' | 'box';
|
|
206
206
|
followPointer?: boolean;
|
|
207
|
-
formatter?: TooltipFormatter<TChartMeta, TSeriesMeta>;
|
|
207
|
+
formatter?: TooltipFormatter<TChartMeta, TSeriesMeta, TPointMeta>;
|
|
208
|
+
/** Highcharts' header template: `''` drops the header, `{point.key}` and friends fill. */
|
|
209
|
+
headerFormat?: string;
|
|
210
|
+
/** One row per point, in place of the default swatch, name and value. */
|
|
211
|
+
pointFormatter?: TooltipCellFormatter<TSeriesMeta, TPointMeta>;
|
|
208
212
|
dateFormat?: string;
|
|
209
213
|
valuePrefix?: string;
|
|
210
214
|
valueSuffix?: string;
|
|
@@ -255,6 +259,8 @@ export interface SeriesStatesOptions {
|
|
|
255
259
|
brightness?: number;
|
|
256
260
|
/** Deepens a translucent body on hover, where brightening a pale fill does nothing. */
|
|
257
261
|
fillOpacity?: number;
|
|
262
|
+
/** The colour to swap to, for a hover that is not a brighter shade of the same one. */
|
|
263
|
+
color?: string;
|
|
258
264
|
};
|
|
259
265
|
inactive?: {
|
|
260
266
|
opacity?: number;
|
|
@@ -298,6 +304,10 @@ export interface SeriesOptionsBase<TSeriesMeta = unknown, TPointMeta = unknown>
|
|
|
298
304
|
*/
|
|
299
305
|
borderPosition?: 'all' | 'top';
|
|
300
306
|
fillOpacity?: number;
|
|
307
|
+
/** The body's own colour, where the fill is not a wash of the series colour. */
|
|
308
|
+
fillColor?: string;
|
|
309
|
+
/** Fades the whole series — body, border and labels — unlike `fillOpacity`. */
|
|
310
|
+
opacity?: number;
|
|
301
311
|
pointPadding?: number;
|
|
302
312
|
groupPadding?: number;
|
|
303
313
|
minPointLength?: number;
|
|
@@ -321,7 +331,7 @@ export interface ChartOptions<TChartMeta = unknown, TSeriesMeta = unknown, TPoin
|
|
|
321
331
|
yAxis?: AxisOptions | readonly AxisOptions[];
|
|
322
332
|
colorAxis?: ColorAxisOptions;
|
|
323
333
|
legend?: LegendOptions<TSeriesMeta>;
|
|
324
|
-
tooltip?: TooltipOptions<TChartMeta, TSeriesMeta>;
|
|
334
|
+
tooltip?: TooltipOptions<TChartMeta, TSeriesMeta, TPointMeta>;
|
|
325
335
|
plotOptions?: PlotOptions<TSeriesMeta, TPointMeta>;
|
|
326
336
|
series?: readonly SeriesOptionsBase<TSeriesMeta, TPointMeta>[];
|
|
327
337
|
colors?: readonly string[];
|
package/package.json
CHANGED
package/presets/AreaChart.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { type ReactElement } from 'react';
|
|
2
2
|
import { type ChartProps } from '../core/Chart/Chart';
|
|
3
|
-
export declare function AreaChart<TChartMeta = unknown, TSeriesMeta = unknown>(props: ChartProps<TChartMeta, TSeriesMeta>): ReactElement;
|
|
3
|
+
export declare function AreaChart<TChartMeta = unknown, TSeriesMeta = unknown, TPointMeta = unknown>(props: ChartProps<TChartMeta, TSeriesMeta, TPointMeta>): ReactElement;
|
package/presets/ColumnChart.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { type ReactElement } from 'react';
|
|
2
2
|
import { type ChartProps } from '../core/Chart/Chart';
|
|
3
|
-
export declare function ColumnChart<TChartMeta = unknown, TSeriesMeta = unknown>(props: ChartProps<TChartMeta, TSeriesMeta>): ReactElement;
|
|
3
|
+
export declare function ColumnChart<TChartMeta = unknown, TSeriesMeta = unknown, TPointMeta = unknown>(props: ChartProps<TChartMeta, TSeriesMeta, TPointMeta>): ReactElement;
|
package/presets/LineChart.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { type ReactElement } from 'react';
|
|
2
2
|
import { type ChartProps } from '../core/Chart/Chart';
|
|
3
|
-
export declare function LineChart<TChartMeta = unknown, TSeriesMeta = unknown>(props: ChartProps<TChartMeta, TSeriesMeta>): ReactElement;
|
|
3
|
+
export declare function LineChart<TChartMeta = unknown, TSeriesMeta = unknown, TPointMeta = unknown>(props: ChartProps<TChartMeta, TSeriesMeta, TPointMeta>): ReactElement;
|
package/presets/PieChart.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { type ReactElement } from 'react';
|
|
2
2
|
import { type ChartProps } from '../core/Chart/Chart';
|
|
3
|
-
export declare function PieChart<TChartMeta = unknown, TSeriesMeta = unknown>(props: ChartProps<TChartMeta, TSeriesMeta>): ReactElement;
|
|
3
|
+
export declare function PieChart<TChartMeta = unknown, TSeriesMeta = unknown, TPointMeta = unknown>(props: ChartProps<TChartMeta, TSeriesMeta, TPointMeta>): ReactElement;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type ReactElement } from 'react';
|
|
2
2
|
import { type ChartProps } from '../core/Chart/Chart';
|
|
3
3
|
import { type Stacking } from '../core/types/options';
|
|
4
|
-
export interface StackedColumnChartProps<TChartMeta = unknown, TSeriesMeta = unknown> extends ChartProps<TChartMeta, TSeriesMeta> {
|
|
4
|
+
export interface StackedColumnChartProps<TChartMeta = unknown, TSeriesMeta = unknown, TPointMeta = unknown> extends ChartProps<TChartMeta, TSeriesMeta, TPointMeta> {
|
|
5
5
|
stacking?: Stacking;
|
|
6
6
|
}
|
|
7
|
-
export declare function StackedColumnChart<TChartMeta = unknown, TSeriesMeta = unknown>(props: StackedColumnChartProps<TChartMeta, TSeriesMeta>): ReactElement;
|
|
7
|
+
export declare function StackedColumnChart<TChartMeta = unknown, TSeriesMeta = unknown, TPointMeta = unknown>(props: StackedColumnChartProps<TChartMeta, TSeriesMeta, TPointMeta>): ReactElement;
|
package/series/area/area.js
CHANGED
|
@@ -27,7 +27,7 @@ export const area = {
|
|
|
27
27
|
return _jsxs("g", {
|
|
28
28
|
children: [_jsx("path", {
|
|
29
29
|
d: toAreaPath(slots),
|
|
30
|
-
fill: color,
|
|
30
|
+
fill: ctx.series.fillColor ?? color,
|
|
31
31
|
fillOpacity: ctx.series.fillOpacity ?? DEFAULT_FILL_OPACITY,
|
|
32
32
|
stroke: "none"
|
|
33
33
|
}), lineWidth ? _jsx("path", {
|
package/series/column/column.js
CHANGED
|
@@ -2,11 +2,10 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { pointKey } from '../../core/data/points';
|
|
3
3
|
import { drawable, projectSeries } from '../../core/geometry/path';
|
|
4
4
|
import { TEXT_BASELINE_SHIFT, VALUE_LABEL_GAP } from '../../core/layout/metrics';
|
|
5
|
-
import {
|
|
5
|
+
import { resolveHoverFill } from '../shared/color';
|
|
6
6
|
import { DataLabels } from '../shared/DataLabels';
|
|
7
7
|
import { includeZero } from '../shared/domain';
|
|
8
8
|
import { seriesLegend } from '../shared/legend';
|
|
9
|
-
const DEFAULT_HOVER_BRIGHTNESS = 0.1;
|
|
10
9
|
const SLOT_FALLBACK = {
|
|
11
10
|
width: 10,
|
|
12
11
|
offset: 0
|
|
@@ -46,7 +45,7 @@ const fillOf = (ctx, bar, hovered) => {
|
|
|
46
45
|
if (!hovered) {
|
|
47
46
|
return base;
|
|
48
47
|
}
|
|
49
|
-
return
|
|
48
|
+
return resolveHoverFill(base, ctx.series.states);
|
|
50
49
|
};
|
|
51
50
|
/**
|
|
52
51
|
* A translucent body under a solid rule is the funnel look: the series keeps its
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { pointKey } from '../../core/data/points';
|
|
3
3
|
import { TEXT_BASELINE_SHIFT, VALUE_LABEL_GAP } from '../../core/layout/metrics';
|
|
4
|
-
import {
|
|
4
|
+
import { resolveHoverFill } from '../shared/color';
|
|
5
5
|
import { DataLabels } from '../shared/DataLabels';
|
|
6
|
-
const DEFAULT_HOVER_BRIGHTNESS = 0.1;
|
|
7
6
|
/** Width of one column: a unit step on the x axis, or the whole plot if there is none. */
|
|
8
7
|
function columnWidth(ctx) {
|
|
9
8
|
const scale = ctx.scales.x;
|
|
@@ -79,7 +78,7 @@ const fillOf = (ctx, cell, hovered) => {
|
|
|
79
78
|
if (!hovered) {
|
|
80
79
|
return base;
|
|
81
80
|
}
|
|
82
|
-
return
|
|
81
|
+
return resolveHoverFill(base, ctx.series.states);
|
|
83
82
|
};
|
|
84
83
|
const labelX = (ctx, cell) => {
|
|
85
84
|
const padding = ctx.series.dataLabels?.padding ?? VALUE_LABEL_GAP;
|
package/series/shared/color.d.ts
CHANGED
|
@@ -1 +1,7 @@
|
|
|
1
|
+
import { type SeriesStatesOptions } from '../../core/types/options';
|
|
1
2
|
export declare function brighten(color: string, amount: number): string;
|
|
3
|
+
/**
|
|
4
|
+
* An explicit hover colour wins over brightening, and over a per-point colour:
|
|
5
|
+
* asking for one colour on hover means one colour, not one per point.
|
|
6
|
+
*/
|
|
7
|
+
export declare function resolveHoverFill(base: string, states: SeriesStatesOptions | undefined): string;
|
package/series/shared/color.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const DEFAULT_HOVER_BRIGHTNESS = 0.1;
|
|
1
2
|
const clamp = value => Math.min(255, Math.max(0, Math.round(value)));
|
|
2
3
|
const parseHex = color => {
|
|
3
4
|
const match = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(color.trim());
|
|
@@ -16,4 +17,12 @@ export function brighten(color, amount) {
|
|
|
16
17
|
const ratio = Math.abs(amount);
|
|
17
18
|
const [r, g, b] = rgb.map(channel => clamp(channel + (target - channel) * ratio));
|
|
18
19
|
return `#${[r, g, b].map(channel => channel.toString(16).padStart(2, '0')).join('')}`;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* An explicit hover colour wins over brightening, and over a per-point colour:
|
|
23
|
+
* asking for one colour on hover means one colour, not one per point.
|
|
24
|
+
*/
|
|
25
|
+
export function resolveHoverFill(base, states) {
|
|
26
|
+
const hover = states?.hover;
|
|
27
|
+
return hover?.color ?? brighten(base, hover?.brightness ?? DEFAULT_HOVER_BRIGHTNESS);
|
|
19
28
|
}
|