@devzwo/ngx-magic-scroll 0.0.1-rc.2 → 1.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.
|
@@ -26,6 +26,18 @@ interface ScrollOptions {
|
|
|
26
26
|
* Defaults to 20 pixels.
|
|
27
27
|
*/
|
|
28
28
|
topOffset?: number;
|
|
29
|
+
/**
|
|
30
|
+
* Target anchor-relative pixel displacement relative to the top offset boundary.
|
|
31
|
+
* Negative values position the element higher (scrolled into), positive values lower.
|
|
32
|
+
* Defaults to 0 (element top aligned to topOffset).
|
|
33
|
+
*/
|
|
34
|
+
displacement?: number;
|
|
35
|
+
/**
|
|
36
|
+
* Whether to transfer DOM focus to the resolved anchor element.
|
|
37
|
+
* Uses `preventScroll: true` and temporary `tabindex="-1"` for non-focusable targets.
|
|
38
|
+
* Defaults to `false`.
|
|
39
|
+
*/
|
|
40
|
+
focus?: boolean;
|
|
29
41
|
}
|
|
30
42
|
/**
|
|
31
43
|
* Central service for scrolling to DOM elements and anchors.
|
|
@@ -60,10 +72,28 @@ declare class ScrollService {
|
|
|
60
72
|
static ɵfac: i0.ɵɵFactoryDeclaration<ScrollService, never>;
|
|
61
73
|
static ɵprov: i0.ɵɵInjectableDeclaration<any>;
|
|
62
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Anchor selection strategy for a `magicScroll` region.
|
|
77
|
+
* - `'all'`: Default strategy. Descendant elements matching any configured `anchorPrefixes`
|
|
78
|
+
* (or all non-empty IDs if no prefix is configured) as well as explicit `scrollAnchor` markers participate.
|
|
79
|
+
* - `'explicit'`: Strict explicit-only mode. Only elements marked with `[scrollAnchor]` or `scrollAnchor`
|
|
80
|
+
* participate, regardless of their element ID or configured prefixes.
|
|
81
|
+
*/
|
|
82
|
+
type AnchorSelectionMode = 'all' | 'explicit';
|
|
63
83
|
/** Defaults for an application or overrides for one `magicScroll` region. */
|
|
64
84
|
interface MagicScrollOptions {
|
|
65
|
-
/**
|
|
66
|
-
|
|
85
|
+
/**
|
|
86
|
+
* Allowed anchor ID prefixes. Descendant elements matching any prefix participate.
|
|
87
|
+
* Explicit `scrollAnchor` markers also participate.
|
|
88
|
+
* Passing an empty array `[]` disables prefix matching so only explicit markers participate.
|
|
89
|
+
*/
|
|
90
|
+
anchorPrefixes?: readonly string[] | string[];
|
|
91
|
+
/**
|
|
92
|
+
* Selection strategy for anchor discovery.
|
|
93
|
+
* - `'all'`: Selects elements matching prefixes or explicit markers (default).
|
|
94
|
+
* - `'explicit'`: Selects only elements with explicit `scrollAnchor` markers.
|
|
95
|
+
*/
|
|
96
|
+
anchorSelection?: AnchorSelectionMode;
|
|
67
97
|
/** Selector for a fixed/sticky header whose height is measured automatically. */
|
|
68
98
|
headerSelector?: string;
|
|
69
99
|
/** Gap below the header, or the complete top offset when no header is selected. */
|
|
@@ -83,9 +113,16 @@ interface MagicScrollOptions {
|
|
|
83
113
|
restoreOnDataChange?: boolean;
|
|
84
114
|
/** Preserve the current anchor on window resize and measured header-height changes. */
|
|
85
115
|
preserveOnResize?: boolean;
|
|
116
|
+
/**
|
|
117
|
+
* Preserve anchor-relative reading position when region content reflows
|
|
118
|
+
* (e.g. image loads, form expansions, accordion toggles) without changing scrollSource.
|
|
119
|
+
* Defaults to false (explicit opt-in).
|
|
120
|
+
*/
|
|
121
|
+
preserveOnLayoutChange?: boolean;
|
|
86
122
|
}
|
|
87
123
|
interface ResolvedMagicScrollOptions {
|
|
88
|
-
|
|
124
|
+
anchorPrefixes: readonly string[];
|
|
125
|
+
anchorSelection: AnchorSelectionMode;
|
|
89
126
|
headerSelector: string;
|
|
90
127
|
headerOffset: number;
|
|
91
128
|
bottomOffset: number;
|
|
@@ -98,6 +135,7 @@ interface ResolvedMagicScrollOptions {
|
|
|
98
135
|
ignoreWhenInView: NonNullable<ScrollOptions['ignoreWhenInView']>;
|
|
99
136
|
restoreOnDataChange: boolean;
|
|
100
137
|
preserveOnResize: boolean;
|
|
138
|
+
preserveOnLayoutChange: boolean;
|
|
101
139
|
}
|
|
102
140
|
declare function resolveMagicScrollOptions(parent: ResolvedMagicScrollOptions, options: MagicScrollOptions): ResolvedMagicScrollOptions;
|
|
103
141
|
declare const MAGIC_SCROLL_OPTIONS: InjectionToken<ResolvedMagicScrollOptions>;
|
|
@@ -106,7 +144,7 @@ declare const MAGIC_SCROLL_OPTIONS: InjectionToken<ResolvedMagicScrollOptions>;
|
|
|
106
144
|
* Per-region `scrollOptions` and per-call `scrollTo` options take precedence.
|
|
107
145
|
*
|
|
108
146
|
* @example
|
|
109
|
-
* provideMagicScroll({
|
|
147
|
+
* provideMagicScroll({ anchorPrefixes: ['project-'], behavior: { interaction: 'smooth' } })
|
|
110
148
|
*/
|
|
111
149
|
export declare function provideMagicScroll(options?: MagicScrollOptions): Provider;
|
|
112
150
|
/** Reactive getters must read signals so loading and data changes are tracked.
|
|
@@ -140,7 +178,7 @@ declare function readScrollSource(source: ScrollSource | typeof NO_SCROLL_SOURCE
|
|
|
140
178
|
* Per-action scroll options for programmatic navigation via {@link MagicScrollDirective.scrollTo}.
|
|
141
179
|
* Top offsets and container scoping are resolved automatically by the directive facade.
|
|
142
180
|
*/
|
|
143
|
-
type MagicScrollToOptions = Pick<ScrollOptions, 'behavior' | 'topOffset' | 'ignoreWhenInView'>;
|
|
181
|
+
type MagicScrollToOptions = Pick<ScrollOptions, 'behavior' | 'topOffset' | 'ignoreWhenInView' | 'focus'>;
|
|
144
182
|
/**
|
|
145
183
|
* Complete facade directive coordinating anchor scrolling, two-tier route synchronization,
|
|
146
184
|
* user-intent cancellation, data readiness restoration, and layout/data position preservation.
|
|
@@ -161,7 +199,7 @@ type MagicScrollToOptions = Pick<ScrollOptions, 'behavior' | 'topOffset' | 'igno
|
|
|
161
199
|
* - Sequenced `afterRenderEffect` execution blocks guarantee sticky header measurement and `ResizeObserver`
|
|
162
200
|
* monitoring are active before initial fragment restoration and data preservation execute.
|
|
163
201
|
* 4. **Preservation & Realignment Engine**:
|
|
164
|
-
* - Automatic realignments (`preserve(
|
|
202
|
+
* - Automatic realignments (`preserve()`) on data updates and container/header resizes based on the live active section.
|
|
165
203
|
* 5. **Container & Anchor Scoping**:
|
|
166
204
|
* - Automatic scroll container resolution (window vs scrollable element) and hierarchical anchor ownership isolation.
|
|
167
205
|
*
|
|
@@ -190,6 +228,7 @@ export declare class MagicScrollDirective {
|
|
|
190
228
|
private readonly host;
|
|
191
229
|
private readonly service;
|
|
192
230
|
private readonly nearest;
|
|
231
|
+
private readonly router;
|
|
193
232
|
/** Bidirectional route fragment synchronization signal (debounced URL writer and listener). */
|
|
194
233
|
private readonly fragment;
|
|
195
234
|
/** Initial route URL fragment snapshot captured upon directive instantiation. */
|
|
@@ -214,31 +253,118 @@ export declare class MagicScrollDirective {
|
|
|
214
253
|
private initialized;
|
|
215
254
|
/**
|
|
216
255
|
* Flag indicating whether manual user interaction (wheel, touch, or navigation keys) occurred
|
|
217
|
-
* before asynchronous data loading became ready. When true, delayed
|
|
256
|
+
* before asynchronous data loading became ready. When true, delayed restoration is skipped.
|
|
218
257
|
*/
|
|
219
258
|
private userInterrupted;
|
|
259
|
+
/**
|
|
260
|
+
* Region-local anchor-relative pixel displacement tracking the distance between
|
|
261
|
+
* the active section's top boundary and the visible activation reading boundary.
|
|
262
|
+
* Preserved during automatic reflows (data prepending, container/header resizing)
|
|
263
|
+
* to keep the reader's view stable without jumping back to section headings.
|
|
264
|
+
*/
|
|
265
|
+
private savedDisplacement;
|
|
266
|
+
/** Pending destination anchor awaiting data readiness or DOM rendering. */
|
|
267
|
+
private pendingDestination;
|
|
268
|
+
/** Tracks the last fragment processed from route fragment synchronization. */
|
|
269
|
+
private lastProcessedFragment;
|
|
220
270
|
private target?;
|
|
221
271
|
private timer?;
|
|
272
|
+
/** Active DOM mutation observer used to retry pending scroll destinations once target anchor elements are inserted. */
|
|
273
|
+
private retryObserver?;
|
|
274
|
+
/** Timeout timer bounding the lifetime of destination retries to prevent memory leaks from non-existent anchors. */
|
|
275
|
+
private retryTimeoutTimer?;
|
|
276
|
+
/** Handle for the active coalesced requestAnimationFrame layout correction. */
|
|
277
|
+
private layoutCorrectionFrameId?;
|
|
278
|
+
/** Cleanups for elements given temporary tabindex="-1" to restore DOM state on blur or directive destruction. */
|
|
279
|
+
private readonly temporaryTabindexCleanups;
|
|
280
|
+
/** Target element pending focus transfer after smooth scroll completion. */
|
|
281
|
+
private pendingFocusTarget?;
|
|
282
|
+
/** Active scrollend event handler waiting for smooth scroll completion. */
|
|
283
|
+
private scrollEndHandler?;
|
|
284
|
+
/** Active EventTarget on which scrollEndHandler was registered. */
|
|
285
|
+
private scrollEndTarget?;
|
|
286
|
+
/** Safety timeout preventing a missed scrollend event from retaining a pending focus request. */
|
|
287
|
+
private focusTimeoutTimer?;
|
|
288
|
+
/** Element focused when the pending focus transfer was initiated. */
|
|
289
|
+
private focusInitiator?;
|
|
290
|
+
/** Flag indicating whether programmatic smooth scrolling animation is actively running. */
|
|
291
|
+
private isSmoothNavigating;
|
|
292
|
+
/** Flag indicating a deferred layout preservation request while smooth navigation was active. */
|
|
293
|
+
private pendingLayoutPreservation;
|
|
294
|
+
/** Active scrollend event handler for programmatic smooth navigation settlement. */
|
|
295
|
+
private smoothNavEndHandler?;
|
|
296
|
+
/** Event target on which smoothNavEndHandler was registered. */
|
|
297
|
+
private smoothNavTarget?;
|
|
298
|
+
/** Safety timeout timer preventing smooth navigation lock if scrollend does not fire. */
|
|
299
|
+
private smoothNavTimeoutTimer?;
|
|
222
300
|
constructor();
|
|
301
|
+
/**
|
|
302
|
+
* Determines whether the active router navigation was initiated internally by scroll-spy.
|
|
303
|
+
*/
|
|
304
|
+
private isInternalNavigation;
|
|
223
305
|
/**
|
|
224
306
|
* Explicitly scrolls the viewport to the target anchor identifier.
|
|
225
307
|
*
|
|
226
308
|
* @param anchor - Target anchor ID (with or without leading `#`).
|
|
227
|
-
* @param options - Per-call scroll overrides (behavior, top offset, ignoreWhenInView).
|
|
309
|
+
* @param options - Per-call scroll overrides (behavior, top offset, ignoreWhenInView, focus).
|
|
228
310
|
* @returns `true` if the target anchor was found and scrolling initiated; `false` otherwise.
|
|
229
311
|
*
|
|
230
312
|
* @remarks
|
|
231
313
|
* Successful execution immediately synchronizes both `liveAnchor` and the router `fragment`
|
|
232
314
|
* with the target anchor, overriding any in-flight debounce delays.
|
|
315
|
+
*
|
|
316
|
+
* When `options.focus` is true, DOM focus is transferred to the resolved anchor element using
|
|
317
|
+
* `preventScroll: true` without triggering secondary browser scrolling. For non-focusable containers,
|
|
318
|
+
* a temporary `tabindex="-1"` is applied and removed upon blur or directive destruction.
|
|
233
319
|
*/
|
|
234
320
|
scrollTo(anchor: string, options?: MagicScrollToOptions): boolean;
|
|
321
|
+
/**
|
|
322
|
+
* Attempts to execute navigation to the pending destination anchor.
|
|
323
|
+
*
|
|
324
|
+
* @param behavior - The scroll animation behavior to apply (`instant` or `smooth`).
|
|
325
|
+
* @returns `true` if the target anchor element was found in the DOM and scrolling was initiated; `false` otherwise.
|
|
326
|
+
*
|
|
327
|
+
* @remarks
|
|
328
|
+
* If execution succeeds, `pendingDestination` is cleared and active retry observers/timers are canceled.
|
|
329
|
+
* If target elements are not yet present in the DOM (e.g. data is ready before DOM elements render),
|
|
330
|
+
* `pendingDestination` remains queued so subsequent DOM updates can retry.
|
|
331
|
+
*/
|
|
332
|
+
private tryExecutePendingDestination;
|
|
333
|
+
/**
|
|
334
|
+
* Schedules a bounded retry mechanism observing DOM additions and attribute modifications
|
|
335
|
+
* within the directive host to re-attempt scrolling when the target anchor element is rendered.
|
|
336
|
+
*
|
|
337
|
+
* @remarks
|
|
338
|
+
* Asynchronous rendering cycles (such as Angular `@for` loops, `@if` blocks, or asynchronous child components)
|
|
339
|
+
* often resolve data/signals (`scrollSource`) before child DOM anchor nodes are attached to the document.
|
|
340
|
+
*
|
|
341
|
+
* To prevent dropped navigations without polling or manual timeouts, a `MutationObserver` watches host mutations
|
|
342
|
+
* for child node insertions and anchor attribute modifications (`id`, `data-scroll-anchor`). When DOM mutations
|
|
343
|
+
* occur and the scroll source is ready, destination execution is re-attempted.
|
|
344
|
+
*
|
|
345
|
+
* A bounded 1000ms safety timeout safeguards against indefinite observation if a non-existent anchor ID was requested.
|
|
346
|
+
*/
|
|
347
|
+
private scheduleDestinationRetry;
|
|
348
|
+
/**
|
|
349
|
+
* Disconnects the DOM mutation observer and clears active retry timers.
|
|
350
|
+
*
|
|
351
|
+
* @remarks
|
|
352
|
+
* Invoked whenever destination execution succeeds, a user interrupts navigation (via scroll, touch, or keyboard),
|
|
353
|
+
* an explicit navigation supersedes the pending target, or the directive is destroyed.
|
|
354
|
+
*/
|
|
355
|
+
private cancelDestinationRetry;
|
|
235
356
|
/**
|
|
236
357
|
* Queries and returns all eligible scroll anchor elements belonging strictly to this directive instance.
|
|
237
358
|
*
|
|
238
359
|
* @remarks
|
|
239
|
-
* Anchors must possess a non-empty `id` and
|
|
240
|
-
* the `data-scroll-anchor` attribute
|
|
241
|
-
*
|
|
360
|
+
* Anchors must possess a non-empty `id` and satisfy the configured selection strategy:
|
|
361
|
+
* - In `'explicit'` mode: Only elements marked with the `data-scroll-anchor` attribute participate,
|
|
362
|
+
* ignoring prefix rules and excluding generated component/form IDs.
|
|
363
|
+
* - In `'all'` mode (default): Elements matching any configured `anchorPrefixes` OR bearing
|
|
364
|
+
* the `data-scroll-anchor` attribute participate.
|
|
365
|
+
*
|
|
366
|
+
* The `closest('[magicScroll]') === this.host` check guarantees that nested child regions
|
|
367
|
+
* retain strict isolation over their own anchors and do not leak across region boundaries.
|
|
242
368
|
*/
|
|
243
369
|
private anchors;
|
|
244
370
|
/**
|
|
@@ -257,15 +383,39 @@ export declare class MagicScrollDirective {
|
|
|
257
383
|
*/
|
|
258
384
|
private execute;
|
|
259
385
|
/**
|
|
260
|
-
*
|
|
261
|
-
|
|
386
|
+
* Determines whether the requested navigation will change the scroll position after clamping.
|
|
387
|
+
*/
|
|
388
|
+
private willScroll;
|
|
389
|
+
/**
|
|
390
|
+
* Resolves whether the browser will animate this request, including `auto` with smooth CSS behavior.
|
|
391
|
+
*/
|
|
392
|
+
private hasSmoothScrollBehavior;
|
|
393
|
+
/**
|
|
394
|
+
* Realigns the viewport instantly to preserve anchor-relative reading position
|
|
395
|
+
* during asynchronous data updates, container resizes, or header dimension changes.
|
|
262
396
|
*
|
|
263
397
|
* @remarks
|
|
264
|
-
* Targets `liveAnchor` (the real-time visible section)
|
|
265
|
-
*
|
|
266
|
-
*
|
|
398
|
+
* Targets `liveAnchor` (the real-time visible section) and restores `savedDisplacement`
|
|
399
|
+
* (the pixel offset between the anchor's top and the container's top boundary).
|
|
400
|
+
* If native browser scroll anchoring (`overflow-anchor`) has already adjusted scroll position,
|
|
401
|
+
* delta computes to 0, inherently avoiding double-compensation.
|
|
402
|
+
* If the active anchor element is no longer present in the DOM, realignment is skipped deterministically.
|
|
267
403
|
*/
|
|
268
404
|
private preserve;
|
|
405
|
+
/**
|
|
406
|
+
* Schedules a coalesced layout realignment via requestAnimationFrame.
|
|
407
|
+
* Batches multiple synchronous or rapid descendant reflows into a single correction per frame.
|
|
408
|
+
*/
|
|
409
|
+
private scheduleLayoutPreservation;
|
|
410
|
+
/**
|
|
411
|
+
* Cancels any pending requestAnimationFrame layout realignment.
|
|
412
|
+
*/
|
|
413
|
+
private cancelLayoutPreservation;
|
|
414
|
+
/**
|
|
415
|
+
* Captures and stores the pixel displacement between the active anchor's top edge
|
|
416
|
+
* and the visible activation reading boundary (`containerTop + topOffset`).
|
|
417
|
+
*/
|
|
418
|
+
private updateSavedDisplacement;
|
|
269
419
|
/**
|
|
270
420
|
* Updates target container references and re-binds passive event listeners whenever
|
|
271
421
|
* the scroll container shifts dynamically.
|
|
@@ -275,21 +425,39 @@ export declare class MagicScrollDirective {
|
|
|
275
425
|
* Attaches passive scroll, gesture, and keyboard listeners to track user intent and scroll motion.
|
|
276
426
|
*/
|
|
277
427
|
private attachListeners;
|
|
428
|
+
/**
|
|
429
|
+
* Applies DOM focus safely to the target anchor element using `preventScroll: true`.
|
|
430
|
+
* Automatically assigns a temporary `tabindex="-1"` to non-focusable landmark containers
|
|
431
|
+
* and registers a one-time blur listener to cleanly remove it when focus shifts away.
|
|
432
|
+
*/
|
|
433
|
+
private applyFocus;
|
|
434
|
+
/**
|
|
435
|
+
* Schedules focus transfer to execute upon smooth scroll completion (`scrollend`).
|
|
436
|
+
*/
|
|
437
|
+
private scheduleFocus;
|
|
438
|
+
/**
|
|
439
|
+
* Cancels any pending scheduled focus transfer and unregisters the `scrollend` listener.
|
|
440
|
+
*/
|
|
441
|
+
private cancelScheduledFocus;
|
|
278
442
|
/**
|
|
279
443
|
* Detaches all registered event listeners to ensure clean teardown without memory leaks.
|
|
280
444
|
*/
|
|
281
445
|
private detachListeners;
|
|
282
446
|
/**
|
|
283
447
|
* Intercepts manual user gestures (`wheel`, `touchstart`, `touchmove`) to flag user interaction
|
|
284
|
-
* and cancel delayed
|
|
448
|
+
* and cancel delayed restorations prior to data readiness.
|
|
285
449
|
*/
|
|
286
450
|
private readonly onUserIntent;
|
|
287
451
|
/**
|
|
288
452
|
* Intercepts navigation keyboard events (`ArrowUp`, `ArrowDown`, `PageUp`, `PageDown`, `Home`, `End`, `Space`).
|
|
289
|
-
* Cancels pending
|
|
453
|
+
* Cancels pending restoration when navigation occurs within the relevant scroll context
|
|
290
454
|
* while safely ignoring interactions on editable input elements.
|
|
291
455
|
*/
|
|
292
456
|
private readonly onKeyDown;
|
|
457
|
+
private readonly onFocusScrollIntent;
|
|
458
|
+
private readonly onFocusIn;
|
|
459
|
+
private readonly onFocusTransferKeyDown;
|
|
460
|
+
private readonly onFocusTransferPointerDown;
|
|
293
461
|
/**
|
|
294
462
|
* Handles scroll events by immediately updating `liveAnchor` for position preservation,
|
|
295
463
|
* while debouncing `scrollRevision` increments to update the browser URL fragment without thrashing history.
|
|
@@ -299,6 +467,20 @@ export declare class MagicScrollDirective {
|
|
|
299
467
|
* Handles window resize events by re-evaluating targets and optionally realigning the active anchor.
|
|
300
468
|
*/
|
|
301
469
|
private readonly onResize;
|
|
470
|
+
/**
|
|
471
|
+
* Initiates smooth programmatic navigation protection, deferring layout realignments
|
|
472
|
+
* and intermediate anchor snapping until scrolling completes (`scrollend`) or times out.
|
|
473
|
+
*/
|
|
474
|
+
private beginSmoothNavigation;
|
|
475
|
+
/**
|
|
476
|
+
* Concludes smooth programmatic navigation, updating the reading position
|
|
477
|
+
* at the settled destination and processing any deferred layout preservation requests.
|
|
478
|
+
*/
|
|
479
|
+
private endSmoothNavigation;
|
|
480
|
+
/**
|
|
481
|
+
* Cancels active smooth navigation tracking and clears timeout timers and scrollend listeners.
|
|
482
|
+
*/
|
|
483
|
+
private cancelSmoothNavigation;
|
|
302
484
|
static ɵfac: i0.ɵɵFactoryDeclaration<MagicScrollDirective, never>;
|
|
303
485
|
static ɵdir: i0.ɵɵDirectiveDeclaration<MagicScrollDirective, "[magicScroll]", ["magicScroll"], {
|
|
304
486
|
"scrollSource": {
|
|
@@ -330,4 +512,4 @@ export declare class ScrollAnchorDirective {
|
|
|
330
512
|
};
|
|
331
513
|
}, {}, never, never, true, never>;
|
|
332
514
|
}
|
|
333
|
-
export type { MagicScrollOptions, MagicScrollToOptions, ScrollDataSource, ScrollResource, ScrollSource };
|
|
515
|
+
export type { AnchorSelectionMode, MagicScrollOptions, MagicScrollToOptions, ScrollDataSource, ScrollResource, ScrollSource };
|