layerchart 0.54.0 → 0.55.0
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 +205 -155
- package/dist/components/Bar.svelte +72 -45
- package/dist/components/Bars.svelte +45 -34
- package/dist/components/Blur.svelte +5 -3
- package/dist/components/Bounds.svelte +37 -21
- package/dist/components/Brush.svelte +269 -112
- package/dist/components/Brush.svelte.d.ts +7 -0
- package/dist/components/Calendar.svelte +51 -38
- package/dist/components/Chart.svelte +295 -74
- package/dist/components/Chart.svelte.d.ts +17 -17
- 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 +75 -48
- 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 +81 -70
- package/dist/components/Legend.svelte +105 -74
- package/dist/components/Legend.svelte.d.ts +1 -1
- package/dist/components/Line.svelte +65 -19
- package/dist/components/Line.svelte.d.ts +13 -1
- package/dist/components/LinearGradient.svelte +21 -15
- package/dist/components/Link.svelte +94 -22
- package/dist/components/Link.svelte.d.ts +17 -1
- package/dist/components/Marker.svelte +81 -0
- package/dist/components/Marker.svelte.d.ts +28 -0
- 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 +50 -42
- package/dist/components/Sankey.svelte +55 -30
- package/dist/components/Spline.svelte +167 -96
- package/dist/components/Spline.svelte.d.ts +15 -0
- 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 +235 -135
- 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 +138 -87
- package/dist/components/charts/AreaChart.svelte.d.ts +4 -4
- package/dist/components/charts/BarChart.svelte +179 -114
- package/dist/components/charts/BarChart.svelte.d.ts +4 -4
- package/dist/components/charts/LineChart.svelte +97 -53
- package/dist/components/charts/LineChart.svelte.d.ts +4 -4
- package/dist/components/charts/PieChart.svelte +104 -54
- package/dist/components/charts/PieChart.svelte.d.ts +3 -3
- package/dist/components/charts/ScatterChart.svelte +83 -48
- package/dist/components/charts/ScatterChart.svelte.d.ts +4 -4
- 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 +137 -91
- package/dist/components/tooltip/Tooltip.svelte.d.ts +1 -1
- package/dist/components/tooltip/TooltipContext.svelte +315 -249
- 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 +39 -33
- 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 +3 -3
- package/package.json +29 -30
|
@@ -1,309 +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
|
-
|
|
61
|
-
|
|
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({
|
|
62
111
|
y: 0,
|
|
63
112
|
x: 0,
|
|
64
|
-
data: null,
|
|
113
|
+
data: null as any,
|
|
65
114
|
show: showTooltip,
|
|
66
115
|
hide: hideTooltip,
|
|
67
|
-
});
|
|
68
|
-
setTooltipContext(tooltip);
|
|
69
|
-
|
|
70
|
-
|
|
116
|
+
});
|
|
117
|
+
setTooltipContext(tooltip);
|
|
118
|
+
|
|
119
|
+
let hideTimeoutId: NodeJS.Timeout;
|
|
120
|
+
|
|
121
|
+
$: bisectX = bisector((d: any) => {
|
|
71
122
|
const value = $x(d);
|
|
72
123
|
if (Array.isArray(value)) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
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;
|
|
81
131
|
}
|
|
82
|
-
}).left;
|
|
83
|
-
|
|
132
|
+
}).left;
|
|
133
|
+
|
|
134
|
+
$: bisectY = bisector((d: any) => {
|
|
84
135
|
const value = $y(d);
|
|
85
136
|
if (Array.isArray(value)) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
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;
|
|
94
144
|
}
|
|
95
|
-
}).left;
|
|
96
|
-
|
|
145
|
+
}).left;
|
|
146
|
+
|
|
147
|
+
function findData(previousValue: any, currentValue: any, valueAtPoint: any, accessor: Function) {
|
|
97
148
|
switch (findTooltipData) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
default:
|
|
115
|
-
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;
|
|
116
165
|
}
|
|
117
|
-
}
|
|
118
|
-
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function showTooltip(e: PointerEvent, tooltipData?: any) {
|
|
119
169
|
// Cancel hiding tooltip if from previous event loop
|
|
120
170
|
clearTimeout(hideTimeoutId);
|
|
121
|
-
|
|
171
|
+
|
|
172
|
+
const referenceNode = (e.target as Element).closest('.layercake-container')!;
|
|
122
173
|
const point = localPoint(referenceNode, e);
|
|
123
174
|
const localX = point?.x ?? 0;
|
|
124
175
|
const localY = point?.y ?? 0;
|
|
176
|
+
|
|
125
177
|
if (
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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;
|
|
137
190
|
}
|
|
191
|
+
|
|
138
192
|
// If tooltipData not provided already (voronoi, etc), attempt to find it
|
|
139
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)
|
|
140
194
|
if (tooltipData == null) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
158
|
-
case 'bisect-y': {
|
|
159
|
-
// `y` value at pointer coordinate
|
|
160
|
-
const yValueAtPoint = scaleInvert($yScale, localY - $padding.top);
|
|
161
|
-
const index = bisectY($flatData, yValueAtPoint, 1);
|
|
162
|
-
const previousValue = $flatData[index - 1];
|
|
163
|
-
const currentValue = $flatData[index];
|
|
164
|
-
tooltipData = findData(previousValue, currentValue, yValueAtPoint, $y);
|
|
165
|
-
break;
|
|
166
|
-
}
|
|
167
|
-
case 'bisect-band': {
|
|
168
|
-
// `x` and `y` values at pointer coordinate
|
|
169
|
-
const xValueAtPoint = scaleInvert($xScale, localX);
|
|
170
|
-
const yValueAtPoint = scaleInvert($yScale, localY);
|
|
171
|
-
if (isScaleBand($xScale)) {
|
|
172
|
-
// Find point closest to pointer within the x band
|
|
173
|
-
const bandData = $flatData
|
|
174
|
-
.filter((d) => $x(d) === xValueAtPoint)
|
|
175
|
-
.sort(sortFunc($y)); // sort for bisect
|
|
176
|
-
const index = bisectY(bandData, yValueAtPoint, 1);
|
|
177
|
-
const previousValue = bandData[index - 1];
|
|
178
|
-
const currentValue = bandData[index];
|
|
179
|
-
tooltipData = findData(previousValue, currentValue, yValueAtPoint, $y);
|
|
180
|
-
}
|
|
181
|
-
else if (isScaleBand($yScale)) {
|
|
182
|
-
// Find point closest to pointer within the y band
|
|
183
|
-
const bandData = $flatData
|
|
184
|
-
.filter((d) => $y(d) === yValueAtPoint)
|
|
185
|
-
.sort(sortFunc($x)); // sort for bisect
|
|
186
|
-
const index = bisectX(bandData, xValueAtPoint, 1);
|
|
187
|
-
const previousValue = bandData[index - 1];
|
|
188
|
-
const currentValue = bandData[index];
|
|
189
|
-
tooltipData = findData(previousValue, currentValue, xValueAtPoint, $x);
|
|
190
|
-
}
|
|
191
|
-
else {
|
|
192
|
-
// TODO: Support `bisect-band` without band? Fallback to bisect?
|
|
193
|
-
}
|
|
194
|
-
break;
|
|
195
|
-
}
|
|
196
|
-
case 'quadtree': {
|
|
197
|
-
tooltipData = quadtree.find(localX, localY, radius);
|
|
198
|
-
break;
|
|
199
|
-
}
|
|
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;
|
|
200
211
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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;
|
|
205
256
|
}
|
|
206
|
-
|
|
207
|
-
...$tooltip,
|
|
208
|
-
x: localX,
|
|
209
|
-
y: localY,
|
|
210
|
-
data: tooltipData,
|
|
211
|
-
};
|
|
257
|
+
}
|
|
212
258
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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();
|
|
216
274
|
}
|
|
217
|
-
}
|
|
218
|
-
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function hideTooltip() {
|
|
219
278
|
// Wait an event loop tick in case `showTooltip` is called immediately on another element, to allow tweeneing (ex. moving between bands/bars)
|
|
220
279
|
hideTimeoutId = setTimeout(() => {
|
|
221
|
-
|
|
280
|
+
$tooltip = { ...$tooltip, data: null };
|
|
222
281
|
});
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
let quadtree: Quadtree<[number, number]>;
|
|
285
|
+
$: if (mode === 'quadtree') {
|
|
226
286
|
quadtree = d3Quadtree()
|
|
227
|
-
|
|
287
|
+
.extent([
|
|
228
288
|
[0, 0],
|
|
229
289
|
[$width, $height],
|
|
230
|
-
|
|
231
|
-
|
|
290
|
+
])
|
|
291
|
+
.x((d) => {
|
|
232
292
|
const value = $xGet(d);
|
|
293
|
+
|
|
233
294
|
if (Array.isArray(value)) {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
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;
|
|
242
302
|
}
|
|
243
|
-
|
|
244
|
-
|
|
303
|
+
})
|
|
304
|
+
.y((d) => {
|
|
245
305
|
const value = $yGet(d);
|
|
306
|
+
|
|
246
307
|
if (Array.isArray(value)) {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
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;
|
|
255
315
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
|
|
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') {
|
|
261
322
|
// @ts-expect-error
|
|
262
323
|
rects = $flatData
|
|
263
|
-
|
|
324
|
+
.map((d) => {
|
|
264
325
|
const xValue = $xGet(d);
|
|
265
326
|
const yValue = $yGet(d);
|
|
327
|
+
|
|
266
328
|
const x = Array.isArray(xValue) ? xValue[0] : xValue;
|
|
267
329
|
const y = Array.isArray(yValue) ? yValue[0] : yValue;
|
|
330
|
+
|
|
268
331
|
const xOffset = isScaleBand($xScale) ? ($xScale.padding() * $xScale.step()) / 2 : 0;
|
|
269
332
|
const yOffset = isScaleBand($yScale) ? ($yScale.padding() * $yScale.step()) / 2 : 0;
|
|
333
|
+
|
|
270
334
|
// @ts-expect-error
|
|
271
335
|
const fullWidth = max($xRange) - min($xRange);
|
|
272
336
|
// @ts-expect-error
|
|
273
337
|
const fullHeight = max($yRange) - min($yRange);
|
|
338
|
+
|
|
274
339
|
if (mode === 'band') {
|
|
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
|
-
|
|
301
|
-
|
|
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
|
+
};
|
|
302
367
|
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
}
|
|
306
|
-
|
|
368
|
+
})
|
|
369
|
+
.sort(sortFunc('x'));
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
$: triggerPointEvents = ['bisect-x', 'bisect-y', 'bisect-band', 'quadtree'].includes(mode);
|
|
307
373
|
</script>
|
|
308
374
|
|
|
309
375
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
|
@@ -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
|