layerchart 0.53.0 → 0.54.1
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/dist/components/Arc.svelte +170 -144
- package/dist/components/Area.svelte +96 -67
- package/dist/components/Area.svelte.d.ts +1 -0
- package/dist/components/Axis.svelte +241 -234
- package/dist/components/Axis.svelte.d.ts +2 -1
- package/dist/components/Bar.svelte +72 -45
- package/dist/components/Bars.svelte +46 -34
- package/dist/components/Bars.svelte.d.ts +1 -0
- package/dist/components/Blur.svelte +5 -3
- package/dist/components/Bounds.svelte +37 -21
- package/dist/components/Brush.svelte +181 -110
- package/dist/components/Calendar.svelte +51 -38
- package/dist/components/Chart.svelte +296 -73
- package/dist/components/Chart.svelte.d.ts +42 -25
- package/dist/components/ChartClipPath.svelte +8 -5
- package/dist/components/ChartContext.svelte +243 -93
- package/dist/components/ChartContext.svelte.d.ts +15 -23
- package/dist/components/Circle.svelte +25 -16
- package/dist/components/CircleClipPath.svelte +16 -10
- package/dist/components/ClipPath.svelte +11 -7
- package/dist/components/ColorRamp.svelte +12 -10
- package/dist/components/ForceSimulation.svelte +185 -116
- package/dist/components/Frame.svelte +10 -6
- package/dist/components/GeoCircle.svelte +15 -9
- package/dist/components/GeoContext.svelte +109 -62
- package/dist/components/GeoEdgeFade.svelte +20 -14
- package/dist/components/GeoPath.svelte +107 -69
- package/dist/components/GeoPoint.svelte +32 -18
- package/dist/components/GeoSpline.svelte +30 -22
- package/dist/components/GeoTile.svelte +40 -30
- package/dist/components/GeoVisible.svelte +10 -7
- package/dist/components/Graticule.svelte +14 -8
- package/dist/components/Grid.svelte +178 -0
- package/dist/components/Grid.svelte.d.ts +35 -0
- package/dist/components/Group.svelte +43 -31
- package/dist/components/Highlight.svelte +284 -243
- package/dist/components/HitCanvas.svelte +75 -42
- package/dist/components/Hull.svelte +40 -20
- package/dist/components/Labels.svelte +84 -63
- package/dist/components/Labels.svelte.d.ts +2 -0
- package/dist/components/Legend.svelte +106 -75
- package/dist/components/Legend.svelte.d.ts +1 -1
- package/dist/components/Line.svelte +29 -19
- package/dist/components/LinearGradient.svelte +21 -15
- package/dist/components/Link.svelte +44 -22
- package/dist/components/MonthPath.svelte +23 -16
- package/dist/components/MotionPath.svelte +34 -25
- package/dist/components/Pack.svelte +21 -14
- package/dist/components/Partition.svelte +35 -20
- package/dist/components/Pattern.svelte +8 -6
- package/dist/components/Pie.svelte +76 -57
- package/dist/components/Point.svelte +11 -7
- package/dist/components/Points.svelte +178 -143
- package/dist/components/RadialGradient.svelte +25 -18
- package/dist/components/Rect.svelte +33 -19
- package/dist/components/RectClipPath.svelte +16 -11
- package/dist/components/Rule.svelte +66 -42
- package/dist/components/Rule.svelte.d.ts +2 -0
- package/dist/components/Sankey.svelte +55 -30
- package/dist/components/Spline.svelte +118 -96
- package/dist/components/Text.svelte +137 -104
- package/dist/components/Threshold.svelte +18 -7
- package/dist/components/TileImage.svelte +56 -50
- package/dist/components/TransformContext.svelte +238 -136
- package/dist/components/TransformControls.svelte +57 -29
- package/dist/components/TransformControls.svelte.d.ts +1 -1
- package/dist/components/Tree.svelte +33 -23
- package/dist/components/Treemap.svelte +69 -41
- package/dist/components/Voronoi.svelte +55 -28
- package/dist/components/charts/AreaChart.svelte +162 -87
- package/dist/components/charts/AreaChart.svelte.d.ts +41 -3
- package/dist/components/charts/BarChart.svelte +207 -108
- package/dist/components/charts/BarChart.svelte.d.ts +41 -3
- package/dist/components/charts/LineChart.svelte +119 -47
- package/dist/components/charts/LineChart.svelte.d.ts +41 -3
- package/dist/components/charts/PieChart.svelte +117 -53
- package/dist/components/charts/PieChart.svelte.d.ts +17 -2
- package/dist/components/charts/ScatterChart.svelte +92 -42
- package/dist/components/charts/ScatterChart.svelte.d.ts +39 -3
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +1 -0
- package/dist/components/layout/Canvas.svelte +63 -43
- package/dist/components/layout/Html.svelte +28 -18
- package/dist/components/layout/Svg.svelte +47 -32
- package/dist/components/tooltip/Tooltip.svelte +143 -82
- package/dist/components/tooltip/Tooltip.svelte.d.ts +1 -1
- package/dist/components/tooltip/TooltipContext.svelte +315 -248
- package/dist/components/tooltip/TooltipContext.svelte.d.ts +7 -0
- package/dist/components/tooltip/TooltipHeader.svelte +9 -3
- package/dist/components/tooltip/TooltipItem.svelte +17 -9
- package/dist/components/tooltip/TooltipList.svelte +2 -1
- package/dist/components/tooltip/TooltipSeparator.svelte +3 -2
- package/dist/docs/Blockquote.svelte +4 -3
- package/dist/docs/Code.svelte +15 -8
- package/dist/docs/CurveMenuField.svelte +17 -12
- package/dist/docs/GeoDebug.svelte +13 -9
- package/dist/docs/Header1.svelte +2 -1
- package/dist/docs/Json.svelte +6 -4
- package/dist/docs/Layout.svelte +6 -6
- package/dist/docs/PathDataMenuField.svelte +52 -44
- package/dist/docs/Preview.svelte +48 -29
- package/dist/docs/TilesetField.svelte +80 -62
- package/dist/docs/TransformDebug.svelte +8 -5
- package/dist/docs/ViewSourceButton.svelte +13 -9
- package/dist/stores/motionStore.d.ts +1 -1
- package/dist/utils/scales.d.ts +4 -4
- package/package.json +29 -30
|
@@ -1,308 +1,375 @@
|
|
|
1
|
-
<script context="module">
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
<script lang="ts" context="module">
|
|
2
|
+
import { getContext, setContext } from 'svelte';
|
|
3
|
+
import type { Readable } from 'svelte/store';
|
|
4
|
+
|
|
5
|
+
export const tooltipContextKey = Symbol();
|
|
6
|
+
|
|
7
|
+
export type TooltipContextValue = {
|
|
8
|
+
x: number;
|
|
9
|
+
y: number;
|
|
10
|
+
data: any;
|
|
11
|
+
show(e: PointerEvent, tooltipData?: any): void;
|
|
12
|
+
hide(e?: PointerEvent): void;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type TooltipContext = Readable<TooltipContextValue>;
|
|
16
|
+
|
|
17
|
+
const defaultContext: TooltipContext = writable({
|
|
4
18
|
x: 0,
|
|
5
19
|
y: 0,
|
|
6
|
-
data: null,
|
|
7
|
-
show: () => {
|
|
8
|
-
hide: () => {
|
|
9
|
-
});
|
|
10
|
-
export function tooltipContext() {
|
|
11
|
-
return getContext(tooltipContextKey) ?? defaultContext;
|
|
12
|
-
}
|
|
13
|
-
|
|
20
|
+
data: null as any,
|
|
21
|
+
show: () => {},
|
|
22
|
+
hide: () => {},
|
|
23
|
+
});
|
|
24
|
+
export function tooltipContext() {
|
|
25
|
+
return getContext<TooltipContext>(tooltipContextKey) ?? defaultContext;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function setTooltipContext(tooltip: TooltipContext) {
|
|
14
29
|
setContext(tooltipContextKey, tooltip);
|
|
15
|
-
}
|
|
30
|
+
}
|
|
16
31
|
</script>
|
|
17
32
|
|
|
18
|
-
<script>
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import
|
|
25
|
-
|
|
26
|
-
import
|
|
27
|
-
import
|
|
28
|
-
import
|
|
29
|
-
import
|
|
30
|
-
|
|
31
|
-
import {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
33
|
+
<script lang="ts">
|
|
34
|
+
import { raise } from 'layercake';
|
|
35
|
+
import { writable } from 'svelte/store';
|
|
36
|
+
import { bisector, max, min } from 'd3-array';
|
|
37
|
+
import { quadtree as d3Quadtree, type Quadtree } from 'd3-quadtree';
|
|
38
|
+
import { sortFunc } from '@layerstack/utils';
|
|
39
|
+
import { cls } from '@layerstack/tailwind';
|
|
40
|
+
|
|
41
|
+
import Svg from './../layout/Svg.svelte';
|
|
42
|
+
import { chartContext } from './../ChartContext.svelte';
|
|
43
|
+
import ChartClipPath from './../ChartClipPath.svelte';
|
|
44
|
+
import Voronoi from './../Voronoi.svelte';
|
|
45
|
+
|
|
46
|
+
import { localPoint } from '../../utils/event.js';
|
|
47
|
+
import { isScaleBand, scaleInvert } from '../../utils/scales.js';
|
|
48
|
+
import { cartesianToPolar } from '../../utils/math.js';
|
|
49
|
+
import { quadtreeRects } from '../../utils/quadtree.js';
|
|
50
|
+
|
|
51
|
+
const {
|
|
52
|
+
flatData,
|
|
53
|
+
x,
|
|
54
|
+
xScale,
|
|
55
|
+
xGet,
|
|
56
|
+
xRange,
|
|
57
|
+
y,
|
|
58
|
+
yScale,
|
|
59
|
+
yGet,
|
|
60
|
+
yRange,
|
|
61
|
+
width,
|
|
62
|
+
height,
|
|
63
|
+
containerWidth,
|
|
64
|
+
containerHeight,
|
|
65
|
+
padding,
|
|
66
|
+
radial,
|
|
67
|
+
} = chartContext<any>();
|
|
68
|
+
|
|
69
|
+
/*
|
|
70
|
+
TODO: Defaults to consider (if possible to detect scale type, which might not be possible)
|
|
71
|
+
- scaleTime / scaleLinear: bisect
|
|
72
|
+
- scaleTime / scaleLinear (multi/stack): bisect
|
|
73
|
+
- scaleTime / scaleBand: bisect (or band)
|
|
74
|
+
- scaleTime (multi) / scaleBand: bounds (or possible band if not overlapping)
|
|
75
|
+
- scaleBand, scaleLinear: band (or bounds)
|
|
76
|
+
- scaleBand, scaleLinear: band (or bounds) - multiple (overlapping) bars
|
|
77
|
+
- scaleLinear, scaleLinear: voronoi (or quadtree)
|
|
78
|
+
*/
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @type {'bisect-x' | 'bisect-y' | 'band' | 'bisect-band' | 'bounds' | 'voronoi' | 'quadtree' | 'manual'}
|
|
82
|
+
*/
|
|
83
|
+
export let mode:
|
|
84
|
+
| 'bisect-x'
|
|
85
|
+
| 'bisect-y'
|
|
86
|
+
| 'band'
|
|
87
|
+
| 'bisect-band'
|
|
88
|
+
| 'bounds'
|
|
89
|
+
| 'voronoi'
|
|
90
|
+
| 'quadtree'
|
|
91
|
+
| 'manual' = 'manual';
|
|
92
|
+
/**
|
|
93
|
+
* @type {'closest' | 'left' | 'right'}
|
|
94
|
+
*/
|
|
95
|
+
export let findTooltipData: 'closest' | 'left' | 'right' = 'closest';
|
|
96
|
+
|
|
97
|
+
/** Similar to d3-selection's raise, re-insert the e.target as the last child of its parent, so to be the top-most element */
|
|
98
|
+
export let raiseTarget = false;
|
|
99
|
+
|
|
100
|
+
/** quadtree search radius
|
|
101
|
+
* @type {number}
|
|
102
|
+
*/
|
|
103
|
+
export let radius: number = Infinity;
|
|
104
|
+
/** Enable debug view (show hit targets, etc) */
|
|
105
|
+
export let debug = false;
|
|
106
|
+
|
|
107
|
+
export let onClick: ({ data }: { data: any }) => any = () => {};
|
|
108
|
+
|
|
109
|
+
/** Exposed to allow binding in Chart */
|
|
110
|
+
export let tooltip = writable({
|
|
61
111
|
y: 0,
|
|
62
112
|
x: 0,
|
|
63
|
-
data: null,
|
|
113
|
+
data: null as any,
|
|
64
114
|
show: showTooltip,
|
|
65
115
|
hide: hideTooltip,
|
|
66
|
-
});
|
|
67
|
-
setTooltipContext(tooltip);
|
|
68
|
-
|
|
69
|
-
|
|
116
|
+
});
|
|
117
|
+
setTooltipContext(tooltip);
|
|
118
|
+
|
|
119
|
+
let hideTimeoutId: NodeJS.Timeout;
|
|
120
|
+
|
|
121
|
+
$: bisectX = bisector((d: any) => {
|
|
70
122
|
const value = $x(d);
|
|
71
123
|
if (Array.isArray(value)) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return value;
|
|
124
|
+
// `x` accessor with multiple properties (ex. `x={['start', 'end']})`)
|
|
125
|
+
// Using first value. Consider using average, max, etc
|
|
126
|
+
// const midpoint = new Date((value[1].valueOf() + value[0].getTime()) / 2);
|
|
127
|
+
// return midpoint;
|
|
128
|
+
return value[0];
|
|
129
|
+
} else {
|
|
130
|
+
return value;
|
|
80
131
|
}
|
|
81
|
-
}).left;
|
|
82
|
-
|
|
132
|
+
}).left;
|
|
133
|
+
|
|
134
|
+
$: bisectY = bisector((d: any) => {
|
|
83
135
|
const value = $y(d);
|
|
84
136
|
if (Array.isArray(value)) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return value;
|
|
137
|
+
// `x` accessor with multiple properties (ex. `x={['start', 'end']})`)
|
|
138
|
+
// Using first value. Consider using average, max, etc
|
|
139
|
+
// const midpoint = new Date((value[1].valueOf() + value[0].getTime()) / 2);
|
|
140
|
+
// return midpoint;
|
|
141
|
+
return value[0];
|
|
142
|
+
} else {
|
|
143
|
+
return value;
|
|
93
144
|
}
|
|
94
|
-
}).left;
|
|
95
|
-
|
|
145
|
+
}).left;
|
|
146
|
+
|
|
147
|
+
function findData(previousValue: any, currentValue: any, valueAtPoint: any, accessor: Function) {
|
|
96
148
|
switch (findTooltipData) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
default:
|
|
114
|
-
return currentValue;
|
|
149
|
+
case 'closest':
|
|
150
|
+
if (currentValue === undefined) {
|
|
151
|
+
return previousValue;
|
|
152
|
+
} else if (previousValue === undefined) {
|
|
153
|
+
return currentValue;
|
|
154
|
+
} else {
|
|
155
|
+
return Number(valueAtPoint) - Number(accessor(previousValue)) >
|
|
156
|
+
Number(accessor(currentValue)) - Number(valueAtPoint)
|
|
157
|
+
? currentValue
|
|
158
|
+
: previousValue;
|
|
159
|
+
}
|
|
160
|
+
case 'left':
|
|
161
|
+
return previousValue;
|
|
162
|
+
case 'right':
|
|
163
|
+
default:
|
|
164
|
+
return currentValue;
|
|
115
165
|
}
|
|
116
|
-
}
|
|
117
|
-
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function showTooltip(e: PointerEvent, tooltipData?: any) {
|
|
118
169
|
// Cancel hiding tooltip if from previous event loop
|
|
119
170
|
clearTimeout(hideTimeoutId);
|
|
120
|
-
|
|
171
|
+
|
|
172
|
+
const referenceNode = (e.target as Element).closest('.layercake-container')!;
|
|
121
173
|
const point = localPoint(referenceNode, e);
|
|
122
174
|
const localX = point?.x ?? 0;
|
|
123
175
|
const localY = point?.y ?? 0;
|
|
176
|
+
|
|
124
177
|
if (
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
178
|
+
// @ts-expect-error
|
|
179
|
+
e.offsetX < e.currentTarget?.offsetLeft ||
|
|
180
|
+
// @ts-expect-error
|
|
181
|
+
e.offsetX > e.currentTarget?.offsetLeft + e.currentTarget?.offsetWidth ||
|
|
182
|
+
// @ts-expect-error
|
|
183
|
+
e.offsetY < e.currentTarget?.offsetTop ||
|
|
184
|
+
// @ts-expect-error
|
|
185
|
+
e.offsetY > e.currentTarget?.offsetTop + e.currentTarget?.offsetHeight
|
|
186
|
+
) {
|
|
187
|
+
// Ignore if within padding of chart
|
|
188
|
+
hideTooltip();
|
|
189
|
+
return;
|
|
136
190
|
}
|
|
191
|
+
|
|
137
192
|
// If tooltipData not provided already (voronoi, etc), attempt to find it
|
|
138
193
|
// TODO: When using bisect-x/y/band, values should be sorted. Typically they are for `x`, but not `y` (and band depends on if x or y scale)
|
|
139
194
|
if (tooltipData == null) {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
157
|
-
case 'bisect-y': {
|
|
158
|
-
// `y` value at pointer coordinate
|
|
159
|
-
const yValueAtPoint = scaleInvert($yScale, localY - $padding.top);
|
|
160
|
-
const index = bisectY($flatData, yValueAtPoint, 1);
|
|
161
|
-
const previousValue = $flatData[index - 1];
|
|
162
|
-
const currentValue = $flatData[index];
|
|
163
|
-
tooltipData = findData(previousValue, currentValue, yValueAtPoint, $y);
|
|
164
|
-
break;
|
|
165
|
-
}
|
|
166
|
-
case 'bisect-band': {
|
|
167
|
-
// `x` and `y` values at pointer coordinate
|
|
168
|
-
const xValueAtPoint = scaleInvert($xScale, localX);
|
|
169
|
-
const yValueAtPoint = scaleInvert($yScale, localY);
|
|
170
|
-
if (isScaleBand($xScale)) {
|
|
171
|
-
// Find point closest to pointer within the x band
|
|
172
|
-
const bandData = $flatData
|
|
173
|
-
.filter((d) => $x(d) === xValueAtPoint)
|
|
174
|
-
.sort(sortFunc($y)); // sort for bisect
|
|
175
|
-
const index = bisectY(bandData, yValueAtPoint, 1);
|
|
176
|
-
const previousValue = bandData[index - 1];
|
|
177
|
-
const currentValue = bandData[index];
|
|
178
|
-
tooltipData = findData(previousValue, currentValue, yValueAtPoint, $y);
|
|
179
|
-
}
|
|
180
|
-
else if (isScaleBand($yScale)) {
|
|
181
|
-
// Find point closest to pointer within the y band
|
|
182
|
-
const bandData = $flatData
|
|
183
|
-
.filter((d) => $y(d) === yValueAtPoint)
|
|
184
|
-
.sort(sortFunc($x)); // sort for bisect
|
|
185
|
-
const index = bisectX(bandData, xValueAtPoint, 1);
|
|
186
|
-
const previousValue = bandData[index - 1];
|
|
187
|
-
const currentValue = bandData[index];
|
|
188
|
-
tooltipData = findData(previousValue, currentValue, xValueAtPoint, $x);
|
|
189
|
-
}
|
|
190
|
-
else {
|
|
191
|
-
// TODO: Support `bisect-band` without band? Fallback to bisect?
|
|
192
|
-
}
|
|
193
|
-
break;
|
|
194
|
-
}
|
|
195
|
-
case 'quadtree': {
|
|
196
|
-
tooltipData = quadtree.find(localX, localY, radius);
|
|
197
|
-
break;
|
|
198
|
-
}
|
|
195
|
+
switch (mode) {
|
|
196
|
+
case 'bisect-x': {
|
|
197
|
+
let xValueAtPoint: any;
|
|
198
|
+
if ($radial) {
|
|
199
|
+
// Assume radial is always centered
|
|
200
|
+
const { radians } = cartesianToPolar(localX - $width / 2, localY - $height / 2);
|
|
201
|
+
xValueAtPoint = scaleInvert($xScale, radians);
|
|
202
|
+
} else {
|
|
203
|
+
xValueAtPoint = scaleInvert($xScale, localX - $padding.left);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const index = bisectX($flatData, xValueAtPoint, 1);
|
|
207
|
+
const previousValue = $flatData[index - 1];
|
|
208
|
+
const currentValue = $flatData[index];
|
|
209
|
+
tooltipData = findData(previousValue, currentValue, xValueAtPoint, $x);
|
|
210
|
+
break;
|
|
199
211
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
212
|
+
|
|
213
|
+
case 'bisect-y': {
|
|
214
|
+
// `y` value at pointer coordinate
|
|
215
|
+
const yValueAtPoint = scaleInvert($yScale, localY - $padding.top);
|
|
216
|
+
|
|
217
|
+
const index = bisectY($flatData, yValueAtPoint, 1);
|
|
218
|
+
const previousValue = $flatData[index - 1];
|
|
219
|
+
const currentValue = $flatData[index];
|
|
220
|
+
tooltipData = findData(previousValue, currentValue, yValueAtPoint, $y);
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
case 'bisect-band': {
|
|
225
|
+
// `x` and `y` values at pointer coordinate
|
|
226
|
+
const xValueAtPoint = scaleInvert($xScale, localX);
|
|
227
|
+
const yValueAtPoint = scaleInvert($yScale, localY);
|
|
228
|
+
|
|
229
|
+
if (isScaleBand($xScale)) {
|
|
230
|
+
// Find point closest to pointer within the x band
|
|
231
|
+
const bandData = $flatData
|
|
232
|
+
.filter((d) => $x(d) === xValueAtPoint)
|
|
233
|
+
.sort(sortFunc($y as () => any)); // sort for bisect
|
|
234
|
+
const index = bisectY(bandData, yValueAtPoint, 1);
|
|
235
|
+
const previousValue = bandData[index - 1];
|
|
236
|
+
const currentValue = bandData[index];
|
|
237
|
+
tooltipData = findData(previousValue, currentValue, yValueAtPoint, $y);
|
|
238
|
+
} else if (isScaleBand($yScale)) {
|
|
239
|
+
// Find point closest to pointer within the y band
|
|
240
|
+
const bandData = $flatData
|
|
241
|
+
.filter((d) => $y(d) === yValueAtPoint)
|
|
242
|
+
.sort(sortFunc($x as () => any)); // sort for bisect
|
|
243
|
+
const index = bisectX(bandData, xValueAtPoint, 1);
|
|
244
|
+
const previousValue = bandData[index - 1];
|
|
245
|
+
const currentValue = bandData[index];
|
|
246
|
+
tooltipData = findData(previousValue, currentValue, xValueAtPoint, $x);
|
|
247
|
+
} else {
|
|
248
|
+
// TODO: Support `bisect-band` without band? Fallback to bisect?
|
|
249
|
+
}
|
|
250
|
+
break;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
case 'quadtree': {
|
|
254
|
+
tooltipData = quadtree.find(localX, localY, radius);
|
|
255
|
+
break;
|
|
204
256
|
}
|
|
205
|
-
|
|
206
|
-
...$tooltip,
|
|
207
|
-
x: localX,
|
|
208
|
-
y: localY,
|
|
209
|
-
data: tooltipData,
|
|
210
|
-
};
|
|
257
|
+
}
|
|
211
258
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
259
|
+
|
|
260
|
+
if (tooltipData) {
|
|
261
|
+
if (raiseTarget) {
|
|
262
|
+
raise(e.target as Element);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
$tooltip = {
|
|
266
|
+
...$tooltip,
|
|
267
|
+
x: localX,
|
|
268
|
+
y: localY,
|
|
269
|
+
data: tooltipData,
|
|
270
|
+
};
|
|
271
|
+
} else {
|
|
272
|
+
// Hide tooltip if unable to locate
|
|
273
|
+
hideTooltip();
|
|
215
274
|
}
|
|
216
|
-
}
|
|
217
|
-
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function hideTooltip() {
|
|
218
278
|
// Wait an event loop tick in case `showTooltip` is called immediately on another element, to allow tweeneing (ex. moving between bands/bars)
|
|
219
279
|
hideTimeoutId = setTimeout(() => {
|
|
220
|
-
|
|
280
|
+
$tooltip = { ...$tooltip, data: null };
|
|
221
281
|
});
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
let quadtree: Quadtree<[number, number]>;
|
|
285
|
+
$: if (mode === 'quadtree') {
|
|
225
286
|
quadtree = d3Quadtree()
|
|
226
|
-
|
|
287
|
+
.extent([
|
|
227
288
|
[0, 0],
|
|
228
289
|
[$width, $height],
|
|
229
|
-
|
|
230
|
-
|
|
290
|
+
])
|
|
291
|
+
.x((d) => {
|
|
231
292
|
const value = $xGet(d);
|
|
293
|
+
|
|
232
294
|
if (Array.isArray(value)) {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
return value;
|
|
295
|
+
// `x` accessor with multiple properties (ex. `x={['start', 'end']})`)
|
|
296
|
+
// Using first value. Consider using average, max, etc
|
|
297
|
+
// const midpoint = new Date((value[1].valueOf() + value[0].getTime()) / 2);
|
|
298
|
+
// return midpoint;
|
|
299
|
+
return min(value);
|
|
300
|
+
} else {
|
|
301
|
+
return value;
|
|
241
302
|
}
|
|
242
|
-
|
|
243
|
-
|
|
303
|
+
})
|
|
304
|
+
.y((d) => {
|
|
244
305
|
const value = $yGet(d);
|
|
306
|
+
|
|
245
307
|
if (Array.isArray(value)) {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
return value;
|
|
308
|
+
// `x` accessor with multiple properties (ex. `x={['start', 'end']})`)
|
|
309
|
+
// Using first value. Consider using average, max, etc
|
|
310
|
+
// const midpoint = new Date((value[1].valueOf() + value[0].getTime()) / 2);
|
|
311
|
+
// return midpoint;
|
|
312
|
+
return min(value);
|
|
313
|
+
} else {
|
|
314
|
+
return value;
|
|
254
315
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
|
|
316
|
+
})
|
|
317
|
+
.addAll($flatData as [number, number][]);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
let rects: Array<{ x: number; y: number; width: number; height: number; data: any }> = [];
|
|
321
|
+
$: if (mode === 'bounds' || mode === 'band') {
|
|
260
322
|
// @ts-expect-error
|
|
261
323
|
rects = $flatData
|
|
262
|
-
|
|
324
|
+
.map((d) => {
|
|
263
325
|
const xValue = $xGet(d);
|
|
264
326
|
const yValue = $yGet(d);
|
|
327
|
+
|
|
265
328
|
const x = Array.isArray(xValue) ? xValue[0] : xValue;
|
|
266
329
|
const y = Array.isArray(yValue) ? yValue[0] : yValue;
|
|
330
|
+
|
|
267
331
|
const xOffset = isScaleBand($xScale) ? ($xScale.padding() * $xScale.step()) / 2 : 0;
|
|
268
332
|
const yOffset = isScaleBand($yScale) ? ($yScale.padding() * $yScale.step()) / 2 : 0;
|
|
333
|
+
|
|
269
334
|
// @ts-expect-error
|
|
270
335
|
const fullWidth = max($xRange) - min($xRange);
|
|
271
336
|
// @ts-expect-error
|
|
272
337
|
const fullHeight = max($yRange) - min($yRange);
|
|
338
|
+
|
|
273
339
|
if (mode === 'band') {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
340
|
+
// full band width/height regardless of value
|
|
341
|
+
return {
|
|
342
|
+
x: isScaleBand($xScale) ? x - xOffset : min($xRange),
|
|
343
|
+
y: isScaleBand($yScale) ? y - yOffset : min($yRange),
|
|
344
|
+
width: isScaleBand($xScale) ? $xScale.step() : fullWidth,
|
|
345
|
+
height: isScaleBand($yScale) ? $yScale.step() : fullHeight,
|
|
346
|
+
data: d,
|
|
347
|
+
};
|
|
348
|
+
} else if (mode === 'bounds') {
|
|
349
|
+
return {
|
|
350
|
+
x: isScaleBand($xScale) || Array.isArray(xValue) ? x - xOffset : min($xRange),
|
|
351
|
+
// y: isScaleBand($yScale) || Array.isArray(yValue) ? y - yOffset : min($yRange),
|
|
352
|
+
y: y - yOffset,
|
|
353
|
+
|
|
354
|
+
width: Array.isArray(xValue)
|
|
355
|
+
? xValue[1] - xValue[0]
|
|
356
|
+
: isScaleBand($xScale)
|
|
357
|
+
? $xScale.step()
|
|
358
|
+
: min($xRange) + x,
|
|
359
|
+
height: Array.isArray(yValue)
|
|
360
|
+
? yValue[1] - yValue[0]
|
|
361
|
+
: isScaleBand($yScale)
|
|
362
|
+
? $yScale.step()
|
|
363
|
+
: // @ts-expect-error
|
|
364
|
+
max($yRange) - y,
|
|
365
|
+
data: d,
|
|
366
|
+
};
|
|
301
367
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
}
|
|
305
|
-
|
|
368
|
+
})
|
|
369
|
+
.sort(sortFunc('x'));
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
$: triggerPointEvents = ['bisect-x', 'bisect-y', 'bisect-band', 'quadtree'].includes(mode);
|
|
306
373
|
</script>
|
|
307
374
|
|
|
308
375
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
|
@@ -26,6 +26,13 @@ declare const __propDef: {
|
|
|
26
26
|
onClick?: ({ data }: {
|
|
27
27
|
data: any;
|
|
28
28
|
}) => any;
|
|
29
|
+
/** Exposed to allow binding in Chart */ tooltip?: import("svelte/store").Writable<{
|
|
30
|
+
y: number;
|
|
31
|
+
x: number;
|
|
32
|
+
data: any;
|
|
33
|
+
show: (e: PointerEvent, tooltipData?: any) => void;
|
|
34
|
+
hide: () => void;
|
|
35
|
+
}>;
|
|
29
36
|
};
|
|
30
37
|
events: {
|
|
31
38
|
[evt: string]: CustomEvent<any>;
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cls } from '@layerstack/tailwind';
|
|
3
|
+
|
|
4
|
+
export let color: string | undefined = undefined;
|
|
5
|
+
|
|
6
|
+
export let classes: {
|
|
7
|
+
root?: string;
|
|
8
|
+
color?: string;
|
|
9
|
+
} = {};
|
|
4
10
|
</script>
|
|
5
11
|
|
|
6
12
|
<div
|