@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.
@@ -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
@@ -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, event: ReactMouseEvent<SVGSVGElement>) => void;
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;
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
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 defaultTooltip(ctx, formatX, formatValue) {
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: formatX(ctx.x)
39
- }), ctx.points.map(point => _jsxs(TooltipRow, {
40
- children: [_jsx(TooltipSwatch, {
41
- "$color": point.color
42
- }), _jsx(TooltipName, {
43
- children: point.seriesName
44
- }), _jsx(TooltipValue, {
45
- children: formatValue(point.value ?? point.y)
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?.formatter, options.tooltip?.table, formatX, formatValue]);
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
+ }
@@ -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.replace(/\{value(?::([^}]*))?\}/g, (_match, pattern) => {
33
- if (pattern && typeof value === 'number') {
34
- return formatTime(pattern, value);
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;
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushwoosh/kit-charts",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "Small React chart library on SVG, with a Highcharts-shaped configuration model",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -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;
@@ -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;
@@ -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;
@@ -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;
@@ -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", {
@@ -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 { brighten } from '../shared/color';
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 brighten(base, ctx.series.states?.hover?.brightness ?? DEFAULT_HOVER_BRIGHTNESS);
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 { brighten } from '../shared/color';
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 brighten(base, ctx.series.states?.hover?.brightness ?? DEFAULT_HOVER_BRIGHTNESS);
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;
@@ -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;
@@ -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
  }