view-anchor 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -44
- package/README.zh-CN.md +32 -44
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/measure-loop.d.ts.map +1 -1
- package/dist/measure-loop.js +22 -3
- package/dist/protocol-publisher.d.ts.map +1 -1
- package/dist/protocol-publisher.js +19 -3
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +2 -5
- package/dist/react.d.ts.map +1 -1
- package/dist/size-advertiser.d.ts.map +1 -1
- package/dist/size-advertiser.js +9 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/view-anchor.d.ts.map +1 -1
- package/dist/view-anchor.js +29 -12
- package/docs/bidirectional-design.md +19 -15
- package/docs/index.html +413 -398
- package/docs/{mechanism.mdx → mechanism.md} +1 -10
- package/docs/performance-report.md +30 -30
- package/docs/protocol.md +31 -2
- package/package.json +9 -12
- package/src/index.ts +2 -9
- package/src/measure-loop.ts +22 -3
- package/src/protocol-publisher.ts +21 -3
- package/src/protocol.ts +8 -20
- package/src/react.ts +3 -12
- package/src/size-advertiser.ts +9 -0
- package/src/types.ts +1 -3
- package/src/view-anchor.ts +41 -34
package/docs/index.html
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>view-anchor 3D demo: align
|
|
7
|
-
<meta name="description" content="view-anchor is a high-performance geometry bridge that keeps an Electron WebContentsView, native webview
|
|
6
|
+
<title>view-anchor 3D demo: align anything outside the DOM to a DOM element</title>
|
|
7
|
+
<meta name="description" content="view-anchor is a high-performance geometry bridge that keeps an anchored view — an Electron WebContentsView, a native webview, a cross-origin iframe — aligned to a DOM element. This interactive 3D demo runs the real core in your browser.">
|
|
8
8
|
<style>
|
|
9
9
|
:root {
|
|
10
10
|
--bg: hsl( 0 0% 7%);
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
display: flex; align-items: center; justify-content: center;
|
|
58
58
|
}
|
|
59
59
|
/* the deck tilts as a whole on "explode" — both the DOM window and the
|
|
60
|
-
|
|
60
|
+
anchored view ride it, so they stay in registration in the 3D side view. */
|
|
61
61
|
.deck {
|
|
62
62
|
position: relative;
|
|
63
63
|
width: 560px; height: 300px;
|
|
@@ -117,7 +117,7 @@
|
|
|
117
117
|
.placeholder .ph-tag { font-size: 11px; color: var(--placeholder-bd); text-align: center; padding: 0 8px; }
|
|
118
118
|
.placeholder .ph-tag small { color: var(--text-3); display:block; margin-top:2px; font-size:10px; }
|
|
119
119
|
|
|
120
|
-
/* top layer:
|
|
120
|
+
/* top layer: the anchored view. A direct child of the deck so it lives
|
|
121
121
|
in the same 3D space; lifted in Z on explode to float above the DOM. */
|
|
122
122
|
.follower {
|
|
123
123
|
position: absolute;
|
|
@@ -213,7 +213,7 @@
|
|
|
213
213
|
<img alt="Node" src="https://img.shields.io/badge/node-%3E%3D24-339933">
|
|
214
214
|
</div>
|
|
215
215
|
<h1>view-anchor: interactive 3D demo</h1>
|
|
216
|
-
<p class="tag-en">Align
|
|
216
|
+
<p class="tag-en">Align an <b style="color:var(--native-bd)">anchored view</b> — an Electron <code>WebContentsView</code>, a native webview, a cross-origin iframe — to a DOM element's geometry. Resize the splitter, toggle <code>present</code>, or explode the 3D scene below: the green anchored view is positioned by the real view-anchor core running in this page.</p>
|
|
217
217
|
<div class="actions">
|
|
218
218
|
<a href="https://github.com/lbb00/view-anchor">GitHub</a>
|
|
219
219
|
<a href="https://www.npmjs.com/package/view-anchor">npm</a>
|
|
@@ -236,13 +236,13 @@
|
|
|
236
236
|
</div>
|
|
237
237
|
</div>
|
|
238
238
|
|
|
239
|
-
<!-- connector pillar: from the placeholder up to the
|
|
239
|
+
<!-- connector pillar: from the placeholder up to the anchored view along Z -->
|
|
240
240
|
<div class="pillar" id="pillar"></div>
|
|
241
241
|
|
|
242
|
-
<!-- top layer: the
|
|
242
|
+
<!-- top layer: the anchored view, driven by view-anchor -->
|
|
243
243
|
<div class="follower" id="follower">
|
|
244
244
|
<div class="row-marker"></div>
|
|
245
|
-
<div class="nv-label">
|
|
245
|
+
<div class="nv-label">Anchored view</div>
|
|
246
246
|
</div>
|
|
247
247
|
</div>
|
|
248
248
|
</div>
|
|
@@ -272,7 +272,7 @@
|
|
|
272
272
|
<div class="legend">
|
|
273
273
|
<span><span class="sw r"></span> DOM window</span>
|
|
274
274
|
<span><span class="sw p"></span> Placeholder div</span>
|
|
275
|
-
<span><span class="sw n"></span>
|
|
275
|
+
<span><span class="sw n"></span> Anchored view</span>
|
|
276
276
|
</div>
|
|
277
277
|
</div>
|
|
278
278
|
|
|
@@ -282,7 +282,7 @@
|
|
|
282
282
|
<a href="https://www.npmjs.com/package/view-anchor">npm · view-anchor</a>
|
|
283
283
|
<span style="color:var(--text-3)">MIT licensed</span>
|
|
284
284
|
</div>
|
|
285
|
-
<div class="hint">Drag <code>Split</code> to resize the placeholder, toggle <code>present</code> to collapse or restore, and toggle <code>Explode</code> for the 3D perspective. The green view's bounds come directly from view-anchor's <code>publish</code> callback.</div>
|
|
285
|
+
<div class="hint">Drag <code>Split</code> to resize the placeholder, toggle <code>present</code> to collapse or restore, and toggle <code>Explode</code> for the 3D perspective. The green anchored view's bounds come directly from view-anchor's <code>publish</code> callback.</div>
|
|
286
286
|
</footer>
|
|
287
287
|
|
|
288
288
|
</div>
|
|
@@ -293,393 +293,408 @@
|
|
|
293
293
|
block cross-origin ES module imports. -->
|
|
294
294
|
<script>
|
|
295
295
|
/* __VIEW_ANCHOR_CORE_START__ — generated from src/view-anchor.ts by `pnpm build:docs`; do not edit */
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
296
|
+
var __viewAnchorCore = (function(exports) {
|
|
297
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
298
|
+
//#region src/view-anchor.ts
|
|
299
|
+
const ZERO = {
|
|
300
|
+
x: 0,
|
|
301
|
+
y: 0,
|
|
302
|
+
width: 0,
|
|
303
|
+
height: 0
|
|
304
|
+
};
|
|
305
|
+
const NOOP_PUBLISH = () => false;
|
|
306
|
+
const clampRect = (r) => ({
|
|
307
|
+
x: Math.round(r.x),
|
|
308
|
+
y: Math.round(r.y),
|
|
309
|
+
width: Math.max(0, Math.round(r.width)),
|
|
310
|
+
height: Math.max(0, Math.round(r.height))
|
|
311
|
+
});
|
|
312
|
+
/**
|
|
313
|
+
* Bind a native view or external surface to the geometry of `target`.
|
|
314
|
+
*
|
|
315
|
+
* - `present === true`: measures `target.getBoundingClientRect()` and publishes
|
|
316
|
+
* immediately, then re-measures synchronously on ResizeObserver and window resize.
|
|
317
|
+
* - `present === false`: publishes a zero rect ({ x: 0, y: 0, width: 0, height: 0 })
|
|
318
|
+
* and stops observing.
|
|
319
|
+
* - `update(opts)`: re-applies options immediately.
|
|
320
|
+
* - `dispose()`: stops observing and prevents any further publishes.
|
|
321
|
+
*
|
|
322
|
+
* Synchronous publishing: measurement and publishing occur directly in the
|
|
323
|
+
* observer tick. Cross-process setBounds calls already have a compositor delay;
|
|
324
|
+
* adding requestAnimationFrame would add a second frame of visual lag during drag
|
|
325
|
+
* operations. High-frequency updates are deduplicated against the last accepted rect.
|
|
326
|
+
*/
|
|
327
|
+
function createViewAnchor(target, opts) {
|
|
328
|
+
let present = opts.present;
|
|
329
|
+
let publish = opts.publish;
|
|
330
|
+
let observer = null;
|
|
331
|
+
let targetRef = target;
|
|
332
|
+
let lastPublished = null;
|
|
333
|
+
let publicationRevision = 0;
|
|
334
|
+
let disposed = false;
|
|
335
|
+
const measure = () => {
|
|
336
|
+
const r = targetRef.getBoundingClientRect();
|
|
337
|
+
if (!Number.isFinite(r.left) || !Number.isFinite(r.top) || !Number.isFinite(r.width) || !Number.isFinite(r.height)) return null;
|
|
338
|
+
return clampRect({
|
|
339
|
+
x: r.left,
|
|
340
|
+
y: r.top,
|
|
341
|
+
width: r.width,
|
|
342
|
+
height: r.height
|
|
343
|
+
});
|
|
344
|
+
};
|
|
345
|
+
const sameRect = (a, b) => a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;
|
|
346
|
+
const publishCandidate = (candidate) => {
|
|
347
|
+
const previous = lastPublished;
|
|
348
|
+
const attempt = ++publicationRevision;
|
|
349
|
+
lastPublished = candidate;
|
|
350
|
+
try {
|
|
351
|
+
const accepted = publish(candidate) !== false;
|
|
352
|
+
if (!accepted && publicationRevision === attempt && !disposed) lastPublished = previous;
|
|
353
|
+
return accepted;
|
|
354
|
+
} catch (error) {
|
|
355
|
+
if (publicationRevision === attempt && !disposed) lastPublished = previous;
|
|
356
|
+
throw error;
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
const emit = () => {
|
|
360
|
+
if (disposed || !present) return;
|
|
361
|
+
const m = measure();
|
|
362
|
+
if (!m) return;
|
|
363
|
+
if (lastPublished && sameRect(lastPublished, m)) return;
|
|
364
|
+
publishCandidate(m);
|
|
365
|
+
};
|
|
366
|
+
const startObserving = () => {
|
|
367
|
+
if (observer) return;
|
|
368
|
+
observer = new ResizeObserver(emit);
|
|
369
|
+
observer.observe(targetRef);
|
|
370
|
+
window.addEventListener("resize", emit);
|
|
371
|
+
};
|
|
372
|
+
const stopObserving = () => {
|
|
373
|
+
if (observer) {
|
|
374
|
+
observer.disconnect();
|
|
375
|
+
observer = null;
|
|
376
|
+
}
|
|
377
|
+
window.removeEventListener("resize", emit);
|
|
378
|
+
};
|
|
379
|
+
const apply = () => {
|
|
380
|
+
lastPublished = null;
|
|
381
|
+
if (present) {
|
|
382
|
+
startObserving();
|
|
383
|
+
const measured = measure();
|
|
384
|
+
if (measured) publishCandidate(measured);
|
|
385
|
+
} else {
|
|
386
|
+
stopObserving();
|
|
387
|
+
publishCandidate(ZERO);
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
apply();
|
|
391
|
+
return {
|
|
392
|
+
update(next) {
|
|
393
|
+
if (disposed) return;
|
|
394
|
+
publish = next.publish;
|
|
395
|
+
present = next.present;
|
|
396
|
+
apply();
|
|
397
|
+
},
|
|
398
|
+
dispose() {
|
|
399
|
+
if (disposed) return;
|
|
400
|
+
disposed = true;
|
|
401
|
+
stopObserving();
|
|
402
|
+
targetRef = null;
|
|
403
|
+
publish = NOOP_PUBLISH;
|
|
404
|
+
lastPublished = null;
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Read target's current rect and return { visible: true, bounds }.
|
|
410
|
+
* Does not infer visibility from dimensions.
|
|
411
|
+
*/
|
|
412
|
+
function measurePlacement(target) {
|
|
413
|
+
const r = target.getBoundingClientRect();
|
|
414
|
+
return {
|
|
415
|
+
visible: true,
|
|
416
|
+
bounds: clampRect({
|
|
417
|
+
x: r.left,
|
|
418
|
+
y: r.top,
|
|
419
|
+
width: r.width,
|
|
420
|
+
height: r.height
|
|
421
|
+
})
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
const samePlacement = (a, b) => {
|
|
425
|
+
if (a.visible !== b.visible) return false;
|
|
426
|
+
if (a.visible && b.visible) return a.bounds.x === b.bounds.x && a.bounds.y === b.bounds.y && a.bounds.width === b.bounds.width && a.bounds.height === b.bounds.height;
|
|
427
|
+
return true;
|
|
428
|
+
};
|
|
429
|
+
/**
|
|
430
|
+
* Explicit-visibility variant of createViewAnchor.
|
|
431
|
+
*/
|
|
432
|
+
function createPlacementAnchor(target, opts) {
|
|
433
|
+
let visible = opts.visible;
|
|
434
|
+
let publish = opts.publish;
|
|
435
|
+
let guardDisplayNone = opts.guardDisplayNone ?? false;
|
|
436
|
+
let followScroll = opts.followScroll ?? false;
|
|
437
|
+
let followGeometry = opts.followGeometry ?? false;
|
|
438
|
+
let targetRef = target;
|
|
439
|
+
let observer = null;
|
|
440
|
+
let io = null;
|
|
441
|
+
let scrollListening = false;
|
|
442
|
+
let geometryListening = false;
|
|
443
|
+
const capture = { capture: true };
|
|
444
|
+
const passiveCapture = {
|
|
445
|
+
capture: true,
|
|
446
|
+
passive: true
|
|
447
|
+
};
|
|
448
|
+
let lastPublished = null;
|
|
449
|
+
let publicationRevision = 0;
|
|
450
|
+
let disposed = false;
|
|
451
|
+
let rafId = null;
|
|
452
|
+
let steadyFrames = 0;
|
|
453
|
+
const STEADY_CLOSE_FRAMES = 2;
|
|
454
|
+
const MAX_HIDDEN_FOLLOW_FRAMES = 30;
|
|
455
|
+
const MAX_INVALID_FOLLOW_FRAMES = 30;
|
|
456
|
+
let invalidFrames = 0;
|
|
457
|
+
let pointerHeld = false;
|
|
458
|
+
let activePointerId = null;
|
|
459
|
+
let sentinelDeadline = null;
|
|
460
|
+
const computePlacement = () => {
|
|
461
|
+
const p = measurePlacement(targetRef);
|
|
462
|
+
if (p.visible && (!Number.isFinite(p.bounds.x) || !Number.isFinite(p.bounds.y) || !Number.isFinite(p.bounds.width) || !Number.isFinite(p.bounds.height))) return null;
|
|
463
|
+
if (guardDisplayNone && p.visible && (p.bounds.width === 0 || p.bounds.height === 0)) return { visible: false };
|
|
464
|
+
return p;
|
|
465
|
+
};
|
|
466
|
+
const publishCandidate = (candidate) => {
|
|
467
|
+
const previous = lastPublished;
|
|
468
|
+
const attempt = ++publicationRevision;
|
|
469
|
+
lastPublished = candidate;
|
|
470
|
+
try {
|
|
471
|
+
const accepted = publish(candidate) !== false;
|
|
472
|
+
if (!accepted && publicationRevision === attempt && !disposed) lastPublished = previous;
|
|
473
|
+
return accepted;
|
|
474
|
+
} catch (error) {
|
|
475
|
+
if (publicationRevision === attempt && !disposed) lastPublished = previous;
|
|
476
|
+
throw error;
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
const emit = () => {
|
|
480
|
+
if (disposed || !visible) return;
|
|
481
|
+
const p = computePlacement();
|
|
482
|
+
if (!p) return;
|
|
483
|
+
if (lastPublished && samePlacement(lastPublished, p)) return;
|
|
484
|
+
publishCandidate(p);
|
|
485
|
+
};
|
|
486
|
+
const shouldCloseOnHiddenPoll = () => {
|
|
487
|
+
if (lastPublished?.visible === false) return true;
|
|
488
|
+
return steadyFrames++ >= MAX_HIDDEN_FOLLOW_FRAMES;
|
|
489
|
+
};
|
|
490
|
+
const sentinelFrame = () => {
|
|
491
|
+
rafId = null;
|
|
492
|
+
if (disposed || !visible) {
|
|
493
|
+
sentinelDeadline = null;
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
if (sentinelDeadline !== null && performance.now() >= sentinelDeadline) {
|
|
497
|
+
sentinelDeadline = null;
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
const p = computePlacement();
|
|
501
|
+
if (!p) {
|
|
502
|
+
if (invalidFrames++ >= MAX_INVALID_FOLLOW_FRAMES) {
|
|
503
|
+
sentinelDeadline = null;
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
if (!disposed && visible && followGeometry) rafId = requestAnimationFrame(sentinelFrame);
|
|
507
|
+
else sentinelDeadline = null;
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
invalidFrames = 0;
|
|
511
|
+
if (!p.visible) {
|
|
512
|
+
if (shouldCloseOnHiddenPoll()) {
|
|
513
|
+
sentinelDeadline = null;
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
if (!disposed && visible && followGeometry) rafId = requestAnimationFrame(sentinelFrame);
|
|
517
|
+
else sentinelDeadline = null;
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
if (lastPublished && samePlacement(lastPublished, p)) {
|
|
521
|
+
steadyFrames++;
|
|
522
|
+
if (steadyFrames >= STEADY_CLOSE_FRAMES && !pointerHeld) {
|
|
523
|
+
sentinelDeadline = null;
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
} else {
|
|
527
|
+
publishCandidate(p);
|
|
528
|
+
steadyFrames = 0;
|
|
529
|
+
}
|
|
530
|
+
if (!disposed && visible && followGeometry) rafId = requestAnimationFrame(sentinelFrame);
|
|
531
|
+
else sentinelDeadline = null;
|
|
532
|
+
};
|
|
533
|
+
const openSentinel = () => {
|
|
534
|
+
if (!followGeometry || disposed) return;
|
|
535
|
+
steadyFrames = 0;
|
|
536
|
+
if (rafId === null) {
|
|
537
|
+
invalidFrames = 0;
|
|
538
|
+
rafId = requestAnimationFrame(sentinelFrame);
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
const closeSentinel = () => {
|
|
542
|
+
if (rafId !== null) {
|
|
543
|
+
cancelAnimationFrame(rafId);
|
|
544
|
+
rafId = null;
|
|
545
|
+
}
|
|
546
|
+
steadyFrames = 0;
|
|
547
|
+
invalidFrames = 0;
|
|
548
|
+
sentinelDeadline = null;
|
|
549
|
+
pointerHeld = false;
|
|
550
|
+
activePointerId = null;
|
|
551
|
+
};
|
|
552
|
+
const onScroll = () => {
|
|
553
|
+
if (followGeometry) openSentinel();
|
|
554
|
+
else emit();
|
|
555
|
+
};
|
|
556
|
+
const onPointerDown = (e) => {
|
|
557
|
+
const t = e.target;
|
|
558
|
+
if (t && t.closest && t.closest("[role=\"separator\"]")) {
|
|
559
|
+
if (!pointerHeld) activePointerId = e.pointerId;
|
|
560
|
+
pointerHeld = true;
|
|
561
|
+
openSentinel();
|
|
562
|
+
}
|
|
563
|
+
};
|
|
564
|
+
const releasePointer = (e) => {
|
|
565
|
+
if (!pointerHeld) return;
|
|
566
|
+
if (e && activePointerId !== e.pointerId) return;
|
|
567
|
+
pointerHeld = false;
|
|
568
|
+
activePointerId = null;
|
|
569
|
+
openSentinel();
|
|
570
|
+
};
|
|
571
|
+
const onPointerUp = (e) => {
|
|
572
|
+
releasePointer(e);
|
|
573
|
+
};
|
|
574
|
+
const onPointerCancel = (e) => {
|
|
575
|
+
releasePointer(e);
|
|
576
|
+
};
|
|
577
|
+
const onWindowBlur = () => {
|
|
578
|
+
releasePointer();
|
|
579
|
+
};
|
|
580
|
+
const startOptionalObserving = () => {
|
|
581
|
+
if (guardDisplayNone && !io && typeof IntersectionObserver !== "undefined") {
|
|
582
|
+
io = new IntersectionObserver(emit);
|
|
583
|
+
io.observe(targetRef);
|
|
584
|
+
}
|
|
585
|
+
if (followScroll && !scrollListening) {
|
|
586
|
+
window.addEventListener("scroll", onScroll, passiveCapture);
|
|
587
|
+
scrollListening = true;
|
|
588
|
+
}
|
|
589
|
+
if (followGeometry && !geometryListening) {
|
|
590
|
+
window.addEventListener("pointerdown", onPointerDown, capture);
|
|
591
|
+
window.addEventListener("pointerup", onPointerUp, capture);
|
|
592
|
+
window.addEventListener("pointercancel", onPointerCancel, capture);
|
|
593
|
+
window.addEventListener("blur", onWindowBlur);
|
|
594
|
+
geometryListening = true;
|
|
595
|
+
}
|
|
596
|
+
};
|
|
597
|
+
const stopOptionalObserving = () => {
|
|
598
|
+
if (io && !guardDisplayNone) {
|
|
599
|
+
io.disconnect();
|
|
600
|
+
io = null;
|
|
601
|
+
}
|
|
602
|
+
if (scrollListening && !followScroll) {
|
|
603
|
+
window.removeEventListener("scroll", onScroll, passiveCapture);
|
|
604
|
+
scrollListening = false;
|
|
605
|
+
}
|
|
606
|
+
if (geometryListening && !followGeometry) {
|
|
607
|
+
window.removeEventListener("pointerdown", onPointerDown, capture);
|
|
608
|
+
window.removeEventListener("pointerup", onPointerUp, capture);
|
|
609
|
+
window.removeEventListener("pointercancel", onPointerCancel, capture);
|
|
610
|
+
window.removeEventListener("blur", onWindowBlur);
|
|
611
|
+
geometryListening = false;
|
|
612
|
+
closeSentinel();
|
|
613
|
+
}
|
|
614
|
+
};
|
|
615
|
+
const stopAllOptionalObserving = () => {
|
|
616
|
+
if (io) {
|
|
617
|
+
io.disconnect();
|
|
618
|
+
io = null;
|
|
619
|
+
}
|
|
620
|
+
if (scrollListening) {
|
|
621
|
+
window.removeEventListener("scroll", onScroll, passiveCapture);
|
|
622
|
+
scrollListening = false;
|
|
623
|
+
}
|
|
624
|
+
if (geometryListening) {
|
|
625
|
+
window.removeEventListener("pointerdown", onPointerDown, capture);
|
|
626
|
+
window.removeEventListener("pointerup", onPointerUp, capture);
|
|
627
|
+
window.removeEventListener("pointercancel", onPointerCancel, capture);
|
|
628
|
+
window.removeEventListener("blur", onWindowBlur);
|
|
629
|
+
geometryListening = false;
|
|
630
|
+
}
|
|
631
|
+
closeSentinel();
|
|
632
|
+
};
|
|
633
|
+
const startObserving = () => {
|
|
634
|
+
if (observer) return;
|
|
635
|
+
observer = new ResizeObserver(emit);
|
|
636
|
+
observer.observe(targetRef);
|
|
637
|
+
window.addEventListener("resize", emit);
|
|
638
|
+
startOptionalObserving();
|
|
639
|
+
};
|
|
640
|
+
const stopObserving = () => {
|
|
641
|
+
if (observer) {
|
|
642
|
+
observer.disconnect();
|
|
643
|
+
observer = null;
|
|
644
|
+
}
|
|
645
|
+
window.removeEventListener("resize", emit);
|
|
646
|
+
stopAllOptionalObserving();
|
|
647
|
+
};
|
|
648
|
+
const apply = () => {
|
|
649
|
+
lastPublished = null;
|
|
650
|
+
if (visible) {
|
|
651
|
+
startObserving();
|
|
652
|
+
const placement = computePlacement();
|
|
653
|
+
if (placement) publishCandidate(placement);
|
|
654
|
+
} else {
|
|
655
|
+
stopObserving();
|
|
656
|
+
publishCandidate({ visible: false });
|
|
657
|
+
}
|
|
658
|
+
};
|
|
659
|
+
apply();
|
|
660
|
+
return {
|
|
661
|
+
update(next) {
|
|
662
|
+
if (disposed) return;
|
|
663
|
+
publish = next.publish;
|
|
664
|
+
visible = next.visible;
|
|
665
|
+
guardDisplayNone = next.guardDisplayNone ?? guardDisplayNone;
|
|
666
|
+
followScroll = next.followScroll ?? followScroll;
|
|
667
|
+
followGeometry = next.followGeometry ?? followGeometry;
|
|
668
|
+
if (visible && observer) {
|
|
669
|
+
stopOptionalObserving();
|
|
670
|
+
startOptionalObserving();
|
|
671
|
+
}
|
|
672
|
+
apply();
|
|
673
|
+
},
|
|
674
|
+
dispose() {
|
|
675
|
+
if (disposed) return;
|
|
676
|
+
disposed = true;
|
|
677
|
+
stopObserving();
|
|
678
|
+
targetRef = null;
|
|
679
|
+
publish = NOOP_PUBLISH;
|
|
680
|
+
lastPublished = null;
|
|
681
|
+
},
|
|
682
|
+
pulse(durationMs) {
|
|
683
|
+
if (disposed || !followGeometry) return;
|
|
684
|
+
if (durationMs !== void 0 && durationMs > 0) {
|
|
685
|
+
const next = performance.now() + durationMs;
|
|
686
|
+
sentinelDeadline = sentinelDeadline === null ? next : Math.max(sentinelDeadline, next);
|
|
687
|
+
}
|
|
688
|
+
openSentinel();
|
|
689
|
+
}
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
//#endregion
|
|
693
|
+
exports.createPlacementAnchor = createPlacementAnchor;
|
|
694
|
+
exports.createViewAnchor = createViewAnchor;
|
|
695
|
+
exports.measurePlacement = measurePlacement;
|
|
696
|
+
return exports;
|
|
697
|
+
})({});
|
|
683
698
|
/* __VIEW_ANCHOR_CORE_END__ */
|
|
684
699
|
</script>
|
|
685
700
|
|
|
@@ -699,7 +714,7 @@ var __viewAnchorCore = (() => {
|
|
|
699
714
|
const readout = document.getElementById('readout')
|
|
700
715
|
|
|
701
716
|
// The placeholder's rect inside the deck (accumulated along the offsetParent
|
|
702
|
-
// chain, independent of the 3D transform) — flat or tilted, the
|
|
717
|
+
// chain, independent of the 3D transform) — flat or tilted, the anchored view
|
|
703
718
|
// stays registered directly above the placeholder.
|
|
704
719
|
function deckLocalRect(el) {
|
|
705
720
|
let x = 0, y = 0, n = el
|