js.foresight 3.5.0 → 4.0.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.
Files changed (38) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +14 -46
  3. package/dist/BaseForesightModule-BxWJ6mzK.mjs +2 -0
  4. package/dist/BaseForesightModule-BxWJ6mzK.mjs.map +1 -0
  5. package/dist/DesktopHandler-BgCQieJ9.mjs +2 -0
  6. package/dist/DesktopHandler-BgCQieJ9.mjs.map +1 -0
  7. package/dist/ElementObservingModule-OqTcnagr.mjs +2 -0
  8. package/dist/ElementObservingModule-OqTcnagr.mjs.map +1 -0
  9. package/dist/ScrollPredictor-r4YnqcfQ.mjs +2 -0
  10. package/dist/ScrollPredictor-r4YnqcfQ.mjs.map +1 -0
  11. package/dist/TabPredictor-CohoaRM5.mjs +2 -0
  12. package/dist/TabPredictor-CohoaRM5.mjs.map +1 -0
  13. package/dist/TouchDeviceHandler-CKhsCqLJ.mjs +2 -0
  14. package/dist/TouchDeviceHandler-CKhsCqLJ.mjs.map +1 -0
  15. package/dist/TouchStartPredictor-Dqk28kjI.mjs +2 -0
  16. package/dist/TouchStartPredictor-Dqk28kjI.mjs.map +1 -0
  17. package/dist/ViewportPredictor-BOtudr8f.mjs +2 -0
  18. package/dist/ViewportPredictor-BOtudr8f.mjs.map +1 -0
  19. package/dist/index.d.mts +556 -0
  20. package/dist/index.d.mts.map +1 -0
  21. package/dist/index.mjs +2 -0
  22. package/dist/index.mjs.map +1 -0
  23. package/dist/lineSegmentIntersectsRect-x9nicliA.mjs +2 -0
  24. package/dist/lineSegmentIntersectsRect-x9nicliA.mjs.map +1 -0
  25. package/dist/rectAndHitSlop-T7Z3PZlb.mjs +2 -0
  26. package/dist/rectAndHitSlop-T7Z3PZlb.mjs.map +1 -0
  27. package/package.json +27 -28
  28. package/dist/DesktopHandler-BOXAW4XX.js +0 -1
  29. package/dist/ScrollPredictor-Y7NELMBI.js +0 -1
  30. package/dist/TabPredictor-HA2SV3CY.js +0 -1
  31. package/dist/TouchDeviceHandler-JWBQ2YOV.js +0 -1
  32. package/dist/TouchStartPredictor-ZH3KJG2C.js +0 -1
  33. package/dist/ViewportPredictor-H3GLDETY.js +0 -1
  34. package/dist/chunk-44N4MCQB.js +0 -1
  35. package/dist/chunk-AODZNE3S.js +0 -1
  36. package/dist/chunk-PAYO6NXN.js +0 -1
  37. package/dist/index.d.ts +0 -531
  38. package/dist/index.js +0 -1
@@ -0,0 +1,556 @@
1
+ //#region src/helpers/CircularBuffer.d.ts
2
+ declare class CircularBuffer<T> {
3
+ private buffer;
4
+ private head;
5
+ private count;
6
+ private capacity;
7
+ constructor(capacity: number);
8
+ add(item: T): void;
9
+ getFirst(): T | undefined;
10
+ getLast(): T | undefined;
11
+ getFirstLast(): [T | undefined, T | undefined];
12
+ resize(newCapacity: number): void;
13
+ private getAllItems;
14
+ clear(): void;
15
+ get length(): number;
16
+ get size(): number;
17
+ get isFull(): boolean;
18
+ get isEmpty(): boolean;
19
+ }
20
+ //#endregion
21
+ //#region src/types/types.d.ts
22
+ type Rect = {
23
+ top: number;
24
+ left: number;
25
+ right: number;
26
+ bottom: number;
27
+ };
28
+ /**
29
+ * A callback function that is executed when a foresight interaction
30
+ * (e.g., hover, trajectory hit) occurs on a registered element.
31
+ */
32
+ type ForesightCallback = (state: ForesightElementState) => void;
33
+ /**
34
+ * Represents the HTML element that is being tracked by the ForesightManager.
35
+ * This is typically a standard DOM `Element`.
36
+ */
37
+ type ForesightElement = Element;
38
+ /**
39
+ * Represents a mouse position captured at a specific point in time.
40
+ * Used for tracking mouse movement history.
41
+ */
42
+ type MousePosition = {
43
+ /** The (x, y) coordinates of the mouse. */point: Point; /** The timestamp (e.g., from `performance.now()`) when this position was recorded. */
44
+ time: number;
45
+ };
46
+ type Point = {
47
+ x: number;
48
+ y: number;
49
+ };
50
+ type TrajectoryPositions = {
51
+ positions: CircularBuffer<MousePosition>;
52
+ currentPoint: Point;
53
+ predictedPoint: Point;
54
+ };
55
+ /**
56
+ * Internal type representing the calculated boundaries of a foresight element,
57
+ * including its original dimensions and the expanded hit area.
58
+ */
59
+ type ElementBounds = {
60
+ /** The expanded rectangle, including hitSlop, used for interaction detection. */expandedRect: Readonly<Rect>; /** The original bounding rectangle of the element, as returned by `getBoundingClientRect()`. */
61
+ originalRect: DOMRectReadOnly; /** The hit slop values applied to this element. */
62
+ hitSlop: Exclude<HitSlop, number>;
63
+ };
64
+ /**
65
+ * Immutable, flat state snapshot for a registered element.
66
+ * The reference is replaced (never mutated) on every change so it can be used
67
+ * with `useSyncExternalStore` and Vue's `shallowRef`.
68
+ */
69
+ type ForesightElementState = {
70
+ /** Unique identifier assigned during registration. */id: string; /** Human-readable name for debugging. */
71
+ name: string; /** Arbitrary user-supplied metadata. */
72
+ meta: Record<string, unknown>; /** The boundary information for the element. */
73
+ elementBounds: ElementBounds; /** Whether the user has connection limitations (network slower than minimum connection type (default: 3g) or data saver enabled) that prevent prefetching. */
74
+ isLimitedConnection: boolean; /** Whether the element is currently intersecting the viewport. */
75
+ isIntersectingWithViewport: boolean;
76
+ /** Whether the element is currently tracked by the manager. Stays `true` from
77
+ * registration until it is explicitly unregistered (via the returned `unregister`).
78
+ * Detaching the element from the DOM does NOT unregister it - it is parked inactive
79
+ * and resumes when it reconnects. On a limited connection it is also registered but
80
+ * inactive - check `isLimitedConnection` / `isActive`. */
81
+ isRegistered: boolean;
82
+ /** Whether the element is currently eligible to fire its callback. False when the
83
+ * element is disabled (`isEnabled: false`), on a limited connection, or temporarily
84
+ * detached from the DOM (see `isParked`). */
85
+ isActive: boolean;
86
+ /** Whether the element is detached from the DOM and parked: kept registered but
87
+ * inactive until it reconnects, at which point it resumes automatically. */
88
+ isParked: boolean;
89
+ /** Whether prediction is enabled for this element. When `false` the element
90
+ * stays registered but inactive. Note: an enabled element is still inactive on a
91
+ * limited connection (see `isLimitedConnection`). */
92
+ isEnabled: boolean; /** True once the element's callback has been triggered by a prediction hit. Stays true until the element is reactivated or unregistered. */
93
+ isPredicted: boolean; /** True while the callback is executing (between invocation and completion). The callback is awaited, so this stays true for async callbacks until they resolve or reject. */
94
+ isCallbackRunning: boolean; /** Number of times the callback has fired for this element. */
95
+ hitCount: number; /** Number of times this element has been (re)registered. */
96
+ registerCount: number; /** Duration in ms of the most recent callback run. */
97
+ durationMs: number | undefined; /** Status of the most recently completed callback. */
98
+ status: callbackStatus; /** Error message from the most recently completed callback. */
99
+ error: string | null; /** Time in ms after which the callback can be fired again (Infinity = never). */
100
+ reactivateAfter: number;
101
+ };
102
+ type ForesightRegisterResult = ForesightElementState & {
103
+ /** Function to unregister the element
104
+ */
105
+ unregister: () => void;
106
+ /**
107
+ * Subscribe to state changes for this element. Returns an unsubscribe function.
108
+ */
109
+ subscribe: (listener: () => void) => () => void;
110
+ /**
111
+ * Returns the current immutable state snapshot for this element.
112
+ */
113
+ getSnapshot: () => ForesightElementState;
114
+ };
115
+ type callbackStatus = "error" | "success" | undefined;
116
+ type MouseCallbackCounts = {
117
+ hover: number;
118
+ trajectory: number;
119
+ };
120
+ type TabCallbackCounts = {
121
+ reverse: number;
122
+ forwards: number;
123
+ };
124
+ type ScrollDirection = "down" | "up" | "left" | "right" | "none";
125
+ type ScrollCallbackCounts = Record<`${Exclude<ScrollDirection, "none">}`, number>;
126
+ type CallbackHits = {
127
+ total: number;
128
+ mouse: MouseCallbackCounts;
129
+ tab: TabCallbackCounts;
130
+ scroll: ScrollCallbackCounts;
131
+ touch: number;
132
+ viewport: number;
133
+ };
134
+ type CallbackHitType = {
135
+ kind: "mouse";
136
+ subType: keyof MouseCallbackCounts;
137
+ } | {
138
+ kind: "tab";
139
+ subType: keyof TabCallbackCounts;
140
+ } | {
141
+ kind: "scroll";
142
+ subType: keyof ScrollCallbackCounts;
143
+ } | {
144
+ kind: "touch";
145
+ subType?: string;
146
+ } | {
147
+ kind: "viewport";
148
+ subType?: string;
149
+ };
150
+ /**
151
+ * Snapshot of the current ForesightManager state
152
+ */
153
+ type ForesightManagerData = {
154
+ registeredElements: ReadonlyMap<ForesightElement, ForesightElementState>;
155
+ globalSettings: Readonly<ForesightManagerSettings>;
156
+ globalCallbackHits: Readonly<CallbackHits>;
157
+ eventListeners: ReadonlyMap<keyof ForesightEventMap, ForesightEventListener[]>;
158
+ currentDeviceStrategy: CurrentDeviceStrategy;
159
+ activeElementCount: number;
160
+ parkedElementCount: number;
161
+ loadedModules: ForesightModules;
162
+ };
163
+ type ForesightModules = {
164
+ desktopHandler: boolean;
165
+ touchHandler: boolean;
166
+ predictors: {
167
+ mouse: boolean;
168
+ tab: boolean;
169
+ scroll: boolean;
170
+ viewport: boolean;
171
+ touchStart: boolean;
172
+ };
173
+ };
174
+ type TouchDeviceStrategy = "none" | "viewport" | "onTouchStart";
175
+ type MinimumConnectionType = "slow-2g" | "2g" | "3g" | "4g";
176
+ type BaseForesightManagerSettings = {
177
+ /**
178
+ * Number of mouse positions to keep in history for trajectory calculation.
179
+ * A higher number might lead to smoother but slightly delayed predictions.
180
+ *
181
+ *
182
+ * @link https://foresightjs.com/docs/getting_started/config#available-global-settings
183
+ *
184
+ *
185
+ * **This value is clamped between 2 and 30.**
186
+ * @default 8
187
+ */
188
+ positionHistorySize: number;
189
+ /**
190
+ *
191
+ * Logs basic information about the ForesightManager and its handlers that doesn't have a dedicated event.
192
+ *
193
+ * Mostly used by the maintainers of ForesightJS to debug the manager. But might be useful for implementers aswell.
194
+ *
195
+ * This is not the same as logging events, this can be done with the actual developer tools.
196
+ * @link https://foresightjs.com/docs/debugging/devtools
197
+ */
198
+ enableManagerLogging: boolean;
199
+ /**
200
+ * How far ahead (in milliseconds) to predict the mouse trajectory.
201
+ * A larger value means the prediction extends further into the future. (meaning it will trigger callbacks sooner)
202
+ *
203
+ * @link https://foresightjs.com/docs/getting_started/config#available-global-settings
204
+ *
205
+ * **This value is clamped between 10 and 200.**
206
+ * @default 120
207
+ */
208
+ trajectoryPredictionTime: number;
209
+ /**
210
+ * Whether to enable mouse trajectory prediction.
211
+ * If false, only direct hover/interaction is considered.
212
+ * @link https://foresightjs.com/docs/getting_started/config#available-global-settings
213
+ * @default true
214
+ */
215
+ enableMousePrediction: boolean;
216
+ /**
217
+ * Toggles whether keyboard prediction is on
218
+ *
219
+ * @link https://foresightjs.com/docs/getting_started/config#available-global-settings
220
+ * @default true
221
+ */
222
+ enableTabPrediction: boolean;
223
+ /**
224
+ * Sets the pixel distance to check from the mouse position in the scroll direction.
225
+ *
226
+ * @link https://foresightjs.com/docs/getting_started/config#available-global-settings
227
+ *
228
+ * **This value is clamped between 30 and 300.**
229
+ * @default 150
230
+ */
231
+ scrollMargin: number;
232
+ /**
233
+ * Toggles whether scroll prediction is on
234
+ * @link https://foresightjs.com/docs/getting_started/config#available-global-settings
235
+ * @default true
236
+ */
237
+ enableScrollPrediction: boolean;
238
+ /**
239
+ * Tab stops away from an element to trigger callback. Only works when @argument enableTabPrediction is true
240
+ *
241
+ * **This value is clamped between 0 and 20.**
242
+ * @default 2
243
+ */
244
+ tabOffset: number;
245
+ /**
246
+ * The prefetch strategy used for touch devices.
247
+ * - `viewport`: Prefetching is done based on the viewport, meaning elements in the viewport are preloaded.
248
+ * - `onTouchStart`: Prefetching is done when the user touches the element
249
+ * @default onTouchStart
250
+ */
251
+ touchDeviceStrategy: TouchDeviceStrategy;
252
+ /**
253
+ * Network effective connection types that should be considered as limited connections.
254
+ * When the user's network matches any of these types, ForesightJS will disable prefetching
255
+ * to avoid consuming data on slow or expensive connections.
256
+ *
257
+ * @link https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/effectiveType
258
+ * @default 3g
259
+ */
260
+ minimumConnectionType: MinimumConnectionType;
261
+ };
262
+ type CurrentDeviceStrategy = "mouse" | "touch" | "pen";
263
+ /**
264
+ * Configuration options for the ForesightManager
265
+ * @link https://foresightjs.com/docs/getting_started/config#available-global-settings
266
+ */
267
+ type ForesightManagerSettings = BaseForesightManagerSettings & {
268
+ defaultHitSlop: Exclude<HitSlop, number>;
269
+ };
270
+ /**
271
+ * Update options for the ForesightManager
272
+ * @link https://foresightjs.com/docs/getting_started/config#available-global-settings
273
+ */
274
+ type UpdateForsightManagerSettings = BaseForesightManagerSettings & {
275
+ defaultHitSlop: HitSlop;
276
+ };
277
+ /**
278
+ * Type used to register elements to the foresight manager
279
+ */
280
+ type ForesightRegisterOptions = ForesightRegisterOptionsWithoutElement & {
281
+ element: ForesightElement;
282
+ };
283
+ type ForesightRegisterNodeListOptions = ForesightRegisterOptionsWithoutElement & {
284
+ element: NodeListOf<ForesightElement>;
285
+ };
286
+ /**
287
+ * Use full for if you want to create a custom button component in a modern framework (for example React).
288
+ * And you want to have the ForesightRegisterOptions used in ForesightManager.instance.register({})
289
+ * without the element as the element will be the ref of the component.
290
+ *
291
+ * @link https://foresightjs.com/docs/getting_started/typescript#foresightregisteroptionswithoutelement
292
+ */
293
+ type ForesightRegisterOptionsWithoutElement = {
294
+ callback: ForesightCallback;
295
+ hitSlop?: HitSlop;
296
+ name?: string;
297
+ /**
298
+ * If set by user, stores additional information about the registered element
299
+ */
300
+ meta?: Record<string, unknown>;
301
+ /**
302
+ * Time in milliseconds after which the callback can be fired again and we reactivate the element.
303
+ * Set to Infinity to prevent callback from firing again after first execution.
304
+ * @default Infinity
305
+ */
306
+ reactivateAfter?: number;
307
+ /**
308
+ * When `false` the element stays registered but inactive: excluded from
309
+ * prediction and never fires its callback.
310
+ * @default true
311
+ */
312
+ enabled?: boolean;
313
+ };
314
+ /**
315
+ * Fully invisible "slop" around the element.
316
+ * Basically increases the hover hitbox
317
+ */
318
+ type HitSlop = Rect | number;
319
+ interface ForesightEventMap {
320
+ elementRegistered: ElementRegisteredEvent;
321
+ elementUnregistered: ElementUnregisteredEvent;
322
+ callbackInvoked: CallbackInvokedEvent;
323
+ callbackCompleted: CallbackCompletedEvent;
324
+ mouseTrajectoryUpdate: MouseTrajectoryUpdateEvent;
325
+ scrollTrajectoryUpdate: ScrollTrajectoryUpdateEvent;
326
+ managerSettingsChanged: ManagerSettingsChangedEvent;
327
+ deviceStrategyChanged: DeviceStrategyChangedEvent;
328
+ }
329
+ type ForesightEvent = "elementRegistered" | "elementUnregistered" | "callbackInvoked" | "callbackCompleted" | "mouseTrajectoryUpdate" | "scrollTrajectoryUpdate" | "managerSettingsChanged" | "deviceStrategyChanged";
330
+ interface DeviceStrategyChangedEvent extends ForesightBaseEvent {
331
+ type: "deviceStrategyChanged";
332
+ newStrategy: CurrentDeviceStrategy;
333
+ oldStrategy: CurrentDeviceStrategy;
334
+ }
335
+ interface ElementRegisteredEvent extends ForesightBaseEvent {
336
+ type: "elementRegistered";
337
+ element: ForesightElement;
338
+ state: ForesightElementState;
339
+ }
340
+ interface ElementUnregisteredEvent extends ForesightBaseEvent {
341
+ type: "elementUnregistered";
342
+ element: ForesightElement;
343
+ state: ForesightElementState;
344
+ unregisterReason: ElementUnregisteredReason;
345
+ wasLastRegisteredElement: boolean;
346
+ }
347
+ /**
348
+ * The reason an element was unregistered from ForesightManager's tracking.
349
+ * - `callbackHit`: The element was automatically unregistered after its callback fired.
350
+ * - `disconnected`: No longer emitted. Elements detached from the DOM are now parked
351
+ * (kept registered but inactive) and resumed on reconnect, rather than unregistered.
352
+ * - `apiCall`: The developer manually called the `unregister()` function for the element.
353
+ * - `devtools`: When clicking the trash icon in the devtools element tab
354
+ * - any other string
355
+ */
356
+ type ElementUnregisteredReason = "disconnected" | "apiCall" | "devtools" | (string & {});
357
+ interface CallbackInvokedEvent extends ForesightBaseEvent {
358
+ type: "callbackInvoked";
359
+ element: ForesightElement;
360
+ state: ForesightElementState;
361
+ hitType: CallbackHitType;
362
+ }
363
+ interface CallbackCompletedEventBase extends ForesightBaseEvent {
364
+ type: "callbackCompleted";
365
+ element: ForesightElement;
366
+ state: ForesightElementState;
367
+ hitType: CallbackHitType;
368
+ elapsed: number;
369
+ wasLastActiveElement: boolean;
370
+ }
371
+ type CallbackCompletedEvent = CallbackCompletedEventBase & {
372
+ status: callbackStatus;
373
+ errorMessage: string | null;
374
+ };
375
+ interface MouseTrajectoryUpdateEvent extends Omit<ForesightBaseEvent, "timestamp"> {
376
+ type: "mouseTrajectoryUpdate";
377
+ trajectoryPositions: TrajectoryPositions;
378
+ predictionEnabled: boolean;
379
+ }
380
+ interface ScrollTrajectoryUpdateEvent extends Omit<ForesightBaseEvent, "timestamp"> {
381
+ type: "scrollTrajectoryUpdate";
382
+ currentPoint: Point;
383
+ predictedPoint: Point;
384
+ scrollDirection: ScrollDirection;
385
+ }
386
+ interface ManagerSettingsChangedEvent extends ForesightBaseEvent {
387
+ type: "managerSettingsChanged";
388
+ managerData: ForesightManagerData;
389
+ updatedSettings: UpdatedManagerSetting[];
390
+ }
391
+ type UpdatedManagerSetting = { [K in keyof ForesightManagerSettings]: {
392
+ setting: K;
393
+ newValue: ForesightManagerSettings[K];
394
+ oldValue: ForesightManagerSettings[K];
395
+ } }[keyof ForesightManagerSettings];
396
+ type ForesightEventListener<K extends ForesightEvent = ForesightEvent> = (event: ForesightEventMap[K]) => void;
397
+ interface ForesightBaseEvent {
398
+ type: ForesightEvent;
399
+ timestamp: number;
400
+ }
401
+ //#endregion
402
+ //#region src/managers/ForesightManager.d.ts
403
+ /**
404
+ * Manages the prediction of user intent based on mouse trajectory and element interactions.
405
+ *
406
+ * ForesightManager is a singleton class responsible for:
407
+ * - Registering HTML elements to monitor.
408
+ * - Tracking mouse movements and predicting future cursor positions.
409
+ * - Detecting when a predicted trajectory intersects with a registered element's bounds.
410
+ * - Invoking callbacks associated with elements upon predicted or actual interaction.
411
+ * - Deactivating elements after their callback completes, with optional reactivation via `reactivateAfter`.
412
+ * - Handling global settings for prediction behavior (e.g., history size, prediction time).
413
+ * - Delegating element bounds observation to device handlers ({@link DesktopHandler}, {@link TouchDeviceHandler}).
414
+ * - Automatically unregistering elements removed from the DOM using {@link MutationObserver}.
415
+ * - Detecting broader layout shifts via {@link MutationObserver} to update element positions.
416
+ *
417
+ * It should be initialized once using {@link ForesightManager.initialize} and then
418
+ * accessed via the static getter {@link ForesightManager.instance}.
419
+ */
420
+ declare class ForesightManager {
421
+ private static manager;
422
+ /** Internal entries containing full element data, callbacks, and subscribers. */
423
+ private elementEntries;
424
+ /** Public read-only view exposing only external state, derived from {@link elementEntries}. */
425
+ readonly registeredElements: ReadonlyMap<ForesightElement, ForesightElementState>;
426
+ private idCounter;
427
+ private activeElementCount;
428
+ private parkedElementCount;
429
+ private desktopHandler;
430
+ private touchDeviceHandler;
431
+ private currentlyActiveHandler;
432
+ private handlerDependencies;
433
+ private isSetup;
434
+ private pendingPointerEvent;
435
+ private rafId;
436
+ private domObserver;
437
+ private currentDeviceStrategy;
438
+ private eventEmitter;
439
+ private _globalCallbackHits;
440
+ private _globalSettings;
441
+ private constructor();
442
+ private getOrCreateDesktopHandler;
443
+ private getOrCreateTouchHandler;
444
+ static initialize(props?: Partial<UpdateForsightManagerSettings>): ForesightManager;
445
+ static get isInitiated(): Readonly<boolean>;
446
+ static get instance(): ForesightManager;
447
+ private generateId;
448
+ private get isUsingDesktopHandler();
449
+ addEventListener<K extends ForesightEvent>(eventType: K, listener: ForesightEventListener<K>, options?: {
450
+ signal?: AbortSignal;
451
+ }): void;
452
+ removeEventListener<K extends ForesightEvent>(eventType: K, listener: ForesightEventListener<K>): void;
453
+ hasListeners<K extends ForesightEvent>(eventType: K): boolean;
454
+ /**
455
+ * Subscribe to state changes for a specific element.
456
+ * The listener is called (with no arguments) whenever the element's
457
+ * immutable state snapshot is replaced. Use {@link registeredElements}
458
+ * to read the latest state inside the listener.
459
+ *
460
+ * @returns An unsubscribe function, or `undefined` if the element is not registered.
461
+ */
462
+ subscribeToElement(element: ForesightElement, listener: () => void): (() => void) | undefined;
463
+ get getManagerData(): Readonly<ForesightManagerData>;
464
+ private getLoadedModulesSnapshot;
465
+ register(options: ForesightRegisterNodeListOptions): ForesightRegisterResult[];
466
+ register(options: ForesightRegisterOptions): ForesightRegisterResult;
467
+ private registerElement;
468
+ /**
469
+ * Updates the options of an already-registered element.
470
+ * Only the provided fields are updated; omitted fields keep their current values.
471
+ * If a reactivation timeout is pending and reactivateAfter changed, the timeout is rescheduled.
472
+ *
473
+ * @throws Error if the element is not registered.
474
+ */
475
+ updateElementOptions(element: ForesightElement, options: Partial<ForesightRegisterOptionsWithoutElement>): ForesightElementState;
476
+ /**
477
+ * Create a subscribe function for an element.
478
+ * Returns an unsubscribe callback when called.
479
+ */
480
+ private makeSubscribe;
481
+ /**
482
+ * Replace the immutable state ref for an element and notify subscribers.
483
+ * No-op when every patch value already matches current state - preserves the
484
+ * stable-reference contract relied on by useSyncExternalStore and shallowRef.
485
+ */
486
+ private updateElementState;
487
+ unregister(element: ForesightElement | NodeListOf<ForesightElement>, unregisterReason?: ElementUnregisteredReason): void;
488
+ private unregisterElement;
489
+ reactivate(element: ForesightElement | NodeListOf<ForesightElement>): void;
490
+ private reactivateElement;
491
+ /**
492
+ * Toggle prediction for a registered element without unregistering it.
493
+ * Disabling deactivates and stops observing; enabling reactivates it.
494
+ */
495
+ private setElementEnabled;
496
+ private clearReactivateTimeout;
497
+ private callCallback;
498
+ private markElementAsRunning;
499
+ private executeCallbackAsync;
500
+ private finalizeCallback;
501
+ private updateHitCounters;
502
+ private setDeviceStrategy;
503
+ private handlePointerMove;
504
+ private initializeGlobalListeners;
505
+ private removeGlobalListeners;
506
+ private handleDomMutations;
507
+ /**
508
+ * Deactivate an element that was detached from the DOM. It is kept in
509
+ * {@link elementEntries} (still registered) and flagged `isParked` so it can be
510
+ * resumed when it reconnects.
511
+ */
512
+ private parkDisconnected;
513
+ /**
514
+ * Re-activate a previously parked element once it is back in the DOM. Mirrors
515
+ * the activation rules used everywhere else: disabled / limited connections stay
516
+ * inactive, and an element that already fired its callback stays inactive too
517
+ * (it resumes the same state it had before it detached).
518
+ */
519
+ private resumeReconnected;
520
+ /**
521
+ * Tear down global listeners only when nothing needs them: no active elements
522
+ * to predict on, and no parked elements waiting to reconnect (which need the
523
+ * MutationObserver to detect their return).
524
+ */
525
+ private removeGlobalListenersIfIdle;
526
+ alterGlobalSettings(props?: Partial<UpdateForsightManagerSettings>): void;
527
+ private forceUpdateAllElementBounds;
528
+ /**
529
+ * ONLY use this function when you want to change the rect bounds via code, if the rects are changing because of updates in the DOM do not use this function.
530
+ * We need an observer for that
531
+ */
532
+ private forceUpdateElementBounds;
533
+ private devLog;
534
+ }
535
+ //#endregion
536
+ //#region src/helpers/createInitialState.d.ts
537
+ /**
538
+ * Snapshot of the flat state shape for an element that is not (yet) tracked by the
539
+ * manager. Reuses the same fields as a registered element so consumers don't need
540
+ * to special-case `null`.
541
+ *
542
+ * Used in two situations:
543
+ * 1. The manager refuses to register the element (touch device, limited connection,
544
+ * etc.) - pass `isLimitedConnection` to reflect that.
545
+ * 2. Framework wrappers (React, Vue) need an initial snapshot before `register()`
546
+ * has run. `register()` requires a real DOM element, which only exists after
547
+ * the consumer's first render commits, so the wrapper returns this snapshot
548
+ * during that brief window. Pass `isLimitedConnection: false` for this case.
549
+ */
550
+ declare const createUnregisteredSnapshot: (isLimitedConnection: boolean) => ForesightElementState;
551
+ //#endregion
552
+ //#region src/core/BaseForesightModule.d.ts
553
+ type HasListenersFunction = <K extends ForesightEvent>(eventType: K) => boolean;
554
+ //#endregion
555
+ export { type CallbackCompletedEvent, type CallbackHitType, type CallbackHits, type CallbackInvokedEvent, type DeviceStrategyChangedEvent, type ElementRegisteredEvent, type ElementUnregisteredEvent, type ForesightCallback, type ForesightElement, type ForesightElementState, type ForesightEvent, type ForesightEventMap, ForesightManager, type ForesightManagerData, type ForesightManagerSettings, type Point as ForesightPoint, type Rect as ForesightRect, type ForesightRegisterNodeListOptions, type ForesightRegisterOptions, type ForesightRegisterOptionsWithoutElement, type ForesightRegisterResult, type HasListenersFunction, type HitSlop, type ManagerSettingsChangedEvent, type MinimumConnectionType, type MouseTrajectoryUpdateEvent, type ScrollDirection, type ScrollTrajectoryUpdateEvent, type TouchDeviceStrategy, type UpdateForsightManagerSettings, type UpdatedManagerSetting, createUnregisteredSnapshot };
556
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/helpers/CircularBuffer.ts","../src/types/types.ts","../src/managers/ForesightManager.ts","../src/helpers/createInitialState.ts","../src/core/BaseForesightModule.ts"],"mappings":"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"}
package/dist/index.mjs ADDED
@@ -0,0 +1,2 @@
1
+ import{a as e,i as t,n,o as r,t as i}from"./rectAndHitSlop-T7Z3PZlb.mjs";var a=class{constructor(e,t){this.source=e,this.derive=t}get size(){return this.source.size}get(e){let t=this.source.get(e);return t===void 0?void 0:this.derive(t)}has(e){return this.source.has(e)}forEach(e){this.source.forEach((t,n)=>e(this.derive(t),n,this))}*entries(){for(let[e,t]of this.source)yield[e,this.derive(t)]}*values(){for(let e of this.source.values())yield this.derive(e)}keys(){return this.source.keys()}[Symbol.iterator](){return this.entries()}get[Symbol.toStringTag](){return`DerivedMapView`}};const o=e=>{let t=s(),n=c(e);return{isTouchDevice:t,isLimitedConnection:n,shouldRegister:!n}},s=()=>typeof window>`u`||typeof navigator>`u`?!1:window.matchMedia(`(pointer: coarse)`).matches&&navigator.maxTouchPoints>0,c=e=>{let t=navigator.connection;if(!t)return!1;let n=[`slow-2g`,`2g`,`3g`,`4g`];return n.indexOf(t.effectiveType)<n.indexOf(e)||t.saveData},l=e=>{if(typeof window>`u`||typeof document>`u`)return!1;let t=window.innerWidth||document.documentElement.clientWidth,n=window.innerHeight||document.documentElement.clientHeight;return e.top<n&&e.bottom>0&&e.left<t&&e.right>0},u=()=>({mouse:{hover:0,trajectory:0},tab:{forwards:0,reverse:0},scroll:{down:0,left:0,right:0,up:0},touch:0,viewport:0,total:0}),d=()=>({enableManagerLogging:!1,enableMousePrediction:!0,enableScrollPrediction:!0,positionHistorySize:8,trajectoryPredictionTime:120,scrollMargin:150,defaultHitSlop:{top:0,left:0,right:0,bottom:0},enableTabPrediction:!0,tabOffset:2,touchDeviceStrategy:`onTouchStart`,minimumConnectionType:`3g`}),f=(e,r,i,a)=>{let{element:o,callback:s,hitSlop:c,name:u,meta:d,reactivateAfter:f,enabled:p}=e,m=o.getBoundingClientRect(),h=c?t(c):i,g=p!==!1;return{state:{id:r,name:u||o.id||`unnamed`,meta:d??{},elementBounds:{originalRect:m,expandedRect:n(m,h),hitSlop:h},isLimitedConnection:a,isIntersectingWithViewport:l(m),isRegistered:!0,isActive:g&&!a,isParked:!1,isEnabled:g,isPredicted:!1,isCallbackRunning:!1,hitCount:0,registerCount:1,durationMs:void 0,status:void 0,error:null,reactivateAfter:f??1/0},invokedAt:void 0,completedAt:void 0,element:o,callback:s,reactivateTimeoutId:void 0,subscribers:new Set}},p={x:0,y:0,width:0,height:0,top:0,right:0,bottom:0,left:0,toJSON:()=>({})},m=e=>({id:``,name:``,meta:{},elementBounds:{originalRect:p,expandedRect:{top:0,left:0,right:0,bottom:0},hitSlop:{top:0,left:0,right:0,bottom:0}},isLimitedConnection:e,isIntersectingWithViewport:!1,isRegistered:!1,isActive:!1,isParked:!1,isEnabled:!1,isPredicted:!1,isCallbackRunning:!1,hitCount:0,registerCount:0,durationMs:void 0,status:void 0,error:null,reactivateAfter:r});var h=class{constructor(){this.eventListeners=new Map}addEventListener(e,t,n){if(n?.signal?.aborted)return;let r=this.eventListeners.get(e)??[];r.push(t),this.eventListeners.set(e,r),n?.signal?.addEventListener(`abort`,()=>this.removeEventListener(e,t))}removeEventListener(e,t){let n=this.eventListeners.get(e);if(!n)return;let r=n.indexOf(t);r>-1&&n.splice(r,1)}emit(e){let t=this.eventListeners.get(e.type);if(!(!t||t.length===0))for(let n=0;n<t.length;n++)try{let r=t[n];r&&r(e)}catch(t){console.error(`Error in ForesightManager event listener ${n} for ${e.type}:`,t)}}hasListeners(e){let t=this.eventListeners.get(e);return t!==void 0&&t.length>0}getEventListeners(){return this.eventListeners}};const g=(e,t)=>e!==void 0&&t!==e,_={trajectoryPredictionTime:{min:10,max:200},positionHistorySize:{min:2,max:30},scrollMargin:{min:30,max:300},tabOffset:{min:0,max:20}},v=(t,n,r)=>{if(!g(r,t[n]))return!1;let{min:i,max:a}=_[n];return t[n]=e(r,i,a,n),!0},y=(e,t,n)=>g(n,e[t])?(e[t]=n,!0):!1,b=(e,n)=>{v(e,`trajectoryPredictionTime`,n.trajectoryPredictionTime),v(e,`positionHistorySize`,n.positionHistorySize),v(e,`scrollMargin`,n.scrollMargin),v(e,`tabOffset`,n.tabOffset),y(e,`enableMousePrediction`,n.enableMousePrediction),y(e,`enableScrollPrediction`,n.enableScrollPrediction),y(e,`enableTabPrediction`,n.enableTabPrediction),y(e,`enableManagerLogging`,n.enableManagerLogging),n.defaultHitSlop!==void 0&&(e.defaultHitSlop=t(n.defaultHitSlop)),n.touchDeviceStrategy!==void 0&&(e.touchDeviceStrategy=n.touchDeviceStrategy),n.minimumConnectionType!==void 0&&(e.minimumConnectionType=n.minimumConnectionType)},x=(e,n)=>{let r=[],a=!1,o=!1,s=!1,c=!1,l=!1;if(!n)return{changedSettings:r,positionHistorySizeChanged:a,scrollPredictionChanged:o,tabPredictionChanged:s,hitSlopChanged:c,touchStrategyChanged:l};for(let t of[`trajectoryPredictionTime`,`positionHistorySize`,`scrollMargin`,`tabOffset`]){let i=e[t];v(e,t,n[t])&&(r.push({setting:t,oldValue:i,newValue:e[t]}),t===`positionHistorySize`&&(a=!0))}for(let t of[`enableMousePrediction`,`enableScrollPrediction`,`enableTabPrediction`]){let i=e[t];y(e,t,n[t])&&(r.push({setting:t,oldValue:i,newValue:e[t]}),t===`enableScrollPrediction`&&(o=!0),t===`enableTabPrediction`&&(s=!0))}if(n.defaultHitSlop!==void 0){let a=e.defaultHitSlop,o=t(n.defaultHitSlop);i(a,o)||(e.defaultHitSlop=o,r.push({setting:`defaultHitSlop`,oldValue:a,newValue:o}),c=!0)}if(n.touchDeviceStrategy!==void 0){let t=e.touchDeviceStrategy;e.touchDeviceStrategy=n.touchDeviceStrategy,r.push({setting:`touchDeviceStrategy`,oldValue:t,newValue:n.touchDeviceStrategy}),l=!0}if(n.minimumConnectionType!==void 0){let t=e.minimumConnectionType;e.minimumConnectionType=n.minimumConnectionType,r.push({setting:`minimumConnectionType`,oldValue:t,newValue:n.minimumConnectionType})}return{changedSettings:r,positionHistorySizeChanged:a,scrollPredictionChanged:o,tabPredictionChanged:s,hitSlopChanged:c,touchStrategyChanged:l}};var S=class e{constructor(e){this.elementEntries=new Map,this.registeredElements=new a(this.elementEntries,e=>e.state),this.idCounter=0,this.activeElementCount=0,this.parkedElementCount=0,this.desktopHandler=null,this.touchDeviceHandler=null,this.currentlyActiveHandler=null,this.isSetup=!1,this.pendingPointerEvent=null,this.rafId=null,this.domObserver=null,this.currentDeviceStrategy=s()?`touch`:`mouse`,this.eventEmitter=new h,this._globalCallbackHits=u(),this._globalSettings=d(),this.handlePointerMove=e=>{this.pendingPointerEvent=e,e.pointerType!==this.currentDeviceStrategy&&(this.eventEmitter.emit({type:`deviceStrategyChanged`,timestamp:Date.now(),newStrategy:e.pointerType,oldStrategy:this.currentDeviceStrategy}),this.currentDeviceStrategy=e.pointerType,this.setDeviceStrategy(this.currentDeviceStrategy)),!this.rafId&&(this.rafId=requestAnimationFrame(()=>{if(!this.isUsingDesktopHandler){this.rafId=null;return}this.pendingPointerEvent&&this.desktopHandler?.processMouseMovement(this.pendingPointerEvent),this.rafId=null}))},this.handleDomMutations=e=>{if(!e.length)return;this.desktopHandler?.invalidateTabCache();let t=!1;for(let n=0;n<e.length;n++){let r=e[n];if(r&&r.type===`childList`&&(r.removedNodes.length>0||r.addedNodes.length>0)){t=!0;break}}if(t)for(let e of this.elementEntries.values())e.state.isParked?e.element.isConnected&&this.resumeReconnected(e):e.element.isConnected||this.parkDisconnected(e)},e!==void 0&&b(this._globalSettings,e),this.handlerDependencies={elements:this.elementEntries,callCallback:this.callCallback.bind(this),emit:this.eventEmitter.emit.bind(this.eventEmitter),hasListeners:this.eventEmitter.hasListeners.bind(this.eventEmitter),updateElementState:this.updateElementState.bind(this),settings:this._globalSettings},this.devLog(`ForesightManager initialized with device strategy: ${this.currentDeviceStrategy}`),this.initializeGlobalListeners()}async getOrCreateDesktopHandler(){if(!this.desktopHandler){let{DesktopHandler:e}=await import(`./DesktopHandler-BgCQieJ9.mjs`);this.desktopHandler=new e(this.handlerDependencies),this.devLog(`DesktopHandler lazy loaded`)}return this.desktopHandler}async getOrCreateTouchHandler(){if(!this.touchDeviceHandler){let{TouchDeviceHandler:e}=await import(`./TouchDeviceHandler-CKhsCqLJ.mjs`);this.touchDeviceHandler=new e(this.handlerDependencies),this.devLog(`TouchDeviceHandler lazy loaded`)}return this.touchDeviceHandler}static initialize(t){return this.isInitiated||(e.manager=new e(t)),e.manager}static get isInitiated(){return!!e.manager}static get instance(){return this.initialize()}generateId(){return`foresight-${++this.idCounter}`}get isUsingDesktopHandler(){return this.currentDeviceStrategy===`mouse`||this.currentDeviceStrategy===`pen`}addEventListener(e,t,n){this.eventEmitter.addEventListener(e,t,n)}removeEventListener(e,t){this.eventEmitter.removeEventListener(e,t)}hasListeners(e){return this.eventEmitter.hasListeners(e)}subscribeToElement(e,t){let n=this.elementEntries.get(e);if(n)return n.subscribers.add(t),()=>{n.subscribers.delete(t)}}get getManagerData(){return{registeredElements:this.registeredElements,globalSettings:this._globalSettings,globalCallbackHits:this._globalCallbackHits,eventListeners:this.eventEmitter.getEventListeners(),currentDeviceStrategy:this.currentDeviceStrategy,activeElementCount:this.activeElementCount,parkedElementCount:this.parkedElementCount,loadedModules:this.getLoadedModulesSnapshot()}}getLoadedModulesSnapshot(){let e=this.desktopHandler?.loadedPredictors,t=this.touchDeviceHandler?.loadedPredictors;return{desktopHandler:this.desktopHandler!==null,touchHandler:this.touchDeviceHandler!==null,predictors:{mouse:e?.mouse??!1,tab:e?.tab??!1,scroll:e?.scroll??!1,viewport:t?.viewport??!1,touchStart:t?.touchStart??!1}}}register(e){let{element:t,...n}=e;return t instanceof NodeList?Array.from(t,e=>this.registerElement({...n,element:e})):this.registerElement({...n,element:t})}registerElement(e){let{isLimitedConnection:t}=o(this._globalSettings.minimumConnectionType),n=this.elementEntries.get(e.element);if(n)return this.updateElementOptions(e.element,e),this.updateElementState(n,{registerCount:n.state.registerCount+1}),{...n.state,unregister:()=>{this.unregister(e.element)},subscribe:this.makeSubscribe(n),getSnapshot:()=>n.state};this.isSetup||this.initializeGlobalListeners();let r=f(e,this.generateId(),this._globalSettings.defaultHitSlop,t);return this.elementEntries.set(e.element,r),r.state.isActive&&(this.activeElementCount++,this.currentlyActiveHandler?.observeElement(e.element)),this.eventEmitter.emit({type:`elementRegistered`,timestamp:Date.now(),element:e.element,state:r.state}),{...r.state,unregister:()=>{this.unregister(e.element)},subscribe:this.makeSubscribe(r),getSnapshot:()=>r.state}}updateElementOptions(e,r){let a=this.elementEntries.get(e);if(!a)throw Error(`Cannot update options: element is not registered.`);r.callback&&(a.callback=r.callback),r.enabled!==void 0&&this.setElementEnabled(a,e,r.enabled!==!1);let o=a.state.elementBounds;if(r.hitSlop!==void 0){let a=t(r.hitSlop);if(!i(a,o.hitSlop)){let t=e.getBoundingClientRect();o={originalRect:t,expandedRect:n(t,a),hitSlop:a}}}let s=a.state.reactivateAfter,c=r.reactivateAfter??s,l=this.updateElementState(a,{name:r.name||a.state.name,meta:r.meta??a.state.meta,reactivateAfter:c,elementBounds:o});return c!==s&&(a.reactivateTimeoutId!==void 0&&this.clearReactivateTimeout(a),c!==1/0&&l.isPredicted&&(a.reactivateTimeoutId=setTimeout(()=>{this.reactivate(e)},c))),l}makeSubscribe(e){return t=>(e.subscribers.add(t),()=>{e.subscribers.delete(t)})}updateElementState(e,t){let n=e.state,r=!1;for(let e in t)if(t[e]!==n[e]){r=!0;break}if(!r)return n;let i={...n,...t};e.state=i;for(let t of e.subscribers)try{t()}catch(e){console.error(`Error in element subscriber for ${i.name}:`,e)}return i}unregister(e,t){e instanceof NodeList?e.forEach(e=>this.unregisterElement(e,t)):this.unregisterElement(e,t)}unregisterElement(e,t){let n=this.elementEntries.get(e);if(!n)return;this.clearReactivateTimeout(n),this.currentlyActiveHandler?.unobserveElement(e),n.state.isActive&&this.activeElementCount--,n.state.isParked&&this.parkedElementCount--;let r=this.updateElementState(n,{isRegistered:!1,isActive:!1,isParked:!1,isPredicted:!1,isCallbackRunning:!1});this.elementEntries.delete(e),n.subscribers.clear();let i=this.elementEntries.size===0&&this.isSetup;i&&(this.devLog(`All elements unregistered, removing global listeners`),this.removeGlobalListeners()),this.eventEmitter.emit({type:`elementUnregistered`,element:e,state:r,timestamp:Date.now(),unregisterReason:t??`by user`,wasLastRegisteredElement:i})}reactivate(e){e instanceof NodeList?e.forEach(e=>this.reactivateElement(e)):this.reactivateElement(e)}reactivateElement(e){let t=this.elementEntries.get(e);!t||!t.state.isEnabled||(this.isSetup||this.initializeGlobalListeners(),this.clearReactivateTimeout(t),!(t.state.isCallbackRunning||t.state.isActive)&&(this.updateElementState(t,{isActive:!0,isPredicted:!1}),this.activeElementCount++,this.currentlyActiveHandler?.observeElement(e)))}setElementEnabled(e,t,n){if(e.state.isEnabled===n)return;let r=n&&!e.state.isLimitedConnection&&!e.state.isParked;r?(this.isSetup||this.initializeGlobalListeners(),this.activeElementCount++,this.currentlyActiveHandler?.observeElement(t)):(this.clearReactivateTimeout(e),this.currentlyActiveHandler?.unobserveElement(t),e.state.isActive&&this.activeElementCount--),this.updateElementState(e,{isEnabled:n,isActive:r,isPredicted:!1,isCallbackRunning:!1}),this.removeGlobalListenersIfIdle()}clearReactivateTimeout(e){clearTimeout(e.reactivateTimeoutId),e.reactivateTimeoutId=void 0}callCallback(e,t){e.state.isPredicted||!e.state.isActive||(this.markElementAsRunning(e),this.executeCallbackAsync(e,t))}markElementAsRunning(e){this.clearReactivateTimeout(e),e.invokedAt=Date.now(),this.updateElementState(e,{isPredicted:!0,isCallbackRunning:!0,hitCount:e.state.hitCount+1})}async executeCallbackAsync(e,t){this.updateHitCounters(t),this.eventEmitter.emit({type:`callbackInvoked`,timestamp:Date.now(),element:e.element,state:e.state,hitType:t});let n=performance.now(),r=null;try{await e.callback(e.state)}catch(t){r=t instanceof Error?t.message:String(t),console.error(`Error in callback for element ${e.state.name}:`,t)}let i=r===null?`success`:`error`;this.finalizeCallback(e,t,n,i,r)}finalizeCallback(e,t,n,r,i){let a=performance.now()-n;e.state.isActive&&this.activeElementCount--,this.currentlyActiveHandler?.unobserveElement(e.element),e.completedAt=Date.now();let o=this.updateElementState(e,{isCallbackRunning:!1,isActive:!1,durationMs:a,status:r,error:i});o.reactivateAfter!==1/0&&(e.reactivateTimeoutId=setTimeout(()=>{this.reactivate(e.element)},o.reactivateAfter));let s=this.activeElementCount===0;this.removeGlobalListenersIfIdle(),this.eventEmitter.emit({type:`callbackCompleted`,timestamp:Date.now(),element:e.element,state:o,hitType:t,elapsed:a,status:r,errorMessage:i,wasLastActiveElement:s})}updateHitCounters(e){switch(e.kind){case`mouse`:this._globalCallbackHits.mouse[e.subType]++;break;case`tab`:this._globalCallbackHits.tab[e.subType]++;break;case`scroll`:this._globalCallbackHits.scroll[e.subType]++;break;case`touch`:this._globalCallbackHits.touch++;break;case`viewport`:this._globalCallbackHits.viewport++;break;default:}this._globalCallbackHits.total++}async setDeviceStrategy(e){let t=this.currentDeviceStrategy;t!==e&&this.devLog(`Switching device strategy from ${t} to ${e}`),this.currentlyActiveHandler?.disconnect(),this.currentlyActiveHandler=e===`mouse`||e===`pen`?await this.getOrCreateDesktopHandler():await this.getOrCreateTouchHandler(),this.currentlyActiveHandler.connect()}initializeGlobalListeners(){this.isSetup||typeof document>`u`||(this.devLog(`Initializing global listeners (pointermove, MutationObserver)`),this.setDeviceStrategy(this.currentDeviceStrategy),document.addEventListener(`pointermove`,this.handlePointerMove),this.domObserver=new MutationObserver(this.handleDomMutations),this.domObserver.observe(document.documentElement,{childList:!0,subtree:!0,attributes:!1}),this.isSetup=!0)}removeGlobalListeners(){typeof document>`u`||(this.isSetup=!1,this.domObserver?.disconnect(),this.domObserver=null,document.removeEventListener(`pointermove`,this.handlePointerMove),this.currentlyActiveHandler?.disconnect(),this.rafId&&=(cancelAnimationFrame(this.rafId),null),this.pendingPointerEvent=null)}parkDisconnected(e){this.clearReactivateTimeout(e),this.currentlyActiveHandler?.unobserveElement(e.element),e.state.isActive&&this.activeElementCount--,this.parkedElementCount++,this.updateElementState(e,{isActive:!1,isCallbackRunning:!1,isParked:!0})}resumeReconnected(e){this.parkedElementCount--;let t=e.state.isEnabled&&!e.state.isLimitedConnection,n=t&&!e.state.isPredicted;n&&(this.isSetup||this.initializeGlobalListeners(),this.activeElementCount++,this.currentlyActiveHandler?.observeElement(e.element)),this.updateElementState(e,{isActive:n,isParked:!1}),t&&e.state.isPredicted&&e.state.reactivateAfter!==1/0&&(e.reactivateTimeoutId=setTimeout(()=>{this.reactivate(e.element)},e.state.reactivateAfter))}removeGlobalListenersIfIdle(){this.activeElementCount>0||this.parkedElementCount>0||this.removeGlobalListeners()}alterGlobalSettings(e){let t=x(this._globalSettings,e);t.positionHistorySizeChanged&&this.desktopHandler&&this.desktopHandler.trajectoryPositions.positions.resize(this._globalSettings.positionHistorySize),t.scrollPredictionChanged&&this.isUsingDesktopHandler&&this.desktopHandler&&(this._globalSettings.enableScrollPrediction?this.desktopHandler.connectScrollPredictor():this.desktopHandler.disconnectScrollPredictor()),t.tabPredictionChanged&&this.isUsingDesktopHandler&&this.desktopHandler&&(this._globalSettings.enableTabPrediction?this.desktopHandler.connectTabPredictor():this.desktopHandler.disconnectTabPredictor()),t.hitSlopChanged&&this.forceUpdateAllElementBounds(),t.touchStrategyChanged&&!this.isUsingDesktopHandler&&this.touchDeviceHandler&&this.touchDeviceHandler.setTouchPredictor(),t.changedSettings.length>0&&this.eventEmitter.emit({type:`managerSettingsChanged`,timestamp:Date.now(),managerData:this.getManagerData,updatedSettings:t.changedSettings})}forceUpdateAllElementBounds(){for(let e of this.elementEntries.values())e.state.isIntersectingWithViewport&&this.forceUpdateElementBounds(e)}forceUpdateElementBounds(e){let t=e.element.getBoundingClientRect(),r=n(t,e.state.elementBounds.hitSlop);i(r,e.state.elementBounds.expandedRect)||this.updateElementState(e,{elementBounds:{hitSlop:e.state.elementBounds.hitSlop,originalRect:t,expandedRect:r}})}devLog(e){this._globalSettings.enableManagerLogging&&console.log(`%c🛠️ ForesightManager: ${e}`,`color: #16a34a; font-weight: bold;`)}};export{S as ForesightManager,m as createUnregisteredSnapshot};
2
+ //# sourceMappingURL=index.mjs.map