@livetemplate/client 0.9.2 → 0.11.3
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/dist/dom/spy.d.ts +36 -0
- package/dist/dom/spy.d.ts.map +1 -0
- package/dist/dom/spy.js +388 -0
- package/dist/dom/spy.js.map +1 -0
- package/dist/livetemplate-client.browser.js +5 -5
- package/dist/livetemplate-client.browser.js.map +4 -4
- package/dist/livetemplate-client.d.ts.map +1 -1
- package/dist/livetemplate-client.js +3 -0
- package/dist/livetemplate-client.js.map +1 -1
- package/dist/tests/spy.test.d.ts +2 -0
- package/dist/tests/spy.test.d.ts.map +1 -0
- package/dist/tests/spy.test.js +432 -0
- package/dist/tests/spy.test.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scroll-spy directive.
|
|
3
|
+
*
|
|
4
|
+
* Highlights navigation links as the user scrolls past corresponding section
|
|
5
|
+
* targets. Pure client-side state — no server round-trip per scroll tick.
|
|
6
|
+
*
|
|
7
|
+
* <article lvt-spy="h1, h2, h3"> // container mode: descendants
|
|
8
|
+
* <h1 id="intro">Intro</h1> matching the selector are
|
|
9
|
+
* <h2 id="usage">Usage</h2> the spy targets.
|
|
10
|
+
* </article>
|
|
11
|
+
*
|
|
12
|
+
* <h2 id="other" lvt-spy>Other</h2> // element mode: this element
|
|
13
|
+
* IS the target. Empty value.
|
|
14
|
+
*
|
|
15
|
+
* <a href="#intro" lvt-spy-link>Intro</a> // gets the `lvt-active` class
|
|
16
|
+
* <a href="#usage" lvt-spy-link>Usage</a> when its href="#<id>" matches
|
|
17
|
+
* the currently active target.
|
|
18
|
+
*
|
|
19
|
+
* Activation rule: walk targets in document order; the latest one whose top
|
|
20
|
+
* edge has scrolled above a trigger line near the top of the viewport is
|
|
21
|
+
* active. That way the first link stays active until the reader has actually
|
|
22
|
+
* passed the first heading, and the active link advances in step with the
|
|
23
|
+
* reader.
|
|
24
|
+
*
|
|
25
|
+
* Configuration via CSS custom property on the spy container:
|
|
26
|
+
* --lvt-spy-margin: <length> (default: 25vh from the top)
|
|
27
|
+
* The trigger line below the viewport top. A target counts as "passed"
|
|
28
|
+
* once its top edge is at or above this line.
|
|
29
|
+
*
|
|
30
|
+
* Implementation: a rAF-throttled scroll listener on the nearest scrollable
|
|
31
|
+
* ancestor (or window). Mirrors the lifecycle shape of `scroll-away.ts` so
|
|
32
|
+
* the morphdom re-scan story is identical.
|
|
33
|
+
*/
|
|
34
|
+
export declare function setupSpy(scanRoot: Element): void;
|
|
35
|
+
export declare function teardownSpy(wrapper?: Element): void;
|
|
36
|
+
//# sourceMappingURL=spy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spy.d.ts","sourceRoot":"","sources":["../../dom/spy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AA0VH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAQhD;AAED,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CA0BnD"}
|
package/dist/dom/spy.js
ADDED
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Scroll-spy directive.
|
|
4
|
+
*
|
|
5
|
+
* Highlights navigation links as the user scrolls past corresponding section
|
|
6
|
+
* targets. Pure client-side state — no server round-trip per scroll tick.
|
|
7
|
+
*
|
|
8
|
+
* <article lvt-spy="h1, h2, h3"> // container mode: descendants
|
|
9
|
+
* <h1 id="intro">Intro</h1> matching the selector are
|
|
10
|
+
* <h2 id="usage">Usage</h2> the spy targets.
|
|
11
|
+
* </article>
|
|
12
|
+
*
|
|
13
|
+
* <h2 id="other" lvt-spy>Other</h2> // element mode: this element
|
|
14
|
+
* IS the target. Empty value.
|
|
15
|
+
*
|
|
16
|
+
* <a href="#intro" lvt-spy-link>Intro</a> // gets the `lvt-active` class
|
|
17
|
+
* <a href="#usage" lvt-spy-link>Usage</a> when its href="#<id>" matches
|
|
18
|
+
* the currently active target.
|
|
19
|
+
*
|
|
20
|
+
* Activation rule: walk targets in document order; the latest one whose top
|
|
21
|
+
* edge has scrolled above a trigger line near the top of the viewport is
|
|
22
|
+
* active. That way the first link stays active until the reader has actually
|
|
23
|
+
* passed the first heading, and the active link advances in step with the
|
|
24
|
+
* reader.
|
|
25
|
+
*
|
|
26
|
+
* Configuration via CSS custom property on the spy container:
|
|
27
|
+
* --lvt-spy-margin: <length> (default: 25vh from the top)
|
|
28
|
+
* The trigger line below the viewport top. A target counts as "passed"
|
|
29
|
+
* once its top edge is at or above this line.
|
|
30
|
+
*
|
|
31
|
+
* Implementation: a rAF-throttled scroll listener on the nearest scrollable
|
|
32
|
+
* ancestor (or window). Mirrors the lifecycle shape of `scroll-away.ts` so
|
|
33
|
+
* the morphdom re-scan story is identical.
|
|
34
|
+
*/
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.setupSpy = setupSpy;
|
|
37
|
+
exports.teardownSpy = teardownSpy;
|
|
38
|
+
const ACTIVE_CLASS = "lvt-active";
|
|
39
|
+
const BINDING_KEY = "__lvt_spy";
|
|
40
|
+
const LINK_HANDLER_KEY = "__lvt_spy_link_handler";
|
|
41
|
+
// `activeBindings` is module-level — i.e. shared by every consumer
|
|
42
|
+
// of this client bundle. In single-instance setups (the common case)
|
|
43
|
+
// that's a non-issue. With multiple LiveTemplateClient mounts sharing
|
|
44
|
+
// the same bundle module instance, the per-container `BINDING_KEY`
|
|
45
|
+
// guard keeps each binding isolated and `teardownSpy(wrapper)`
|
|
46
|
+
// touches only the wrapped subset, so the shared list is safe in
|
|
47
|
+
// practice. Listed here so a future audit reading "two clients
|
|
48
|
+
// stomping each other" finds the design intent.
|
|
49
|
+
const activeBindings = [];
|
|
50
|
+
// Tracks spy-target elements we've already warned about (missing id).
|
|
51
|
+
// processContainer re-runs detach+attach whenever the target set
|
|
52
|
+
// changes, so without per-element dedup the warning would spam the
|
|
53
|
+
// console on every morphdom render that includes an id-less target.
|
|
54
|
+
// WeakSet so cleared targets eventually get GC'd with the DOM nodes.
|
|
55
|
+
const warnedMissingId = new WeakSet();
|
|
56
|
+
function pruneDisconnectedBindings() {
|
|
57
|
+
for (let i = activeBindings.length - 1; i >= 0; i--) {
|
|
58
|
+
const b = activeBindings[i];
|
|
59
|
+
if (b.container.isConnected)
|
|
60
|
+
continue;
|
|
61
|
+
detach(b);
|
|
62
|
+
activeBindings.splice(i, 1);
|
|
63
|
+
}
|
|
64
|
+
// Mirror teardownSpy's click-handler gate so a container removed
|
|
65
|
+
// outside the framework lifecycle (direct DOM removal that never
|
|
66
|
+
// calls teardownSpy) still ends with the document-level click
|
|
67
|
+
// listener unhooked once the last binding is gone — instead of
|
|
68
|
+
// leaving an orphaned no-op handler bound to the document.
|
|
69
|
+
if (activeBindings.length === 0) {
|
|
70
|
+
const handler = document[LINK_HANDLER_KEY];
|
|
71
|
+
if (handler) {
|
|
72
|
+
document.removeEventListener("click", handler);
|
|
73
|
+
delete document[LINK_HANDLER_KEY];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// detach fully removes a binding's effects from the page: event
|
|
78
|
+
// listeners, the per-container guard, AND any lvt-active classes the
|
|
79
|
+
// binding had applied to its links. Folding the class-clear into detach
|
|
80
|
+
// means every caller (processContainer's re-attach, prune of
|
|
81
|
+
// disconnected containers, teardownSpy) gets the cleanup for free —
|
|
82
|
+
// without it, a stale lvt-active leaked anywhere detach was called but
|
|
83
|
+
// teardownSpy's surviving-id sweep wasn't (e.g. on every morphdom
|
|
84
|
+
// update that removed a target).
|
|
85
|
+
function detach(b) {
|
|
86
|
+
applyActive(b, null);
|
|
87
|
+
b.scrollTarget.removeEventListener("scroll", b.scrollHandler);
|
|
88
|
+
window.removeEventListener("resize", b.resizeHandler);
|
|
89
|
+
delete b.container[BINDING_KEY];
|
|
90
|
+
}
|
|
91
|
+
function readMarginPx(container) {
|
|
92
|
+
const raw = getComputedStyle(container).getPropertyValue("--lvt-spy-margin").trim();
|
|
93
|
+
const fallback = Math.round(window.innerHeight * 0.25);
|
|
94
|
+
if (!raw)
|
|
95
|
+
return fallback;
|
|
96
|
+
const n = parseFloat(raw);
|
|
97
|
+
if (isNaN(n))
|
|
98
|
+
return fallback;
|
|
99
|
+
// Supported units: bare px (`200` or `200px`) and vh (`25vh`). Other
|
|
100
|
+
// CSS units (rem, em, %, etc.) come through `getComputedStyle` as raw
|
|
101
|
+
// strings, NOT resolved — `parseFloat("2rem")` returns 2 and we'd
|
|
102
|
+
// silently treat it as 2 px, which is wildly off. Reject explicitly
|
|
103
|
+
// and warn so the author fixes the declaration.
|
|
104
|
+
if (raw.endsWith("vh"))
|
|
105
|
+
return Math.round((n / 100) * window.innerHeight);
|
|
106
|
+
// Accept "200" or "200.0" as unitless px (parseFloat strips zeros so
|
|
107
|
+
// `raw === String(n)` would miss the latter). The regex pins the
|
|
108
|
+
// entire string to a signed number, matching CSS's <number> grammar.
|
|
109
|
+
if (raw.endsWith("px") || /^-?\d+(\.\d+)?$/.test(raw))
|
|
110
|
+
return n;
|
|
111
|
+
console.warn(`lvt-spy: unsupported --lvt-spy-margin unit ${JSON.stringify(raw)}; supported units are vh and px (or unitless). Falling back to 25vh.`);
|
|
112
|
+
return fallback;
|
|
113
|
+
}
|
|
114
|
+
function collectTargets(container) {
|
|
115
|
+
const selector = container.getAttribute("lvt-spy");
|
|
116
|
+
if (selector && selector.trim() !== "") {
|
|
117
|
+
// Guard against typos in author-supplied selectors. An invalid
|
|
118
|
+
// selector (e.g. `lvt-spy="h1, h2,"` — trailing comma) makes
|
|
119
|
+
// querySelectorAll throw SyntaxError, which would propagate out of
|
|
120
|
+
// setupSpy and abort directive initialization for the whole scan
|
|
121
|
+
// root. Warn and treat as empty so the rest of the page still works.
|
|
122
|
+
try {
|
|
123
|
+
return Array.from(container.querySelectorAll(selector));
|
|
124
|
+
}
|
|
125
|
+
catch (e) {
|
|
126
|
+
console.warn(`lvt-spy: invalid selector ${JSON.stringify(selector)}:`, e);
|
|
127
|
+
return [];
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return [container];
|
|
131
|
+
}
|
|
132
|
+
// applyActive updates lvt-active state for the links that belong to a
|
|
133
|
+
// single binding. Ownership is decided by href matching one of the
|
|
134
|
+
// binding's target ids — links pointing elsewhere (e.g. to a
|
|
135
|
+
// neighbouring spy container's targets) are left untouched. This is
|
|
136
|
+
// what lets multiple LiveTemplateClient mounts coexist on one page
|
|
137
|
+
// without their scroll-spy state stomping each other.
|
|
138
|
+
//
|
|
139
|
+
// Hot path: called from the rAF-throttled scroll handler. Iterates the
|
|
140
|
+
// pre-cached `binding.links` rather than re-querying the DOM each
|
|
141
|
+
// frame; the cache is refreshed by refreshLinks() at attach time and
|
|
142
|
+
// on every processContainer pass.
|
|
143
|
+
function applyActive(binding, activeId) {
|
|
144
|
+
for (const link of binding.links) {
|
|
145
|
+
const href = link.getAttribute("href") || "";
|
|
146
|
+
const id = href.startsWith("#") ? href.slice(1) : "";
|
|
147
|
+
if (activeId !== null && id === activeId) {
|
|
148
|
+
link.classList.add(ACTIVE_CLASS);
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
link.classList.remove(ACTIVE_CLASS);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// refreshLinks rebuilds binding.links to the current set of
|
|
156
|
+
// [lvt-spy-link] elements in the document whose href points to one of
|
|
157
|
+
// the binding's target ids. Called once per render cycle (cheap),
|
|
158
|
+
// never per scroll tick (would defeat the cache).
|
|
159
|
+
function refreshLinks(binding) {
|
|
160
|
+
const ownIds = new Set();
|
|
161
|
+
for (const t of binding.targets) {
|
|
162
|
+
if (t.id)
|
|
163
|
+
ownIds.add(t.id);
|
|
164
|
+
}
|
|
165
|
+
const matched = [];
|
|
166
|
+
document.querySelectorAll("[lvt-spy-link]").forEach((link) => {
|
|
167
|
+
const href = link.getAttribute("href") || "";
|
|
168
|
+
const id = href.startsWith("#") ? href.slice(1) : "";
|
|
169
|
+
if (ownIds.has(id))
|
|
170
|
+
matched.push(link);
|
|
171
|
+
});
|
|
172
|
+
binding.links = matched;
|
|
173
|
+
}
|
|
174
|
+
// findBindingForId locates the binding that owns the given id, i.e.
|
|
175
|
+
// has a target element whose id matches. Used by the optimistic click
|
|
176
|
+
// handler so a click on a link only updates its own binding's scope —
|
|
177
|
+
// other bindings' active links stay put.
|
|
178
|
+
function findBindingForId(id) {
|
|
179
|
+
for (const b of activeBindings) {
|
|
180
|
+
for (const t of b.targets) {
|
|
181
|
+
if (t.id === id)
|
|
182
|
+
return b;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
function pickActive(targets, marginPx) {
|
|
188
|
+
let activeId = null;
|
|
189
|
+
// Walk every target. We deliberately do NOT bail early on the first
|
|
190
|
+
// one below the trigger line — that optimisation assumes visual
|
|
191
|
+
// top-to-bottom order (which holds for typical document flow) but
|
|
192
|
+
// silently produces the wrong active link when targets are reordered
|
|
193
|
+
// via CSS (sticky headers with negative top, transform: translateY,
|
|
194
|
+
// flex/grid order). The full walk is O(n) with n = number of TOC
|
|
195
|
+
// entries — small in practice, dwarfed by the rAF tick itself.
|
|
196
|
+
for (const t of targets) {
|
|
197
|
+
if (!t.id)
|
|
198
|
+
continue;
|
|
199
|
+
const top = t.getBoundingClientRect().top;
|
|
200
|
+
if (top <= marginPx) {
|
|
201
|
+
activeId = t.id;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return activeId;
|
|
205
|
+
}
|
|
206
|
+
function findScrollTarget(el) {
|
|
207
|
+
// Walk up to the nearest ancestor whose computed overflow-y is auto or
|
|
208
|
+
// scroll. If none, the document scrolls — return window. Covers both
|
|
209
|
+
// the common whole-document case and apps where a flex child scrolls
|
|
210
|
+
// independently (prereview's main.viewer).
|
|
211
|
+
let cur = el.parentElement;
|
|
212
|
+
while (cur && cur !== document.documentElement) {
|
|
213
|
+
const oy = getComputedStyle(cur).overflowY;
|
|
214
|
+
// 'overlay' is a non-standard Chromium value that behaves like
|
|
215
|
+
// 'auto' but draws the scrollbar over the content. Apps that opt
|
|
216
|
+
// into it (a common iOS-feel scrollbar pattern) would otherwise
|
|
217
|
+
// be walked past and end up scrolling the document — wrong root,
|
|
218
|
+
// wrong trigger line.
|
|
219
|
+
if (oy === "auto" || oy === "scroll" || oy === "overlay")
|
|
220
|
+
return cur;
|
|
221
|
+
cur = cur.parentElement;
|
|
222
|
+
}
|
|
223
|
+
return window;
|
|
224
|
+
}
|
|
225
|
+
function sameTargets(a, b) {
|
|
226
|
+
if (a.length !== b.length)
|
|
227
|
+
return false;
|
|
228
|
+
for (let i = 0; i < a.length; i++) {
|
|
229
|
+
if (a[i] !== b[i])
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
function attach(container, preCollected) {
|
|
235
|
+
// Accept an optional pre-collected target list so processContainer's
|
|
236
|
+
// re-attach path doesn't pay for collectTargets twice (once for the
|
|
237
|
+
// diff check, once inside this function). Low-volume code path
|
|
238
|
+
// (per-render, not per-tick) but easy to dedupe.
|
|
239
|
+
const targets = preCollected ?? collectTargets(container);
|
|
240
|
+
if (targets.length === 0)
|
|
241
|
+
return;
|
|
242
|
+
// Surface authoring mistakes once-per-element at attach time rather
|
|
243
|
+
// than letting them silently produce a TOC entry that never lights
|
|
244
|
+
// up. A target matched by the spy selector but missing `id` cannot
|
|
245
|
+
// be the destination of `<a href="#...">`, so no link can ever
|
|
246
|
+
// activate for it. The WeakSet dedup is what keeps re-attach (which
|
|
247
|
+
// fires on every morphdom render that adds/removes a heading) from
|
|
248
|
+
// spamming the console — once we've warned about a given element,
|
|
249
|
+
// we don't warn about it again, but a NEW id-less target on a later
|
|
250
|
+
// render still gets surfaced.
|
|
251
|
+
const missingId = targets.filter((t) => !t.id && !warnedMissingId.has(t));
|
|
252
|
+
if (missingId.length > 0) {
|
|
253
|
+
console.warn(`lvt-spy: ${missingId.length} target(s) without an id attribute; they cannot be linked from [lvt-spy-link]. Add id="..." or drop them from the selector. First offender:`, missingId[0]);
|
|
254
|
+
for (const t of missingId)
|
|
255
|
+
warnedMissingId.add(t);
|
|
256
|
+
}
|
|
257
|
+
const binding = {
|
|
258
|
+
container,
|
|
259
|
+
targets,
|
|
260
|
+
marginPx: readMarginPx(container),
|
|
261
|
+
links: [],
|
|
262
|
+
scrollTarget: findScrollTarget(container),
|
|
263
|
+
// scrollHandler/resizeHandler are populated below — declared here so
|
|
264
|
+
// the closures can reference `binding` for the cached margin lookup.
|
|
265
|
+
scrollHandler: () => { },
|
|
266
|
+
resizeHandler: () => { },
|
|
267
|
+
};
|
|
268
|
+
refreshLinks(binding);
|
|
269
|
+
let ticking = false;
|
|
270
|
+
binding.scrollHandler = () => {
|
|
271
|
+
if (ticking)
|
|
272
|
+
return;
|
|
273
|
+
ticking = true;
|
|
274
|
+
requestAnimationFrame(() => {
|
|
275
|
+
ticking = false;
|
|
276
|
+
applyActive(binding, pickActive(binding.targets, binding.marginPx));
|
|
277
|
+
});
|
|
278
|
+
};
|
|
279
|
+
binding.resizeHandler = () => {
|
|
280
|
+
// The trigger line is vh-relative (or px), so on viewport resize
|
|
281
|
+
// we need to recompute. Refresh active immediately because resize
|
|
282
|
+
// doesn't fire `scroll`, so the rAF path won't otherwise reconcile.
|
|
283
|
+
binding.marginPx = readMarginPx(binding.container);
|
|
284
|
+
applyActive(binding, pickActive(binding.targets, binding.marginPx));
|
|
285
|
+
};
|
|
286
|
+
binding.scrollTarget.addEventListener("scroll", binding.scrollHandler, { passive: true });
|
|
287
|
+
window.addEventListener("resize", binding.resizeHandler, { passive: true });
|
|
288
|
+
container[BINDING_KEY] = binding;
|
|
289
|
+
activeBindings.push(binding);
|
|
290
|
+
// Initial synchronous pick so the right link is highlighted on first
|
|
291
|
+
// paint, before any scroll event fires.
|
|
292
|
+
applyActive(binding, pickActive(binding.targets, binding.marginPx));
|
|
293
|
+
}
|
|
294
|
+
function processContainer(container) {
|
|
295
|
+
const existing = container[BINDING_KEY];
|
|
296
|
+
if (existing) {
|
|
297
|
+
const fresh = collectTargets(container);
|
|
298
|
+
if (sameTargets(existing.targets, fresh)) {
|
|
299
|
+
// No structural change — just re-pick in case scroll position
|
|
300
|
+
// shifted via a non-scroll mechanism (history restore, etc.).
|
|
301
|
+
// Also refresh the cached margin in case CSS variables changed
|
|
302
|
+
// between renders.
|
|
303
|
+
existing.marginPx = readMarginPx(container);
|
|
304
|
+
// Targets stayed put but the link soup may have shifted (morphdom
|
|
305
|
+
// could have added a TOC entry without touching headings). Refresh
|
|
306
|
+
// the cached link set every render so the hot-path applyActive
|
|
307
|
+
// never goes stale.
|
|
308
|
+
refreshLinks(existing);
|
|
309
|
+
applyActive(existing, pickActive(existing.targets, existing.marginPx));
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
detach(existing);
|
|
313
|
+
const idx = activeBindings.indexOf(existing);
|
|
314
|
+
if (idx !== -1)
|
|
315
|
+
activeBindings.splice(idx, 1);
|
|
316
|
+
// We already collected `fresh` above for the diff check — hand it
|
|
317
|
+
// to attach() so it doesn't re-walk the DOM.
|
|
318
|
+
attach(container, fresh);
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
attach(container);
|
|
322
|
+
}
|
|
323
|
+
function installLinkClickHandler() {
|
|
324
|
+
if (document[LINK_HANDLER_KEY])
|
|
325
|
+
return;
|
|
326
|
+
// Optimistic activation: clicking a link instantly applies lvt-active
|
|
327
|
+
// to it. The next scroll-driven pick reconciles. Without this, clicking
|
|
328
|
+
// the *last* heading (which may never become topmost-visible if the
|
|
329
|
+
// doc ends shortly after it) would leave some earlier link highlighted
|
|
330
|
+
// even though the user just asked to be at the last heading.
|
|
331
|
+
const handler = (e) => {
|
|
332
|
+
const link = e.target?.closest("[lvt-spy-link]");
|
|
333
|
+
if (!link)
|
|
334
|
+
return;
|
|
335
|
+
const href = link.getAttribute("href") || "";
|
|
336
|
+
const id = href.startsWith("#") ? href.slice(1) : "";
|
|
337
|
+
if (!id)
|
|
338
|
+
return;
|
|
339
|
+
// Route the optimistic flip to the binding that actually owns this
|
|
340
|
+
// id. Otherwise clicking a link in TOC A would clear TOC B's
|
|
341
|
+
// active highlight in a multi-instance layout.
|
|
342
|
+
const owner = findBindingForId(id);
|
|
343
|
+
if (owner)
|
|
344
|
+
applyActive(owner, id);
|
|
345
|
+
};
|
|
346
|
+
// passive: true — we never preventDefault on the click (the native
|
|
347
|
+
// <a href="#…"> scroll is what we want), so promising passive lets
|
|
348
|
+
// the browser dispatch without parking the main thread waiting for
|
|
349
|
+
// a possible cancel.
|
|
350
|
+
document.addEventListener("click", handler, { passive: true });
|
|
351
|
+
document[LINK_HANDLER_KEY] = handler;
|
|
352
|
+
}
|
|
353
|
+
function setupSpy(scanRoot) {
|
|
354
|
+
pruneDisconnectedBindings();
|
|
355
|
+
installLinkClickHandler();
|
|
356
|
+
if (scanRoot.hasAttribute("lvt-spy")) {
|
|
357
|
+
processContainer(scanRoot);
|
|
358
|
+
}
|
|
359
|
+
scanRoot.querySelectorAll("[lvt-spy]").forEach(processContainer);
|
|
360
|
+
}
|
|
361
|
+
function teardownSpy(wrapper) {
|
|
362
|
+
// detach() now clears each binding's lvt-active classes as part of
|
|
363
|
+
// its contract, so there's no separate global sweep here — surviving
|
|
364
|
+
// bindings keep their highlights, and removed bindings drop theirs.
|
|
365
|
+
for (let i = activeBindings.length - 1; i >= 0; i--) {
|
|
366
|
+
const b = activeBindings[i];
|
|
367
|
+
if (wrapper && b.container.isConnected && !wrapper.contains(b.container)) {
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
detach(b);
|
|
371
|
+
activeBindings.splice(i, 1);
|
|
372
|
+
}
|
|
373
|
+
// Detach the document-level optimistic-click handler when there are
|
|
374
|
+
// no more live spy bindings. Gating on `activeBindings.length === 0`
|
|
375
|
+
// (instead of `!wrapper`) covers the common case where the framework
|
|
376
|
+
// always passes a wrapper to teardown: without this gate the click
|
|
377
|
+
// handler outlives every binding, and any subsequent [lvt-spy-link]
|
|
378
|
+
// click silently re-applies lvt-active with no scroll reconciliation
|
|
379
|
+
// to undo it.
|
|
380
|
+
if (activeBindings.length === 0) {
|
|
381
|
+
const handler = document[LINK_HANDLER_KEY];
|
|
382
|
+
if (handler) {
|
|
383
|
+
document.removeEventListener("click", handler);
|
|
384
|
+
delete document[LINK_HANDLER_KEY];
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
//# sourceMappingURL=spy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spy.js","sourceRoot":"","sources":["../../dom/spy.ts"],"names":[],"mappings":"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"}
|