@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 w, createComponent as x, setAttribute as l, effect as S, className as K, template as I, memo as ae, setStyleProperty as T, use as ie, delegateEvents as oe } from "solid-js/web";
2
- import { createUniqueId as le, createSignal as O, createMemo as y, onMount as ce, For as j, Show as D, onCleanup as de } from "solid-js";
3
- import { InfiniteCanvas as ue } from "../ui/InfiniteCanvas.js";
4
- import { resolveSurfaceInteractionTargetRole as se } from "../ui/localInteractionSurface.js";
5
- import { graphRelatedEdges as U, graphEdgeLabelPosition as he, graphSectionPath as ge } from "./geometry.js";
6
- import { GraphMinimap as fe } from "./GraphMinimap.js";
7
- var ve = /* @__PURE__ */ I("<div data-floe-canvas-pan-surface=true role=group tabindex=0>"), me = /* @__PURE__ */ I("<div role=region tabindex=0>"), be = /* @__PURE__ */ I('<svg class=floe-graph__edges width=1 height=1><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>'), xe = /* @__PURE__ */ I("<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), ye = /* @__PURE__ */ I("<svg><text class=floe-graph__edge-label aria-hidden=true></svg>", !1, !0, !1);
8
- function Le(i) {
9
- const H = `graph-arrow-${le()}`;
10
- let g;
11
- const [J, Q] = O(null), [V, Z] = O({
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
- }), X = () => i.highlight === void 0 ? J() : i.highlight, p = y(() => new Map(i.layout.nodes.map((e) => [e.id, e]))), ee = y(() => {
15
- const e = X();
16
- return e?.kind === "node" ? U(i.layout, e.id) : new Set(e ? [e.id] : []);
17
- }), Y = y(() => {
18
- const e = X();
19
- if (e?.kind !== "node") return null;
20
- const t = p().get(e.id), n = t?.kind === "group" ? t.id : t?.parentId;
21
- return n ? U(i.layout, n) : null;
22
- }), A = (e) => i.selected?.kind === e.kind && i.selected.id === e.id, B = (e) => {
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: t,
25
- height: n
24
+ width: e,
25
+ height: r
26
26
  } = V();
27
- if (!t || !n) return;
28
- const r = -e.x / e.scale - 240, a = -e.y / e.scale - 240, m = r + t / e.scale + 480, c = a + n / e.scale + 480;
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: r,
30
+ left: n,
31
31
  top: a,
32
- right: m,
33
- bottom: c
32
+ right: y,
33
+ bottom: s
34
34
  };
35
- }, te = (e) => {
36
- const t = B(e);
37
- if (!t) return i.layout.nodes;
35
+ }, rt = (t) => {
36
+ const e = D(t);
37
+ if (!e) return i.layout.nodes;
38
38
  const {
39
- left: n,
40
- top: r,
39
+ left: r,
40
+ top: n,
41
41
  right: a,
42
- bottom: m
43
- } = t;
44
- return i.layout.nodes.filter((c) => c.id === i.selected?.id || c.x + c.width >= n && c.x <= a && c.y + c.height >= r && c.y <= m);
45
- }, N = y(() => i.layout.edges.map((e) => ({
46
- edge: e,
47
- path: e.sections.map(ge).join(" "),
48
- label: he(e.sections),
49
- segments: e.sections.flatMap((t) => t.slice(1).map((n, r) => ({
50
- left: Math.min(n.x, t[r].x),
51
- right: Math.max(n.x, t[r].x),
52
- top: Math.min(n.y, t[r].y),
53
- bottom: Math.max(n.y, t[r].y)
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
- }))), ne = (e) => {
56
- const t = B(e);
57
- return t ? N().filter(({
58
- edge: n,
59
- segments: r
60
- }) => n.id === i.selected?.id || r.some((a) => a.left <= t.right && a.right >= t.left && a.top <= t.bottom && a.bottom >= t.top)) : N();
61
- }, f = (e) => {
62
- Q(e), i.onHover?.(e);
63
- }, k = (e, t, n) => {
64
- const r = t.getBoundingClientRect();
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: e,
67
- owner: t,
68
- position: n && (n.clientX !== 0 || n.clientY !== 0) ? {
69
- x: n.clientX,
70
- y: n.clientY
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: r.left + r.width / 2,
73
- y: r.top + Math.min(32, r.height / 2)
72
+ x: n.left + n.width / 2,
73
+ y: n.top + Math.min(32, n.height / 2)
74
74
  }
75
75
  };
76
- }, z = (e, t) => {
77
- i.onContextMenu && (t.preventDefault(), t.stopPropagation(), i.onContextMenu(k(e, t.currentTarget, t)));
78
- }, G = (e, t) => {
79
- t.target === t.currentTarget && (t.key === "ContextMenu" || t.shiftKey && t.key === "F10" ? (t.preventDefault(), t.stopPropagation(), i.onContextMenu?.(k(e, t.currentTarget))) : t.key === "Enter" ? (t.preventDefault(), t.stopPropagation(), i.onActivate?.(k(e, t.currentTarget))) : t.key === "Escape" && (f(null), i.onActivate?.(k(null, g))));
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
- ce(() => {
82
- if (!g) return;
83
- const e = () => Z({
84
- width: g.clientWidth,
85
- height: g.clientHeight
81
+ ct(() => {
82
+ if (!v) return;
83
+ const t = () => tt({
84
+ width: v.clientWidth,
85
+ height: v.clientHeight
86
86
  });
87
- e();
88
- const t = new ResizeObserver(e);
89
- t.observe(g), de(() => t.disconnect());
87
+ t();
88
+ const e = new ResizeObserver(t);
89
+ e.observe(v), ut(() => e.disconnect());
90
90
  });
91
- const F = (e) => {
92
- const t = {
91
+ const K = (t) => {
92
+ const e = {
93
93
  kind: "node",
94
- id: e.id
95
- }, n = {
96
- selected: () => A(t),
97
- openMenu: (r) => z(t, r)
94
+ id: t.id
95
+ }, r = {
96
+ selected: () => j(e),
97
+ openMenu: (n) => H(e, n)
98
98
  };
99
99
  return (() => {
100
- var r = ve();
101
- return r.$$keydown = (a) => G(t, a), r.$$contextmenu = (a) => z(t, a), r.$$click = (a) => {
102
- a.target.closest("button, a, input, select, textarea, [data-floe-canvas-interactive]") || (a.stopPropagation(), i.onActivate?.(k(t, a.currentTarget, a)));
103
- }, r.$$focusout = () => f(null), r.$$focusin = () => f(t), r.addEventListener("pointerleave", () => f(null)), r.addEventListener("pointerenter", () => f(t)), w(r, (() => {
104
- var a = ae(() => !!(e.kind === "group" && i.renderGroup));
105
- return () => a() ? i.renderGroup(e, n) : i.renderNode(e, n);
106
- })()), S((a) => {
107
- var m = e.kind === "group" ? "floe-graph__group" : "floe-graph__node", c = e.id, d = A(t), _ = `${e.x}px`, P = `${e.y}px`, $ = `${e.width}px`, u = `${e.height}px`, v = e.label;
108
- return m !== a.e && K(r, a.e = m), c !== a.t && l(r, "data-graph-object", a.t = c), d !== a.a && l(r, "data-selected", a.a = d), _ !== a.o && T(r, "left", a.o = _), P !== a.i && T(r, "top", a.i = P), $ !== a.n && T(r, "width", a.n = $), u !== a.s && T(r, "height", a.s = u), v !== a.h && l(r, "aria-label", a.h = v), a;
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
- }), r;
118
+ }), n;
119
119
  })();
120
120
  };
121
121
  return (() => {
122
- var e = me();
123
- e.$$keydown = (n) => G(null, n);
124
- var t = g;
125
- return typeof t == "function" ? ie(t, e) : g = e, w(e, x(ue, {
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: (n) => (n.target instanceof Element ? n.target : null)?.closest('button, a, input, select, textarea, [data-floe-canvas-interactive="true"]') ? "local_surface" : se(n),
139
- onCanvasPointerDown: (n) => {
140
- !(n.target instanceof Element) || n.target.closest("[data-graph-object]") || i.onActivate?.(k(null, g, n));
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: (n) => i.onContextMenu?.({
142
+ onCanvasContextMenu: (r) => i.onContextMenu?.({
143
143
  object: null,
144
- owner: g,
144
+ owner: v,
145
145
  position: {
146
- x: n.clientX,
147
- y: n.clientY
146
+ x: r.clientX,
147
+ y: r.clientY
148
148
  }
149
149
  }),
150
- children: (n) => {
151
- const r = y(() => te(n())), a = y(() => r().filter((d) => d.kind === "group")), m = y(() => r().filter((d) => d.kind !== "group")), c = y(() => ne(n()));
152
- return [x(j, {
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 a();
169
+ return y();
155
170
  },
156
- children: F
171
+ children: K
157
172
  }), (() => {
158
- var d = be(), _ = d.firstChild, P = _.firstChild;
159
- return l(P, "id", H), w(d, x(j, {
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 c();
176
+ return G();
162
177
  },
163
- children: ($) => {
178
+ children: (l) => {
164
179
  const {
165
180
  edge: u
166
- } = $, v = {
181
+ } = l, m = {
167
182
  kind: "edge",
168
183
  id: u.id
169
- }, W = () => $.path, re = () => $.label;
170
- return x(D, {
184
+ }, I = () => l.path, R = () => l.label;
185
+ return w(_, {
171
186
  get when() {
172
- return !Y() || Y().has(u.id);
187
+ return !W() || W().has(u.id);
173
188
  },
174
189
  get children() {
175
- var C = xe(), R = C.firstChild, s = R.nextSibling;
176
- return l(R, "marker-end", `url(#${H})`), w(C, x(D, {
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 re();
193
+ return R();
179
194
  },
180
195
  children: (o) => (() => {
181
- var b = ye();
182
- return w(b, () => u.label), S((h) => {
183
- var M = /^\d+$/.test(u.label), E = o().x, L = o().y - 6;
184
- return M !== h.e && l(b, "data-count", h.e = M), E !== h.t && l(b, "x", h.t = E), L !== h.a && l(b, "y", h.a = L), h;
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
- }), b;
204
+ }), k;
190
205
  })()
191
- }), s), s.$$keydown = (o) => G(v, o), s.$$contextmenu = (o) => z(v, o), s.$$click = (o) => {
192
- o.stopPropagation(), i.onActivate?.(k(v, o.currentTarget, o));
193
- }, s.addEventListener("blur", () => f(null)), s.addEventListener("focus", () => f(v)), s.addEventListener("pointerleave", () => f(null)), s.addEventListener("pointerenter", () => f(v)), S((o) => {
194
- var b = u.id, h = ee().has(u.id), M = A(v), E = W(), L = W(), q = u.label;
195
- return b !== o.e && l(C, "data-graph-object", o.e = b), h !== o.t && l(C, "data-emphasized", o.t = h), M !== o.a && l(C, "data-selected", o.a = M), E !== o.o && l(R, "d", o.o = E), L !== o.i && l(s, "d", o.i = L), q !== o.n && l(s, "aria-label", o.n = q), o;
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
- }), C;
218
+ }), b;
204
219
  }
205
220
  });
206
221
  }
207
- }), null), S(() => l(d, "aria-label", i.ariaLabel)), d;
208
- })(), x(j, {
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 m();
235
+ return s();
211
236
  },
212
- children: F
237
+ children: K
213
238
  })];
214
239
  }
215
- }), null), w(e, x(D, {
240
+ }), null), S(t, w(_, {
216
241
  get when() {
217
242
  return i.minimap;
218
243
  },
219
- children: (n) => x(fe, {
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 n().ariaLabel;
261
+ return r().ariaLabel;
262
+ },
263
+ get nodeStyle() {
264
+ return r().nodeStyle;
237
265
  }
238
266
  })
239
- }), null), w(e, () => i.overlay, null), S((n) => {
240
- var r = `floe-graph ${i.class ?? ""}`, a = i.ariaLabel;
241
- return r !== n.e && K(e, n.e = r), a !== n.t && l(e, "aria-label", n.t = a), n;
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
- }), e;
273
+ }), t;
246
274
  })();
247
275
  }
248
- oe(["focusin", "focusout", "click", "contextmenu", "keydown"]);
276
+ lt(["focusin", "focusout", "click", "contextmenu", "keydown"]);
249
277
  export {
250
- Le as GraphCanvas
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 S, setAttribute as d, template as z, use as A, delegateEvents as k } from "solid-js/web";
2
- import { createMemo as w } from "solid-js";
3
- import { graphSectionPath as D, fitGraphViewport as L } from "./geometry.js";
4
- var V = /* @__PURE__ */ z('<svg class=floe-graph__minimap role=region tabindex=0 preserveAspectRatio="xMidYMid meet"data-floe-canvas-interactive=true><path class=floe-graph__minimap-groups></path><path class=floe-graph__minimap-edges></path><path class=floe-graph__minimap-nodes></path><rect class=floe-graph__minimap-viewport>');
5
- function R(e) {
6
- let h, n;
7
- const c = w(() => {
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: i,
10
- y: o,
11
- width: s,
12
- height: v
13
- } = e.layout.bounds, a = Math.max(s, v, 1) * 0.04;
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: i - a,
16
- y: o - a,
17
- width: s + a * 2,
18
- height: v + a * 2
15
+ x: e - t,
16
+ y: l - t,
17
+ width: d + t * 2,
18
+ height: n + t * 2
19
19
  };
20
- }), g = (i) => e.layout.nodes.filter((o) => (o.kind === "group" ? "group" : "node") === i).map((o) => `M${o.x},${o.y}h${o.width}v${o.height}h${-o.width}Z`).join(""), C = w(() => g("node")), I = w(() => g("group")), P = w(() => e.layout.edges.flatMap((i) => i.sections.map(D)).join(" ")), u = (i) => {
21
- const o = h?.getScreenCTM();
22
- return o ? new DOMPoint(i.clientX, i.clientY).matrixTransform(o.inverse()) : void 0;
23
- }, f = (i) => {
24
- n?.id === i.pointerId && (n = void 0, h?.hasPointerCapture(i.pointerId) && h.releasePointerCapture(i.pointerId));
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 i = V(), o = i.firstChild, s = o.nextSibling, v = s.nextSibling, a = v.nextSibling;
28
- i.$$keydown = (t) => {
29
- const l = {
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
- !l && t.key !== "Home" || (t.preventDefault(), t.stopPropagation(), e.onInteractionStart?.(), e.onViewportChange(l ? {
36
- ...e.viewport,
37
- x: e.viewport.x + l[0] * e.size.width * 0.2,
38
- y: e.viewport.y + l[1] * e.size.height * 0.2
39
- } : L(e.layout.bounds, e.size, 24)));
40
- }, i.addEventListener("lostpointercapture", () => {
41
- n = void 0;
42
- }), i.addEventListener("pointercancel", f), i.$$pointerup = f, i.$$pointermove = (t) => {
43
- if (n?.id !== t.pointerId) return;
44
- const r = u(t);
45
- r && e.onViewportChange({
46
- ...n.viewport,
47
- x: n.viewport.x - (r.x - n.x) * n.viewport.scale,
48
- y: n.viewport.y - (r.y - n.y) * n.viewport.scale
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
- }, i.$$pointerdown = (t) => {
75
+ }, e.$$pointerdown = (t) => {
51
76
  if (t.button !== 0) return;
52
- const r = u(t);
53
- r && (t.preventDefault(), t.stopPropagation(), e.onInteractionStart?.(), h.focus({
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") || e.onViewportChange({
56
- ...e.viewport,
57
- x: e.size.width / 2 - r.x * e.viewport.scale,
58
- y: e.size.height / 2 - r.y * e.viewport.scale
59
- }), n = {
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: r.x,
62
- y: r.y,
86
+ x: o.x,
87
+ y: o.y,
63
88
  viewport: {
64
- ...e.viewport
89
+ ...i.viewport
65
90
  }
66
- }, h.setPointerCapture(t.pointerId));
91
+ }, v.setPointerCapture(t.pointerId));
67
92
  };
68
- var m = h;
69
- return typeof m == "function" ? A(m, i) : h = i, S((t) => {
70
- var r = e.ariaLabel, l = `${c().x} ${c().y} ${c().width} ${c().height}`, x = I(), y = P(), p = C(), $ = -e.viewport.x / e.viewport.scale, b = -e.viewport.y / e.viewport.scale, _ = e.size.width / e.viewport.scale, M = e.size.height / e.viewport.scale;
71
- return r !== t.e && d(i, "aria-label", t.e = r), l !== t.t && d(i, "viewBox", t.t = l), x !== t.a && d(o, "d", t.a = x), y !== t.o && d(s, "d", t.o = y), p !== t.i && d(v, "d", t.i = p), $ !== t.n && d(a, "x", t.n = $), b !== t.s && d(a, "y", t.s = b), _ !== t.h && d(a, "width", t.h = _), M !== t.r && d(a, "height", t.r = M), t;
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
- h: void 0,
81
- r: void 0
82
- }), i;
132
+ s: void 0
133
+ }), e;
83
134
  })();
84
135
  }
85
- k(["pointerdown", "pointermove", "pointerup", "keydown"]);
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
- inset: 0;
41
- overflow: visible;
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
- opacity: 0.65;
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 {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@floegence/floe-webapp-core",
3
- "version": "0.86.19",
3
+ "version": "0.86.21",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",