view-anchor 0.1.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/LICENSE +21 -0
- package/README.md +114 -0
- package/README.zh-CN.md +114 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/measure-loop.d.ts +43 -0
- package/dist/measure-loop.d.ts.map +1 -0
- package/dist/measure-loop.js +49 -0
- package/dist/react.d.ts +38 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +145 -0
- package/dist/size-advertiser.d.ts +21 -0
- package/dist/size-advertiser.d.ts.map +1 -0
- package/dist/size-advertiser.js +83 -0
- package/dist/types.d.ts +111 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +18 -0
- package/dist/view-anchor.d.ts +111 -0
- package/dist/view-anchor.d.ts.map +1 -0
- package/dist/view-anchor.js +413 -0
- package/docs/anchor-3d.html +615 -0
- package/docs/bidirectional-design.md +140 -0
- package/docs/mechanism.mdx +119 -0
- package/package.json +83 -0
- package/src/index.ts +39 -0
- package/src/measure-loop.ts +87 -0
- package/src/react.ts +171 -0
- package/src/size-advertiser.ts +95 -0
- package/src/types.ts +123 -0
- package/src/view-anchor.ts +500 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { createMeasureLoop } from './measure-loop.js';
|
|
2
|
+
/**
|
|
3
|
+
* Reverse of `createViewAnchor`: runs in a downstream WebContentsView's own
|
|
4
|
+
* renderer, measures the content's own size on ONE owned axis (from the
|
|
5
|
+
* `ResizeObserver` border-box — no `getBoundingClientRect`, no forced reflow),
|
|
6
|
+
* and advertises it via the injected `publish`. Shares the forward primitive's
|
|
7
|
+
* measure/coalesce/dedupe/dispose engine (`createMeasureLoop`).
|
|
8
|
+
*
|
|
9
|
+
* The extent is `Math.round`ed and clamped to `>= 0`; non-finite measurements
|
|
10
|
+
* drop the frame.
|
|
11
|
+
*
|
|
12
|
+
* FOOTGUN — `target` must be shrink-to-fit on the owned axis: its owned-axis
|
|
13
|
+
* size must NOT be driven by the host-applied view size, or the cross-process
|
|
14
|
+
* loop (advertise → host resizes view → remeasure) never converges (it
|
|
15
|
+
* oscillates or stays "stable but wrong"). Measuring `<body>`/`<html>` is the
|
|
16
|
+
* classic mistake — their size *is* the view size. See
|
|
17
|
+
* `docs/bidirectional-design.md`'s single-axis-ownership and trust-boundary
|
|
18
|
+
* sections.
|
|
19
|
+
*/
|
|
20
|
+
export function createSizeAdvertiser(target, opts) {
|
|
21
|
+
const axis = opts.axis; // immutable for the advertiser's life
|
|
22
|
+
let publish = opts.publish;
|
|
23
|
+
let observer = null;
|
|
24
|
+
let disposed = false;
|
|
25
|
+
// Latest border-box, stashed by the RO callback and read by `produce` in the
|
|
26
|
+
// RAF body (keep the entry out of the shared, DOM-agnostic loop).
|
|
27
|
+
let latest = null;
|
|
28
|
+
const produce = () => {
|
|
29
|
+
if (!latest)
|
|
30
|
+
return null;
|
|
31
|
+
const raw = axis === 'block' ? latest.blockSize : latest.inlineSize;
|
|
32
|
+
if (!Number.isFinite(raw))
|
|
33
|
+
return null;
|
|
34
|
+
return { axis, extent: Math.max(0, Math.round(raw)) };
|
|
35
|
+
};
|
|
36
|
+
const loop = createMeasureLoop({
|
|
37
|
+
produce,
|
|
38
|
+
same: (a, b) => a.extent === b.extent, // axis is constant
|
|
39
|
+
sink: (size) => publish(size),
|
|
40
|
+
});
|
|
41
|
+
const onResize = (entries) => {
|
|
42
|
+
const entry = entries[entries.length - 1];
|
|
43
|
+
if (entry) {
|
|
44
|
+
latest = entry.borderBoxSize?.[0] ?? entry.contentBoxSize?.[0] ?? latest;
|
|
45
|
+
}
|
|
46
|
+
loop.schedule();
|
|
47
|
+
};
|
|
48
|
+
// One cheap, once-per-advertiser guard for the textbook feedback-loop footgun.
|
|
49
|
+
const doc = target.ownerDocument;
|
|
50
|
+
if (target === doc.body || target === doc.documentElement) {
|
|
51
|
+
console.warn(`[view-anchor] size-advertiser: <${target === doc.body ? 'body' : 'html'}>'s ` +
|
|
52
|
+
`${axis} size is the host-given view size, not the content size — the ` +
|
|
53
|
+
`advertiser will never shrink to content. Measure a shrink-to-fit wrapper. ` +
|
|
54
|
+
`See bidirectional-design.md's single-axis-ownership section.`);
|
|
55
|
+
}
|
|
56
|
+
loop.setActive(true);
|
|
57
|
+
observer = new ResizeObserver(onResize);
|
|
58
|
+
observer.observe(target);
|
|
59
|
+
return {
|
|
60
|
+
update(nextPublish) {
|
|
61
|
+
if (disposed)
|
|
62
|
+
return;
|
|
63
|
+
publish = nextPublish;
|
|
64
|
+
// Re-advertise the current size to the new sink immediately (mirrors the
|
|
65
|
+
// forward anchor's re-publish on update) so the new channel is not left
|
|
66
|
+
// sizeless until the next ResizeObserver tick.
|
|
67
|
+
const cur = produce();
|
|
68
|
+
if (cur)
|
|
69
|
+
loop.emitNow(cur);
|
|
70
|
+
},
|
|
71
|
+
dispose() {
|
|
72
|
+
if (disposed)
|
|
73
|
+
return;
|
|
74
|
+
disposed = true;
|
|
75
|
+
loop.cancel();
|
|
76
|
+
if (observer) {
|
|
77
|
+
observer.disconnect();
|
|
78
|
+
observer = null;
|
|
79
|
+
}
|
|
80
|
+
loop.dispose();
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* view-anchor — sync a main-process native view's bounds to a DOM element.
|
|
3
|
+
*
|
|
4
|
+
* Self-contained, engine-agnostic primitive. It knows nothing about
|
|
5
|
+
* Electron (the `publish` callback owns the IPC → `setBounds`), nothing
|
|
6
|
+
* about React (the core is imperative; see `react.ts` for the adapter),
|
|
7
|
+
* and nothing about the host layout engine (the `target` element may come
|
|
8
|
+
* from our own `compile`/`FrameTree`, or a dockview panel's
|
|
9
|
+
* `content.element`; the mechanism is identical).
|
|
10
|
+
*
|
|
11
|
+
* It is the modern, alive replacement for the archived
|
|
12
|
+
* `react-electron-browser-view`: the cross-process bridge that DOM layout
|
|
13
|
+
* libraries (dockview included) deliberately do not provide. dockview's
|
|
14
|
+
* internal `OverlayRenderContainer` does the same getBoundingClientRect →
|
|
15
|
+
* RAF → reposition dance, but its follower is a DOM node; ours is a native
|
|
16
|
+
* `WebContentsView` positioned via the injected `publish`.
|
|
17
|
+
*/
|
|
18
|
+
/** A screen-space rectangle, in CSS pixels. Structurally compatible with
|
|
19
|
+
* the host's `ViewBounds` so a publisher typed against either works. */
|
|
20
|
+
export interface Bounds {
|
|
21
|
+
x: number;
|
|
22
|
+
y: number;
|
|
23
|
+
width: number;
|
|
24
|
+
height: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Explicit visibility + geometry for a native view, replacing the legacy
|
|
28
|
+
* magic-`{0,0,0,0}` "hidden" convention (`present:false → ZERO bounds`).
|
|
29
|
+
*
|
|
30
|
+
* Visibility is a DISCRIMINANT, never inferred from geometry. The whole
|
|
31
|
+
* reason this type exists: a genuinely zero-SIZED but on-screen view
|
|
32
|
+
* (`{ visible:true, bounds:{...,width:0,height:0} }`) is now distinct from a
|
|
33
|
+
* detached/hidden one (`{ visible:false }`, which carries no `bounds` at
|
|
34
|
+
* all). Under the old ZERO convention both collapsed to the same value and
|
|
35
|
+
* were indistinguishable.
|
|
36
|
+
*/
|
|
37
|
+
export type Placement = {
|
|
38
|
+
visible: true;
|
|
39
|
+
bounds: Bounds;
|
|
40
|
+
} | {
|
|
41
|
+
visible: false;
|
|
42
|
+
};
|
|
43
|
+
export interface ViewAnchorOptions {
|
|
44
|
+
/**
|
|
45
|
+
* Whether the native view should be attached. When `false`, the anchor
|
|
46
|
+
* publishes zero bounds (`{0,0,0,0}`) — the host treats `width === 0 ||
|
|
47
|
+
* height === 0` as "detach the child view but keep its WebContents
|
|
48
|
+
* alive" (detach-but-keep-alive). No DOM measurement is needed in this
|
|
49
|
+
* state.
|
|
50
|
+
*/
|
|
51
|
+
present: boolean;
|
|
52
|
+
/** Receives the live rect, or `{0,0,0,0}` when detached. Owns IPC. */
|
|
53
|
+
publish: (bounds: Bounds) => void;
|
|
54
|
+
}
|
|
55
|
+
export interface ViewAnchorHandle {
|
|
56
|
+
/**
|
|
57
|
+
* Apply new options. Re-publishes immediately to reflect the new state
|
|
58
|
+
* (present=true → measure + observe; present=false → zero bounds).
|
|
59
|
+
*/
|
|
60
|
+
update(opts: ViewAnchorOptions): void;
|
|
61
|
+
/** Stop observing and remove listeners. After dispose the anchor never
|
|
62
|
+
* publishes again (every emit reads `disposed` synchronously, so there is
|
|
63
|
+
* no queued frame that could fire late). */
|
|
64
|
+
dispose(): void;
|
|
65
|
+
}
|
|
66
|
+
/** Which axis this advertiser owns. `block` = height, `inline` = width
|
|
67
|
+
* (logical-property naming, axis-agnostic to writing mode). */
|
|
68
|
+
export type AdvertisedAxis = 'block' | 'inline';
|
|
69
|
+
/**
|
|
70
|
+
* One frame of advertised size. A pure scalar plus the owning axis — there is
|
|
71
|
+
* deliberately no field for the *other* axis, so "advertise two axes" is not
|
|
72
|
+
* expressible (single-axis ownership is enforced in the type, not at runtime).
|
|
73
|
+
*/
|
|
74
|
+
export interface AdvertisedSize {
|
|
75
|
+
/** Mirrors the factory's `axis`; constant across frames. Lets the host
|
|
76
|
+
* whitelist-check the axis it is willing to accept. */
|
|
77
|
+
readonly axis: AdvertisedAxis;
|
|
78
|
+
/** The owned axis's content extent, in CSS px — already rounded and clamped
|
|
79
|
+
* to `>= 0`. */
|
|
80
|
+
readonly extent: number;
|
|
81
|
+
}
|
|
82
|
+
export interface SizeAdvertiserOptions {
|
|
83
|
+
/** The single axis this advertiser owns. Fixed for the advertiser's life. */
|
|
84
|
+
axis: AdvertisedAxis;
|
|
85
|
+
/** Receives each advertised size. Owns the IPC/postMessage → host. Mirrors
|
|
86
|
+
* the forward `publish` (same role: the injected, transport-owning sink). */
|
|
87
|
+
publish: (size: AdvertisedSize) => void;
|
|
88
|
+
}
|
|
89
|
+
export interface SizeAdvertiserHandle {
|
|
90
|
+
/**
|
|
91
|
+
* Swap the `publish` sink (e.g. a new IPC channel) and immediately
|
|
92
|
+
* re-advertise the current size to it (mirrors the forward anchor's
|
|
93
|
+
* re-publish on update), so the new channel is not left sizeless until the
|
|
94
|
+
* next `ResizeObserver` tick.
|
|
95
|
+
*
|
|
96
|
+
* Takes only the new sink — `axis` is immutable by construction, so it is
|
|
97
|
+
* deliberately not expressible here (you cannot attempt to change it). To
|
|
98
|
+
* advertise a different axis, dispose and create a new advertiser.
|
|
99
|
+
*/
|
|
100
|
+
update(publish: (size: AdvertisedSize) => void): void;
|
|
101
|
+
/**
|
|
102
|
+
* Stop observing, cancel any pending RAF. After dispose nothing is
|
|
103
|
+
* advertised again. (There is no ZERO/terminal value — collapsing is the
|
|
104
|
+
* host's policy, unlike the forward anchor's `present:false`.)
|
|
105
|
+
*
|
|
106
|
+
* The first advertised value is asynchronous: it awaits the observer's first
|
|
107
|
+
* frame, and a `display:none` target advertises nothing until shown.
|
|
108
|
+
*/
|
|
109
|
+
dispose(): void;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH;yEACyE;AACzE,MAAM,WAAW,MAAM;IACrB,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;;;;;;;;GAUG;AACH,MAAM,MAAM,SAAS,GACjB;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,OAAO,EAAE,KAAK,CAAA;CAAE,CAAA;AAEtB,MAAM,WAAW,iBAAiB;IAChC;;;;;;OAMG;IACH,OAAO,EAAE,OAAO,CAAA;IAChB,sEAAsE;IACtE,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;CAClC;AAED,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,MAAM,CAAC,IAAI,EAAE,iBAAiB,GAAG,IAAI,CAAA;IACrC;;iDAE6C;IAC7C,OAAO,IAAI,IAAI,CAAA;CAChB;AAUD;gEACgE;AAChE,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,QAAQ,CAAA;AAE/C;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B;4DACwD;IACxD,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAA;IAC7B;qBACiB;IACjB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,qBAAqB;IACpC,6EAA6E;IAC7E,IAAI,EAAE,cAAc,CAAA;IACpB;kFAC8E;IAC9E,OAAO,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAA;CACxC;AAED,MAAM,WAAW,oBAAoB;IACnC;;;;;;;;;OASG;IACH,MAAM,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,GAAG,IAAI,CAAA;IACrD;;;;;;;OAOG;IACH,OAAO,IAAI,IAAI,CAAA;CAChB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* view-anchor — sync a main-process native view's bounds to a DOM element.
|
|
3
|
+
*
|
|
4
|
+
* Self-contained, engine-agnostic primitive. It knows nothing about
|
|
5
|
+
* Electron (the `publish` callback owns the IPC → `setBounds`), nothing
|
|
6
|
+
* about React (the core is imperative; see `react.ts` for the adapter),
|
|
7
|
+
* and nothing about the host layout engine (the `target` element may come
|
|
8
|
+
* from our own `compile`/`FrameTree`, or a dockview panel's
|
|
9
|
+
* `content.element`; the mechanism is identical).
|
|
10
|
+
*
|
|
11
|
+
* It is the modern, alive replacement for the archived
|
|
12
|
+
* `react-electron-browser-view`: the cross-process bridge that DOM layout
|
|
13
|
+
* libraries (dockview included) deliberately do not provide. dockview's
|
|
14
|
+
* internal `OverlayRenderContainer` does the same getBoundingClientRect →
|
|
15
|
+
* RAF → reposition dance, but its follower is a DOM node; ours is a native
|
|
16
|
+
* `WebContentsView` positioned via the injected `publish`.
|
|
17
|
+
*/
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import type { Placement, ViewAnchorOptions, ViewAnchorHandle } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Create an anchor binding ONE native view's bounds to `target`'s geometry.
|
|
4
|
+
*
|
|
5
|
+
* Imperative core — no React, no Electron. Behaviour:
|
|
6
|
+
* - `present === true`: publish `target.getBoundingClientRect()` (x/y rounded,
|
|
7
|
+
* width/height `Math.max(0, Math.round(...))`) immediately, then re-publish
|
|
8
|
+
* SYNCHRONOUSLY on every `ResizeObserver` tick and window `resize`.
|
|
9
|
+
* - `present === false`: publish `{0,0,0,0}` immediately; do not observe.
|
|
10
|
+
* - `update(opts)`: re-apply synchronously.
|
|
11
|
+
* - `dispose()`: stop observing, never publish again.
|
|
12
|
+
*
|
|
13
|
+
* Synchronous, NOT RAF-deferred: the native overlay is a cross-process
|
|
14
|
+
* `WebContentsView` whose `setBounds` already lands ~1 compositor frame behind
|
|
15
|
+
* the renderer's DOM paint (the two processes composite on different frames).
|
|
16
|
+
* Deferring the measure+publish to a RAF stacked a SECOND frame on top — during
|
|
17
|
+
* a height/splitter drag that read as the overlay visibly trailing the region
|
|
18
|
+
* edge (worst when GROWING, where the not-yet-followed edge exposes background).
|
|
19
|
+
* Publishing in the observer tick itself removes that self-inflicted frame and
|
|
20
|
+
* leaves only the unavoidable cross-process frame (masked by matching the
|
|
21
|
+
* placeholder/desk background colour). The anti-flood role the RAF used to play
|
|
22
|
+
* — collapsing a burst of RO+resize ticks in one frame into one publish — is now
|
|
23
|
+
* served by `lastPublished` dedup: a tick whose measured rect is byte-identical
|
|
24
|
+
* to the last published one is dropped, so a continuous drag still emits at most
|
|
25
|
+
* one publish per distinct rect.
|
|
26
|
+
*
|
|
27
|
+
* Teardown safety: there is no queued frame to outrun a state change — every
|
|
28
|
+
* emit reads `disposed`/`present` synchronously, so a tick after
|
|
29
|
+
* `update`/`dispose` can never write a stale rect over the live one.
|
|
30
|
+
*/
|
|
31
|
+
export declare function createViewAnchor(target: HTMLElement, opts: ViewAnchorOptions): ViewAnchorHandle;
|
|
32
|
+
export interface PlacementAnchorOptions {
|
|
33
|
+
/**
|
|
34
|
+
* Caller's INTENT: should the native view be on-screen? `true` →
|
|
35
|
+
* publish the measured rect as `{ visible:true, bounds }`; `false` →
|
|
36
|
+
* publish `{ visible:false }`. Crucially, hiddenness comes from this
|
|
37
|
+
* flag, not from a measured zero size — so a legitimately 0-sized but
|
|
38
|
+
* visible target still publishes `visible:true`.
|
|
39
|
+
*/
|
|
40
|
+
visible: boolean;
|
|
41
|
+
/** Receives each explicit Placement. Owns IPC → host. */
|
|
42
|
+
publish: (placement: Placement) => void;
|
|
43
|
+
/**
|
|
44
|
+
* Opt-in geometry detach. When true, a measured zero-area target (no
|
|
45
|
+
* geometry box — display:none / unmounted / unstable first layout) publishes
|
|
46
|
+
* `{ visible:false }` (detach-but-keep) instead of `{ visible:true,
|
|
47
|
+
* bounds:0×0 }`, and an IntersectionObserver is attached so a display:none
|
|
48
|
+
* transition (which ResizeObserver does not report) re-publishes. Default
|
|
49
|
+
* false keeps the legitimate 0×0-visible semantics.
|
|
50
|
+
*/
|
|
51
|
+
guardDisplayNone?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Opt-in capture-phase ancestor-scroll follow. When true, the anchor
|
|
54
|
+
* listens for `scroll` on `window` in the CAPTURE phase (scroll events don't
|
|
55
|
+
* bubble, but reach `window` while capturing), so an ancestor scroll
|
|
56
|
+
* container scrolling the target re-measures and re-publishes. With
|
|
57
|
+
* `followGeometry` off, the scroll callback does a single synchronous
|
|
58
|
+
* `emit()`; with it on, the scroll OPENS the RAF sentinel window so the
|
|
59
|
+
* follow tracks every frame of a scroll burst. Default false.
|
|
60
|
+
*/
|
|
61
|
+
followScroll?: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Opt-in windowed RAF geometry sentinel. Catches ancestor
|
|
64
|
+
* transform / reflow moves that no DOM event reports. The sentinel is
|
|
65
|
+
* NON-resident: it is OPENED on demand (a scroll burst, a `[role="separator"]`
|
|
66
|
+
* splitter pointerdown, or an explicit `pulse()`), polls geometry once per
|
|
67
|
+
* animation frame publishing IN-FRAME, and AUTO-CLOSES once the rect goes
|
|
68
|
+
* steady (a few unchanged frames). While closed it schedules no frame, so the
|
|
69
|
+
* static cost when idle is exactly zero. Default false.
|
|
70
|
+
*/
|
|
71
|
+
followGeometry?: boolean;
|
|
72
|
+
}
|
|
73
|
+
export interface PlacementAnchorHandle {
|
|
74
|
+
/** Apply new options; re-publishes immediately (mirrors `createViewAnchor`). */
|
|
75
|
+
update(opts: PlacementAnchorOptions): void;
|
|
76
|
+
/** Stop observing; never publish again. */
|
|
77
|
+
dispose(): void;
|
|
78
|
+
/**
|
|
79
|
+
* Open the RAF sentinel window (animation follow); auto-closes after going
|
|
80
|
+
* steady or after `durationMs`. No-op when `followGeometry` is false.
|
|
81
|
+
*/
|
|
82
|
+
pulse(durationMs?: number): void;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Pure measure: read `target`'s rect and wrap it as an explicit visible
|
|
86
|
+
* Placement. Always `{ visible:true }` — hiddenness is a caller decision
|
|
87
|
+
* (see `createPlacementAnchor`), so this never returns `{ visible:false }`
|
|
88
|
+
* and never infers visibility from a 0 size. A collapsed (0×0) but present
|
|
89
|
+
* element therefore yields `{ visible:true, bounds:{...,width:0,height:0} }`,
|
|
90
|
+
* distinct from any hidden Placement.
|
|
91
|
+
*/
|
|
92
|
+
export declare function measurePlacement(target: HTMLElement): Placement;
|
|
93
|
+
/**
|
|
94
|
+
* The explicit-Placement mirror of `createViewAnchor`. Same observer/dedup/
|
|
95
|
+
* teardown machinery, but the sink receives a `Placement`:
|
|
96
|
+
* - `visible === true` → publish `measurePlacement(target)` and re-publish
|
|
97
|
+
* SYNCHRONOUSLY on every `ResizeObserver`/`resize` tick.
|
|
98
|
+
* - `visible === false` → publish `{ visible:false }` (NOT a ZERO bounds);
|
|
99
|
+
* do not observe.
|
|
100
|
+
*
|
|
101
|
+
* Dedup carries the discriminant (`samePlacement`), so a visibility flip is
|
|
102
|
+
* never coalesced away.
|
|
103
|
+
*
|
|
104
|
+
* Opt-in `guardDisplayNone` (default false): when on, a measured zero-area
|
|
105
|
+
* target (display:none / unmounted / unstable first layout) publishes
|
|
106
|
+
* `{ visible:false }` instead of `{ visible:true, bounds:0×0 }`, and an
|
|
107
|
+
* IntersectionObserver is attached so a display:none transition (which
|
|
108
|
+
* ResizeObserver does not report) re-publishes.
|
|
109
|
+
*/
|
|
110
|
+
export declare function createPlacementAnchor(target: HTMLElement, opts: PlacementAnchorOptions): PlacementAnchorHandle;
|
|
111
|
+
//# sourceMappingURL=view-anchor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"view-anchor.d.ts","sourceRoot":"","sources":["../src/view-anchor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,SAAS,EACT,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,YAAY,CAAA;AAwBnB;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,WAAW,EACnB,IAAI,EAAE,iBAAiB,GACtB,gBAAgB,CA6ElB;AASD,MAAM,WAAW,sBAAsB;IACrC;;;;;;OAMG;IACH,OAAO,EAAE,OAAO,CAAA;IAChB,yDAAyD;IACzD,OAAO,EAAE,CAAC,SAAS,EAAE,SAAS,KAAK,IAAI,CAAA;IACvC;;;;;;;OAOG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB;AAED,MAAM,WAAW,qBAAqB;IACpC,gFAAgF;IAChF,MAAM,CAAC,IAAI,EAAE,sBAAsB,GAAG,IAAI,CAAA;IAC1C,2CAA2C;IAC3C,OAAO,IAAI,IAAI,CAAA;IACf;;;OAGG;IACH,KAAK,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACjC;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,CAM/D;AAiBD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,WAAW,EACnB,IAAI,EAAE,sBAAsB,GAC3B,qBAAqB,CAuPvB"}
|