svelteplot 0.3.9-pr-150.0 → 0.3.10-pr-151.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.
|
@@ -64,6 +64,14 @@
|
|
|
64
64
|
transform="translate({useFacetX ? facetXScale(facetX) : 0}, {useFacetY
|
|
65
65
|
? facetYScale(facetY)
|
|
66
66
|
: 0})">
|
|
67
|
+
<!-- facets need invisible rect -->
|
|
68
|
+
<rect
|
|
69
|
+
x={plot.options.marginLeft}
|
|
70
|
+
y={plot.options.marginTop}
|
|
71
|
+
width={facetWidth}
|
|
72
|
+
height={facetHeight}
|
|
73
|
+
fill="transparent"
|
|
74
|
+
pointer-events="all" />
|
|
67
75
|
<Facet
|
|
68
76
|
fx={facetX}
|
|
69
77
|
fy={facetY}
|
|
@@ -9,6 +9,11 @@
|
|
|
9
9
|
* maximum cursor distance to select data points
|
|
10
10
|
*/
|
|
11
11
|
maxDistance?: number;
|
|
12
|
+
/**
|
|
13
|
+
* tolerance for considering points as "the same" when sharing x or y values
|
|
14
|
+
* defaults to 0 pixel
|
|
15
|
+
*/
|
|
16
|
+
tolerance?: number;
|
|
12
17
|
/**
|
|
13
18
|
* called whenever the selection changes
|
|
14
19
|
* @param data
|
|
@@ -28,6 +33,9 @@
|
|
|
28
33
|
const { getPlotState } = getContext<PlotContext>('svelteplot');
|
|
29
34
|
const plot = $derived(getPlotState());
|
|
30
35
|
|
|
36
|
+
const POINTER_X = Symbol('pointerX');
|
|
37
|
+
const POINTER_Y = Symbol('pointerY');
|
|
38
|
+
|
|
31
39
|
let markProps: PointerMarkProps = $props();
|
|
32
40
|
|
|
33
41
|
const DEFAULTS = {
|
|
@@ -41,6 +49,7 @@
|
|
|
41
49
|
y,
|
|
42
50
|
z,
|
|
43
51
|
maxDistance = 15,
|
|
52
|
+
tolerance = Number.NEGATIVE_INFINITY,
|
|
44
53
|
onupdate = null
|
|
45
54
|
}: PointerMarkProps = $derived({
|
|
46
55
|
...DEFAULTS,
|
|
@@ -49,27 +58,21 @@
|
|
|
49
58
|
|
|
50
59
|
let selectedData = $state([]);
|
|
51
60
|
|
|
52
|
-
function
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
function onTouchMove(evt: TouchEvent) {
|
|
57
|
-
if (evt.touches) {
|
|
58
|
-
const rect = (evt.target as HTMLElement).getBoundingClientRect();
|
|
59
|
-
const pageTop = window.scrollY || document.documentElement.scrollTop;
|
|
60
|
-
const ox = rect.left;
|
|
61
|
-
const oy = rect.top + pageTop;
|
|
62
|
-
|
|
63
|
-
const touch = evt.touches[0] || evt.changedTouches[0];
|
|
64
|
-
if (touch) {
|
|
65
|
-
const ex = touch.pageX - ox;
|
|
66
|
-
const ey = touch.pageY - oy;
|
|
67
|
-
updateSelection(ex, ey);
|
|
68
|
-
}
|
|
61
|
+
function onPointerMove(evt: MouseEvent) {
|
|
62
|
+
let facetEl = evt.target as SVGElement;
|
|
63
|
+
while (facetEl && !facetEl.classList.contains('facet')) {
|
|
64
|
+
facetEl = facetEl.parentElement;
|
|
69
65
|
}
|
|
66
|
+
const facetRect = (facetEl?.firstChild ?? plot.body).getBoundingClientRect();
|
|
67
|
+
|
|
68
|
+
const relativeX = evt.clientX - facetRect.left + (plot.options.marginLeft ?? 0);
|
|
69
|
+
const relativeY = evt.clientY - facetRect.top + (plot.options.marginTop ?? 0);
|
|
70
|
+
|
|
71
|
+
// console.log({ relativeX, relativeY }, evt);
|
|
72
|
+
updateSelection(relativeX, relativeY);
|
|
70
73
|
}
|
|
71
74
|
|
|
72
|
-
function
|
|
75
|
+
function onPointerLeave() {
|
|
73
76
|
selectedData = [];
|
|
74
77
|
if (onupdate) onupdate(selectedData);
|
|
75
78
|
}
|
|
@@ -79,19 +82,30 @@
|
|
|
79
82
|
const points = trees.map((tree) =>
|
|
80
83
|
tree.find(x != null ? ex : 0, y != null ? ey : 0, maxDistance)
|
|
81
84
|
);
|
|
82
|
-
|
|
85
|
+
// also include other points that share the same x or y value
|
|
86
|
+
const otherPoints = trees.flatMap((tree, i) => {
|
|
87
|
+
return tree
|
|
88
|
+
.data()
|
|
89
|
+
.filter((d) => d !== points[i])
|
|
90
|
+
.filter(
|
|
91
|
+
(d) =>
|
|
92
|
+
(!isFinite(d[POINTER_X]) ||
|
|
93
|
+
Math.abs(d[POINTER_X] - points[i]?.[POINTER_X]) < tolerance) &&
|
|
94
|
+
(!isFinite(d[POINTER_Y]) ||
|
|
95
|
+
Math.abs(d[POINTER_Y] - points[i]?.[POINTER_Y]) < tolerance)
|
|
96
|
+
);
|
|
97
|
+
});
|
|
98
|
+
selectedData = [...points, ...otherPoints].filter((d) => d != null);
|
|
83
99
|
if (onupdate) onupdate(selectedData);
|
|
84
100
|
}
|
|
85
101
|
|
|
86
102
|
$effect(() => {
|
|
87
|
-
plot.body?.addEventListener('
|
|
88
|
-
plot.body?.addEventListener('
|
|
89
|
-
plot.body?.addEventListener('touchmove', onTouchMove);
|
|
103
|
+
plot.body?.addEventListener('pointermove', onPointerMove);
|
|
104
|
+
plot.body?.addEventListener('pointerleave', onPointerLeave);
|
|
90
105
|
|
|
91
106
|
return () => {
|
|
92
|
-
plot.body?.removeEventListener('
|
|
93
|
-
plot.body?.removeEventListener('
|
|
94
|
-
plot.body?.removeEventListener('touchmove', onTouchMove);
|
|
107
|
+
plot.body?.removeEventListener('pointermove', onPointerMove);
|
|
108
|
+
plot.body?.removeEventListener('pointerleave', onPointerLeave);
|
|
95
109
|
};
|
|
96
110
|
});
|
|
97
111
|
|
|
@@ -102,8 +116,8 @@
|
|
|
102
116
|
const trees = $derived(
|
|
103
117
|
groups.map(([, items]) =>
|
|
104
118
|
quadtree()
|
|
105
|
-
.x(x != null ? (d) => d
|
|
106
|
-
.y(y != null ? (d) => d
|
|
119
|
+
.x(x != null ? (d) => d[POINTER_X] : () => 0)
|
|
120
|
+
.y(y != null ? (d) => d[POINTER_Y] : () => 0)
|
|
107
121
|
.addAll(
|
|
108
122
|
items?.map((d) => {
|
|
109
123
|
const [px, py] = projectXY(
|
|
@@ -115,8 +129,8 @@
|
|
|
115
129
|
);
|
|
116
130
|
return {
|
|
117
131
|
...(isDataRecord(d) ? d : { [RAW_VALUE]: d }),
|
|
118
|
-
|
|
119
|
-
|
|
132
|
+
[POINTER_X]: px,
|
|
133
|
+
[POINTER_Y]: py
|
|
120
134
|
};
|
|
121
135
|
}) ?? []
|
|
122
136
|
)
|
|
@@ -13,6 +13,11 @@ declare class __sveltets_Render<Datum extends DataRow> {
|
|
|
13
13
|
* maximum cursor distance to select data points
|
|
14
14
|
*/
|
|
15
15
|
maxDistance?: number;
|
|
16
|
+
/**
|
|
17
|
+
* tolerance for considering points as "the same" when sharing x or y values
|
|
18
|
+
* defaults to 0 pixel
|
|
19
|
+
*/
|
|
20
|
+
tolerance?: number;
|
|
16
21
|
/**
|
|
17
22
|
* called whenever the selection changes
|
|
18
23
|
* @param data
|