@floegence/floe-webapp-core 0.86.19 → 0.86.21
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.
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type JSX } from 'solid-js';
|
|
2
|
+
import { type GraphMinimapProps } from './GraphMinimap';
|
|
2
3
|
import type { GraphLayout, GraphLayoutNode, GraphObjectEvent, GraphObjectRef, GraphViewport } from './types';
|
|
3
4
|
export interface GraphNodeRenderContext {
|
|
4
5
|
selected: () => boolean;
|
|
@@ -22,9 +23,7 @@ export interface GraphCanvasProps {
|
|
|
22
23
|
/** Overlay content should use shared SurfaceFloatingLayer for point placement. */
|
|
23
24
|
overlay?: JSX.Element;
|
|
24
25
|
/** Optional interactive overview. Its geometry follows the same visible layout. */
|
|
25
|
-
minimap?:
|
|
26
|
-
ariaLabel: string;
|
|
27
|
-
};
|
|
26
|
+
minimap?: Pick<GraphMinimapProps, 'ariaLabel' | 'nodeStyle'>;
|
|
28
27
|
class?: string;
|
|
29
28
|
}
|
|
30
29
|
/** Controlled graph surface. Hosts own documents and selection; Floe owns geometry and input. */
|
|
@@ -1,111 +1,111 @@
|
|
|
1
|
-
import { insert as
|
|
2
|
-
import { createUniqueId as
|
|
3
|
-
import { InfiniteCanvas as
|
|
4
|
-
import { resolveSurfaceInteractionTargetRole as
|
|
5
|
-
import { graphRelatedEdges as
|
|
6
|
-
import { GraphMinimap as
|
|
7
|
-
var
|
|
8
|
-
function
|
|
9
|
-
const
|
|
10
|
-
let
|
|
11
|
-
const [
|
|
1
|
+
import { insert as S, createComponent as w, setAttribute as c, effect as z, setStyleProperty as L, className as U, template as B, memo as it, use as ot, delegateEvents as lt } from "solid-js/web";
|
|
2
|
+
import { createUniqueId as dt, createSignal as J, createMemo as x, onMount as ct, For as Y, Show as _, onCleanup as ut } from "solid-js";
|
|
3
|
+
import { InfiniteCanvas as st } from "../ui/InfiniteCanvas.js";
|
|
4
|
+
import { resolveSurfaceInteractionTargetRole as ht } from "../ui/localInteractionSurface.js";
|
|
5
|
+
import { graphRelatedEdges as Q, graphEdgeLabelPosition as gt, graphSectionPath as vt } from "./geometry.js";
|
|
6
|
+
import { GraphMinimap as ft } from "./GraphMinimap.js";
|
|
7
|
+
var mt = /* @__PURE__ */ B("<div data-floe-canvas-pan-surface=true role=group tabindex=0>"), bt = /* @__PURE__ */ B("<div role=region tabindex=0>"), xt = /* @__PURE__ */ B('<svg class=floe-graph__edges><defs><marker viewBox="0 0 8 8"refX=7 refY=4 markerWidth=7 markerHeight=7 orient=auto-start-reverse><path d="M1 1 L7 4 L1 7"fill=none stroke=context-stroke stroke-width=1.2>'), yt = /* @__PURE__ */ B("<svg><g><path class=floe-graph__edge></path><path class=floe-graph__edge-target tabindex=0 role=button data-floe-canvas-pan-surface=true></svg>", !1, !0, !1), kt = /* @__PURE__ */ B("<svg><text class=floe-graph__edge-label aria-hidden=true></svg>", !1, !0, !1);
|
|
8
|
+
function Lt(i) {
|
|
9
|
+
const N = `graph-arrow-${dt()}`;
|
|
10
|
+
let v;
|
|
11
|
+
const [Z, p] = J(null), [V, tt] = J({
|
|
12
12
|
width: 0,
|
|
13
13
|
height: 0
|
|
14
|
-
}),
|
|
15
|
-
const
|
|
16
|
-
return
|
|
17
|
-
}),
|
|
18
|
-
const
|
|
19
|
-
if (
|
|
20
|
-
const
|
|
21
|
-
return
|
|
22
|
-
}),
|
|
14
|
+
}), F = () => i.highlight === void 0 ? Z() : i.highlight, et = x(() => new Map(i.layout.nodes.map((t) => [t.id, t]))), nt = x(() => {
|
|
15
|
+
const t = F();
|
|
16
|
+
return t?.kind === "node" ? Q(i.layout, t.id) : new Set(t ? [t.id] : []);
|
|
17
|
+
}), W = x(() => {
|
|
18
|
+
const t = F();
|
|
19
|
+
if (t?.kind !== "node") return null;
|
|
20
|
+
const e = et().get(t.id), r = e?.kind === "group" ? e.id : e?.parentId;
|
|
21
|
+
return r ? Q(i.layout, r) : null;
|
|
22
|
+
}), j = (t) => i.selected?.kind === t.kind && i.selected.id === t.id, D = (t) => {
|
|
23
23
|
const {
|
|
24
|
-
width:
|
|
25
|
-
height:
|
|
24
|
+
width: e,
|
|
25
|
+
height: r
|
|
26
26
|
} = V();
|
|
27
|
-
if (!
|
|
28
|
-
const
|
|
27
|
+
if (!e || !r) return;
|
|
28
|
+
const n = -t.x / t.scale - 240, a = -t.y / t.scale - 240, y = n + e / t.scale + 480, s = a + r / t.scale + 480;
|
|
29
29
|
return {
|
|
30
|
-
left:
|
|
30
|
+
left: n,
|
|
31
31
|
top: a,
|
|
32
|
-
right:
|
|
33
|
-
bottom:
|
|
32
|
+
right: y,
|
|
33
|
+
bottom: s
|
|
34
34
|
};
|
|
35
|
-
},
|
|
36
|
-
const
|
|
37
|
-
if (!
|
|
35
|
+
}, rt = (t) => {
|
|
36
|
+
const e = D(t);
|
|
37
|
+
if (!e) return i.layout.nodes;
|
|
38
38
|
const {
|
|
39
|
-
left:
|
|
40
|
-
top:
|
|
39
|
+
left: r,
|
|
40
|
+
top: n,
|
|
41
41
|
right: a,
|
|
42
|
-
bottom:
|
|
43
|
-
} =
|
|
44
|
-
return i.layout.nodes.filter((
|
|
45
|
-
},
|
|
46
|
-
edge:
|
|
47
|
-
path:
|
|
48
|
-
label:
|
|
49
|
-
segments:
|
|
50
|
-
left: Math.min(
|
|
51
|
-
right: Math.max(
|
|
52
|
-
top: Math.min(
|
|
53
|
-
bottom: Math.max(
|
|
42
|
+
bottom: y
|
|
43
|
+
} = e;
|
|
44
|
+
return i.layout.nodes.filter((s) => s.id === i.selected?.id || s.x + s.width >= r && s.x <= a && s.y + s.height >= n && s.y <= y);
|
|
45
|
+
}, q = x(() => i.layout.edges.map((t) => ({
|
|
46
|
+
edge: t,
|
|
47
|
+
path: t.sections.map(vt).join(" "),
|
|
48
|
+
label: gt(t.sections),
|
|
49
|
+
segments: t.sections.flatMap((e) => e.slice(1).map((r, n) => ({
|
|
50
|
+
left: Math.min(r.x, e[n].x),
|
|
51
|
+
right: Math.max(r.x, e[n].x),
|
|
52
|
+
top: Math.min(r.y, e[n].y),
|
|
53
|
+
bottom: Math.max(r.y, e[n].y)
|
|
54
54
|
})))
|
|
55
|
-
}))),
|
|
56
|
-
const
|
|
57
|
-
return
|
|
58
|
-
edge:
|
|
59
|
-
segments:
|
|
60
|
-
}) =>
|
|
61
|
-
}, f = (
|
|
62
|
-
|
|
63
|
-
},
|
|
64
|
-
const
|
|
55
|
+
}))), at = (t) => {
|
|
56
|
+
const e = D(t);
|
|
57
|
+
return e ? q().filter(({
|
|
58
|
+
edge: r,
|
|
59
|
+
segments: n
|
|
60
|
+
}) => r.id === i.selected?.id || n.some((a) => a.left <= e.right && a.right >= e.left && a.top <= e.bottom && a.bottom >= e.top)) : q();
|
|
61
|
+
}, f = (t) => {
|
|
62
|
+
p(t), i.onHover?.(t);
|
|
63
|
+
}, $ = (t, e, r) => {
|
|
64
|
+
const n = e.getBoundingClientRect();
|
|
65
65
|
return {
|
|
66
|
-
object:
|
|
67
|
-
owner:
|
|
68
|
-
position:
|
|
69
|
-
x:
|
|
70
|
-
y:
|
|
66
|
+
object: t,
|
|
67
|
+
owner: e,
|
|
68
|
+
position: r && (r.clientX !== 0 || r.clientY !== 0) ? {
|
|
69
|
+
x: r.clientX,
|
|
70
|
+
y: r.clientY
|
|
71
71
|
} : {
|
|
72
|
-
x:
|
|
73
|
-
y:
|
|
72
|
+
x: n.left + n.width / 2,
|
|
73
|
+
y: n.top + Math.min(32, n.height / 2)
|
|
74
74
|
}
|
|
75
75
|
};
|
|
76
|
-
},
|
|
77
|
-
i.onContextMenu && (
|
|
78
|
-
},
|
|
79
|
-
|
|
76
|
+
}, H = (t, e) => {
|
|
77
|
+
i.onContextMenu && (e.preventDefault(), e.stopPropagation(), i.onContextMenu($(t, e.currentTarget, e)));
|
|
78
|
+
}, X = (t, e) => {
|
|
79
|
+
e.target === e.currentTarget && (e.key === "ContextMenu" || e.shiftKey && e.key === "F10" ? (e.preventDefault(), e.stopPropagation(), i.onContextMenu?.($(t, e.currentTarget))) : e.key === "Enter" ? (e.preventDefault(), e.stopPropagation(), i.onActivate?.($(t, e.currentTarget))) : e.key === "Escape" && (f(null), i.onActivate?.($(null, v))));
|
|
80
80
|
};
|
|
81
|
-
|
|
82
|
-
if (!
|
|
83
|
-
const
|
|
84
|
-
width:
|
|
85
|
-
height:
|
|
81
|
+
ct(() => {
|
|
82
|
+
if (!v) return;
|
|
83
|
+
const t = () => tt({
|
|
84
|
+
width: v.clientWidth,
|
|
85
|
+
height: v.clientHeight
|
|
86
86
|
});
|
|
87
|
-
|
|
88
|
-
const
|
|
89
|
-
|
|
87
|
+
t();
|
|
88
|
+
const e = new ResizeObserver(t);
|
|
89
|
+
e.observe(v), ut(() => e.disconnect());
|
|
90
90
|
});
|
|
91
|
-
const
|
|
92
|
-
const
|
|
91
|
+
const K = (t) => {
|
|
92
|
+
const e = {
|
|
93
93
|
kind: "node",
|
|
94
|
-
id:
|
|
95
|
-
},
|
|
96
|
-
selected: () =>
|
|
97
|
-
openMenu: (
|
|
94
|
+
id: t.id
|
|
95
|
+
}, r = {
|
|
96
|
+
selected: () => j(e),
|
|
97
|
+
openMenu: (n) => H(e, n)
|
|
98
98
|
};
|
|
99
99
|
return (() => {
|
|
100
|
-
var
|
|
101
|
-
return
|
|
102
|
-
a.target.closest("button, a, input, select, textarea, [data-floe-canvas-interactive]") || (a.stopPropagation(), i.onActivate?.(
|
|
103
|
-
},
|
|
104
|
-
var a =
|
|
105
|
-
return () => a() ? i.renderGroup(
|
|
106
|
-
})()),
|
|
107
|
-
var
|
|
108
|
-
return
|
|
100
|
+
var n = mt();
|
|
101
|
+
return n.$$keydown = (a) => X(e, a), n.$$contextmenu = (a) => H(e, a), n.$$click = (a) => {
|
|
102
|
+
a.target.closest("button, a, input, select, textarea, [data-floe-canvas-interactive]") || (a.stopPropagation(), i.onActivate?.($(e, a.currentTarget, a)));
|
|
103
|
+
}, n.$$focusout = () => f(null), n.$$focusin = () => f(e), n.addEventListener("pointerleave", () => f(null)), n.addEventListener("pointerenter", () => f(e)), S(n, (() => {
|
|
104
|
+
var a = it(() => !!(t.kind === "group" && i.renderGroup));
|
|
105
|
+
return () => a() ? i.renderGroup(t, r) : i.renderNode(t, r);
|
|
106
|
+
})()), z((a) => {
|
|
107
|
+
var y = t.kind === "group" ? "floe-graph__group" : "floe-graph__node", s = t.id, G = j(e), d = `${t.x}px`, C = `${t.y}px`, M = `${t.width}px`, l = `${t.height}px`, u = t.label;
|
|
108
|
+
return y !== a.e && U(n, a.e = y), s !== a.t && c(n, "data-graph-object", a.t = s), G !== a.a && c(n, "data-selected", a.a = G), d !== a.o && L(n, "left", a.o = d), C !== a.i && L(n, "top", a.i = C), M !== a.n && L(n, "width", a.n = M), l !== a.s && L(n, "height", a.s = l), u !== a.h && c(n, "aria-label", a.h = u), a;
|
|
109
109
|
}, {
|
|
110
110
|
e: void 0,
|
|
111
111
|
t: void 0,
|
|
@@ -115,14 +115,14 @@ function Le(i) {
|
|
|
115
115
|
n: void 0,
|
|
116
116
|
s: void 0,
|
|
117
117
|
h: void 0
|
|
118
|
-
}),
|
|
118
|
+
}), n;
|
|
119
119
|
})();
|
|
120
120
|
};
|
|
121
121
|
return (() => {
|
|
122
|
-
var
|
|
123
|
-
|
|
124
|
-
var
|
|
125
|
-
return typeof
|
|
122
|
+
var t = bt();
|
|
123
|
+
t.$$keydown = (r) => X(null, r);
|
|
124
|
+
var e = v;
|
|
125
|
+
return typeof e == "function" ? ot(e, t) : v = t, S(t, w(st, {
|
|
126
126
|
get viewport() {
|
|
127
127
|
return i.viewport;
|
|
128
128
|
},
|
|
@@ -135,64 +135,79 @@ function Le(i) {
|
|
|
135
135
|
return i.onViewportChange;
|
|
136
136
|
},
|
|
137
137
|
onViewportInteractionStart: () => i.onInteractionStart?.(),
|
|
138
|
-
resolveTargetRole: (
|
|
139
|
-
onCanvasPointerDown: (
|
|
140
|
-
!(
|
|
138
|
+
resolveTargetRole: (r) => (r.target instanceof Element ? r.target : null)?.closest('button, a, input, select, textarea, [data-floe-canvas-interactive="true"]') ? "local_surface" : ht(r),
|
|
139
|
+
onCanvasPointerDown: (r) => {
|
|
140
|
+
!(r.target instanceof Element) || r.target.closest("[data-graph-object]") || i.onActivate?.($(null, v, r));
|
|
141
141
|
},
|
|
142
|
-
onCanvasContextMenu: (
|
|
142
|
+
onCanvasContextMenu: (r) => i.onContextMenu?.({
|
|
143
143
|
object: null,
|
|
144
|
-
owner:
|
|
144
|
+
owner: v,
|
|
145
145
|
position: {
|
|
146
|
-
x:
|
|
147
|
-
y:
|
|
146
|
+
x: r.clientX,
|
|
147
|
+
y: r.clientY
|
|
148
148
|
}
|
|
149
149
|
}),
|
|
150
|
-
children: (
|
|
151
|
-
const
|
|
152
|
-
|
|
150
|
+
children: (r) => {
|
|
151
|
+
const n = x(() => {
|
|
152
|
+
const d = D(r());
|
|
153
|
+
if (d) return d;
|
|
154
|
+
const {
|
|
155
|
+
x: C,
|
|
156
|
+
y: M,
|
|
157
|
+
width: l,
|
|
158
|
+
height: u
|
|
159
|
+
} = i.layout.bounds;
|
|
160
|
+
return {
|
|
161
|
+
left: C - 240,
|
|
162
|
+
top: M - 240,
|
|
163
|
+
right: C + l + 240,
|
|
164
|
+
bottom: M + u + 240
|
|
165
|
+
};
|
|
166
|
+
}), a = x(() => rt(r())), y = x(() => a().filter((d) => d.kind === "group")), s = x(() => a().filter((d) => d.kind !== "group")), G = x(() => at(r()));
|
|
167
|
+
return [w(Y, {
|
|
153
168
|
get each() {
|
|
154
|
-
return
|
|
169
|
+
return y();
|
|
155
170
|
},
|
|
156
|
-
children:
|
|
171
|
+
children: K
|
|
157
172
|
}), (() => {
|
|
158
|
-
var d =
|
|
159
|
-
return
|
|
173
|
+
var d = xt(), C = d.firstChild, M = C.firstChild;
|
|
174
|
+
return c(M, "id", N), S(d, w(Y, {
|
|
160
175
|
get each() {
|
|
161
|
-
return
|
|
176
|
+
return G();
|
|
162
177
|
},
|
|
163
|
-
children: (
|
|
178
|
+
children: (l) => {
|
|
164
179
|
const {
|
|
165
180
|
edge: u
|
|
166
|
-
} =
|
|
181
|
+
} = l, m = {
|
|
167
182
|
kind: "edge",
|
|
168
183
|
id: u.id
|
|
169
|
-
},
|
|
170
|
-
return
|
|
184
|
+
}, I = () => l.path, R = () => l.label;
|
|
185
|
+
return w(_, {
|
|
171
186
|
get when() {
|
|
172
|
-
return !
|
|
187
|
+
return !W() || W().has(u.id);
|
|
173
188
|
},
|
|
174
189
|
get children() {
|
|
175
|
-
var
|
|
176
|
-
return
|
|
190
|
+
var b = yt(), E = b.firstChild, h = E.nextSibling;
|
|
191
|
+
return c(E, "marker-end", `url(#${N})`), S(b, w(_, {
|
|
177
192
|
get when() {
|
|
178
|
-
return
|
|
193
|
+
return R();
|
|
179
194
|
},
|
|
180
195
|
children: (o) => (() => {
|
|
181
|
-
var
|
|
182
|
-
return
|
|
183
|
-
var
|
|
184
|
-
return
|
|
196
|
+
var k = kt();
|
|
197
|
+
return S(k, () => u.label), z((g) => {
|
|
198
|
+
var P = /^\d+$/.test(u.label), T = o().x, A = o().y - 6;
|
|
199
|
+
return P !== g.e && c(k, "data-count", g.e = P), T !== g.t && c(k, "x", g.t = T), A !== g.a && c(k, "y", g.a = A), g;
|
|
185
200
|
}, {
|
|
186
201
|
e: void 0,
|
|
187
202
|
t: void 0,
|
|
188
203
|
a: void 0
|
|
189
|
-
}),
|
|
204
|
+
}), k;
|
|
190
205
|
})()
|
|
191
|
-
}),
|
|
192
|
-
o.stopPropagation(), i.onActivate?.(
|
|
193
|
-
},
|
|
194
|
-
var
|
|
195
|
-
return
|
|
206
|
+
}), h), h.$$keydown = (o) => X(m, o), h.$$contextmenu = (o) => H(m, o), h.$$click = (o) => {
|
|
207
|
+
o.stopPropagation(), i.onActivate?.($(m, o.currentTarget, o));
|
|
208
|
+
}, h.addEventListener("blur", () => f(null)), h.addEventListener("focus", () => f(m)), h.addEventListener("pointerleave", () => f(null)), h.addEventListener("pointerenter", () => f(m)), z((o) => {
|
|
209
|
+
var k = u.id, g = nt().has(u.id), P = j(m), T = I(), A = I(), O = u.label;
|
|
210
|
+
return k !== o.e && c(b, "data-graph-object", o.e = k), g !== o.t && c(b, "data-emphasized", o.t = g), P !== o.a && c(b, "data-selected", o.a = P), T !== o.o && c(E, "d", o.o = T), A !== o.i && c(h, "d", o.i = A), O !== o.n && c(h, "aria-label", o.n = O), o;
|
|
196
211
|
}, {
|
|
197
212
|
e: void 0,
|
|
198
213
|
t: void 0,
|
|
@@ -200,23 +215,33 @@ function Le(i) {
|
|
|
200
215
|
o: void 0,
|
|
201
216
|
i: void 0,
|
|
202
217
|
n: void 0
|
|
203
|
-
}),
|
|
218
|
+
}), b;
|
|
204
219
|
}
|
|
205
220
|
});
|
|
206
221
|
}
|
|
207
|
-
}), null),
|
|
208
|
-
|
|
222
|
+
}), null), z((l) => {
|
|
223
|
+
var u = n().right - n().left, m = n().bottom - n().top, I = `${n().left} ${n().top} ${n().right - n().left} ${n().bottom - n().top}`, R = `${n().left}px`, b = `${n().top}px`, E = i.ariaLabel;
|
|
224
|
+
return u !== l.e && c(d, "width", l.e = u), m !== l.t && c(d, "height", l.t = m), I !== l.a && c(d, "viewBox", l.a = I), R !== l.o && L(d, "left", l.o = R), b !== l.i && L(d, "top", l.i = b), E !== l.n && c(d, "aria-label", l.n = E), l;
|
|
225
|
+
}, {
|
|
226
|
+
e: void 0,
|
|
227
|
+
t: void 0,
|
|
228
|
+
a: void 0,
|
|
229
|
+
o: void 0,
|
|
230
|
+
i: void 0,
|
|
231
|
+
n: void 0
|
|
232
|
+
}), d;
|
|
233
|
+
})(), w(Y, {
|
|
209
234
|
get each() {
|
|
210
|
-
return
|
|
235
|
+
return s();
|
|
211
236
|
},
|
|
212
|
-
children:
|
|
237
|
+
children: K
|
|
213
238
|
})];
|
|
214
239
|
}
|
|
215
|
-
}), null),
|
|
240
|
+
}), null), S(t, w(_, {
|
|
216
241
|
get when() {
|
|
217
242
|
return i.minimap;
|
|
218
243
|
},
|
|
219
|
-
children: (
|
|
244
|
+
children: (r) => w(ft, {
|
|
220
245
|
get layout() {
|
|
221
246
|
return i.layout;
|
|
222
247
|
},
|
|
@@ -233,19 +258,22 @@ function Le(i) {
|
|
|
233
258
|
return i.onInteractionStart;
|
|
234
259
|
},
|
|
235
260
|
get ariaLabel() {
|
|
236
|
-
return
|
|
261
|
+
return r().ariaLabel;
|
|
262
|
+
},
|
|
263
|
+
get nodeStyle() {
|
|
264
|
+
return r().nodeStyle;
|
|
237
265
|
}
|
|
238
266
|
})
|
|
239
|
-
}), null),
|
|
240
|
-
var
|
|
241
|
-
return
|
|
267
|
+
}), null), S(t, () => i.overlay, null), z((r) => {
|
|
268
|
+
var n = `floe-graph ${i.class ?? ""}`, a = i.ariaLabel;
|
|
269
|
+
return n !== r.e && U(t, r.e = n), a !== r.t && c(t, "aria-label", r.t = a), r;
|
|
242
270
|
}, {
|
|
243
271
|
e: void 0,
|
|
244
272
|
t: void 0
|
|
245
|
-
}),
|
|
273
|
+
}), t;
|
|
246
274
|
})();
|
|
247
275
|
}
|
|
248
|
-
|
|
276
|
+
lt(["focusin", "focusout", "click", "contextmenu", "keydown"]);
|
|
249
277
|
export {
|
|
250
|
-
|
|
278
|
+
Lt as GraphCanvas
|
|
251
279
|
};
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import type { GraphLayout, GraphViewport } from './types';
|
|
1
|
+
import type { GraphLayout, GraphLayoutNode, GraphViewport } from './types';
|
|
2
|
+
export interface GraphMinimapNodeStyle {
|
|
3
|
+
/** CSS colors, including theme variables. Omitted colors retain the defaults. */
|
|
4
|
+
fill?: string;
|
|
5
|
+
stroke?: string;
|
|
6
|
+
}
|
|
2
7
|
export interface GraphMinimapProps {
|
|
3
8
|
layout: GraphLayout;
|
|
4
9
|
viewport: GraphViewport;
|
|
@@ -9,6 +14,8 @@ export interface GraphMinimapProps {
|
|
|
9
14
|
onViewportChange: (viewport: GraphViewport) => void;
|
|
10
15
|
onInteractionStart?: () => void;
|
|
11
16
|
ariaLabel: string;
|
|
17
|
+
/** Host appearance over the exact layout. Equal styles share one SVG path. */
|
|
18
|
+
nodeStyle?: (node: GraphLayoutNode) => GraphMinimapNodeStyle | undefined;
|
|
12
19
|
}
|
|
13
20
|
/** A lightweight navigator over the exact main-canvas geometry. No second layout. */
|
|
14
21
|
export declare function GraphMinimap(props: GraphMinimapProps): import("solid-js").JSX.Element;
|
|
@@ -1,74 +1,127 @@
|
|
|
1
|
-
import { effect as
|
|
2
|
-
import { createMemo as w } from "solid-js";
|
|
3
|
-
import { graphSectionPath as
|
|
4
|
-
var
|
|
5
|
-
function R(
|
|
6
|
-
let
|
|
7
|
-
const
|
|
1
|
+
import { insert as k, createComponent as _, effect as g, setAttribute as h, style as C, template as f, use as S, delegateEvents as z } from "solid-js/web";
|
|
2
|
+
import { createMemo as w, For as I } from "solid-js";
|
|
3
|
+
import { graphSectionPath as A, fitGraphViewport as D } from "./geometry.js";
|
|
4
|
+
var L = /* @__PURE__ */ f('<svg class=floe-graph__minimap role=region tabindex=0 preserveAspectRatio="xMidYMid meet"data-floe-canvas-interactive=true><path class=floe-graph__minimap-edges></path><rect class=floe-graph__minimap-viewport>'), V = /* @__PURE__ */ f("<svg><path class=floe-graph__minimap-groups></svg>", !1, !0, !1), E = /* @__PURE__ */ f("<svg><path class=floe-graph__minimap-nodes></svg>", !1, !0, !1);
|
|
5
|
+
function R(i) {
|
|
6
|
+
let v, s;
|
|
7
|
+
const u = w(() => {
|
|
8
8
|
const {
|
|
9
|
-
x:
|
|
10
|
-
y:
|
|
11
|
-
width:
|
|
12
|
-
height:
|
|
13
|
-
} =
|
|
9
|
+
x: e,
|
|
10
|
+
y: l,
|
|
11
|
+
width: d,
|
|
12
|
+
height: n
|
|
13
|
+
} = i.layout.bounds, t = Math.max(d, n, 1) * 0.04;
|
|
14
14
|
return {
|
|
15
|
-
x:
|
|
16
|
-
y:
|
|
17
|
-
width:
|
|
18
|
-
height:
|
|
15
|
+
x: e - t,
|
|
16
|
+
y: l - t,
|
|
17
|
+
width: d + t * 2,
|
|
18
|
+
height: n + t * 2
|
|
19
19
|
};
|
|
20
|
-
}),
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
}), m = w(() => {
|
|
21
|
+
const e = /* @__PURE__ */ new Map(), l = /* @__PURE__ */ new Map();
|
|
22
|
+
for (const n of i.layout.nodes) {
|
|
23
|
+
const t = i.nodeStyle?.(n) ?? {}, o = n.kind === "group" ? e : l, r = JSON.stringify([t.fill ?? null, t.stroke ?? null]);
|
|
24
|
+
let a = o.get(r);
|
|
25
|
+
a || (a = {
|
|
26
|
+
...t,
|
|
27
|
+
paths: []
|
|
28
|
+
}, o.set(r, a)), a.paths.push(`M${n.x},${n.y}h${n.width}v${n.height}h${-n.width}Z`);
|
|
29
|
+
}
|
|
30
|
+
const d = (n) => [...n.values()].map(({
|
|
31
|
+
paths: t,
|
|
32
|
+
...o
|
|
33
|
+
}) => ({
|
|
34
|
+
...o,
|
|
35
|
+
path: t.join("")
|
|
36
|
+
}));
|
|
37
|
+
return {
|
|
38
|
+
groups: d(e),
|
|
39
|
+
nodes: d(l)
|
|
40
|
+
};
|
|
41
|
+
}), p = (e) => ({
|
|
42
|
+
"--graph-minimap-fill": e.fill,
|
|
43
|
+
"--graph-minimap-stroke": e.stroke,
|
|
44
|
+
"--graph-minimap-opacity": e.fill || e.stroke ? "1" : void 0
|
|
45
|
+
}), P = w(() => i.layout.edges.flatMap((e) => e.sections.map(A)).join(" ")), y = (e) => {
|
|
46
|
+
const l = v?.getScreenCTM();
|
|
47
|
+
return l ? new DOMPoint(e.clientX, e.clientY).matrixTransform(l.inverse()) : void 0;
|
|
48
|
+
}, x = (e) => {
|
|
49
|
+
s?.id === e.pointerId && (s = void 0, v?.hasPointerCapture(e.pointerId) && v.releasePointerCapture(e.pointerId));
|
|
25
50
|
};
|
|
26
51
|
return (() => {
|
|
27
|
-
var
|
|
28
|
-
|
|
29
|
-
const
|
|
52
|
+
var e = L(), l = e.firstChild, d = l.nextSibling;
|
|
53
|
+
e.$$keydown = (t) => {
|
|
54
|
+
const r = {
|
|
30
55
|
ArrowLeft: [1, 0],
|
|
31
56
|
ArrowRight: [-1, 0],
|
|
32
57
|
ArrowUp: [0, 1],
|
|
33
58
|
ArrowDown: [0, -1]
|
|
34
59
|
}[t.key];
|
|
35
|
-
!
|
|
36
|
-
...
|
|
37
|
-
x:
|
|
38
|
-
y:
|
|
39
|
-
} :
|
|
40
|
-
},
|
|
41
|
-
|
|
42
|
-
}),
|
|
43
|
-
if (
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
...
|
|
47
|
-
x:
|
|
48
|
-
y:
|
|
60
|
+
!r && t.key !== "Home" || (t.preventDefault(), t.stopPropagation(), i.onInteractionStart?.(), i.onViewportChange(r ? {
|
|
61
|
+
...i.viewport,
|
|
62
|
+
x: i.viewport.x + r[0] * i.size.width * 0.2,
|
|
63
|
+
y: i.viewport.y + r[1] * i.size.height * 0.2
|
|
64
|
+
} : D(i.layout.bounds, i.size, 24)));
|
|
65
|
+
}, e.addEventListener("lostpointercapture", () => {
|
|
66
|
+
s = void 0;
|
|
67
|
+
}), e.addEventListener("pointercancel", x), e.$$pointerup = x, e.$$pointermove = (t) => {
|
|
68
|
+
if (s?.id !== t.pointerId) return;
|
|
69
|
+
const o = y(t);
|
|
70
|
+
o && i.onViewportChange({
|
|
71
|
+
...s.viewport,
|
|
72
|
+
x: s.viewport.x - (o.x - s.x) * s.viewport.scale,
|
|
73
|
+
y: s.viewport.y - (o.y - s.y) * s.viewport.scale
|
|
49
74
|
});
|
|
50
|
-
},
|
|
75
|
+
}, e.$$pointerdown = (t) => {
|
|
51
76
|
if (t.button !== 0) return;
|
|
52
|
-
const
|
|
53
|
-
|
|
77
|
+
const o = y(t);
|
|
78
|
+
o && (t.preventDefault(), t.stopPropagation(), i.onInteractionStart?.(), v.focus({
|
|
54
79
|
preventScroll: !0
|
|
55
|
-
}), t.target.matches(".floe-graph__minimap-viewport") ||
|
|
56
|
-
...
|
|
57
|
-
x:
|
|
58
|
-
y:
|
|
59
|
-
}),
|
|
80
|
+
}), t.target.matches(".floe-graph__minimap-viewport") || i.onViewportChange({
|
|
81
|
+
...i.viewport,
|
|
82
|
+
x: i.size.width / 2 - o.x * i.viewport.scale,
|
|
83
|
+
y: i.size.height / 2 - o.y * i.viewport.scale
|
|
84
|
+
}), s = {
|
|
60
85
|
id: t.pointerId,
|
|
61
|
-
x:
|
|
62
|
-
y:
|
|
86
|
+
x: o.x,
|
|
87
|
+
y: o.y,
|
|
63
88
|
viewport: {
|
|
64
|
-
...
|
|
89
|
+
...i.viewport
|
|
65
90
|
}
|
|
66
|
-
},
|
|
91
|
+
}, v.setPointerCapture(t.pointerId));
|
|
67
92
|
};
|
|
68
|
-
var
|
|
69
|
-
return typeof
|
|
70
|
-
|
|
71
|
-
|
|
93
|
+
var n = v;
|
|
94
|
+
return typeof n == "function" ? S(n, e) : v = e, k(e, _(I, {
|
|
95
|
+
get each() {
|
|
96
|
+
return m().groups;
|
|
97
|
+
},
|
|
98
|
+
children: (t) => (() => {
|
|
99
|
+
var o = V();
|
|
100
|
+
return g((r) => {
|
|
101
|
+
var a = t.path, c = p(t);
|
|
102
|
+
return a !== r.e && h(o, "d", r.e = a), r.t = C(o, c, r.t), r;
|
|
103
|
+
}, {
|
|
104
|
+
e: void 0,
|
|
105
|
+
t: void 0
|
|
106
|
+
}), o;
|
|
107
|
+
})()
|
|
108
|
+
}), l), k(e, _(I, {
|
|
109
|
+
get each() {
|
|
110
|
+
return m().nodes;
|
|
111
|
+
},
|
|
112
|
+
children: (t) => (() => {
|
|
113
|
+
var o = E();
|
|
114
|
+
return g((r) => {
|
|
115
|
+
var a = t.path, c = p(t);
|
|
116
|
+
return a !== r.e && h(o, "d", r.e = a), r.t = C(o, c, r.t), r;
|
|
117
|
+
}, {
|
|
118
|
+
e: void 0,
|
|
119
|
+
t: void 0
|
|
120
|
+
}), o;
|
|
121
|
+
})()
|
|
122
|
+
}), d), g((t) => {
|
|
123
|
+
var o = i.ariaLabel, r = `${u().x} ${u().y} ${u().width} ${u().height}`, a = P(), c = -i.viewport.x / i.viewport.scale, $ = -i.viewport.y / i.viewport.scale, b = i.size.width / i.viewport.scale, M = i.size.height / i.viewport.scale;
|
|
124
|
+
return o !== t.e && h(e, "aria-label", t.e = o), r !== t.t && h(e, "viewBox", t.t = r), a !== t.a && h(l, "d", t.a = a), c !== t.o && h(d, "x", t.o = c), $ !== t.i && h(d, "y", t.i = $), b !== t.n && h(d, "width", t.n = b), M !== t.s && h(d, "height", t.s = M), t;
|
|
72
125
|
}, {
|
|
73
126
|
e: void 0,
|
|
74
127
|
t: void 0,
|
|
@@ -76,13 +129,11 @@ function R(e) {
|
|
|
76
129
|
o: void 0,
|
|
77
130
|
i: void 0,
|
|
78
131
|
n: void 0,
|
|
79
|
-
s: void 0
|
|
80
|
-
|
|
81
|
-
r: void 0
|
|
82
|
-
}), i;
|
|
132
|
+
s: void 0
|
|
133
|
+
}), e;
|
|
83
134
|
})();
|
|
84
135
|
}
|
|
85
|
-
|
|
136
|
+
z(["pointerdown", "pointermove", "pointerup", "keydown"]);
|
|
86
137
|
export {
|
|
87
138
|
R as GraphMinimap
|
|
88
139
|
};
|
package/dist/graph.css
CHANGED
|
@@ -37,8 +37,9 @@
|
|
|
37
37
|
}
|
|
38
38
|
.floe-graph__edges {
|
|
39
39
|
position: absolute;
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
/* A visible route may have endpoints far outside the viewport. Clip painting,
|
|
41
|
+
* not geometry, so its full world bounds do not expand the composited layer. */
|
|
42
|
+
overflow: hidden;
|
|
42
43
|
z-index: 1;
|
|
43
44
|
pointer-events: none;
|
|
44
45
|
}
|
|
@@ -90,13 +91,16 @@
|
|
|
90
91
|
outline-offset: 2px;
|
|
91
92
|
}
|
|
92
93
|
.floe-graph__minimap-nodes {
|
|
93
|
-
fill: var(--muted-foreground);
|
|
94
|
-
|
|
94
|
+
fill: var(--graph-minimap-fill, var(--muted-foreground));
|
|
95
|
+
stroke: var(--graph-minimap-stroke, none);
|
|
96
|
+
stroke-width: 0.7px;
|
|
97
|
+
vector-effect: non-scaling-stroke;
|
|
98
|
+
opacity: var(--graph-minimap-opacity, 0.65);
|
|
95
99
|
pointer-events: none;
|
|
96
100
|
}
|
|
97
101
|
.floe-graph__minimap-groups {
|
|
98
|
-
fill: color-mix(in srgb, var(--primary) 10%, transparent);
|
|
99
|
-
stroke: var(--muted-foreground);
|
|
102
|
+
fill: var(--graph-minimap-fill, color-mix(in srgb, var(--primary) 10%, transparent));
|
|
103
|
+
stroke: var(--graph-minimap-stroke, var(--muted-foreground));
|
|
100
104
|
stroke-width: 0.7px;
|
|
101
105
|
vector-effect: non-scaling-stroke;
|
|
102
106
|
pointer-events: none;
|
|
@@ -147,6 +151,11 @@
|
|
|
147
151
|
}
|
|
148
152
|
.floe-graph__minimap-nodes {
|
|
149
153
|
fill: CanvasText;
|
|
154
|
+
stroke: CanvasText;
|
|
155
|
+
opacity: 1;
|
|
156
|
+
}
|
|
157
|
+
.floe-graph__minimap-groups {
|
|
158
|
+
fill: Canvas;
|
|
150
159
|
}
|
|
151
160
|
.floe-graph__minimap-groups,
|
|
152
161
|
.floe-graph__minimap-edges {
|