@pushwoosh/kit-charts 0.0.21 → 0.0.23
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/core/Chart/Chart.js +10 -1
- package/core/Chart/Chart.styles.d.ts +2 -1
- package/core/Chart/Chart.styles.js +12 -12
- package/core/Chart/Tooltip.d.ts +3 -1
- package/core/Chart/Tooltip.js +9 -6
- package/core/layout/metrics.d.ts +5 -2
- package/core/layout/metrics.js +5 -2
- package/core/types/renderer.d.ts +4 -0
- package/package.json +2 -2
- package/series/area/area.js +3 -14
- package/series/column/column.js +8 -8
- package/series/line/line.js +3 -14
- package/series/shared/Markers.d.ts +5 -0
- package/series/shared/Markers.js +29 -1
- package/series/shared/hit.js +3 -0
package/core/Chart/Chart.js
CHANGED
|
@@ -325,7 +325,8 @@ function ChartBody({
|
|
|
325
325
|
anchor: {
|
|
326
326
|
x: bounds.left + highest.anchor.x,
|
|
327
327
|
y: bounds.top + highest.anchor.y
|
|
328
|
-
}
|
|
328
|
+
},
|
|
329
|
+
reach: highest.reach ?? 0
|
|
329
330
|
});
|
|
330
331
|
}, [model, hitsAt, options.tooltip, drag]);
|
|
331
332
|
const handlePointerLeave = useCallback(() => setHover(null), []);
|
|
@@ -578,6 +579,13 @@ function ChartBody({
|
|
|
578
579
|
}), model.axisless ? null : _jsx(XAxisLine, {
|
|
579
580
|
plot: model.plot,
|
|
580
581
|
axis: model.xAxis
|
|
582
|
+
}), model.series.map(series => {
|
|
583
|
+
const ctx = series.hidden ? null : contextFor(series);
|
|
584
|
+
const overlay = ctx ? series.renderer.renderOverlay?.(ctx) : null;
|
|
585
|
+
return overlay ? _jsx("g", {
|
|
586
|
+
"data-series-index": series.index,
|
|
587
|
+
children: overlay
|
|
588
|
+
}, `overlay-${series.index}`) : null;
|
|
581
589
|
}), decorations('over'), drag ? _jsx(ZoomBand, {
|
|
582
590
|
x: Math.min(drag.from, drag.to),
|
|
583
591
|
y: model.plot.y,
|
|
@@ -627,6 +635,7 @@ function ChartBody({
|
|
|
627
635
|
children: options.chart?.resetZoomText ?? 'Reset zoom'
|
|
628
636
|
}) : null, hover && tooltipContent ? _jsx(Tooltip, {
|
|
629
637
|
at: hover.anchor,
|
|
638
|
+
reach: hover.reach,
|
|
630
639
|
pointer: hover.pointer,
|
|
631
640
|
options: options.tooltip,
|
|
632
641
|
children: tooltipContent
|
|
@@ -64,7 +64,8 @@ export interface TooltipBoxProps {
|
|
|
64
64
|
export declare const TooltipBox: import("styled-components").StyledComponent<"div", any, TooltipBoxProps, never>;
|
|
65
65
|
export interface TailProps {
|
|
66
66
|
$left: number;
|
|
67
|
-
$
|
|
67
|
+
$width: number;
|
|
68
|
+
$height: number;
|
|
68
69
|
$color: string;
|
|
69
70
|
$below: boolean;
|
|
70
71
|
}
|
|
@@ -54,7 +54,7 @@ export const Canvas = styled.svg.withConfig({
|
|
|
54
54
|
export const SeriesLayer = styled.g.withConfig({
|
|
55
55
|
displayName: "SeriesLayer",
|
|
56
56
|
componentId: "sc-1pit1b9-4"
|
|
57
|
-
})(["opacity:", ";pointer-events:", ";cursor:", ";transition:opacity ", "ms ", ";", " @media (prefers-reduced-motion:reduce){transition:none;path,rect,circle,
|
|
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
59
|
$inactive,
|
|
60
60
|
$opacity
|
|
@@ -72,7 +72,7 @@ export const SeriesLayer = styled.g.withConfig({
|
|
|
72
72
|
}) => $motionMs, MOTION_EASING, ({
|
|
73
73
|
$selfAnimated,
|
|
74
74
|
$motionMs
|
|
75
|
-
}) => $selfAnimated ? '' : css(["path,rect,circle,
|
|
75
|
+
}) => $selfAnimated ? '' : css(["path,rect,circle,text{transition:d ", "ms ", ",x ", "ms ", ",y ", "ms ", ",width ", "ms ", ",height ", "ms ", ",cx ", "ms ", ",cy ", "ms ", ",opacity ", "ms ", ";}"], $motionMs, MOTION_EASING, $motionMs, MOTION_EASING, $motionMs, MOTION_EASING, $motionMs, MOTION_EASING, $motionMs, MOTION_EASING, $motionMs, MOTION_EASING, $motionMs, MOTION_EASING, $motionMs, MOTION_EASING));
|
|
76
76
|
export const LegendItemBox = styled.g.withConfig({
|
|
77
77
|
displayName: "LegendItemBox",
|
|
78
78
|
componentId: "sc-1pit1b9-5"
|
|
@@ -133,19 +133,19 @@ export const Tail = styled.span.withConfig({
|
|
|
133
133
|
componentId: "sc-1pit1b9-9"
|
|
134
134
|
})(["position:absolute;", " left:", ";width:0;height:0;border-right:", " solid transparent;border-left:", " solid transparent;", ""], ({
|
|
135
135
|
$below,
|
|
136
|
-
$
|
|
137
|
-
}) => $below ? `top: -${$
|
|
136
|
+
$height
|
|
137
|
+
}) => $below ? `top: -${$height}px;` : `bottom: -${$height}px;`, ({
|
|
138
138
|
$left,
|
|
139
|
-
$
|
|
140
|
-
}) => `${$left - $
|
|
141
|
-
$
|
|
142
|
-
}) => `${$
|
|
143
|
-
$
|
|
144
|
-
}) => `${$
|
|
139
|
+
$width
|
|
140
|
+
}) => `${$left - $width / 2}px`, ({
|
|
141
|
+
$width
|
|
142
|
+
}) => `${$width / 2}px`, ({
|
|
143
|
+
$width
|
|
144
|
+
}) => `${$width / 2}px`, ({
|
|
145
145
|
$below,
|
|
146
|
-
$
|
|
146
|
+
$height,
|
|
147
147
|
$color
|
|
148
|
-
}) => $below ? `border-bottom: ${$
|
|
148
|
+
}) => $below ? `border-bottom: ${$height}px solid ${$color};` : `border-top: ${$height}px solid ${$color};`);
|
|
149
149
|
export const TooltipTitle = styled.div.withConfig({
|
|
150
150
|
displayName: "TooltipTitle",
|
|
151
151
|
componentId: "sc-1pit1b9-10"
|
package/core/Chart/Tooltip.d.ts
CHANGED
|
@@ -4,10 +4,12 @@ import { type TooltipOptions } from '../types/options';
|
|
|
4
4
|
interface TooltipProps {
|
|
5
5
|
/** Anchor the callout points at, in viewport coordinates. */
|
|
6
6
|
at: Point;
|
|
7
|
+
/** How far the hovered mark reaches past the anchor, so the beak clears it. */
|
|
8
|
+
reach?: number;
|
|
7
9
|
/** Pointer position, used when the tooltip follows the cursor instead. */
|
|
8
10
|
pointer: Point;
|
|
9
11
|
options: TooltipOptions | undefined;
|
|
10
12
|
children: ReactNode;
|
|
11
13
|
}
|
|
12
|
-
export declare function Tooltip({ at, options, pointer, children, }: TooltipProps): ReactElement | null;
|
|
14
|
+
export declare function Tooltip({ at, reach: markReach, options, pointer, children, }: TooltipProps): ReactElement | null;
|
|
13
15
|
export {};
|
package/core/Chart/Tooltip.js
CHANGED
|
@@ -2,11 +2,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useLayoutEffect, useRef, useState } from 'react';
|
|
3
3
|
import { createPortal } from 'react-dom';
|
|
4
4
|
import { DEFAULTS, Tail, TooltipBox } from './Chart.styles';
|
|
5
|
-
import { TOOLTIP_CURSOR_OFFSET, TOOLTIP_PADDING, TOOLTIP_RADIUS,
|
|
5
|
+
import { TOOLTIP_ANCHOR_GAP, TOOLTIP_CURSOR_OFFSET, TOOLTIP_PADDING, TOOLTIP_RADIUS, TOOLTIP_TAIL_HEIGHT, TOOLTIP_TAIL_WIDTH, VIEWPORT_MARGIN } from '../layout/metrics';
|
|
6
6
|
const SHADOW_COLOR = 'rgba(0, 0, 0, 0.12)';
|
|
7
7
|
const DEFAULT_SHADOW = `0 2px 8px ${SHADOW_COLOR}`;
|
|
8
8
|
export function Tooltip({
|
|
9
9
|
at,
|
|
10
|
+
reach: markReach = 0,
|
|
10
11
|
options,
|
|
11
12
|
pointer,
|
|
12
13
|
children
|
|
@@ -44,15 +45,16 @@ export function Tooltip({
|
|
|
44
45
|
// Sit above the point with the tail pointing down at it, and flip under the
|
|
45
46
|
// point when there is no room. The box may be nudged sideways to stay on
|
|
46
47
|
// screen, so the tail keeps its own offset and still points at the anchor.
|
|
47
|
-
const
|
|
48
|
+
const reach = TOOLTIP_TAIL_HEIGHT + TOOLTIP_ANCHOR_GAP + markReach;
|
|
49
|
+
const below = target.y - height - reach < VIEWPORT_MARGIN;
|
|
48
50
|
const x = limitX(target.x - width / 2);
|
|
49
51
|
setPlacement({
|
|
50
52
|
x,
|
|
51
|
-
y: below ? target.y +
|
|
52
|
-
tailX: Math.min(Math.max(
|
|
53
|
+
y: below ? target.y + reach : target.y - height - reach,
|
|
54
|
+
tailX: Math.min(Math.max(TOOLTIP_TAIL_WIDTH, target.x - x), Math.max(TOOLTIP_TAIL_WIDTH, width - TOOLTIP_TAIL_WIDTH)),
|
|
53
55
|
below
|
|
54
56
|
});
|
|
55
|
-
}, [target.x, target.y, callout, children]);
|
|
57
|
+
}, [target.x, target.y, markReach, callout, children]);
|
|
56
58
|
if (typeof document === 'undefined') {
|
|
57
59
|
return null;
|
|
58
60
|
}
|
|
@@ -78,7 +80,8 @@ export function Tooltip({
|
|
|
78
80
|
"$shadow": shadow ? `${shadow.offsetX ?? 0}px ${shadow.offsetY ?? 2}px ${shadow.width ?? 8}px ` + `${shadow.color ?? SHADOW_COLOR}` : DEFAULT_SHADOW,
|
|
79
81
|
children: [children, placement.tailX === null ? null : _jsx(Tail, {
|
|
80
82
|
"$left": placement.tailX,
|
|
81
|
-
"$
|
|
83
|
+
"$width": TOOLTIP_TAIL_WIDTH,
|
|
84
|
+
"$height": TOOLTIP_TAIL_HEIGHT,
|
|
82
85
|
"$color": background,
|
|
83
86
|
"$below": placement.below
|
|
84
87
|
})]
|
package/core/layout/metrics.d.ts
CHANGED
|
@@ -36,8 +36,11 @@ export declare const HINT_ICON_GAP = 6;
|
|
|
36
36
|
export declare const HINT_PADDING = 12;
|
|
37
37
|
/** Between the title block and the plot below it. */
|
|
38
38
|
export declare const TITLE_BLOCK_GAP = 10;
|
|
39
|
-
/** Tooltip beak
|
|
40
|
-
export declare const
|
|
39
|
+
/** Tooltip beak, as the design system draws it: a wide, shallow triangle. */
|
|
40
|
+
export declare const TOOLTIP_TAIL_WIDTH = 20;
|
|
41
|
+
export declare const TOOLTIP_TAIL_HEIGHT = 4;
|
|
42
|
+
/** Air between the beak tip and the edge of the mark it points at. */
|
|
43
|
+
export declare const TOOLTIP_ANCHOR_GAP = 8;
|
|
41
44
|
/** Tooltip to the cursor, when it follows the pointer instead of a point. */
|
|
42
45
|
export declare const TOOLTIP_CURSOR_OFFSET = 12;
|
|
43
46
|
/** Keep a floating element this far inside the viewport. */
|
package/core/layout/metrics.js
CHANGED
|
@@ -36,8 +36,11 @@ export const HINT_ICON_GAP = 6;
|
|
|
36
36
|
export const HINT_PADDING = 12;
|
|
37
37
|
/** Between the title block and the plot below it. */
|
|
38
38
|
export const TITLE_BLOCK_GAP = 10;
|
|
39
|
-
/** Tooltip beak
|
|
40
|
-
export const
|
|
39
|
+
/** Tooltip beak, as the design system draws it: a wide, shallow triangle. */
|
|
40
|
+
export const TOOLTIP_TAIL_WIDTH = 20;
|
|
41
|
+
export const TOOLTIP_TAIL_HEIGHT = 4;
|
|
42
|
+
/** Air between the beak tip and the edge of the mark it points at. */
|
|
43
|
+
export const TOOLTIP_ANCHOR_GAP = 8;
|
|
41
44
|
/** Tooltip to the cursor, when it follows the pointer instead of a point. */
|
|
42
45
|
export const TOOLTIP_CURSOR_OFFSET = 12;
|
|
43
46
|
/** Keep a floating element this far inside the viewport. */
|
package/core/types/renderer.d.ts
CHANGED
|
@@ -61,6 +61,8 @@ export interface PointHit {
|
|
|
61
61
|
y: number | null;
|
|
62
62
|
value: number | null;
|
|
63
63
|
anchor: Point;
|
|
64
|
+
/** How far the hovered mark reaches past `anchor`; 0 when it draws none. */
|
|
65
|
+
reach?: number;
|
|
64
66
|
distance: number;
|
|
65
67
|
}
|
|
66
68
|
export interface LegendItem {
|
|
@@ -95,6 +97,8 @@ export interface SeriesRenderer<TSeries extends SeriesOptionsBase = SeriesOption
|
|
|
95
97
|
readonly animatesGeometry?: boolean;
|
|
96
98
|
extendDomain(series: TSeries, acc: DomainAcc): DomainAcc;
|
|
97
99
|
render(ctx: RenderContext<TSeries>): ReactNode;
|
|
100
|
+
/** Drawn above every series and the axis line — the hovered point marker lives here. */
|
|
101
|
+
renderOverlay?(ctx: RenderContext<TSeries>): ReactNode;
|
|
98
102
|
hitTest(ctx: RenderContext<TSeries>, pointer: Point): PointHit[] | null;
|
|
99
103
|
legendItems(series: TSeries, seriesIndex: number, colors: readonly string[]): LegendItem[];
|
|
100
104
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pushwoosh/kit-charts",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.23",
|
|
4
4
|
"description": "Small React chart library on SVG, with a Highcharts-shaped configuration model",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"directory": "dist"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@pushwoosh/kit-constants": "^2.
|
|
29
|
+
"@pushwoosh/kit-constants": "^2.1.0"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@pushwoosh/frontend-builder-engine": "^3.1.0",
|
package/series/area/area.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { drawable, projectSeries, toAreaPath, toLinePath } from '../../core/geometry/path';
|
|
3
|
-
import { showsHoverMarker } from '../../core/options/normalize';
|
|
4
3
|
import { includeZero } from '../shared/domain';
|
|
5
4
|
import { nearestByX } from '../shared/hit';
|
|
6
5
|
import { seriesLegend } from '../shared/legend';
|
|
7
|
-
import { Markers } from '../shared/Markers';
|
|
6
|
+
import { hoverMarkerFor, Markers } from '../shared/Markers';
|
|
8
7
|
const DEFAULT_FILL_OPACITY = 0.75;
|
|
9
8
|
export const area = {
|
|
10
9
|
type: 'area',
|
|
@@ -21,10 +20,6 @@ export const area = {
|
|
|
21
20
|
}
|
|
22
21
|
const color = ctx.color;
|
|
23
22
|
const lineWidth = ctx.series.lineWidth ?? 2;
|
|
24
|
-
const hoveredIndex = ctx.hovered?.pointIndex;
|
|
25
|
-
const hovered = hoveredIndex === undefined ? undefined : projected.find(({
|
|
26
|
-
point
|
|
27
|
-
}) => point.index === hoveredIndex);
|
|
28
23
|
return _jsxs("g", {
|
|
29
24
|
children: [_jsx("path", {
|
|
30
25
|
d: toAreaPath(slots),
|
|
@@ -42,16 +37,10 @@ export const area = {
|
|
|
42
37
|
marker: ctx.series.marker,
|
|
43
38
|
projected: projected,
|
|
44
39
|
color: color
|
|
45
|
-
})
|
|
46
|
-
cx: hovered.px,
|
|
47
|
-
cy: hovered.py,
|
|
48
|
-
r: ctx.series.marker?.states?.hover?.radius ?? 4,
|
|
49
|
-
fill: color,
|
|
50
|
-
stroke: "#FFFFFF",
|
|
51
|
-
strokeWidth: 2
|
|
52
|
-
}) : null]
|
|
40
|
+
})]
|
|
53
41
|
});
|
|
54
42
|
},
|
|
43
|
+
renderOverlay: ctx => hoverMarkerFor(ctx),
|
|
55
44
|
hitTest: (ctx, pointer) => nearestByX(ctx, pointer),
|
|
56
45
|
legendItems: (series, seriesIndex) => seriesLegend(series, seriesIndex, 'square')
|
|
57
46
|
};
|
package/series/column/column.js
CHANGED
|
@@ -89,15 +89,15 @@ export const column = {
|
|
|
89
89
|
stroke: borderWidth && !capped ? ctx.series.borderColor : undefined,
|
|
90
90
|
strokeWidth: borderWidth && !capped ? borderWidth : undefined
|
|
91
91
|
}, `c-${bar.point.index}`)), capped ? bars.map(bar => {
|
|
92
|
-
// The rule sits on the value edge, so a bar counting down wears it below.
|
|
92
|
+
// The rule sits on the value edge, so a bar counting down wears it below. It is a rect
|
|
93
|
+
// rather than a line: only `x`/`y`/`width`/`height` are CSS properties an SVG can tween.
|
|
93
94
|
const edge = bar.py <= bar.baseY ? bar.y : bar.y + bar.height;
|
|
94
|
-
return _jsx("
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
strokeWidth: borderWidth
|
|
95
|
+
return _jsx("rect", {
|
|
96
|
+
x: bar.x,
|
|
97
|
+
y: edge - borderWidth / 2,
|
|
98
|
+
width: bar.width,
|
|
99
|
+
height: borderWidth,
|
|
100
|
+
fill: ctx.series.borderColor ?? ctx.color
|
|
101
101
|
}, `cap-${bar.point.index}`);
|
|
102
102
|
}) : null, _jsx(DataLabels, {
|
|
103
103
|
ctx: ctx,
|
package/series/line/line.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { drawable, projectSeries, toLinePath } from '../../core/geometry/path';
|
|
3
|
-
import { showsHoverMarker } from '../../core/options/normalize';
|
|
4
3
|
import { nearestByX } from '../shared/hit';
|
|
5
4
|
import { seriesLegend } from '../shared/legend';
|
|
6
|
-
import { Markers } from '../shared/Markers';
|
|
5
|
+
import { hoverMarkerFor, Markers } from '../shared/Markers';
|
|
7
6
|
export const line = {
|
|
8
7
|
type: 'line',
|
|
9
8
|
scales: {
|
|
@@ -18,10 +17,6 @@ export const line = {
|
|
|
18
17
|
return null;
|
|
19
18
|
}
|
|
20
19
|
const color = ctx.color;
|
|
21
|
-
const hoveredIndex = ctx.hovered?.pointIndex;
|
|
22
|
-
const hovered = hoveredIndex === undefined ? undefined : projected.find(({
|
|
23
|
-
point
|
|
24
|
-
}) => point.index === hoveredIndex);
|
|
25
20
|
return _jsxs("g", {
|
|
26
21
|
children: [_jsx("path", {
|
|
27
22
|
d: toLinePath(slots),
|
|
@@ -34,16 +29,10 @@ export const line = {
|
|
|
34
29
|
marker: ctx.series.marker,
|
|
35
30
|
projected: projected,
|
|
36
31
|
color: color
|
|
37
|
-
})
|
|
38
|
-
cx: hovered.px,
|
|
39
|
-
cy: hovered.py,
|
|
40
|
-
r: ctx.series.marker?.states?.hover?.radius ?? 4,
|
|
41
|
-
fill: color,
|
|
42
|
-
stroke: "#FFFFFF",
|
|
43
|
-
strokeWidth: 2
|
|
44
|
-
}) : null]
|
|
32
|
+
})]
|
|
45
33
|
});
|
|
46
34
|
},
|
|
35
|
+
renderOverlay: ctx => hoverMarkerFor(ctx),
|
|
47
36
|
hitTest: (ctx, pointer) => nearestByX(ctx, pointer),
|
|
48
37
|
legendItems: (series, seriesIndex) => seriesLegend(series, seriesIndex, 'line')
|
|
49
38
|
};
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
2
|
import { type Projected } from '../../core/geometry/path';
|
|
3
3
|
import { type MarkerOptions } from '../../core/types/options';
|
|
4
|
+
import { type RenderContext } from '../../core/types/renderer';
|
|
5
|
+
/** How far the hover mark reaches past its point, so a tooltip can clear it. */
|
|
6
|
+
export declare const hoverReachOf: (marker: MarkerOptions | undefined) => number;
|
|
7
|
+
/** The hovered point of a series, or nothing when it has none. */
|
|
8
|
+
export declare function hoverMarkerFor(ctx: RenderContext): ReactNode;
|
|
4
9
|
export interface MarkersProps {
|
|
5
10
|
marker: MarkerOptions | undefined;
|
|
6
11
|
projected: readonly Projected[];
|
package/series/shared/Markers.js
CHANGED
|
@@ -1,6 +1,34 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { drawable, projectSeries } from '../../core/geometry/path';
|
|
3
|
+
import { DEFAULT_SURFACE_COLOR } from '../../core/model/palette';
|
|
4
|
+
import { showsHoverMarker, showsMarkers } from '../../core/options/normalize';
|
|
5
|
+
/** Hovered point per the design system: a ring in the series colour on the surface. */
|
|
6
|
+
const HOVER_RADIUS = 2.5;
|
|
7
|
+
const HOVER_RING_WIDTH = 2;
|
|
3
8
|
const markerRadius = marker => marker?.radius ?? (marker?.width ? marker.width / 2 : 3);
|
|
9
|
+
/** How far the hover mark reaches past its point, so a tooltip can clear it. */
|
|
10
|
+
export const hoverReachOf = marker => (marker?.states?.hover?.radius ?? HOVER_RADIUS) + HOVER_RING_WIDTH / 2;
|
|
11
|
+
/** The hovered point of a series, or nothing when it has none. */
|
|
12
|
+
export function hoverMarkerFor(ctx) {
|
|
13
|
+
const {
|
|
14
|
+
marker
|
|
15
|
+
} = ctx.series;
|
|
16
|
+
const hoveredIndex = ctx.hovered?.pointIndex;
|
|
17
|
+
if (hoveredIndex === undefined || !showsHoverMarker(marker)) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
const at = drawable(projectSeries(ctx)).find(({
|
|
21
|
+
point
|
|
22
|
+
}) => point.index === hoveredIndex);
|
|
23
|
+
return at ? _jsx("circle", {
|
|
24
|
+
cx: at.px,
|
|
25
|
+
cy: at.py,
|
|
26
|
+
r: marker?.states?.hover?.radius ?? HOVER_RADIUS,
|
|
27
|
+
fill: DEFAULT_SURFACE_COLOR,
|
|
28
|
+
stroke: ctx.color,
|
|
29
|
+
strokeWidth: HOVER_RING_WIDTH
|
|
30
|
+
}) : null;
|
|
31
|
+
}
|
|
4
32
|
export function Markers({
|
|
5
33
|
marker,
|
|
6
34
|
projected,
|
package/series/shared/hit.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { hoverReachOf } from './Markers';
|
|
1
2
|
import { pointKey } from '../../core/data/points';
|
|
2
3
|
import { drawable, projectSeries } from '../../core/geometry/path';
|
|
4
|
+
import { showsHoverMarker } from '../../core/options/normalize';
|
|
3
5
|
const HIT_RADIUS = 40;
|
|
4
6
|
export function nearestByX(ctx, pointer, radius = HIT_RADIUS) {
|
|
5
7
|
const projected = drawable(projectSeries(ctx));
|
|
@@ -28,6 +30,7 @@ export function nearestByX(ctx, pointer, radius = HIT_RADIUS) {
|
|
|
28
30
|
x: best.px,
|
|
29
31
|
y: best.py
|
|
30
32
|
},
|
|
33
|
+
reach: showsHoverMarker(ctx.series.marker) ? hoverReachOf(ctx.series.marker) : 0,
|
|
31
34
|
distance: bestDistance
|
|
32
35
|
}];
|
|
33
36
|
}
|