unfold-nav 0.1.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.
@@ -0,0 +1,2211 @@
1
+ //#region src/icons.ts
2
+ var e = /^(https?:|data:|blob:|\/|\.\.?\/)|\.(svg|png|jpe?g|gif|webp|avif)(\?.*)?$/i, t = /^[a-z][\w-]{2,}$/i;
3
+ function n(r, i, a, o = 0) {
4
+ if (r == null || r === "" || o > 3) return null;
5
+ if (typeof r == "function") return n(r(), i, a, o + 1);
6
+ if (typeof r != "string") return r.cloneNode(!0);
7
+ let s = r.trim();
8
+ if (s.startsWith("<")) {
9
+ let e = document.createElement("template");
10
+ return e.innerHTML = s, e.content;
11
+ }
12
+ if (e.test(s)) {
13
+ let e = document.createElement("img");
14
+ return e.src = s, e.alt = "", e.decoding = "async", e.draggable = !1, e;
15
+ }
16
+ if (t.test(s)) {
17
+ let e = i?.(s, a);
18
+ return e == null || e === s ? null : n(e, void 0, a, o + 1);
19
+ }
20
+ let c = document.createElement("span");
21
+ return c.className = "glyph", c.textContent = s, c;
22
+ }
23
+ function r(e) {
24
+ let t = document.createElement("span");
25
+ return t.className = "mono", t.textContent = Array.from(e.trim())[0]?.toUpperCase() ?? "•", t;
26
+ }
27
+ var i = (e) => `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${e}</svg>`, a = i("<path d=\"M12 17.5 6 8.6M12 17.5V6.2M12 17.5l6-8.9\"/><circle cx=\"12\" cy=\"18.2\" r=\"2.5\" fill=\"currentColor\" stroke=\"none\"/><circle cx=\"5.6\" cy=\"8\" r=\"2\" fill=\"currentColor\" stroke=\"none\"/><circle cx=\"12\" cy=\"5.4\" r=\"2\" fill=\"currentColor\" stroke=\"none\"/><circle cx=\"18.4\" cy=\"8\" r=\"2\" fill=\"currentColor\" stroke=\"none\"/>"), o = i("<path d=\"M6.5 6.5l11 11M17.5 6.5l-11 11\"/>"), s = i("<path d=\"M19 12H5.5M11 6l-6 6 6 6\"/>"), c = (e) => Array.isArray(e) ? e : [e], l = Math.PI * 2, u = Math.PI / 180, d = 4;
28
+ function f(e, t) {
29
+ let n = Math.max(t.x - e.x, 0, e.x - (t.x + t.w)), r = Math.max(t.y - e.y, 0, e.y - (t.y + t.h));
30
+ return Math.hypot(n, r);
31
+ }
32
+ function p(e, t, n = 0) {
33
+ return e.x < t.x + t.w + n && t.x < e.x + e.w + n && e.y < t.y + t.h + n && t.y < e.y + e.h + n;
34
+ }
35
+ var m = (e, t) => t.x >= e.x && t.x <= e.x + e.w && t.y >= e.y && t.y <= e.y + e.h, h = (e, t) => t.x >= e.x - .5 && t.y >= e.y - .5 && t.x + t.w <= e.x + e.w + .5 && t.y + t.h <= e.y + e.h + .5, g = (e, t) => ({
36
+ x: e.x - t,
37
+ y: e.y - t,
38
+ w: e.w + t * 2,
39
+ h: e.h + t * 2
40
+ });
41
+ function _(e, t, n) {
42
+ let r = n.x - t.x, i = n.y - t.y, a = r * r + i * i, o = a ? Math.max(0, Math.min(1, ((e.x - t.x) * r + (e.y - t.y) * i) / a)) : 0;
43
+ return Math.hypot(e.x - (t.x + o * r), e.y - (t.y + o * i));
44
+ }
45
+ function v(e, t) {
46
+ let n = Infinity;
47
+ for (let r = 0; r < t.length - 1; r++) n = Math.min(n, _(e, t[r], t[r + 1]));
48
+ return n;
49
+ }
50
+ function ee(e, t, n) {
51
+ let r = 0, i = 1, a = t.x - e.x, o = t.y - e.y, s = [
52
+ -a,
53
+ a,
54
+ -o,
55
+ o
56
+ ], c = [
57
+ e.x - n.x,
58
+ n.x + n.w - e.x,
59
+ e.y - n.y,
60
+ n.y + n.h - e.y
61
+ ];
62
+ for (let e = 0; e < 4; e++) if (s[e] === 0) {
63
+ if (c[e] < 0) return !1;
64
+ } else {
65
+ let t = c[e] / s[e];
66
+ if (s[e] < 0) {
67
+ if (t > i) return !1;
68
+ t > r && (r = t);
69
+ } else {
70
+ if (t < r) return !1;
71
+ t < i && (i = t);
72
+ }
73
+ }
74
+ return !0;
75
+ }
76
+ function y(e, t) {
77
+ for (let n = 0; n < e.length - 1; n++) if (ee(e[n], e[n + 1], t)) return !0;
78
+ return !1;
79
+ }
80
+ function b(e, t, n) {
81
+ let r = t.x - e.x, i = t.y - e.y, a = Math.hypot(r, i) || 1;
82
+ return {
83
+ x: e.x + r / a * n,
84
+ y: e.y + i / a * n
85
+ };
86
+ }
87
+ var x = (e) => e.toFixed(1);
88
+ function S(e, t, n, r) {
89
+ let i = () => {
90
+ let n = b(e, t, e.r), r = b(t, e, t.r);
91
+ return {
92
+ d: `M${x(n.x)} ${x(n.y)}L${x(r.x)} ${x(r.y)}`,
93
+ points: [n, r]
94
+ };
95
+ };
96
+ if (n === "step") {
97
+ let n = b(e, t, e.r), r = b(t, e, t.r);
98
+ if (Math.min(Math.abs(r.x - n.x), Math.abs(r.y - n.y)) < 1 || Math.hypot(t.x - e.x, t.y - e.y) <= e.r + t.r) return i();
99
+ let a = Math.abs(t.x - e.x) >= Math.abs(t.y - e.y), o = a ? (n.x + r.x) / 2 : (n.y + r.y) / 2, s = a ? [{
100
+ x: o,
101
+ y: n.y
102
+ }, {
103
+ x: o,
104
+ y: r.y
105
+ }] : [{
106
+ x: n.x,
107
+ y: o
108
+ }, {
109
+ x: r.x,
110
+ y: o
111
+ }], c = [
112
+ n,
113
+ ...s,
114
+ r
115
+ ], l = [n], u = `M${x(n.x)} ${x(n.y)}`;
116
+ return s.forEach((e, t) => {
117
+ let n = c[t], r = c[t + 2], i = Math.min(10, Math.hypot(e.x - n.x, e.y - n.y) / 2, Math.hypot(r.x - e.x, r.y - e.y) / 2), a = b(e, n, i), o = b(e, r, i);
118
+ u += `L${x(a.x)} ${x(a.y)}Q${x(e.x)} ${x(e.y)} ${x(o.x)} ${x(o.y)}`, l.push(a);
119
+ for (let t = 1; t <= 4; t++) {
120
+ let n = t / 4, r = 1 - n;
121
+ l.push({
122
+ x: r * r * a.x + 2 * r * n * e.x + n * n * o.x,
123
+ y: r * r * a.y + 2 * r * n * e.y + n * n * o.y
124
+ });
125
+ }
126
+ }), l.push(r), {
127
+ d: `${u}L${x(r.x)} ${x(r.y)}`,
128
+ points: l
129
+ };
130
+ }
131
+ if (n !== "curved" || r == null) return i();
132
+ let a = Math.hypot(t.x - e.x, t.y - e.y) || 1, o = {
133
+ x: e.x + Math.cos(r) * a * .5,
134
+ y: e.y + Math.sin(r) * a * .5
135
+ }, s = b(e, o, e.r), c = b(t, o, t.r), l = [];
136
+ for (let e = 0; e <= 6; e++) {
137
+ let t = e / 6, n = 1 - t;
138
+ l.push({
139
+ x: n * n * s.x + 2 * n * t * o.x + t * t * c.x,
140
+ y: n * n * s.y + 2 * n * t * o.y + t * t * c.y
141
+ });
142
+ }
143
+ return {
144
+ d: `M${x(s.x)} ${x(s.y)}Q${x(o.x)} ${x(o.y)} ${x(c.x)} ${x(c.y)}`,
145
+ points: l
146
+ };
147
+ }
148
+ var C = [
149
+ 0,
150
+ ...Array.from({ length: 7 }, (e, t) => [(t + 1) * 22.5, -(t + 1) * 22.5]).flat(),
151
+ 180
152
+ ].map((e) => e * u), w = [
153
+ 0,
154
+ 12,
155
+ 28
156
+ ], T = [
157
+ 0,
158
+ 12,
159
+ 28,
160
+ 48
161
+ ];
162
+ function E(e, t, n, r, i, a) {
163
+ let o = Math.cos(i), s = Math.sin(i), c = 0, l = a + n + r;
164
+ for (let i = 0; i < 14; i++) {
165
+ let i = (c + l) / 2;
166
+ f({
167
+ x: e,
168
+ y: t
169
+ }, {
170
+ x: e + o * i - n / 2,
171
+ y: t + s * i - r / 2,
172
+ w: n,
173
+ h: r
174
+ }) >= a ? l = i : c = i;
175
+ }
176
+ return {
177
+ x: e + o * l - n / 2,
178
+ y: t + s * l - r / 2,
179
+ w: n,
180
+ h: r
181
+ };
182
+ }
183
+ function D(e, t, n) {
184
+ for (let r of n.circles) if (r !== t && f(r, e) < r.r + n.pad) return !0;
185
+ for (let t of n.taken) if (p(e, t, d)) return !0;
186
+ if (n.edges.length) {
187
+ let t = g(e, 2);
188
+ for (let e of n.edges) if (y(e, t)) return !0;
189
+ }
190
+ return !1;
191
+ }
192
+ function te(e, t, n, r) {
193
+ let i = c(t)[0], a = 0;
194
+ for (let t of w) for (let o of C) {
195
+ let s = E(e.x, e.y, i.w, i.h, n + o, e.r + (r.ownPad ?? r.pad) + t);
196
+ h(r.bounds, s) && !D(s, e, r) && a++;
197
+ }
198
+ return a;
199
+ }
200
+ function O(e, t, n = (e, t) => k(e.node, e.size, e.angle, t)) {
201
+ let r = (r) => {
202
+ let i = e.map(() => null), a = {
203
+ ...t,
204
+ taken: [...t.taken]
205
+ }, o = 0;
206
+ for (let t of r) {
207
+ let r = n(e[t], a);
208
+ r ? a.taken.push(r) : o++, i[t] = r;
209
+ }
210
+ return {
211
+ out: i,
212
+ missing: o
213
+ };
214
+ }, i = e.map((e, t) => t), a = r(i);
215
+ if (a.missing === 0 || e.length < 2) return a.out;
216
+ let o = e.map((e) => te(e.node, e.size, e.angle, t));
217
+ for (let e of [[...i].sort((e, t) => o[e] - o[t]), [...i].reverse()]) {
218
+ let t = r(e);
219
+ if (t.missing < a.missing && (a = t), a.missing === 0) break;
220
+ }
221
+ return a.out;
222
+ }
223
+ function k(e, t, n, r) {
224
+ for (let i of c(t)) {
225
+ let t = ne(e, i, n, r);
226
+ if (t) return t;
227
+ }
228
+ return null;
229
+ }
230
+ function ne(e, t, n, r) {
231
+ for (let i of r.rings ?? w) for (let a of C) {
232
+ let o = E(e.x, e.y, t.w, t.h, n + a, e.r + (r.ownPad ?? r.pad) + i);
233
+ if (h(r.bounds, o) && !D(o, e, r)) return o;
234
+ }
235
+ return null;
236
+ }
237
+ var re = Array.from({ length: 41 }, (e, t) => 1 + t * .06), ie = [
238
+ 1,
239
+ 1.15,
240
+ 1.3,
241
+ 1.5,
242
+ 1.75,
243
+ 2,
244
+ 2.3
245
+ ], A = 5 * u, ae = 200;
246
+ function j(e, t, n, r, i) {
247
+ let a = r - i * (n - 1) / 2;
248
+ return Array.from({ length: n }, (n, r) => {
249
+ let o = a + r * i;
250
+ return {
251
+ x: e.x + t * Math.cos(o),
252
+ y: e.y + t * Math.sin(o)
253
+ };
254
+ });
255
+ }
256
+ function M(e, t, n, r) {
257
+ return Array.from({ length: n }, (i, a) => {
258
+ let o = r + a * l / n;
259
+ return {
260
+ x: e.x + t * Math.cos(o),
261
+ y: e.y + t * Math.sin(o)
262
+ };
263
+ });
264
+ }
265
+ function oe(e, t, n, r = !1) {
266
+ let { bounds: i, radius: a } = t, o = t.labelPad ?? 8, s = 0, c = 0;
267
+ for (let n of e) {
268
+ s += Math.max(0, i.x + a - n.x) + Math.max(0, n.x + a - (i.x + i.w)), s += Math.max(0, i.y + a - n.y) + Math.max(0, n.y + a - (i.y + i.h));
269
+ for (let e of t.obstacles) {
270
+ let r = a + e.r + t.gap * .75, i = Math.hypot(n.x - e.x, n.y - e.y);
271
+ i < r && (s += r - i);
272
+ }
273
+ if (!r) for (let e of t.blocked ?? []) {
274
+ let t = f(n, e);
275
+ t < a + o && (s += a + o - t);
276
+ }
277
+ }
278
+ if (n && t.edgeStyle !== "none" && s === 0) {
279
+ let n = t.edgeStyle ?? "straight";
280
+ for (let n of e) for (let e of t.edges ?? []) {
281
+ let t = v(n, e);
282
+ t < a + 4 && (c += a + 4 - t);
283
+ }
284
+ e.forEach((r, i) => {
285
+ let o = S(t.origin, {
286
+ ...r,
287
+ r: a
288
+ }, n, t.parentAngle).points;
289
+ for (let e of t.obstacles) {
290
+ let t = v(e, o);
291
+ t < e.r + 4 && (c += e.r + 4 - t);
292
+ }
293
+ e.forEach((e, t) => {
294
+ if (t === i) return;
295
+ let n = v(e, o);
296
+ n < a + 4 && (c += a + 4 - n);
297
+ });
298
+ });
299
+ }
300
+ return {
301
+ base: s,
302
+ edges: c
303
+ };
304
+ }
305
+ function N(e, t, n, r, i = "optional") {
306
+ if (!t.labels?.some(Boolean) && !t.originLabel) return {
307
+ labels: e.map(() => null),
308
+ originLabel: null
309
+ };
310
+ let a = t.radius, o = t.edgeStyle ?? "straight", s = e.map((e) => ({
311
+ x: e.x,
312
+ y: e.y,
313
+ r: a
314
+ })), c = o === "none" ? [] : s.map((e) => S(t.origin, e, o, t.parentAngle).points), l = o === "none" ? [] : [...t.edges ?? [], ...c], u = {
315
+ bounds: t.bounds,
316
+ circles: [
317
+ ...t.obstacles,
318
+ t.origin,
319
+ ...s
320
+ ],
321
+ taken: [...t.blocked ?? []],
322
+ edges: l,
323
+ pad: t.labelPad ?? 8
324
+ }, d = s.map((e, n) => ({
325
+ i: n,
326
+ node: e,
327
+ size: t.labels?.[n] ?? null,
328
+ angle: Math.atan2(e.y - t.origin.y, e.x - t.origin.x)
329
+ })).filter((e) => !!e.size), f = O(d, u, (e, t) => {
330
+ let r = e.i;
331
+ return k(e.node, e.size, e.angle, t) ?? (n === "strict" ? null : k(e.node, e.size, e.angle, {
332
+ ...t,
333
+ edges: c
334
+ })) ?? (n === "loose" ? k(e.node, e.size, e.angle, {
335
+ ...t,
336
+ edges: c.length ? [c[r]] : []
337
+ }) : null);
338
+ }), p = s.map(() => null);
339
+ if (d.forEach((e, t) => p[e.i] = f[t]), !r && d.some((e, t) => !f[t])) return null;
340
+ let m = null;
341
+ if (t.originLabel) {
342
+ let e = {
343
+ ...u,
344
+ taken: [...u.taken, ...p.filter((e) => !!e)],
345
+ rings: T
346
+ }, n = t.parentEdge ? [t.parentEdge] : [];
347
+ if (m = k(t.origin, t.originLabel, t.direction, {
348
+ ...e,
349
+ edges: [...e.edges, ...n]
350
+ }), !m && i !== "clean" && (m = k(t.origin, t.originLabel, t.direction + Math.PI, {
351
+ ...e,
352
+ edges: e.edges.filter((e) => e !== t.parentEdge)
353
+ })), !m && i !== "optional" && !r) return null;
354
+ }
355
+ return {
356
+ labels: p,
357
+ originLabel: m
358
+ };
359
+ }
360
+ function P(e) {
361
+ let { origin: t, count: n, radius: r, gap: i } = e;
362
+ if (n <= 0) return {
363
+ points: [],
364
+ labels: []
365
+ };
366
+ let a = 2 * r + i, o = null, s = (t, n, r, i) => {
367
+ let a = oe(t, e, n);
368
+ if ((!o || a.base < o.penalty) && (o = {
369
+ points: t,
370
+ penalty: a.base
371
+ }), a.base > 0 || n && a.edges > 0 || r.left <= 0) return null;
372
+ r.left--;
373
+ let s = N(t, e, n ? "strict" : "relaxed", !1, i);
374
+ return s ? {
375
+ points: t,
376
+ ...s
377
+ } : null;
378
+ }, c = re.map((t) => e.distance * t);
379
+ if (n > 1 && e.maxSpread != null) {
380
+ let t = a / (2 * Math.sin(e.maxSpread / (2 * (n - 1)))) + .5;
381
+ t > e.distance && c.push(t);
382
+ }
383
+ c.sort((e, t) => e - t);
384
+ let l = Math.min(e.maxDeviation ?? Math.PI, Math.PI), u = e.maxSpread != null && !e.ring ? Math.min(e.maxSpread * 1.5, 1.4 * Math.PI) : e.maxSpread, d = [
385
+ {
386
+ dev: l,
387
+ strict: !0,
388
+ spread: e.maxSpread
389
+ },
390
+ {
391
+ dev: Math.PI,
392
+ strict: !0,
393
+ spread: u
394
+ },
395
+ {
396
+ dev: Math.PI,
397
+ strict: !1,
398
+ spread: u
399
+ }
400
+ ], f = (e.originLabel ? [
401
+ "clean",
402
+ "breadcrumb",
403
+ "optional"
404
+ ] : ["optional"]).flatMap((e) => d.map((t) => ({
405
+ ...t,
406
+ needOrigin: e
407
+ })));
408
+ for (let r of f) {
409
+ if (e.ring && n >= 3) {
410
+ let i = Math.max(e.distance, a / (2 * Math.sin(Math.PI / n)));
411
+ for (let a of ie) {
412
+ let o = s(M(t, i * a, n, e.direction), r.strict, { left: 1 }, r.needOrigin);
413
+ if (o) return o;
414
+ }
415
+ }
416
+ for (let i of c) {
417
+ if (a >= 2 * i) continue;
418
+ let o = 2 * Math.asin(a / (2 * i)), c = Math.max(o, e.preferredStep ?? 0), l = c > o + 1e-9 ? [c, o] : [o], u = [];
419
+ for (let t of l) {
420
+ if (n > 1 && r.spread != null && t * (n - 1) > r.spread + 1e-9) continue;
421
+ let i = t === c ? 0 : .15;
422
+ for (let n = 0; n * A <= r.dev + 1e-9; n++) for (let r of n === 0 ? [0] : [1, -1]) {
423
+ let a = r * n * A;
424
+ u.push({
425
+ dir: e.direction + a,
426
+ step: t,
427
+ cost: Math.abs(a) + i
428
+ });
429
+ }
430
+ }
431
+ u.sort((e, t) => e.cost - t.cost);
432
+ let d = { left: ae };
433
+ for (let e of u) {
434
+ let a = s(j(t, i, n, e.dir, e.step), r.strict, d, r.needOrigin);
435
+ if (a) return a;
436
+ }
437
+ }
438
+ }
439
+ let p = null, m = -1, h = 0, g = e.edgeStyle ?? "straight", _ = (t) => {
440
+ if (oe(t, e, !1, !0).base > 0) return;
441
+ h++;
442
+ let i = N(t, e, "loose", !0), a = i.labels, o = g === "none" ? [] : [...e.edges ?? [], ...t.map((t) => S(e.origin, {
443
+ ...t,
444
+ r
445
+ }, g, e.parentAngle).points)], s = a.filter(Boolean).length + +!!i.originLabel, c = a.filter((e) => e && !o.some((t) => y(t, e))).length, l = s * (n + 2) + c;
446
+ l > m && (p = {
447
+ points: t,
448
+ ...i
449
+ }, m = l);
450
+ };
451
+ if (e.ring && n >= 3) {
452
+ let r = Math.max(e.distance, a / (2 * Math.sin(Math.PI / n)));
453
+ for (let i of ie) _(M(t, r * i, n, e.direction));
454
+ }
455
+ for (let r of c) {
456
+ let i = (n + +!!e.originLabel) * (n + 2) + n;
457
+ if (h >= 160 || m === i || a >= 2 * r) continue;
458
+ let o = 2 * Math.asin(a / (2 * r));
459
+ for (let a = 0; a * A <= Math.PI && h < 160 && m < i; a++) for (let i of a === 0 ? [0] : [1, -1]) _(j(t, r, n, e.direction + i * a * A, o));
460
+ }
461
+ if (p) return p;
462
+ let v = o?.points ?? j(t, e.distance, n, e.direction, a / e.distance);
463
+ return {
464
+ points: v,
465
+ ...N(v, e, "loose", !0)
466
+ };
467
+ }
468
+ function F(e, t = /* @__PURE__ */ new Map()) {
469
+ let n = /* @__PURE__ */ new Map(), r = e.edgeStyle ?? "straight", i = e.nodeRadius, a = [e.hub], o = [], s = [], c = /* @__PURE__ */ new Set(), l = {
470
+ x: e.bounds.x + e.bounds.w / 2,
471
+ y: e.bounds.y + e.bounds.h / 2
472
+ };
473
+ for (let d of e.levels) {
474
+ let p = d.parentId == null ? null : n.get(d.parentId);
475
+ if (d.parentId != null && !p) continue;
476
+ d.parentId != null && c.add(d.parentId);
477
+ let m = p ? {
478
+ x: p.x,
479
+ y: p.y,
480
+ r: i
481
+ } : e.hub, h = t.get(d.key);
482
+ if (!h || h.points.length !== d.childIds.length) {
483
+ let n = Math.atan2(l.y - e.hub.y, l.x - e.hub.x), f = Math.hypot(l.x - e.hub.x, l.y - e.hub.y) < 1;
484
+ h = P({
485
+ origin: m,
486
+ parentAngle: p ? p.angle : null,
487
+ count: d.childIds.length,
488
+ direction: p ? p.angle : f ? -Math.PI / 2 : n,
489
+ radius: i,
490
+ distance: e.distance,
491
+ gap: e.gap,
492
+ bounds: e.bounds,
493
+ obstacles: a.filter((e) => e.x !== m.x || e.y !== m.y),
494
+ blocked: s.filter((e) => !c.has(e.id)).map((e) => e.rect),
495
+ edges: o,
496
+ edgeStyle: r,
497
+ labels: d.childIds.map((t) => e.labelSize?.(t) ?? null),
498
+ originLabel: p && d.parentId ? (e.branchLabelSize ?? e.labelSize)?.(d.parentId) ?? null : null,
499
+ parentEdge: p ? p.edge : null,
500
+ labelPad: e.labelPad,
501
+ ...p ? {
502
+ maxSpread: 150 * u,
503
+ maxDeviation: 110 * u
504
+ } : {
505
+ ring: !0,
506
+ preferredStep: 50 * u,
507
+ maxSpread: Math.PI
508
+ }
509
+ }), t.set(d.key, h);
510
+ }
511
+ p && (p.pathLabel = h.originLabel ?? null, p.pathLabel && s.push({
512
+ id: `${d.parentId}\u0000open`,
513
+ rect: p.pathLabel
514
+ }));
515
+ let g = e.labelPad ?? 8;
516
+ for (let e of s) {
517
+ if (c.has(e.id) || !h.points.some((t) => f(t, e.rect) < i + g - .5)) continue;
518
+ let [t, r] = e.id.split("\0"), a = n.get(t);
519
+ a && (r ? a.pathLabel = null : a.label = null);
520
+ }
521
+ d.childIds.forEach((e, t) => {
522
+ let c = h.points[t], l = {
523
+ x: c.x,
524
+ y: c.y,
525
+ r: i
526
+ }, u = p ? p.angle : null, d = S(m, l, r, u).points;
527
+ n.set(e, {
528
+ x: c.x,
529
+ y: c.y,
530
+ from: {
531
+ x: m.x,
532
+ y: m.y
533
+ },
534
+ angle: Math.atan2(c.y - m.y, c.x - m.x),
535
+ parentAngle: u,
536
+ label: h.labels[t] ?? null,
537
+ edge: d
538
+ }), a.push(l), r !== "none" && o.push(d);
539
+ let f = h.labels[t];
540
+ f && s.push({
541
+ id: e,
542
+ rect: f
543
+ });
544
+ });
545
+ }
546
+ let d = e.levels[e.levels.length - 1];
547
+ if (d && e.labelSize) {
548
+ let t = e.labelPad ?? 8, a = /* @__PURE__ */ new Map();
549
+ for (let [e, t] of n) a.set(e, {
550
+ x: t.x,
551
+ y: t.y,
552
+ r: i
553
+ });
554
+ let s = d.childIds.filter((e) => n.has(e)), c = (e) => o.some((t) => y(t, e)), l = s.filter((t) => {
555
+ let r = n.get(t).label;
556
+ return e.labelSize(t) && (!r || c(r));
557
+ });
558
+ if (l.length) {
559
+ let i = {
560
+ bounds: e.bounds,
561
+ circles: [e.hub, ...a.values()],
562
+ taken: s.filter((e) => !l.includes(e)).map((e) => n.get(e).label).filter((e) => !!e),
563
+ edges: o,
564
+ pad: t
565
+ }, c = l.map((t) => ({
566
+ id: t,
567
+ node: a.get(t),
568
+ size: e.labelSize(t),
569
+ angle: n.get(t).angle
570
+ })), u = O(c, i, (e, t) => {
571
+ let i = n.get(e.id);
572
+ return k(e.node, e.size, e.angle, t) ?? i.label ?? k(e.node, e.size, e.angle, {
573
+ ...t,
574
+ edges: r === "none" ? [] : [i.edge]
575
+ });
576
+ });
577
+ c.forEach((e, t) => {
578
+ let r = n.get(e.id);
579
+ u[t] && u[t] !== r.label && (r.label = u[t], r.borrowed = !0);
580
+ });
581
+ }
582
+ }
583
+ if (r === "step" && d && e.labelSize) {
584
+ let t = new Set(e.levels.flatMap((e) => e.parentId == null ? [] : [e.parentId])), r = [.../* @__PURE__ */ new Set([...t, ...d.childIds])].filter((e) => n.has(e)), a = (e) => t.has(e) ? n.get(e).pathLabel : n.get(e).label;
585
+ for (let s of r) {
586
+ let c = n.get(s), l = a(s), u = t.has(s) ? o.filter((e) => e !== c.edge) : o;
587
+ if (l && !u.some((e) => y(e, l))) continue;
588
+ let d = (t.has(s) ? e.branchLabelSize ?? e.labelSize : e.labelSize)(s);
589
+ if (!d) continue;
590
+ let f = {
591
+ x: c.x,
592
+ y: c.y,
593
+ r: i
594
+ }, p = {
595
+ bounds: e.bounds,
596
+ circles: [e.hub, ...[...n.values()].map((e) => ({
597
+ x: e.x,
598
+ y: e.y,
599
+ r: i
600
+ }))],
601
+ taken: r.filter((e) => e !== s).map(a).filter((e) => !!e),
602
+ edges: u,
603
+ pad: e.labelPad ?? 8,
604
+ rings: [
605
+ ...T,
606
+ 64,
607
+ 80
608
+ ]
609
+ }, m = k(f, d, c.angle, p);
610
+ t.has(s) ? c.pathLabel = m : c.label = m, m && (c.borrowed = !0);
611
+ }
612
+ }
613
+ return n;
614
+ }
615
+ function se(e, t, n) {
616
+ let r = -1, i = Infinity;
617
+ return t.forEach((t, a) => {
618
+ let o = Math.hypot(e.x - t.x, e.y - t.y);
619
+ o <= t.r + n && o < i && (r = a, i = o);
620
+ }), r;
621
+ }
622
+ //#endregion
623
+ //#region src/styles.ts
624
+ var ce = "\n --_fg: var(--unfold-fg, #17171c);\n --_muted: var(--unfold-muted, rgb(23 23 28 / .68));\n --_surface: var(--unfold-surface, rgb(255 255 255 / .78));\n --_surface-active: var(--unfold-surface-active, rgb(255 255 255 / .96));\n --_border: var(--unfold-border, rgb(255 255 255 / .7));\n --_outline: var(--unfold-outline, rgb(15 15 30 / .09));\n --_shadow: var(--unfold-shadow, 0 1px 2px rgb(15 15 30 / .08), 0 10px 28px -8px rgb(15 15 30 / .28));\n --_edge: var(--unfold-edge, rgb(23 23 28 / .2));\n --_label-bg: var(--unfold-label-bg, rgb(255 255 255 / .9));\n --_label-fg: var(--unfold-label-fg, var(--_fg));\n --_backdrop: var(--unfold-backdrop, rgb(244 244 248 / .42));\n", I = "\n --_fg: var(--unfold-fg, #f3f3f6);\n --_muted: var(--unfold-muted, rgb(243 243 246 / .7));\n --_surface: var(--unfold-surface, rgb(38 38 44 / .72));\n --_surface-active: var(--unfold-surface-active, rgb(56 56 64 / .92));\n --_border: var(--unfold-border, rgb(255 255 255 / .12));\n --_outline: var(--unfold-outline, rgb(0 0 0 / .4));\n --_shadow: var(--unfold-shadow, 0 1px 2px rgb(0 0 0 / .3), 0 12px 30px -8px rgb(0 0 0 / .6));\n --_edge: var(--unfold-edge, rgb(243 243 246 / .22));\n --_label-bg: var(--unfold-label-bg, rgb(30 30 36 / .9));\n --_label-fg: var(--unfold-label-fg, var(--_fg));\n --_backdrop: var(--unfold-backdrop, rgb(8 8 12 / .45));\n", le = `
625
+ :host {
626
+ display: inline-block;
627
+ vertical-align: middle;
628
+ }
629
+ :host([hidden]) { display: none; }
630
+
631
+ .root {
632
+ --_accent: var(--unfold-accent, #5b5bf0);
633
+ --_accent-fg: var(--unfold-accent-fg, #fff);
634
+ --_blur: var(--unfold-blur, 18px);
635
+ --_backdrop-blur: var(--unfold-backdrop-blur, 6px);
636
+ --_edge-width: var(--unfold-edge-width, 1.5px);
637
+ --_icon-size: var(--unfold-icon-size, 22px);
638
+ --_dur: var(--unfold-duration, 340ms);
639
+ --_spring: var(--unfold-easing, cubic-bezier(.22, 1.2, .36, 1));
640
+ --_ease: cubic-bezier(.3, .7, .2, 1);
641
+ --_z: var(--unfold-z-index, 2147483000);
642
+ --_node-radius: var(--unfold-node-radius, 50%);
643
+ --_trigger-radius: var(--unfold-trigger-radius, 50%);
644
+ --_border-width: var(--unfold-border-width, 1px);
645
+ --_active-scale: var(--unfold-active-scale, 1.14);
646
+ --_dim-opacity: var(--unfold-dim-opacity, .7);
647
+ --_dim-scale: var(--unfold-dim-scale, .86);
648
+ --_label-radius: var(--unfold-label-radius, 9px);
649
+ /* Follows the reader's font-size preference, never smaller than 13px. */
650
+ --_label-size: var(--unfold-label-size, max(13px, .8125rem));
651
+ --_desc-size: var(--unfold-description-size, max(12px, .75rem));
652
+ --_dock-bg: var(--unfold-dock-bg, color-mix(in srgb, var(--_surface-active) 88%, transparent));
653
+ --_dock-border: var(--unfold-dock-border, var(--_outline));
654
+ --_dock-blur: var(--unfold-dock-blur, 16px);
655
+ --_label-weight: var(--unfold-label-weight, 600);
656
+ --_label-tracking: var(--unfold-label-tracking, normal);
657
+ --_label-case: var(--unfold-label-case, none);
658
+ ${ce}
659
+ /* Longhands on purpose: with no --unfold-font the family declaration is invalid and simply inherits the
660
+ page's font, while size and weight stay ours. (A shorthand with an inherited part would be dropped.) */
661
+ font-family: var(--unfold-font);
662
+ font-size: 13px;
663
+ font-weight: 500;
664
+ line-height: 1.25;
665
+ color: var(--_fg);
666
+ }
667
+ .root[data-theme="dark"] { ${I} }
668
+ @media (prefers-color-scheme: dark) {
669
+ .root[data-theme="auto"] { ${I} }
670
+ }
671
+
672
+ /* ---------------------------------------------------------------- button */
673
+
674
+ /* dock="bar": a strip along the button's edge, so page content never sits under the button. */
675
+ .dock-bar {
676
+ display: none;
677
+ position: fixed;
678
+ z-index: calc(var(--_z) - 1);
679
+ background: var(--_dock-bg);
680
+ border: 0 solid var(--_dock-border);
681
+ -webkit-backdrop-filter: blur(var(--_dock-blur)) saturate(1.4);
682
+ backdrop-filter: blur(var(--_dock-blur)) saturate(1.4);
683
+ }
684
+ .root[data-dock="bar"] .dock-bar[data-edge] { display: block; }
685
+ .dock-bar[data-edge="top"], .dock-bar[data-edge="bottom"] { left: 0; right: 0; }
686
+ .dock-bar[data-edge="left"], .dock-bar[data-edge="right"] { top: 0; bottom: 0; }
687
+ .dock-bar[data-edge="top"] { top: 0; height: calc(var(--_trigger-size) + var(--_offset-y) * 2 + env(safe-area-inset-top, 0px)); border-bottom-width: 1px; }
688
+ .dock-bar[data-edge="bottom"] { bottom: 0; height: calc(var(--_trigger-size) + var(--_offset-y) * 2 + env(safe-area-inset-bottom, 0px)); border-top-width: 1px; }
689
+ .dock-bar[data-edge="left"] { left: 0; width: calc(var(--_trigger-size) + var(--_offset-x) * 2 + env(safe-area-inset-left, 0px)); border-right-width: 1px; }
690
+ .dock-bar[data-edge="right"] { right: 0; width: calc(var(--_trigger-size) + var(--_offset-x) * 2 + env(safe-area-inset-right, 0px)); border-left-width: 1px; }
691
+
692
+ .dock {
693
+ position: fixed;
694
+ z-index: var(--_z);
695
+ width: var(--_trigger-size);
696
+ height: var(--_trigger-size);
697
+ --_ox: calc(var(--_offset-x) + env(safe-area-inset-left, 0px));
698
+ --_ox-r: calc(var(--_offset-x) + env(safe-area-inset-right, 0px));
699
+ --_oy: calc(var(--_offset-y) + env(safe-area-inset-top, 0px));
700
+ --_oy-b: calc(var(--_offset-y) + env(safe-area-inset-bottom, 0px));
701
+ }
702
+ .dock[data-position="inline"] { position: relative; z-index: auto; }
703
+ .dock[data-position^="top"] { top: var(--_oy); }
704
+ .dock[data-position^="bottom"] { bottom: var(--_oy-b); }
705
+ .dock[data-position$="left"] { left: var(--_ox); }
706
+ .dock[data-position$="right"] { right: var(--_ox-r); }
707
+ .dock[data-position="top"], .dock[data-position="bottom"], .dock[data-position="center"] {
708
+ left: calc(50% - var(--_trigger-size) / 2);
709
+ }
710
+ .dock[data-position="left"], .dock[data-position="right"], .dock[data-position="center"] {
711
+ top: calc(50% - var(--_trigger-size) / 2);
712
+ }
713
+
714
+ .trigger, .hub, .node {
715
+ all: unset;
716
+ box-sizing: border-box;
717
+ display: grid;
718
+ place-items: center;
719
+ border-radius: var(--_node-radius);
720
+ color: var(--_fg);
721
+ background: var(--_surface);
722
+ border: var(--_border-width) solid var(--_border);
723
+ box-shadow: var(--_shadow), 0 0 0 .5px var(--_outline);
724
+ -webkit-backdrop-filter: blur(var(--_blur)) saturate(1.8);
725
+ backdrop-filter: blur(var(--_blur)) saturate(1.8);
726
+ cursor: pointer;
727
+ touch-action: none;
728
+ user-select: none;
729
+ -webkit-user-select: none;
730
+ -webkit-touch-callout: none;
731
+ -webkit-tap-highlight-color: transparent;
732
+ }
733
+ .trigger:focus-visible, .hub:focus-visible, .node:focus-visible {
734
+ outline: 3px solid var(--_accent);
735
+ outline-offset: 3px;
736
+ }
737
+
738
+ .trigger, .hub { border-radius: var(--_trigger-radius); }
739
+ .trigger {
740
+ position: relative;
741
+ width: 100%;
742
+ height: 100%;
743
+ transition: scale 220ms var(--_ease), box-shadow 220ms;
744
+ }
745
+ .trigger:hover { box-shadow: var(--_shadow), 0 0 0 .5px var(--_outline), 0 0 0 6px color-mix(in srgb, var(--_accent) 10%, transparent); }
746
+ .trigger[data-pressing] { scale: .92; }
747
+ .trigger-icon, .hub-icon {
748
+ display: grid;
749
+ place-items: center;
750
+ width: var(--_icon-size);
751
+ height: var(--_icon-size);
752
+ pointer-events: none;
753
+ }
754
+ .trigger-icon[data-default] { rotate: var(--_icon-rotate, 0deg); }
755
+ .icon-own { display: contents; }
756
+ .trigger-icon[data-slotted] .icon-own { display: none; }
757
+ .icon-own > svg, .icon-own > img, .hub-icon > svg, .trigger-icon ::slotted(*) { width: 100%; height: 100%; display: block; }
758
+ .icon-own > img { object-fit: contain; }
759
+
760
+ .hold {
761
+ position: absolute;
762
+ inset: -5px;
763
+ width: calc(100% + 10px);
764
+ height: calc(100% + 10px);
765
+ rotate: -90deg;
766
+ pointer-events: none;
767
+ overflow: visible;
768
+ }
769
+ .hold circle {
770
+ fill: none;
771
+ stroke: var(--_accent);
772
+ stroke-width: 2.5;
773
+ stroke-linecap: round;
774
+ stroke-dasharray: 1 1;
775
+ stroke-dashoffset: 1;
776
+ opacity: 0;
777
+ transition: opacity 120ms, stroke-dashoffset 120ms;
778
+ }
779
+ .trigger[data-pressing] .hold circle {
780
+ opacity: 1;
781
+ stroke-dashoffset: 0;
782
+ transition: opacity 80ms, stroke-dashoffset var(--_hold) linear;
783
+ }
784
+
785
+ /* --------------------------------------------------------------- overlay */
786
+
787
+ .overlay {
788
+ position: fixed;
789
+ inset: 0;
790
+ width: 100%;
791
+ height: 100%;
792
+ max-width: none;
793
+ max-height: none;
794
+ margin: 0;
795
+ padding: 0;
796
+ border: 0;
797
+ background: transparent;
798
+ color: inherit;
799
+ overflow: hidden;
800
+ overscroll-behavior: none;
801
+ z-index: var(--_z);
802
+ touch-action: none;
803
+ }
804
+ .overlay:not([data-state]) { display: none; }
805
+ .overlay::backdrop { background: transparent; }
806
+ .overlay:focus { outline: none; }
807
+
808
+ .backdrop {
809
+ position: absolute;
810
+ inset: 0;
811
+ background: var(--_backdrop);
812
+ -webkit-backdrop-filter: blur(var(--_backdrop-blur)) saturate(1.2);
813
+ backdrop-filter: blur(var(--_backdrop-blur)) saturate(1.2);
814
+ opacity: 0;
815
+ transition: opacity var(--_dur) var(--_ease);
816
+ }
817
+ .root:not([data-backdrop]) .backdrop { background: transparent; -webkit-backdrop-filter: none; backdrop-filter: none; }
818
+ .overlay[data-state="open"] .backdrop { opacity: 1; }
819
+
820
+ .edges {
821
+ position: absolute;
822
+ inset: 0;
823
+ width: 100%;
824
+ height: 100%;
825
+ overflow: visible;
826
+ pointer-events: none;
827
+ }
828
+ .edge {
829
+ fill: none;
830
+ stroke: var(--_edge);
831
+ stroke-width: var(--_edge-width);
832
+ stroke-linecap: round;
833
+ stroke-dasharray: 1 1;
834
+ stroke-dashoffset: 1;
835
+ opacity: 0;
836
+ transition: stroke-dashoffset var(--_dur) var(--_ease) var(--_delay, 0ms), opacity 160ms var(--_delay, 0ms), stroke 160ms, stroke-width 160ms;
837
+ }
838
+ .edge[data-shown] { stroke-dashoffset: 0; opacity: 1; }
839
+ .edge[data-dim] { opacity: .45; }
840
+ .edge[data-lit] { stroke: var(--_lit, var(--_accent)); stroke-width: calc(var(--_edge-width) + .5px); }
841
+
842
+ .root[data-edges="none"] .edges { display: none; }
843
+
844
+ .layer, .labels { position: absolute; inset: 0; pointer-events: none; }
845
+
846
+ /* ----------------------------------------------------------------- nodes */
847
+
848
+ .hub {
849
+ position: absolute;
850
+ pointer-events: auto;
851
+ transition: scale 220ms var(--_ease);
852
+ }
853
+ .hub[data-active] { scale: 1.08; }
854
+ .hub-icon { transition: rotate var(--_dur) var(--_spring), opacity 160ms; }
855
+ .overlay:not([data-state="open"]) .hub-icon { rotate: -90deg; opacity: 0; }
856
+
857
+ .node {
858
+ position: absolute;
859
+ left: 0;
860
+ top: 0;
861
+ width: var(--_node-size);
862
+ height: var(--_node-size);
863
+ margin: calc(var(--_node-size) / -2) 0 0 calc(var(--_node-size) / -2);
864
+ pointer-events: auto;
865
+ scale: 1;
866
+ --_tint: var(--_node-accent, var(--_accent));
867
+ transition:
868
+ translate var(--_dur) var(--_spring) var(--_delay, 0ms),
869
+ scale var(--_dur) var(--_spring) var(--_delay, 0ms),
870
+ opacity calc(var(--_dur) * .6) var(--_ease) var(--_delay, 0ms),
871
+ background-color 160ms, border-color 160ms, box-shadow 160ms, color 160ms;
872
+ }
873
+ .node[data-state="dim"] { opacity: var(--_dim-opacity); scale: var(--_dim-scale); }
874
+ .node[data-state="path"] { border-color: color-mix(in srgb, var(--_tint) 55%, transparent); color: var(--_tint); }
875
+ .node[data-active] {
876
+ background: var(--_surface-active);
877
+ color: var(--_tint);
878
+ opacity: 1;
879
+ scale: var(--_active-scale);
880
+ box-shadow: var(--_shadow), 0 0 0 2px var(--_tint), 0 0 0 7px color-mix(in srgb, var(--_tint) 18%, transparent);
881
+ }
882
+ .node[data-current] { color: var(--_tint); }
883
+ .node[data-current]::before {
884
+ content: "";
885
+ position: absolute;
886
+ inset: -4px;
887
+ border-radius: inherit;
888
+ border: 1.5px dashed color-mix(in srgb, var(--_tint) 70%, transparent);
889
+ pointer-events: none;
890
+ }
891
+ .node[aria-disabled="true"] { opacity: .35; cursor: not-allowed; }
892
+ .node[data-enter], .node[data-leave] { scale: .35; opacity: 0; }
893
+ .node[data-leave] { pointer-events: none; transition-duration: calc(var(--_dur) * .7); }
894
+
895
+ /* A knob on the rim, pointing where the children will unfold. */
896
+ .node[data-branch]::after {
897
+ content: "";
898
+ position: absolute;
899
+ width: 7px;
900
+ height: 7px;
901
+ border-radius: 50%;
902
+ background: var(--_surface-active);
903
+ box-shadow: 0 0 0 1.5px var(--_muted);
904
+ translate: calc(cos(var(--_a, 0deg)) * (var(--_node-size) / 2 - 1px)) calc(sin(var(--_a, 0deg)) * (var(--_node-size) / 2 - 1px));
905
+ transition: background-color 160ms, box-shadow 160ms;
906
+ }
907
+ .node[data-branch][data-current-trail]::after,
908
+ .node[data-branch][data-state="path"]::after,
909
+ .node[data-branch][data-active]::after { background: var(--_tint); box-shadow: 0 0 0 1.5px var(--_tint); }
910
+ .node[data-state="path"]::after { opacity: 0; }
911
+
912
+ .icon {
913
+ display: grid;
914
+ place-items: center;
915
+ width: var(--_icon-size);
916
+ height: var(--_icon-size);
917
+ pointer-events: none;
918
+ }
919
+ .icon > svg, .icon > img { width: 100%; height: 100%; display: block; }
920
+ .icon > img { object-fit: contain; border-radius: 4px; }
921
+ .glyph { font-size: calc(var(--_icon-size) * .92); line-height: 1; }
922
+ .mono { font-weight: 650; font-size: calc(var(--_icon-size) * .8); line-height: 1; letter-spacing: -.01em; }
923
+
924
+ /* ---------------------------------------------------------------- labels */
925
+
926
+ .label {
927
+ position: absolute;
928
+ left: 0;
929
+ top: 0;
930
+ box-sizing: border-box;
931
+ width: max-content;
932
+ max-width: var(--unfold-label-max-width, 160px);
933
+ padding: 5px 10px 6px;
934
+ border-radius: var(--_label-radius);
935
+ background: var(--_label-bg);
936
+ color: var(--_label-fg);
937
+ box-shadow: 0 1px 2px rgb(0 0 0 / .06), 0 4px 14px -4px rgb(0 0 0 / .18), 0 0 0 .5px var(--_outline);
938
+ -webkit-backdrop-filter: blur(var(--_blur));
939
+ backdrop-filter: blur(var(--_blur));
940
+ overflow-wrap: anywhere;
941
+ pointer-events: none;
942
+ opacity: 0;
943
+ /* Labels never slide: a sliding label could cross another one on its way. */
944
+ transition: opacity 140ms var(--_ease);
945
+ }
946
+ .label[data-visible] { opacity: 1; transition-delay: var(--_label-delay, 0ms), 0ms; }
947
+ .label[data-instant] { transition: none; }
948
+ .label .title {
949
+ display: block;
950
+ text-wrap: balance;
951
+ font-family: var(--unfold-label-font, var(--unfold-font));
952
+ font-size: var(--_label-size);
953
+ font-weight: var(--_label-weight);
954
+ line-height: 1.25;
955
+ letter-spacing: var(--_label-tracking);
956
+ text-transform: var(--_label-case);
957
+ }
958
+ .label[data-go] .title::after { content: " →"; color: var(--_muted); font-weight: 500; }
959
+ .label .desc { display: none; margin-top: 2px; font-weight: 450; font-size: var(--_desc-size); line-height: 1.35; color: var(--_muted); }
960
+ .label[data-active] { max-width: var(--unfold-label-max-width-active, 240px); }
961
+ /* The wrapped variant, used where a one-line label doesn't fit. Words stay whole (hyphenated if needed). */
962
+ .label[data-narrow] {
963
+ max-width: var(--unfold-label-narrow-width, 96px);
964
+ min-width: min-content;
965
+ overflow-wrap: normal;
966
+ hyphens: auto;
967
+ }
968
+ .label[data-active]:not([data-compact]) .desc { display: block; }
969
+ .labels .measure { position: absolute; left: 0; top: 0; visibility: hidden; }
970
+
971
+ .sr-only {
972
+ position: absolute;
973
+ width: 1px;
974
+ height: 1px;
975
+ overflow: hidden;
976
+ clip-path: inset(50%);
977
+ white-space: nowrap;
978
+ }
979
+ .safe-probe {
980
+ position: fixed;
981
+ inset: 0;
982
+ visibility: hidden;
983
+ pointer-events: none;
984
+ padding: env(safe-area-inset-top, 0px) env(safe-area-inset-right, 0px) env(safe-area-inset-bottom, 0px) env(safe-area-inset-left, 0px);
985
+ }
986
+
987
+ /* Right-to-left: "back" points the other way. */
988
+ :host(:dir(rtl)) .hub[data-icon="back"] .hub-icon { scale: -1 1; }
989
+
990
+ @media (prefers-contrast: more) {
991
+ .root {
992
+ --_border-width: 2px;
993
+ --_border: currentColor;
994
+ --_muted: var(--_fg);
995
+ --_surface: var(--_surface-active);
996
+ --_label-bg: var(--_surface-active);
997
+ --_edge: color-mix(in srgb, var(--_fg) 60%, transparent);
998
+ --_dim-opacity: 1;
999
+ --_blur: 0px;
1000
+ }
1001
+ .label { border: 1.5px solid currentColor; }
1002
+ }
1003
+
1004
+ @media (prefers-reduced-transparency: reduce) {
1005
+ .root { --_surface: var(--_surface-active); --_label-bg: var(--_surface-active); --_blur: 0px; --_backdrop-blur: 0px; --_dock-blur: 0px; }
1006
+ }
1007
+
1008
+ /* Windows High Contrast and other forced palettes: states move from colour/shadow to outlines. */
1009
+ @media (forced-colors: active) {
1010
+ .trigger, .hub, .node { border: 2px solid ButtonText; }
1011
+ .node[data-active], .hub[data-active] { outline: 3px solid Highlight; outline-offset: 2px; }
1012
+ .node[data-state="path"] { border-color: Highlight; }
1013
+ .node[data-state="dim"] { opacity: 1; }
1014
+ .node[data-current]::before { border-color: Highlight; }
1015
+ .node[data-branch]::after { background: ButtonText; box-shadow: none; }
1016
+ .edge { stroke: CanvasText; }
1017
+ .edge[data-lit] { stroke: Highlight; }
1018
+ .label { border: 1px solid CanvasText; }
1019
+ .backdrop { background: Canvas; opacity: .9; }
1020
+ .hold circle { stroke: Highlight; }
1021
+ }
1022
+
1023
+ @media (prefers-reduced-motion: reduce) {
1024
+ .root { --_dur: 1ms; --_spring: linear; }
1025
+ .label { transition: opacity 120ms; }
1026
+ }
1027
+ `, L = Object.freeze({
1028
+ pages: "Pages",
1029
+ close: "Close navigation",
1030
+ backToTop: "Back to all pages",
1031
+ back: "Back to {label}"
1032
+ }), R = Object.freeze({
1033
+ pages: Object.freeze([]),
1034
+ position: "bottom-right",
1035
+ dock: "float",
1036
+ offset: 24,
1037
+ holdDelay: 220,
1038
+ expandDelay: 140,
1039
+ openOnTap: !0,
1040
+ nodeSize: 52,
1041
+ triggerSize: 60,
1042
+ spacing: 96,
1043
+ gap: 16,
1044
+ labels: "auto",
1045
+ edges: "curved",
1046
+ backdrop: !0,
1047
+ haptics: !0,
1048
+ theme: "auto",
1049
+ current: void 0,
1050
+ label: "Navigation",
1051
+ arrowKeys: "tree",
1052
+ strings: Object.freeze({}),
1053
+ triggerIcon: void 0,
1054
+ iconResolver: void 0,
1055
+ navigate: void 0
1056
+ });
1057
+ function z(e) {
1058
+ return typeof e == "string" || typeof e == "function" || typeof e == "object" && !!e && "cloneNode" in e && typeof e.cloneNode == "function";
1059
+ }
1060
+ var ue = {
1061
+ position: [
1062
+ "top-left",
1063
+ "top",
1064
+ "top-right",
1065
+ "left",
1066
+ "center",
1067
+ "right",
1068
+ "bottom-left",
1069
+ "bottom",
1070
+ "bottom-right",
1071
+ "inline"
1072
+ ],
1073
+ dock: ["float", "bar"],
1074
+ labels: [
1075
+ "auto",
1076
+ "always",
1077
+ "hover",
1078
+ "none"
1079
+ ],
1080
+ edges: [
1081
+ "curved",
1082
+ "straight",
1083
+ "step",
1084
+ "none"
1085
+ ],
1086
+ theme: [
1087
+ "auto",
1088
+ "light",
1089
+ "dark"
1090
+ ],
1091
+ arrowKeys: ["tree", "spatial"]
1092
+ };
1093
+ function de(e, t) {
1094
+ if (!t || typeof t != "object" || Array.isArray(t)) throw TypeError("[unfold-nav] Options must be an object.");
1095
+ let n = { ...e };
1096
+ for (let [e, r] of Object.entries(t)) {
1097
+ if (!Object.hasOwn(R, e)) continue;
1098
+ let t = e;
1099
+ n[t] = r === void 0 ? R[t] : r;
1100
+ }
1101
+ for (let [e, t] of Object.entries(ue)) if (!t.includes(n[e])) throw TypeError(`[unfold-nav] Invalid ${e}. Expected ${t.join(", ")}.`);
1102
+ for (let e of [
1103
+ "nodeSize",
1104
+ "triggerSize",
1105
+ "spacing",
1106
+ "gap",
1107
+ "holdDelay",
1108
+ "expandDelay"
1109
+ ]) {
1110
+ let t = n[e], r = e === "nodeSize" || e === "triggerSize" || e === "spacing";
1111
+ if (typeof t != "number" || !Number.isFinite(t) || (r ? t <= 0 : t < 0)) throw RangeError(`[unfold-nav] ${e} must be a finite ${r ? "positive" : "non-negative"} number.`);
1112
+ }
1113
+ let r = n.offset, i = typeof r == "number" ? [r] : r && typeof r == "object" ? [r.x, r.y] : [];
1114
+ if (!i.length || i.some((e) => typeof e != "number" || !Number.isFinite(e) || e < 0)) throw RangeError("[unfold-nav] offset must be a non-negative number or { x, y }.");
1115
+ for (let e of [
1116
+ "openOnTap",
1117
+ "backdrop",
1118
+ "haptics"
1119
+ ]) if (typeof n[e] != "boolean") throw TypeError(`[unfold-nav] ${e} must be a boolean.`);
1120
+ if (typeof n.label != "string" || !n.label.trim()) throw TypeError("[unfold-nav] label must be a non-empty string.");
1121
+ if (n.current != null && typeof n.current != "string") throw TypeError("[unfold-nav] current must be a URL string, null or undefined.");
1122
+ for (let e of ["navigate", "iconResolver"]) if (n[e] !== void 0 && typeof n[e] != "function") throw TypeError(`[unfold-nav] ${e} must be a function.`);
1123
+ if (n.triggerIcon !== void 0 && !z(n.triggerIcon)) throw TypeError("[unfold-nav] Invalid triggerIcon.");
1124
+ if (!n.strings || typeof n.strings != "object" || Array.isArray(n.strings)) throw TypeError("[unfold-nav] strings must be an object.");
1125
+ for (let e of Object.keys(L)) if (n.strings[e] !== void 0 && typeof n.strings[e] != "string") throw TypeError(`[unfold-nav] strings.${e} must be a string.`);
1126
+ return n.strings = Object.fromEntries(Object.entries(n.strings).filter(([e, t]) => Object.hasOwn(L, e) && t !== void 0)), n;
1127
+ }
1128
+ //#endregion
1129
+ //#region src/tree.ts
1130
+ function B(e) {
1131
+ let t = Array.isArray(e) ? {
1132
+ label: "Navigation",
1133
+ children: e
1134
+ } : e ?? {
1135
+ label: "Navigation",
1136
+ children: []
1137
+ }, n = /* @__PURE__ */ new Map(), r = {
1138
+ id: "",
1139
+ page: t,
1140
+ parent: null,
1141
+ children: [],
1142
+ depth: 0,
1143
+ index: 0
1144
+ }, i = /* @__PURE__ */ new WeakSet(), a = (e, t) => {
1145
+ if (!e || typeof e != "object" || Array.isArray(e)) throw TypeError(`[unfold-nav] ${t} must be a page object.`);
1146
+ if (typeof e.label != "string" || !e.label.trim()) throw TypeError(`[unfold-nav] ${t}.label must be a non-empty string.`);
1147
+ for (let n of [
1148
+ "id",
1149
+ "href",
1150
+ "target",
1151
+ "description",
1152
+ "color"
1153
+ ]) if (e[n] !== void 0 && typeof e[n] != "string") throw TypeError(`[unfold-nav] ${t}.${n} must be a string.`);
1154
+ if (e.children !== void 0 && !Array.isArray(e.children)) throw TypeError(`[unfold-nav] ${t}.children must be an array.`);
1155
+ if (e.disabled !== void 0 && typeof e.disabled != "boolean") throw TypeError(`[unfold-nav] ${t}.disabled must be a boolean.`);
1156
+ if (e.icon !== void 0 && !z(e.icon)) throw TypeError(`[unfold-nav] ${t}.icon is invalid.`);
1157
+ if (i.has(e)) throw TypeError(`[unfold-nav] ${t} contains a cyclic page tree.`);
1158
+ };
1159
+ a(t, "pages"), i.add(t);
1160
+ let o = (e, t, r) => {
1161
+ (t ?? []).forEach((t, s) => {
1162
+ let c = [...r, s];
1163
+ a(t, `pages[${c.join("].children[")}]`);
1164
+ let l = t.id != null && t.id !== "" ? String(t.id) : `n${c.join(".")}`;
1165
+ if (n.has(l)) {
1166
+ let e = 2;
1167
+ for (; n.has(`${l}~${e}`);) e++;
1168
+ l = `${l}~${e}`;
1169
+ }
1170
+ let u = {
1171
+ id: l,
1172
+ page: t,
1173
+ parent: e,
1174
+ children: [],
1175
+ depth: e.depth + 1,
1176
+ index: s
1177
+ };
1178
+ n.set(l, u), e.children.push(u), i.add(t), o(u, t.children, c), i.delete(t);
1179
+ });
1180
+ };
1181
+ return o(r, t.children, []), {
1182
+ root: r,
1183
+ byId: n
1184
+ };
1185
+ }
1186
+ function V(e) {
1187
+ let t = [];
1188
+ for (let n = e; n && n.depth > 0; n = n.parent) t.unshift(n.id);
1189
+ return t;
1190
+ }
1191
+ var H = (e) => e.length > 1 ? e.replace(/\/+$/, "") : e;
1192
+ function U(e, t, n) {
1193
+ let r;
1194
+ try {
1195
+ r = new URL(t, n);
1196
+ } catch {
1197
+ return null;
1198
+ }
1199
+ let i = H(r.pathname), a = null, o = -1, s = null, c = -1;
1200
+ for (let t of e.byId.values()) {
1201
+ let e = t.page.href;
1202
+ if (!e) continue;
1203
+ let l;
1204
+ try {
1205
+ l = new URL(e, n);
1206
+ } catch {
1207
+ continue;
1208
+ }
1209
+ if (l.origin !== r.origin || l.search && l.search !== r.search || l.hash && l.hash !== r.hash) continue;
1210
+ let u = H(l.pathname);
1211
+ if (u === i) {
1212
+ let e = +!!l.search + +!!l.hash;
1213
+ (!a || e > o || e === o && t.depth > a.depth) && (a = t, o = e);
1214
+ } else u !== "/" && i.startsWith(u + "/") && u.length > c && (s = t, c = u.length);
1215
+ }
1216
+ return a ?? s;
1217
+ }
1218
+ //#endregion
1219
+ //#region src/insets.ts
1220
+ var fe = [
1221
+ "top",
1222
+ "right",
1223
+ "bottom",
1224
+ "left"
1225
+ ], W = /* @__PURE__ */ new WeakMap();
1226
+ function pe(e, t, n, r = 0) {
1227
+ let i = W.get(t);
1228
+ if (!i && !n) return;
1229
+ i || (i = {
1230
+ owners: /* @__PURE__ */ new Map(),
1231
+ previous: /* @__PURE__ */ new Map()
1232
+ }, W.set(t, i)), n ? i.owners.set(e, {
1233
+ edge: n,
1234
+ size: r
1235
+ }) : i.owners.delete(e);
1236
+ let a = t.documentElement.style;
1237
+ for (let e of fe) {
1238
+ let t = `--unfold-inset-${e}`, n = [...i.owners.values()].filter((t) => t.edge === e).map((e) => e.size);
1239
+ if (n.length) i.previous.has(e) || i.previous.set(e, {
1240
+ value: a.getPropertyValue(t),
1241
+ priority: a.getPropertyPriority(t)
1242
+ }), a.setProperty(t, `calc(${Math.max(...n)}px + env(safe-area-inset-${e}, 0px))`);
1243
+ else {
1244
+ let n = i.previous.get(e);
1245
+ n && (n.value ? a.setProperty(t, n.value, n.priority) : a.removeProperty(t), i.previous.delete(e));
1246
+ }
1247
+ }
1248
+ i.owners.size || W.delete(t);
1249
+ }
1250
+ //#endregion
1251
+ //#region src/navigation.ts
1252
+ function me(e, t) {
1253
+ try {
1254
+ let n = new URL(e, t);
1255
+ return [
1256
+ "http:",
1257
+ "https:",
1258
+ "mailto:",
1259
+ "tel:",
1260
+ "sms:"
1261
+ ].includes(n.protocol) ? n.href : null;
1262
+ } catch {
1263
+ return null;
1264
+ }
1265
+ }
1266
+ //#endregion
1267
+ //#region src/element.ts
1268
+ var G = Object.keys(R), K = {
1269
+ pages: "pages",
1270
+ position: "position",
1271
+ dock: "dock",
1272
+ offset: "offset",
1273
+ "hold-delay": "holdDelay",
1274
+ "expand-delay": "expandDelay",
1275
+ "open-on-tap": "openOnTap",
1276
+ "node-size": "nodeSize",
1277
+ "trigger-size": "triggerSize",
1278
+ spacing: "spacing",
1279
+ gap: "gap",
1280
+ labels: "labels",
1281
+ edges: "edges",
1282
+ backdrop: "backdrop",
1283
+ haptics: "haptics",
1284
+ theme: "theme",
1285
+ current: "current",
1286
+ label: "label",
1287
+ "arrow-keys": "arrowKeys",
1288
+ "trigger-icon": "triggerIcon"
1289
+ };
1290
+ function he(e, t) {
1291
+ if (t === null) return R[e];
1292
+ if (e === "offset") {
1293
+ let [e, n = e] = t.trim().split(/[\s,]+/).map(Number);
1294
+ return {
1295
+ x: e,
1296
+ y: n
1297
+ };
1298
+ }
1299
+ if (e === "pages") return JSON.parse(t);
1300
+ switch (typeof R[e]) {
1301
+ case "number": return Number(t);
1302
+ case "boolean": return !/^(false|0|off|no)$/i.test(t.trim());
1303
+ }
1304
+ return t;
1305
+ }
1306
+ var ge = "http://www.w3.org/2000/svg", _e = "", q = typeof HTMLElement < "u" && "showPopover" in HTMLElement.prototype, ve = typeof HTMLElement > "u" ? class {} : HTMLElement, J = {
1307
+ ArrowUp: {
1308
+ x: 0,
1309
+ y: -1
1310
+ },
1311
+ ArrowDown: {
1312
+ x: 0,
1313
+ y: 1
1314
+ },
1315
+ ArrowLeft: {
1316
+ x: -1,
1317
+ y: 0
1318
+ },
1319
+ ArrowRight: {
1320
+ x: 1,
1321
+ y: 0
1322
+ }
1323
+ };
1324
+ function Y(e, t, n, r = "") {
1325
+ n ? e.setAttribute(t, r) : e.removeAttribute(t);
1326
+ }
1327
+ function X(e, t, n) {
1328
+ let r = [t];
1329
+ for (let [e, i] of Object.entries(n)) i && r.push(`${t}-${e}`);
1330
+ let i = r.join(" ");
1331
+ e.getAttribute("part") !== i && e.setAttribute("part", i);
1332
+ }
1333
+ var Z = class extends ve {
1334
+ static get observedAttributes() {
1335
+ return Object.keys(K);
1336
+ }
1337
+ opts = { ...R };
1338
+ text = L;
1339
+ tree = B([]);
1340
+ currentId = null;
1341
+ currentTrail = /* @__PURE__ */ new Set();
1342
+ state = "closed";
1343
+ mode = "tap";
1344
+ path = [];
1345
+ active = null;
1346
+ focusId = null;
1347
+ views = /* @__PURE__ */ new Map();
1348
+ placed = /* @__PURE__ */ new Map();
1349
+ fanCache = /* @__PURE__ */ new Map();
1350
+ labelSizes = /* @__PURE__ */ new Map();
1351
+ branchLabelSizes = /* @__PURE__ */ new Map();
1352
+ narrowSizes = /* @__PURE__ */ new Map();
1353
+ narrowBranchSizes = /* @__PURE__ */ new Map();
1354
+ hub = {
1355
+ x: 0,
1356
+ y: 0,
1357
+ r: 30
1358
+ };
1359
+ bounds = {
1360
+ x: 0,
1361
+ y: 0,
1362
+ w: 0,
1363
+ h: 0
1364
+ };
1365
+ animationTimers = /* @__PURE__ */ new Set();
1366
+ pointerId = null;
1367
+ pointerType = "mouse";
1368
+ downAt = {
1369
+ x: 0,
1370
+ y: 0
1371
+ };
1372
+ lastPoint = {
1373
+ x: 0,
1374
+ y: 0
1375
+ };
1376
+ leftHub = !1;
1377
+ holdTimer = 0;
1378
+ dwellTimer = 0;
1379
+ dwellTarget = null;
1380
+ closeTimer = 0;
1381
+ resizeFrame = 0;
1382
+ openedAt = 0;
1383
+ lastPointerUp = -Infinity;
1384
+ typeahead = {
1385
+ text: "",
1386
+ at: 0
1387
+ };
1388
+ lastKeyActivation = -Infinity;
1389
+ els;
1390
+ constructor() {
1391
+ if (typeof document > "u") throw Error("[unfold-nav] Create navigation elements in a browser after mounting.");
1392
+ super();
1393
+ let e = this.attachShadow({ mode: "open" });
1394
+ e.innerHTML = `<style>${le}</style>
1395
+ <div class="root" part="root">
1396
+ <div class="dock-bar" part="dock-bar" aria-hidden="true"></div>
1397
+ <div class="dock" part="dock">
1398
+ <button class="trigger" part="trigger" type="button" aria-haspopup="dialog" aria-expanded="false" aria-controls="graph">
1399
+ <svg class="hold" viewBox="0 0 100 100" aria-hidden="true"><circle cx="50" cy="50" r="47" pathLength="1"/></svg>
1400
+ <span class="trigger-icon" part="trigger-icon" aria-hidden="true"><slot name="icon"></slot><span class="icon-own"></span></span>
1401
+ </button>
1402
+ </div>
1403
+ <dialog class="overlay" part="overlay" id="graph">
1404
+ <div class="backdrop" part="backdrop" aria-hidden="true"></div>
1405
+ <svg class="edges" part="edges" aria-hidden="true"></svg>
1406
+ <div class="layer" role="tree"></div>
1407
+ <div class="labels" aria-hidden="true"><div class="measure"></div></div>
1408
+ <button class="hub" part="hub" type="button"><span class="hub-icon" aria-hidden="true"></span></button>
1409
+ </dialog>
1410
+ <div class="safe-probe" aria-hidden="true"></div>
1411
+ </div>`;
1412
+ let t = (t) => e.querySelector(t);
1413
+ this.els = {
1414
+ root: t(".root"),
1415
+ dockBar: t(".dock-bar"),
1416
+ dock: t(".dock"),
1417
+ trigger: t(".trigger"),
1418
+ triggerIcon: t(".trigger-icon"),
1419
+ iconSlot: t("slot[name=\"icon\"]"),
1420
+ iconOwn: t(".icon-own"),
1421
+ overlay: t(".overlay"),
1422
+ edges: t(".edges"),
1423
+ layer: t(".layer"),
1424
+ labels: t(".labels"),
1425
+ measure: t(".measure"),
1426
+ hub: t(".hub"),
1427
+ hubIcon: t(".hub-icon"),
1428
+ probe: t(".safe-probe")
1429
+ }, q && this.els.overlay.setAttribute("popover", "manual"), this.els.iconSlot.addEventListener("slotchange", () => this.syncSlotted());
1430
+ let { trigger: n, overlay: r } = this.els;
1431
+ n.addEventListener("pointerdown", this.onPointerDown), n.addEventListener("pointermove", this.onPointerMove), n.addEventListener("pointerup", this.onPointerUp), n.addEventListener("pointercancel", this.onPointerCancel), n.addEventListener("lostpointercapture", this.onLostCapture), n.addEventListener("click", this.onTriggerClick), n.addEventListener("keydown", this.onTriggerKey), n.addEventListener("contextmenu", (e) => e.preventDefault()), r.addEventListener("click", this.onOverlayClick), r.addEventListener("pointermove", this.onOverlayHover), r.addEventListener("keydown", this.onOverlayKey), r.addEventListener("cancel", this.onCancel), r.addEventListener("close", this.onDialogClose), r.addEventListener("wheel", (e) => e.preventDefault(), { passive: !1 }), r.addEventListener("touchmove", (e) => e.preventDefault(), { passive: !1 }), r.addEventListener("contextmenu", (e) => e.preventDefault()), this.applyOptions();
1432
+ }
1433
+ configure(e) {
1434
+ let t = de(this.opts, e), n = Object.hasOwn(e, "pages"), r = n ? B(t.pages) : this.tree;
1435
+ if (this.opts = t, this.tree = r, n && this.state !== "closed") {
1436
+ let e = this.state === "open", t = this.focusInside();
1437
+ this.finishClose(), t && this.isConnected && this.els.trigger.focus({ preventScroll: !0 }), e && this.emit("unfold-close");
1438
+ }
1439
+ return this.fanCache.clear(), this.clearLabelSizes(), this.applyOptions(), this.state === "open" && (this.measure(), this.render()), this;
1440
+ }
1441
+ get isOpen() {
1442
+ return this.state === "open";
1443
+ }
1444
+ open({ focus: e = !1 } = {}) {
1445
+ this.show("tap", e ? "keyboard" : "pointer");
1446
+ }
1447
+ close({ focusTrigger: e = !1 } = {}) {
1448
+ if (this.state !== "open") return;
1449
+ let t = this.focusInside();
1450
+ this.state = "closing", window.clearTimeout(this.holdTimer), window.clearTimeout(this.dwellTimer), this.holdTimer = this.dwellTimer = 0, this.dwellTarget = null, this.pointerId = null, window.removeEventListener("keydown", this.onWindowKey, !0);
1451
+ let { overlay: n, trigger: r, hub: i } = this.els;
1452
+ if (n.dataset.state = "closing", n.setAttribute("aria-hidden", "true"), i.tabIndex = -1, n.open && (n.close(), q)) try {
1453
+ n.showPopover();
1454
+ } catch {}
1455
+ r.setAttribute("aria-expanded", "false");
1456
+ let a = [...this.views.values()].filter((e) => !e.leaveTimer).reverse(), o = this.reducedMotion ? 0 : Math.min(12, 120 / Math.max(1, a.length));
1457
+ a.forEach((e, t) => this.leave(e, this.hub, t * o)), this.closeTimer = window.setTimeout(() => this.finishClose(), this.duration + a.length * o + 60), (e || t) && r.focus({ preventScroll: !0 }), this.emit("unfold-close");
1458
+ }
1459
+ toggle() {
1460
+ this.state === "open" ? this.close() : this.open();
1461
+ }
1462
+ connectedCallback() {
1463
+ for (let e of G) this.upgradeProperty(e);
1464
+ if (!this.tree.root.children.length) {
1465
+ let e = this.querySelector("script[type=\"application/json\"]");
1466
+ if (e?.textContent?.trim()) try {
1467
+ this.configure({ pages: JSON.parse(e.textContent) });
1468
+ } catch (e) {
1469
+ console.warn("[unfold-nav] Invalid inline pages:", e);
1470
+ }
1471
+ }
1472
+ window.addEventListener("resize", this.onResize), document.fonts?.addEventListener("loadingdone", this.onFontsLoaded), this.publishInsets(), this.onResize();
1473
+ }
1474
+ disconnectedCallback() {
1475
+ window.removeEventListener("resize", this.onResize), window.removeEventListener("keydown", this.onWindowKey, !0), document.fonts?.removeEventListener("loadingdone", this.onFontsLoaded), window.clearTimeout(this.holdTimer), window.clearTimeout(this.dwellTimer), this.holdTimer = this.dwellTimer = 0, this.pointerId = null, this.els.trigger.removeAttribute("data-pressing"), cancelAnimationFrame(this.resizeFrame), this.resizeFrame = 0, this.finishClose(), this.publishInsets();
1476
+ }
1477
+ attributeChangedCallback(e, t, n) {
1478
+ if (t === n) return;
1479
+ let r = K[e];
1480
+ if (r) try {
1481
+ this.configure({ [r]: he(r, n) });
1482
+ } catch (t) {
1483
+ console.warn(`[unfold-nav] Ignoring invalid ${e} attribute:`, t);
1484
+ }
1485
+ }
1486
+ upgradeProperty(e) {
1487
+ if (Object.prototype.hasOwnProperty.call(this, e)) {
1488
+ let t = this[e];
1489
+ delete this[e], this.configure({ [e]: t });
1490
+ }
1491
+ }
1492
+ applyOptions() {
1493
+ let e = this.opts, { root: t, dock: n, dockBar: r, trigger: i, layer: a, overlay: o } = this.els;
1494
+ this.text = {
1495
+ ...L,
1496
+ ...e.strings
1497
+ }, t.dataset.theme = e.theme, t.dataset.edges = e.edges, t.dataset.dock = e.dock, Y(t, "data-backdrop", e.backdrop), n.dataset.position = e.position;
1498
+ let s = this.dockEdge();
1499
+ Y(r, "data-edge", !!s, s ?? "");
1500
+ let c = this.offsets;
1501
+ t.style.setProperty("--_offset-x", `${c.x}px`), t.style.setProperty("--_offset-y", `${c.y}px`), t.style.setProperty("--_trigger-size", `${e.triggerSize}px`), t.style.setProperty("--_node-size", `${e.nodeSize}px`), t.style.setProperty("--_hold", `${e.holdDelay}ms`), i.setAttribute("aria-label", e.label), o.setAttribute("aria-label", e.label), a.setAttribute("aria-label", this.text.pages), this.syncIcon(), this.publishInsets();
1502
+ }
1503
+ get offsets() {
1504
+ let e = this.opts.offset;
1505
+ return typeof e == "number" ? {
1506
+ x: e,
1507
+ y: e
1508
+ } : e;
1509
+ }
1510
+ dockEdge() {
1511
+ let e = this.opts.position;
1512
+ return e === "center" || e === "inline" ? null : e.startsWith("top") ? "top" : e.startsWith("bottom") ? "bottom" : e;
1513
+ }
1514
+ publishInsets() {
1515
+ let e = this.isConnected ? this.dockEdge() : null, t = this.offsets, n = this.opts.triggerSize + 2 * (e === "top" || e === "bottom" ? t.y : t.x);
1516
+ pe(this, this.ownerDocument, e, n);
1517
+ }
1518
+ syncIcon() {
1519
+ let e = this.opts, t = n(e.triggerIcon ?? this.tree.root.page.icon, e.iconResolver);
1520
+ this.els.iconOwn.replaceChildren(t ?? n(a, void 0)), this.els.iconOwn.toggleAttribute("data-custom", !!t), this.syncSlotted();
1521
+ }
1522
+ syncSlotted() {
1523
+ let e = this.els.iconSlot.assignedNodes().length > 0;
1524
+ Y(this.els.triggerIcon, "data-slotted", e), Y(this.els.triggerIcon, "data-default", !e && !this.els.iconOwn.hasAttribute("data-custom")), this.isConnected && this.orientIcon();
1525
+ }
1526
+ show(e, t) {
1527
+ if (this.state === "open" || !this.isConnected) return;
1528
+ window.clearTimeout(this.closeTimer), this.state = "open", this.mode = e, this.path = [], this.active = null, this.focusId = null, this.dwellTarget = null, this.computeCurrent();
1529
+ let { overlay: n, trigger: r, hub: i } = this.els;
1530
+ if (n.dataset.state = "open", n.dataset.mode = e, n.removeAttribute("aria-hidden"), i.tabIndex = 0, this.present(e === "tap"), r.setAttribute("aria-expanded", "true"), this.openedAt = performance.now(), this.measure(), this.render(), this.haptic(10), e === "drag" && window.addEventListener("keydown", this.onWindowKey, !0), this.emit("unfold-open", { mode: e }), e === "tap" && t !== "none") {
1531
+ let e = this.initialFocusId();
1532
+ e && this.focusNode(e, t === "keyboard");
1533
+ }
1534
+ }
1535
+ present(e) {
1536
+ let t = this.els.overlay;
1537
+ try {
1538
+ e ? (q && t.matches(":popover-open") && t.hidePopover(), t.open || t.showModal()) : !t.open && !(q && t.matches(":popover-open")) && (q ? t.showPopover() : t.show());
1539
+ } catch {}
1540
+ }
1541
+ finishClose() {
1542
+ window.clearTimeout(this.closeTimer), this.state = "closed", window.removeEventListener("keydown", this.onWindowKey, !0), window.clearTimeout(this.holdTimer), window.clearTimeout(this.dwellTimer), this.holdTimer = this.dwellTimer = 0, this.dwellTarget = null, this.pointerId = null, this.els.trigger.removeAttribute("data-pressing");
1543
+ for (let e of this.animationTimers) window.clearTimeout(e);
1544
+ this.animationTimers.clear();
1545
+ for (let e of this.views.values()) window.clearTimeout(e.leaveTimer), e.node.remove(), e.label.remove(), e.edge.remove();
1546
+ this.views.clear(), this.placed.clear();
1547
+ let { overlay: e } = this.els;
1548
+ try {
1549
+ e.open && e.close(), q && e.matches(":popover-open") && e.hidePopover();
1550
+ } catch {}
1551
+ e.removeAttribute("data-state"), e.removeAttribute("aria-hidden"), this.els.trigger.setAttribute("aria-expanded", "false");
1552
+ }
1553
+ onCancel = (e) => {
1554
+ e.preventDefault(), this.state === "open" && (this.path.length ? this.goUp(!0) : this.close({ focusTrigger: !0 }));
1555
+ };
1556
+ onDialogClose = () => {
1557
+ this.state === "open" && !this.els.overlay.open && !(q && this.els.overlay.matches(":popover-open")) && (this.finishClose(), this.emit("unfold-close"), this.els.trigger.focus({ preventScroll: !0 }));
1558
+ };
1559
+ onWindowKey = (e) => {
1560
+ e.key === "Escape" && this.state === "open" && this.mode === "drag" && (e.preventDefault(), this.close());
1561
+ };
1562
+ focusInside() {
1563
+ let e = this.shadowRoot?.activeElement;
1564
+ return !!e && this.els.overlay.contains(e);
1565
+ }
1566
+ computeCurrent() {
1567
+ let e = this.opts.current === void 0 ? location.href : this.opts.current, t = e ? U(this.tree, e, location.href) : null;
1568
+ this.currentId = t?.id ?? null, this.currentTrail = new Set(V(t));
1569
+ }
1570
+ initialFocusId() {
1571
+ return (this.currentId ? V(this.tree.byId.get(this.currentId)) : [])[0] ?? this.tree.root.children[0]?.id ?? null;
1572
+ }
1573
+ measure() {
1574
+ let { trigger: e, hub: t, probe: n, overlay: r } = this.els, i = e.getBoundingClientRect(), a = e.offsetWidth / 2, o = {
1575
+ x: i.left + i.width / 2,
1576
+ y: i.top + i.height / 2,
1577
+ r: a
1578
+ };
1579
+ (o.x !== this.hub.x || o.y !== this.hub.y || o.r !== this.hub.r) && this.fanCache.clear(), this.hub = o, Object.assign(t.style, {
1580
+ left: `${o.x - a}px`,
1581
+ top: `${o.y - a}px`,
1582
+ width: `${a * 2}px`,
1583
+ height: `${a * 2}px`
1584
+ });
1585
+ let s = r.getBoundingClientRect(), c = s.width || document.documentElement.clientWidth, l = s.height || window.innerHeight, u = getComputedStyle(n), d = {
1586
+ t: parseFloat(u.paddingTop) || 0,
1587
+ r: parseFloat(u.paddingRight) || 0,
1588
+ b: parseFloat(u.paddingBottom) || 0,
1589
+ l: parseFloat(u.paddingLeft) || 0
1590
+ }, f = {
1591
+ x: d.l + 8,
1592
+ y: d.t + 8,
1593
+ w: c - d.l - d.r - 16,
1594
+ h: l - d.t - d.b - 16
1595
+ };
1596
+ (f.w !== this.bounds.w || f.h !== this.bounds.h) && this.fanCache.clear(), this.bounds = f;
1597
+ }
1598
+ orientIcon() {
1599
+ let e = this.els.trigger.getBoundingClientRect(), t = e.left + e.width / 2, n = e.top + e.height / 2, r = document.documentElement.clientWidth / 2 - t, i = window.innerHeight / 2 - n, a = Math.hypot(r, i) < 2 ? 0 : Math.atan2(i, r) * 180 / Math.PI + 90;
1600
+ this.els.root.style.setProperty("--_icon-rotate", `${a.toFixed(1)}deg`);
1601
+ }
1602
+ onResize = () => {
1603
+ cancelAnimationFrame(this.resizeFrame), this.resizeFrame = requestAnimationFrame(() => {
1604
+ this.resizeFrame = 0, this.isConnected && (this.orientIcon(), this.state === "open" && (this.measure(), this.render()));
1605
+ });
1606
+ };
1607
+ clearLabelSizes() {
1608
+ for (let e of [
1609
+ this.labelSizes,
1610
+ this.branchLabelSizes,
1611
+ this.narrowSizes,
1612
+ this.narrowBranchSizes
1613
+ ]) e.clear();
1614
+ }
1615
+ onFontsLoaded = () => {
1616
+ this.clearLabelSizes(), this.fanCache.clear(), this.state === "open" && this.render();
1617
+ };
1618
+ render() {
1619
+ if (this.state !== "open") return;
1620
+ let { root: e, byId: t } = this.tree, n = this.opts.nodeSize / 2, r = [{
1621
+ key: "",
1622
+ parentId: null,
1623
+ childIds: e.children.map((e) => e.id)
1624
+ }], i = [];
1625
+ for (let e of this.path) {
1626
+ let n = t.get(e);
1627
+ if (!n || !n.children.length || (n.parent?.id ?? "") !== (i[i.length - 1] ?? "")) break;
1628
+ i.push(e), r.push({
1629
+ key: i.join(_e),
1630
+ parentId: e,
1631
+ childIds: n.children.map((e) => e.id)
1632
+ });
1633
+ }
1634
+ this.path = i;
1635
+ let a = new Set(i), c = r[r.length - 1].parentId ?? "", l = this.opts.labels !== "none";
1636
+ l && this.measureLabels(r.flatMap((e) => e.childIds)), this.placed = F({
1637
+ hub: this.hub,
1638
+ bounds: this.bounds,
1639
+ levels: r,
1640
+ nodeRadius: n,
1641
+ distance: this.opts.spacing,
1642
+ gap: this.opts.gap,
1643
+ edgeStyle: this.opts.edges,
1644
+ labelSize: l ? (e) => this.sizesOf(e, !1) : void 0,
1645
+ branchLabelSize: l ? (e) => this.sizesOf(e, !0) : void 0,
1646
+ labelPad: this.labelPad
1647
+ }, this.fanCache);
1648
+ for (let e of this.views.values()) !this.placed.has(e.tree.id) && !e.leaveTimer && this.leave(e, e.from, 0);
1649
+ let u = this.focusId && this.placed.has(this.focusId) ? this.focusId : this.initialFocusId(), d = [];
1650
+ for (let e of r) {
1651
+ let r = (e.parentId ? this.views.get(e.parentId) : null)?.node ?? null;
1652
+ for (let i of e.childIds) {
1653
+ let o = this.placed.get(i), s = this.views.get(i);
1654
+ s ? s.leaveTimer && (window.clearTimeout(s.leaveTimer), s.leaveTimer = 0, s.node.removeAttribute("data-leave"), s.edge.setAttribute("data-shown", "")) : (s = this.createView(t.get(i), o), this.views.set(i, s), r ? r.after(s.node) : this.els.layer.append(s.node), d.push(s)), r = s.node, s.from = o.from;
1655
+ let { node: l, edge: f, tree: p } = s, m = a.has(i) ? "path" : (p.parent?.id ?? "") === c ? "frontier" : "dim";
1656
+ l.dataset.state = m, Y(l, "data-active", i === this.active), Y(l, "data-current", i === this.currentId), Y(l, "data-current-trail", this.currentTrail.has(i) && i !== this.currentId), i === this.currentId ? l.setAttribute("aria-current", "page") : l.removeAttribute("aria-current"), p.children.length && l.setAttribute("aria-expanded", String(a.has(i))), l.tabIndex = i === u ? 0 : -1, l.style.setProperty("--_a", `${o.angle.toFixed(4)}rad`), d.includes(s) || (l.style.translate = `${o.x}px ${o.y}px`), X(l, "node", {
1657
+ active: i === this.active,
1658
+ current: i === this.currentId,
1659
+ trail: this.currentTrail.has(i) && i !== this.currentId,
1660
+ path: m === "path",
1661
+ frontier: m === "frontier",
1662
+ dim: m === "dim",
1663
+ branch: p.children.length > 0,
1664
+ disabled: !!p.page.disabled
1665
+ });
1666
+ let h = e.parentId == null ? this.hub : {
1667
+ ...this.placed.get(e.parentId),
1668
+ r: n
1669
+ }, g = a.has(i) || i === this.active, _ = m === "dim" && i !== this.active;
1670
+ f.setAttribute("d", S(h, {
1671
+ x: o.x,
1672
+ y: o.y,
1673
+ r: n
1674
+ }, this.opts.edges, o.parentAngle).d), Y(f, "data-lit", g), Y(f, "data-dim", _), X(f, "edge", {
1675
+ lit: g,
1676
+ dim: _
1677
+ });
1678
+ }
1679
+ }
1680
+ d.length && this.animateIn(d), this.renderLabels();
1681
+ let f = this.els.hub, p = i.length ? "back" : "close";
1682
+ f.dataset.icon !== p && (f.dataset.icon = p, this.els.hubIcon.innerHTML = i.length ? s : o);
1683
+ let m = i.length > 1 ? t.get(i[i.length - 2]).page.label : null;
1684
+ f.setAttribute("aria-label", i.length ? m ? this.text.back.replace("{label}", m) : this.text.backToTop : this.text.close), Y(f, "data-active", this.mode === "drag" && this.dwellTarget === "hub"), X(f, "hub", {
1685
+ back: i.length > 0,
1686
+ active: f.hasAttribute("data-active")
1687
+ });
1688
+ }
1689
+ get labelPad() {
1690
+ let e = parseFloat(getComputedStyle(this.els.root).getPropertyValue("--_active-scale")) || 1.14;
1691
+ return this.opts.nodeSize / 2 * Math.max(0, e - 1) + 8;
1692
+ }
1693
+ buildLabel(e) {
1694
+ let t = document.createElement("div");
1695
+ t.className = "label", t.setAttribute("part", "label");
1696
+ let n = document.createElement("span");
1697
+ if (n.className = "title", n.textContent = e.page.label, t.append(n), e.page.description) {
1698
+ let n = document.createElement("span");
1699
+ n.className = "desc", n.textContent = e.page.description, t.append(n);
1700
+ }
1701
+ return t;
1702
+ }
1703
+ sizesOf(e, t) {
1704
+ let n = (t ? this.branchLabelSizes.get(e) : null) ?? this.labelSizes.get(e);
1705
+ if (!n) return null;
1706
+ let r = (t ? this.narrowBranchSizes.get(e) : null) ?? this.narrowSizes.get(e);
1707
+ return r ? [n, r] : n;
1708
+ }
1709
+ measureLabels(e) {
1710
+ let t = e.filter((e) => !this.labelSizes.has(e));
1711
+ if (!t.length) return;
1712
+ let n = this.els.measure, r = (e) => e.children.length && e.page.href ? [!1, !0] : [!1], i = t.flatMap((e) => {
1713
+ let t = this.tree.byId.get(e);
1714
+ return r(t).flatMap((r) => [!1, !0].map((i) => {
1715
+ let a = this.buildLabel(t);
1716
+ return Y(a, "data-go", r), Y(a, "data-narrow", i), n.append(a), {
1717
+ id: e,
1718
+ el: a,
1719
+ go: r,
1720
+ narrow: i
1721
+ };
1722
+ }));
1723
+ }), a = (e) => ({
1724
+ w: Math.ceil(e.offsetWidth) + 1,
1725
+ h: Math.ceil(e.offsetHeight) + 1
1726
+ });
1727
+ for (let { id: e, el: t, go: n, narrow: r } of i) (r ? n ? this.narrowBranchSizes : this.narrowSizes : n ? this.branchLabelSizes : this.labelSizes).set(e, a(t));
1728
+ for (let [e, n] of [[this.labelSizes, this.narrowSizes], [this.branchLabelSizes, this.narrowBranchSizes]]) for (let r of t) {
1729
+ let t = n.get(r);
1730
+ t && t.w >= (e.get(r)?.w ?? 0) - 1 && n.delete(r);
1731
+ }
1732
+ n.replaceChildren();
1733
+ }
1734
+ createView(e, t) {
1735
+ let i = e.page, a = document.createElement("button");
1736
+ a.type = "button", a.className = "node", a.setAttribute("part", "node"), a.setAttribute("role", "treeitem"), a.setAttribute("aria-label", i.label), a.setAttribute("aria-level", String(e.depth)), a.setAttribute("aria-setsize", String(e.parent?.children.length ?? 1)), a.setAttribute("aria-posinset", String(e.index + 1)), i.description && a.setAttribute("aria-description", i.description), i.disabled && a.setAttribute("aria-disabled", "true"), e.children.length && (a.dataset.branch = ""), i.color && a.style.setProperty("--_node-accent", i.color), a.dataset.id = e.id;
1737
+ let o = document.createElement("span");
1738
+ o.className = "icon", o.setAttribute("part", "icon"), o.setAttribute("aria-hidden", "true"), o.append(n(i.icon, this.opts.iconResolver, i) ?? r(i.label)), a.append(o);
1739
+ let s = this.buildLabel(e);
1740
+ this.els.labels.append(s);
1741
+ let c = document.createElementNS(ge, "path");
1742
+ return c.setAttribute("class", "edge"), c.setAttribute("part", "edge"), c.setAttribute("pathLength", "1"), i.color && c.style.setProperty("--_lit", i.color), this.els.edges.append(c), {
1743
+ tree: e,
1744
+ node: a,
1745
+ label: s,
1746
+ edge: c,
1747
+ from: t.from,
1748
+ leaveTimer: 0,
1749
+ shownAt: null
1750
+ };
1751
+ }
1752
+ animateIn(e) {
1753
+ let t = this.reducedMotion ? 0 : 22;
1754
+ for (let t of e) t.node.style.transition = "none", t.node.style.translate = `${t.from.x}px ${t.from.y}px`, t.node.dataset.enter = "";
1755
+ this.els.layer.offsetWidth, e.forEach((e, n) => {
1756
+ let r = this.placed.get(e.tree.id), i = n * t;
1757
+ e.node.style.transition = "", e.node.style.setProperty("--_delay", `${i}ms`), e.edge.style.setProperty("--_delay", `${i}ms`), e.label.style.setProperty("--_label-delay", `${i + this.duration * .6}ms`), e.node.style.translate = `${r.x}px ${r.y}px`, e.node.removeAttribute("data-enter"), e.edge.setAttribute("data-shown", "");
1758
+ let a = window.setTimeout(() => {
1759
+ this.animationTimers.delete(a), e.node.style.removeProperty("--_delay"), e.edge.style.removeProperty("--_delay"), e.label.style.removeProperty("--_label-delay");
1760
+ }, i + this.duration);
1761
+ this.animationTimers.add(a);
1762
+ });
1763
+ }
1764
+ leave(e, t, n) {
1765
+ let { node: r, edge: i, label: a } = e;
1766
+ if (this.shadowRoot?.activeElement === r && this.state === "open") {
1767
+ let t = e.tree.parent ? this.views.get(e.tree.parent.id) : null;
1768
+ t && !t.leaveTimer ? (this.focusId = t.tree.id, t.node.tabIndex = 0, t.node.focus({ preventScroll: !0 })) : this.els.hub.focus({ preventScroll: !0 });
1769
+ }
1770
+ r.style.setProperty("--_delay", `${n}ms`), i.style.setProperty("--_delay", `${n}ms`), r.style.translate = `${t.x}px ${t.y}px`, r.setAttribute("data-leave", ""), r.removeAttribute("data-active"), r.setAttribute("part", "node node-leaving"), i.setAttribute("part", "edge"), a.setAttribute("part", "label"), r.tabIndex = -1, i.removeAttribute("data-shown"), a.removeAttribute("data-visible"), e.shownAt = null, e.leaveTimer = window.setTimeout(() => {
1771
+ r.remove(), a.remove(), i.remove(), this.views.delete(e.tree.id);
1772
+ }, n + this.duration * .7 + 40);
1773
+ }
1774
+ renderLabels() {
1775
+ let e = this.opts.labels, t = this.opts.nodeSize / 2, n = [...this.views.values()].filter((e) => !e.leaveTimer), r = /* @__PURE__ */ new Map();
1776
+ for (let e of n) {
1777
+ let n = this.placed.get(e.tree.id);
1778
+ r.set(e.tree.id, {
1779
+ x: n.x,
1780
+ y: n.y,
1781
+ r: t
1782
+ });
1783
+ }
1784
+ let i = this.opts.edges === "none" ? [] : n.map((e) => this.placed.get(e.tree.id).edge), a = /* @__PURE__ */ new Map(), o = /* @__PURE__ */ new Set(), s = /* @__PURE__ */ new Set(), c = this.labelPad, l = (e, t = i) => ({
1785
+ bounds: this.bounds,
1786
+ circles: [this.hub, ...r.values()],
1787
+ taken: [...a].filter(([t]) => t !== e).map(([, e]) => e),
1788
+ edges: t,
1789
+ pad: c
1790
+ }), u = (e, t = i) => {
1791
+ let n = l(e, t), a = this.active ? r.get(this.active) : null, o = r.get(e), s = n.circles.map((e) => e === a && e !== o ? {
1792
+ ...e,
1793
+ r: e.r + c - 6
1794
+ } : e);
1795
+ return {
1796
+ ...n,
1797
+ circles: s,
1798
+ pad: 6,
1799
+ ownPad: c
1800
+ };
1801
+ }, d = (e, t) => ![...a].some(([n, r]) => n !== t && p(e, r, 4)), f = (e, t, n) => {
1802
+ a.set(e, t), t.w < n.w - 1 ? o.add(e) : o.delete(e);
1803
+ }, m = (e, t) => !!e && !!t && e.w >= t.w - 1 && e.h >= t.h - 1;
1804
+ for (let e of n) Y(e.label, "data-go", this.mode === "tap" && e.node.dataset.state === "path" && !!e.tree.page.href), e.tree.id !== this.active && e.label.removeAttribute("data-active"), e.label.removeAttribute("data-compact"), e.label.removeAttribute("data-narrow");
1805
+ if (e === "auto" || e === "always") {
1806
+ let t = [...n].sort((e, t) => Number(t.node.dataset.state === "frontier") - Number(e.node.dataset.state === "frontier"));
1807
+ for (let n of t) {
1808
+ let t = n.tree.id, r = n.node.dataset.state, i = this.placed.get(t).label;
1809
+ (r === "frontier" || r === "dim" && e === "always") && i && d(i, t) && f(t, i, this.labelSizes.get(t) ?? i);
1810
+ }
1811
+ for (let e of n) {
1812
+ let t = e.tree.id;
1813
+ if (a.has(t) || t === this.active || e.node.dataset.state !== "frontier") continue;
1814
+ let n = this.placed.get(t), i = this.sizesOf(t, !1);
1815
+ if (!i) continue;
1816
+ let o = r.get(t), s = u(t), c = k(o, i, n.angle, s) ?? k(o, i, n.angle, {
1817
+ ...s,
1818
+ edges: [n.edge]
1819
+ });
1820
+ c && f(t, c, this.labelSizes.get(t));
1821
+ }
1822
+ }
1823
+ let h = this.active ? this.views.get(this.active) : null;
1824
+ if (h && !h.leaveTimer && e !== "none") {
1825
+ let e = h.tree.id, t = this.placed.get(e), n = r.get(e), i = h.label, o = h.node.dataset.state === "path", c = (r) => {
1826
+ let i = u(e);
1827
+ return k(n, r, t.angle, i) ?? k(n, r, t.angle, {
1828
+ ...i,
1829
+ edges: [t.edge]
1830
+ }) ?? (o ? k(n, r, t.angle, {
1831
+ ...i,
1832
+ edges: []
1833
+ }) : null);
1834
+ }, l = () => ({
1835
+ w: i.offsetWidth + 1,
1836
+ h: i.offsetHeight + 1
1837
+ });
1838
+ i.setAttribute("data-active", "");
1839
+ let p = l(), g = h.tree.page.description ? c(p) : null;
1840
+ if (!g) {
1841
+ s.add(e), i.setAttribute("data-compact", ""), p = l();
1842
+ let n = (o ? this.narrowBranchSizes.get(e) : null) ?? this.narrowSizes.get(e), r = n ? [p, n] : p, f = o ? t.pathLabel : t.label, h = a.get(e) ?? (f && d(f, e) ? f : null);
1843
+ if (g = h && (m(h, p) || m(h, n)) ? h : c(r), !g) {
1844
+ let t = u(e), n = Math.atan2(this.bounds.y + this.bounds.h / 2 - this.hub.y, this.bounds.x + this.bounds.w / 2 - this.hub.x);
1845
+ g = k(this.hub, r, n, t) ?? k(this.hub, r, n, {
1846
+ ...t,
1847
+ edges: []
1848
+ });
1849
+ }
1850
+ }
1851
+ g ? f(e, g, p) : a.delete(e);
1852
+ }
1853
+ if (e === "auto" || e === "always") for (let e of this.path) {
1854
+ if (e === this.active || a.has(e)) continue;
1855
+ let t = this.views.get(e);
1856
+ if (!t || t.leaveTimer) continue;
1857
+ let n = this.placed.get(e), o = {
1858
+ w: t.label.offsetWidth + 1,
1859
+ h: t.label.offsetHeight + 1
1860
+ }, s = this.narrowBranchSizes.get(e) ?? this.narrowSizes.get(e), c = s ? [o, s] : o, l = n.pathLabel && (m(n.pathLabel, o) || m(n.pathLabel, s)) && d(n.pathLabel, e) ? n.pathLabel : null, p = r.get(e), h = [
1861
+ 0,
1862
+ 12,
1863
+ 28,
1864
+ 48
1865
+ ], g = l ?? k(p, c, n.angle, {
1866
+ ...u(e),
1867
+ rings: h
1868
+ }) ?? k(p, c, n.angle + Math.PI, {
1869
+ ...u(e, i.filter((e) => e !== n.edge)),
1870
+ rings: h
1871
+ });
1872
+ g && f(e, g, o);
1873
+ }
1874
+ for (let e of this.views.values()) {
1875
+ let t = e.tree.id, n = e.leaveTimer ? null : a.get(t) ?? null, r = t === this.active && !!n;
1876
+ if (Y(e.label, "data-active", r), Y(e.label, "data-narrow", !!n && o.has(t)), s.has(t) || e.label.removeAttribute("data-compact"), X(e.label, "label", {
1877
+ active: r,
1878
+ path: e.node.dataset.state === "path"
1879
+ }), !n) {
1880
+ e.label.removeAttribute("data-visible"), e.shownAt = null;
1881
+ continue;
1882
+ }
1883
+ let i = e.shownAt && (Math.abs(e.shownAt.x - n.x) > 1 || Math.abs(e.shownAt.y - n.y) > 1);
1884
+ e.label.style.translate = `${n.x.toFixed(1)}px ${n.y.toFixed(1)}px`, e.label.setAttribute("data-visible", ""), i && !this.reducedMotion && e.label.animate([{ opacity: 0 }, { opacity: 1 }], {
1885
+ duration: 160,
1886
+ easing: "ease-out"
1887
+ }), e.shownAt = n;
1888
+ }
1889
+ }
1890
+ onPointerDown = (e) => {
1891
+ if (!e.isPrimary || e.pointerType === "mouse" && e.button !== 0 || this.state === "open") return;
1892
+ e.preventDefault();
1893
+ let { trigger: t } = this.els;
1894
+ try {
1895
+ t.setPointerCapture(e.pointerId);
1896
+ } catch {}
1897
+ this.pointerId = e.pointerId, this.pointerType = e.pointerType, this.downAt = this.lastPoint = {
1898
+ x: e.clientX,
1899
+ y: e.clientY
1900
+ }, this.leftHub = !1, t.setAttribute("data-pressing", ""), this.holdTimer = window.setTimeout(() => this.beginDrag(), this.opts.holdDelay);
1901
+ };
1902
+ beginDrag() {
1903
+ window.clearTimeout(this.holdTimer), this.holdTimer = 0, this.els.trigger.removeAttribute("data-pressing"), this.show("drag", "none");
1904
+ }
1905
+ onPointerMove = (e) => {
1906
+ if (e.pointerId !== this.pointerId) return;
1907
+ let t = this.lastPoint = {
1908
+ x: e.clientX,
1909
+ y: e.clientY
1910
+ };
1911
+ if (this.state !== "open") {
1912
+ if (this.holdTimer && Math.hypot(t.x - this.downAt.x, t.y - this.downAt.y) > 10) this.beginDrag();
1913
+ else return;
1914
+ }
1915
+ this.mode === "drag" && this.dragTo(t);
1916
+ };
1917
+ onPointerUp = (e) => {
1918
+ if (e.pointerId !== this.pointerId) return;
1919
+ this.pointerId = null, this.lastPointerUp = performance.now();
1920
+ let t = this.holdTimer !== 0;
1921
+ if (window.clearTimeout(this.holdTimer), this.holdTimer = 0, this.els.trigger.removeAttribute("data-pressing"), this.state !== "open") {
1922
+ t && this.opts.openOnTap && this.show("tap", "pointer");
1923
+ return;
1924
+ }
1925
+ this.mode === "drag" && this.release({
1926
+ x: e.clientX,
1927
+ y: e.clientY
1928
+ });
1929
+ };
1930
+ onPointerCancel = (e) => {
1931
+ e.pointerId === this.pointerId && (this.pointerId = null, window.clearTimeout(this.holdTimer), this.holdTimer = 0, this.els.trigger.removeAttribute("data-pressing"), this.state === "open" && this.mode === "drag" && this.close());
1932
+ };
1933
+ onLostCapture = (e) => {
1934
+ e.pointerId === this.pointerId && (this.pointerId = null, window.clearTimeout(this.holdTimer), this.holdTimer = 0, this.els.trigger.removeAttribute("data-pressing"), this.state === "open" && this.mode === "drag" && this.release(this.lastPoint));
1935
+ };
1936
+ onTriggerClick = (e) => {
1937
+ performance.now() - this.lastPointerUp < 700 || (this.state === "open" ? this.close({ focusTrigger: !0 }) : this.show("tap", e.detail === 0 ? "keyboard" : "pointer"));
1938
+ };
1939
+ onTriggerKey = (e) => {
1940
+ this.state === "closed" && (e.key === "ArrowUp" || e.key === "ArrowDown") && (e.preventDefault(), this.show("tap", "keyboard"));
1941
+ };
1942
+ hitAt(e) {
1943
+ let t = [], n = [], r = this.opts.nodeSize / 2;
1944
+ for (let [e, i] of this.placed) {
1945
+ let a = this.views.get(e);
1946
+ a && !a.leaveTimer && (t.push(e), n.push({
1947
+ x: i.x,
1948
+ y: i.y,
1949
+ r
1950
+ }));
1951
+ }
1952
+ t.push("hub"), n.push(this.hub);
1953
+ let i = se(e, n, this.pointerType === "mouse" ? 6 : 14);
1954
+ if (i >= 0) return t[i];
1955
+ for (let t of this.views.values()) if (!t.leaveTimer && t.shownAt && m(t.shownAt, e)) return t.tree.id;
1956
+ return null;
1957
+ }
1958
+ dragTo(e) {
1959
+ let t = this.hitAt(e);
1960
+ t === "hub" ? (this.setActive(null), this.dwell(this.leftHub && this.path.length ? "hub" : null)) : t ? (this.leftHub = !0, this.setActive(t), this.dwell(t)) : (Math.hypot(e.x - this.hub.x, e.y - this.hub.y) > this.hub.r + 6 && (this.leftHub = !0), this.setActive(null), this.dwellTarget && this.dwellTarget !== "hub" && this.isBeyond(e, this.dwellTarget) || this.dwell(null)), Y(this.els.hub, "data-active", this.dwellTarget === "hub");
1961
+ }
1962
+ isBeyond(e, t) {
1963
+ let n = this.placed.get(t);
1964
+ if (!n || !this.tree.byId.get(t)?.children.length) return !1;
1965
+ let r = n.x - n.from.x, i = n.y - n.from.y, a = Math.hypot(r, i) || 1, o = r / a, s = i / a, c = e.x - n.from.x, l = e.y - n.from.y, u = c * o + l * s, d = Math.abs(c * s - l * o);
1966
+ return u > a && u < a + this.opts.spacing * 1.2 && d < this.opts.spacing * .55;
1967
+ }
1968
+ dwell(e) {
1969
+ e !== this.dwellTarget && (window.clearTimeout(this.dwellTimer), this.dwellTimer = 0, this.dwellTarget = e, e && (this.dwellTimer = window.setTimeout(() => {
1970
+ if (this.dwellTimer = 0, e === "hub") return this.setPath([]);
1971
+ let t = this.tree.byId.get(e);
1972
+ t && !t.page.disabled && this.setPath(V(t.children.length ? t : t.parent));
1973
+ }, this.opts.expandDelay)));
1974
+ }
1975
+ release(e) {
1976
+ window.clearTimeout(this.dwellTimer), this.dwellTimer = 0, this.dwellTarget = null;
1977
+ let t = this.hitAt(e);
1978
+ if (t && t !== "hub") {
1979
+ let e = this.tree.byId.get(t);
1980
+ if (!e.page.disabled) {
1981
+ if (e.page.href || !e.children.length) return this.select(e, "release");
1982
+ this.path = V(e);
1983
+ }
1984
+ return this.toTapMode(t);
1985
+ }
1986
+ if (!this.leftHub) return this.toTapMode(null);
1987
+ this.close();
1988
+ }
1989
+ toTapMode(e) {
1990
+ this.mode = "tap", this.els.overlay.dataset.mode = "tap", window.removeEventListener("keydown", this.onWindowKey, !0), this.present(!0), this.render();
1991
+ let t = e ?? this.initialFocusId();
1992
+ t && this.focusNode(t, !1);
1993
+ }
1994
+ onOverlayHover = (e) => {
1995
+ if (this.state !== "open" || this.mode !== "tap" || e.pointerType !== "mouse") return;
1996
+ let t = this.hitAt({
1997
+ x: e.clientX,
1998
+ y: e.clientY
1999
+ });
2000
+ this.setActive(t === "hub" ? null : t);
2001
+ };
2002
+ onOverlayClick = (e) => {
2003
+ if (this.state !== "open" || this.mode !== "tap") return;
2004
+ let t = performance.now();
2005
+ if (t - this.openedAt < 250 || e.detail === 0 && t - this.lastKeyActivation < 500) return;
2006
+ let n = e.target;
2007
+ if (n.closest(".hub")) return this.hubAction(e.detail === 0);
2008
+ let r = n.closest(".node")?.dataset.id ?? null;
2009
+ if (!r && e.detail !== 0) {
2010
+ let t = this.hitAt({
2011
+ x: e.clientX,
2012
+ y: e.clientY
2013
+ });
2014
+ r = t === "hub" ? null : t;
2015
+ }
2016
+ let i = r ? this.tree.byId.get(r) : null;
2017
+ if (!i) return this.close();
2018
+ let a = e.detail === 0 ? "keyboard" : "click";
2019
+ this.focusNode(i.id, a === "keyboard"), this.activate(i, a, e.metaKey || e.ctrlKey || e.button === 1);
2020
+ };
2021
+ hubAction(e) {
2022
+ this.path.length ? this.goUp(e) : this.close({ focusTrigger: !0 });
2023
+ }
2024
+ onOverlayKey = (e) => {
2025
+ if (this.state !== "open") return;
2026
+ let t = e.key;
2027
+ if (t === "Escape") {
2028
+ e.preventDefault(), this.close({ focusTrigger: !0 });
2029
+ return;
2030
+ }
2031
+ if (this.mode !== "tap") return;
2032
+ let n = e.composedPath()[0], r = n instanceof Element ? n.closest(".node") : null;
2033
+ if (!r) {
2034
+ if (n === this.els.hub) return;
2035
+ if (t in J || t === "Home" || t === "End") {
2036
+ e.preventDefault();
2037
+ let t = this.focusId && this.placed.has(this.focusId) ? this.focusId : this.initialFocusId();
2038
+ t && this.focusNode(t);
2039
+ }
2040
+ return;
2041
+ }
2042
+ let i = this.tree.byId.get(r.dataset.id);
2043
+ if (i) {
2044
+ if (this.focusId !== i.id && (this.focusId = i.id), t === "Enter" || t === " ") return e.preventDefault(), this.lastKeyActivation = performance.now(), this.activateKey(i, e.metaKey || e.ctrlKey);
2045
+ if (t === "Backspace") return e.preventDefault(), this.stepBack(i);
2046
+ if (this.opts.arrowKeys === "spatial" ? this.spatialKey(i, t) : this.treeKey(i, t)) {
2047
+ e.preventDefault();
2048
+ return;
2049
+ }
2050
+ t.length === 1 && !e.metaKey && !e.ctrlKey && !e.altKey && t !== " " && this.typeAhead(i, t);
2051
+ }
2052
+ };
2053
+ visibleOrder() {
2054
+ let e = [], t = (n) => {
2055
+ for (let r of n) e.push(r.id), this.path.includes(r.id) && t(r.children);
2056
+ };
2057
+ return t(this.tree.root.children), e;
2058
+ }
2059
+ treeKey(e, t) {
2060
+ let n = getComputedStyle(this).direction === "rtl", r = this.visibleOrder(), i = r.indexOf(e.id), a = this.path.includes(e.id);
2061
+ if (t === "ArrowDown") i < r.length - 1 && this.focusNode(r[i + 1]);
2062
+ else if (t === "ArrowUp") i > 0 && this.focusNode(r[i - 1]);
2063
+ else if (t === (n ? "ArrowLeft" : "ArrowRight")) e.children.length && !e.page.disabled && (a ? this.focusNode(e.children[0].id) : (this.path = V(e), this.haptic(6), this.focusNode(e.id)));
2064
+ else if (t === (n ? "ArrowRight" : "ArrowLeft")) a ? (this.path = V(e.parent), this.focusNode(e.id)) : e.parent && e.parent.depth > 0 && this.focusNode(e.parent.id);
2065
+ else if (t === "Home") this.focusNode(r[0]);
2066
+ else if (t === "End") this.focusNode(r[r.length - 1]);
2067
+ else return !1;
2068
+ return !0;
2069
+ }
2070
+ spatialKey(e, t) {
2071
+ if (t in J) {
2072
+ let n = this.placed.get(e.id);
2073
+ if (!n) return !0;
2074
+ let r = J[t], i = null, a = Infinity;
2075
+ for (let [t, o] of this.placed) {
2076
+ if (t === e.id || this.views.get(t)?.leaveTimer) continue;
2077
+ let s = o.x - n.x, c = o.y - n.y, l = s * r.x + c * r.y;
2078
+ if (l <= 1) continue;
2079
+ let u = Math.abs(s * r.y - c * r.x);
2080
+ if (u > l * 2.2) continue;
2081
+ let d = l + u * 2;
2082
+ d < a && (a = d, i = t);
2083
+ }
2084
+ return i && this.focusNode(i), !0;
2085
+ }
2086
+ if (t === "Home" || t === "End") {
2087
+ let n = e.parent?.children ?? [], r = t === "Home" ? n[0] : n[n.length - 1];
2088
+ return r && this.focusNode(r.id), !0;
2089
+ }
2090
+ return !1;
2091
+ }
2092
+ activateKey(e, t) {
2093
+ if (!e.page.disabled) {
2094
+ if (this.opts.arrowKeys === "spatial") return this.activate(e, "keyboard", t);
2095
+ if (e.page.href || !e.children.length) return this.select(e, "keyboard", t);
2096
+ this.path = this.path.includes(e.id) ? V(e.parent) : V(e), this.haptic(6), this.focusNode(e.id);
2097
+ }
2098
+ }
2099
+ stepBack(e) {
2100
+ e.parent && e.parent.depth > 0 ? (this.path = V(e.parent.parent), this.focusNode(e.parent.id)) : this.path.length && (this.path = [], this.focusNode(e.id));
2101
+ }
2102
+ typeAhead(e, t) {
2103
+ let n = performance.now();
2104
+ this.typeahead.text = n - this.typeahead.at < 700 ? this.typeahead.text + t.toLowerCase() : t.toLowerCase(), this.typeahead.at = n;
2105
+ let r = this.opts.arrowKeys === "spatial" ? (e.parent?.children ?? []).map((e) => e.id) : this.visibleOrder(), i = this.typeahead.text, a = r.indexOf(e.id), o = i.length === 1 ? a + 1 : a;
2106
+ for (let e = 0; e < r.length; e++) {
2107
+ let t = r[(o + e) % r.length];
2108
+ if (this.tree.byId.get(t).page.label.toLowerCase().startsWith(i)) return this.focusNode(t);
2109
+ }
2110
+ }
2111
+ goUp(e) {
2112
+ let t = this.path[this.path.length - 1];
2113
+ this.path = this.path.slice(0, -1), t ? this.focusNode(t, e) : this.render();
2114
+ }
2115
+ focusNode(e, t = !0) {
2116
+ this.focusId = e, t && (this.active = e), this.render(), this.views.get(e)?.node.focus({ preventScroll: !0 }), t && this.emitHighlight();
2117
+ }
2118
+ activate(e, t, n = !1) {
2119
+ if (e.page.disabled) return;
2120
+ if (n && e.page.href) return this.select(e, t, !0);
2121
+ let r = this.path.includes(e.id);
2122
+ if (e.children.length && !r) {
2123
+ this.path = V(e), this.haptic(6), t === "keyboard" ? this.focusNode(e.children[0].id) : (this.active = e.id, this.render());
2124
+ return;
2125
+ }
2126
+ if (e.children.length && !e.page.href) {
2127
+ this.path = V(e.parent), t === "keyboard" ? this.focusNode(e.id) : this.render();
2128
+ return;
2129
+ }
2130
+ this.select(e, t, n);
2131
+ }
2132
+ select(e, t, n = !1) {
2133
+ let r = {
2134
+ page: e.page,
2135
+ trail: V(e).map((e) => this.tree.byId.get(e).page),
2136
+ via: t
2137
+ }, i = this.emit("unfold-select", r, !0);
2138
+ if (this.haptic(12), this.close({ focusTrigger: t === "keyboard" }), !i) return;
2139
+ let { href: a, target: o } = e.page;
2140
+ if (this.opts.navigate && !(a && (n || o && o !== "_self"))) {
2141
+ this.opts.navigate(e.page, r);
2142
+ return;
2143
+ }
2144
+ if (!a) return;
2145
+ let s = me(a, document.baseURI);
2146
+ if (!s) {
2147
+ console.warn("[unfold-nav] Refusing an unsupported navigation URL:", a);
2148
+ return;
2149
+ }
2150
+ n || o && o !== "_self" ? window.open(s, n ? "_blank" : o, "noopener") : location.assign(s);
2151
+ }
2152
+ setActive(e) {
2153
+ e !== this.active && (this.active = e, e && this.mode === "drag" && this.haptic(4), this.render(), this.emitHighlight());
2154
+ }
2155
+ setPath(e) {
2156
+ e.length === this.path.length && e.every((e, t) => e === this.path[t]) || (this.path = e, this.haptic(6), this.render());
2157
+ }
2158
+ emit(e, t, n = !1) {
2159
+ return this.dispatchEvent(new CustomEvent(e, {
2160
+ detail: t,
2161
+ bubbles: !0,
2162
+ composed: !0,
2163
+ cancelable: n
2164
+ }));
2165
+ }
2166
+ emitHighlight() {
2167
+ let e = this.active ? this.tree.byId.get(this.active)?.page ?? null : null;
2168
+ this.emit("unfold-highlight", { page: e });
2169
+ }
2170
+ haptic(e) {
2171
+ if (this.opts.haptics) try {
2172
+ navigator.vibrate?.(e);
2173
+ } catch {}
2174
+ }
2175
+ get reducedMotion() {
2176
+ return matchMedia("(prefers-reduced-motion: reduce)").matches;
2177
+ }
2178
+ get duration() {
2179
+ let e = getComputedStyle(this.els.root).getPropertyValue("--_dur").trim(), t = parseFloat(e);
2180
+ return Number.isFinite(t) ? e.endsWith("ms") ? t : e.endsWith("s") ? t * 1e3 : t : 340;
2181
+ }
2182
+ };
2183
+ if (typeof HTMLElement < "u") for (let e of G) Object.defineProperty(Z.prototype, e, {
2184
+ configurable: !0,
2185
+ enumerable: !0,
2186
+ get() {
2187
+ return this.opts[e];
2188
+ },
2189
+ set(t) {
2190
+ this.configure({ [e]: t });
2191
+ }
2192
+ });
2193
+ //#endregion
2194
+ //#region src/index.ts
2195
+ var Q = "unfold-nav";
2196
+ function $(e = Q) {
2197
+ typeof customElements > "u" || customElements.get(e) || customElements.define(e, e === "unfold-nav" ? Z : class extends Z {});
2198
+ }
2199
+ $();
2200
+ function ye(e, t) {
2201
+ if (typeof document > "u") throw Error("[unfold-nav] Call createUnfoldNav in a browser after mounting.");
2202
+ let n = t ?? document.body;
2203
+ if (!n) throw Error("[unfold-nav] Wait for document.body or provide a container.");
2204
+ $();
2205
+ let r = document.createElement(Q);
2206
+ return r.configure(e), n.append(r), r;
2207
+ }
2208
+ //#endregion
2209
+ export { R as DEFAULT_OPTIONS, L as DEFAULT_STRINGS, Q as TAG, Z as UnfoldNav, B as buildTree, ye as createUnfoldNav, $ as define, U as findCurrent, F as layoutGraph, P as placeFan, k as placeLabel };
2210
+
2211
+ //# sourceMappingURL=unfold-nav.js.map