js.foresight 2.1.5 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,7 +10,7 @@
10
10
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
11
11
  [![Demo](https://img.shields.io/badge/demo-live-blue)](https://foresightjs.com/)
12
12
 
13
- ForesightJS is a lightweight JavaScript library with full TypeScript support that predicts user intent based on mouse movements and keyboard navigation. By analyzing cursor trajectory and tab sequences, it anticipates which elements a user is likely to interact with, allowing developers to trigger actions before the actual hover or click occurs (for example prefetching).
13
+ ForesightJS is a lightweight JavaScript library with full TypeScript support that predicts user intent based on mouse movements, scroll and keyboard navigation. By analyzing cursor/scroll trajectory and tab sequences, it anticipates which elements a user is likely to interact with, allowing developers to trigger actions before the actual hover or click occurs (for example prefetching).
14
14
 
15
15
  ### Understanding ForesightJS's Role:
16
16
 
@@ -54,11 +54,11 @@ Many routers rely on hover-based prefetching, but this approach completely exclu
54
54
 
55
55
  ### The ForesightJS Solution
56
56
 
57
- ForesightJS bridges the gap between wasteful viewport prefetching and basic hover prefetching. The `ForesightManager` predicts user interactions by analyzing mouse trajectory patterns and keyboard navigation sequences. This allows you to prefetch resources at the optimal time to improve performance, but targeted enough to avoid waste.
57
+ ForesightJS bridges the gap between wasteful viewport prefetching and basic hover prefetching. The `ForesightManager` predicts user interactions by analyzing mouse trajectory patterns, scroll direction and keyboard navigation sequences. This allows you to prefetch resources at the optimal time to improve performance, but targeted enough to avoid waste.
58
58
 
59
59
  ## Basic Usage Example
60
60
 
61
- Both global and element speicif configuration details can be found [here](https://foresightjs.com/docs/getting_started/config).
61
+ This basic example is in vanilla JS, ofcourse most people will use ForesightJS with a framework. You can read about framework integrations in the [docs](https://foresightjs.com/docs/integrations).
62
62
 
63
63
  ```javascript
64
64
  import { ForesightManager } from "foresightjs"
@@ -85,15 +85,9 @@ const { isTouchDevice, unregister } = ForesightManager.instance.register({
85
85
  unregister()
86
86
  ```
87
87
 
88
- ## What About Touch Devices?
89
-
90
- ForesightJS focuses on using mouse movement for prefetching, so you'll need your own approach for touch devices like phones and tablets. The `ForesightManager.instance.register()` method returns an `isTouchDevice` boolean that you can use to create this separate logic. You can safely call `register()` even on touch devices, as the Foresight manager will bounce touch devices to avoid unnecessary processing.
91
-
92
- An example of what to do with touch devices can be found in the [Next.js](https://foresightjs.com/docs/integrations/nextjs) or [React Router](https://foresightjs.com/docs/integrations/react) ForesightLink components.
93
-
94
88
  ## Integrations
95
89
 
96
- Since ForesightJS is framework agnostic, it can be integrated with any JavaScript framework. While I haven't yet built integrations for every framework, ready-to-use implementations for [Next.js](https://foresightjs.com/docs/integrations/nextjs) and [React Router](https://foresightjs.com/docs/integrations/react) are already available. Sharing integrations for other frameworks/packages is highly appreciated!
90
+ Since ForesightJS is framework agnostic, it can be integrated with any JavaScript framework. While I haven't yet built [integrations](https://foresightjs.com/docs/integrations) for every framework, ready-to-use implementations for [Next.js](https://foresightjs.com/docs/integrations/react/nextjs) and [React Router](https://foresightjs.com/docs/integrations/react/react-router) are already available. Sharing integrations for other frameworks/packages is highly appreciated!
97
91
 
98
92
  ## Configuration
99
93
 
@@ -103,6 +97,19 @@ ForesightJS can be used bare-bones but also can be configured. For all configura
103
97
 
104
98
  ForesightJS includes a [Visual Debugging](https://foresightjs.com/docs/getting_started/debug) system that helps you understand and tune how foresight is working in your application. This is particularly helpful when setting up ForesightJS for the first time or when fine-tuning for specific UI components.
105
99
 
100
+ ## What About Touch Devices and Slow Connections?
101
+
102
+ Since ForesightJS relies on the keyboard/mouse it will not register elements for touch devices. For limited connections (2G or data-saver mode), we respect the user's preference to minimize data usage and skip registration aswell.
103
+
104
+ The `ForesightManager.instance.register()` method returns these properties:
105
+
106
+ - `isTouchDevice` - true if user is on a touch device
107
+ - `isLimitedConnection` - true when user is on a 2G connection or has data-saver enabled
108
+ - `isRegistered` - true if element was actually registered
109
+
110
+ With these properties you could create your own fallback prefetching methods if required. For example if the user is on a touch device you could prefetch based on viewport.
111
+ An example of this can be found in the [Next.js](https://foresightjs.com/docs/integrations/react/nextjs) or [React Router](https://foresightjs.com/docs/integrations/react/react-router) ForesightLink components.
112
+
106
113
  ## How Does ForesightJS Work?
107
114
 
108
115
  For a detailed technical explanation of its prediction algorithms and internal architecture, see the **[Behind the Scenes documentation](https://foresightjs.com/docs/Behind_the_Scenes)**.
package/dist/index.d.ts CHANGED
@@ -22,16 +22,45 @@ type ElementBounds = {
22
22
  /** The expanded rectangle, including hitSlop, used for interaction detection. */
23
23
  expandedRect: Rect;
24
24
  /** The original bounding rectangle of the element, as returned by `getBoundingClientRect()`. */
25
- originalRect?: DOMRectReadOnly;
25
+ originalRect?: DOMRectReadOnly | undefined;
26
26
  /** The hit slop values applied to this element. */
27
27
  hitSlop: Exclude<HitSlop, number>;
28
28
  };
29
29
  type DebuggerSettings = {
30
- /** If the control panel should be minimized on default @default false */
30
+ /**
31
+ * Determines if the debugger control panel should be initialized in a minimized state.
32
+ *
33
+ * @link https://foresightjs.com/docs/getting_started/debug
34
+ *
35
+ * @default false
36
+ */
31
37
  isControlPanelDefaultMinimized?: boolean;
32
- /** If we should show the name tags above elements @default false */
38
+ /**
39
+ * Determines if name tags should be displayed visually above each registered element.
40
+ * This is a helpful visual aid for identifying which elements are being tracked.
41
+ *
42
+ * @link https://foresightjs.com/docs/getting_started/debug
43
+ *
44
+ * @default false
45
+ */
33
46
  showNameTags?: boolean;
47
+ /**
48
+ * Specifies the default sorting order for the list of registered elements in the debugger panel.
49
+ * - `'visibility'`: Sorts elements by their viewport visibility (visible elements first),
50
+ * with a secondary documentOrder sort.
51
+ * - `'documentOrder'`: Sorts elements based on their order of appearance in the
52
+ * document's structure (matching the HTML source).
53
+ * - `'insertionOrder'`: Sorts by registration order.
54
+ *
55
+ *
56
+ * @link https://foresightjs.com/docs/getting_started/debug
57
+ *
58
+ * @default 'visibility'
59
+ *
60
+ */
61
+ sortElementList?: SortElementList;
34
62
  };
63
+ type SortElementList = "documentOrder" | "visibility" | "insertionOrder";
35
64
  /**
36
65
  * Represents trajectory hit related data for a foresight element.
37
66
  */
@@ -46,7 +75,11 @@ type TrajectoryHitData = {
46
75
  type ForesightRegisterResult = {
47
76
  /** Whether the current device is a touch device. This is important as ForesightJS only works based on cursor movement. If the user is using a touch device you should handle prefetching differently */
48
77
  isTouchDevice: boolean;
49
- /** Function to unregister the element, optional to add the elementdata */
78
+ /** Whether the user has connection limitations (slow network (2g) or data saver enabled) that should prevent prefetching */
79
+ isLimitedConnection: boolean;
80
+ /** Whether ForesightJS will actively track this element. False if touch device or limited connection, true otherwise */
81
+ isRegistered: boolean;
82
+ /** Function to unregister the element */
50
83
  unregister: () => void;
51
84
  };
52
85
  /**
@@ -82,22 +115,31 @@ type TabCallbackCounts = {
82
115
  reverse: number;
83
116
  forwards: number;
84
117
  };
118
+ type ScrollDirection = "down" | "up" | "left" | "right" | "none";
119
+ type ScrollCallbackCounts = Record<`${Exclude<ScrollDirection, "none">}`, number>;
85
120
  type CallbackHits = {
86
121
  total: number;
87
122
  mouse: MouseCallbackCounts;
88
123
  tab: TabCallbackCounts;
124
+ scroll: ScrollCallbackCounts;
89
125
  };
126
+ /**
127
+ * Snapshot of the current ForesightManager state
128
+ */
90
129
  type ForesightManagerData = {
91
- registeredElements: Map<ForesightElement, ForesightElementData>;
130
+ registeredElements: ReadonlyMap<ForesightElement, ForesightElementData>;
92
131
  globalSettings: Readonly<ForesightManagerSettings>;
93
132
  globalCallbackHits: Readonly<CallbackHits>;
94
- positionObserverElements: Map<Element, IntersectionObserverEntry> | undefined;
95
133
  };
96
134
  type BaseForesightManagerSettings = {
97
135
  /**
98
136
  * Number of mouse positions to keep in history for trajectory calculation.
99
137
  * A higher number might lead to smoother but slightly delayed predictions.
100
138
  *
139
+ *
140
+ * @link https://foresightjs.com/docs/getting_started/config#available-global-settings
141
+ *
142
+ *
101
143
  * **This value is clamped between 2 and 30.**
102
144
  * @default 8
103
145
  */
@@ -106,6 +148,8 @@ type BaseForesightManagerSettings = {
106
148
  * How far ahead (in milliseconds) to predict the mouse trajectory.
107
149
  * A larger value means the prediction extends further into the future. (meaning it will trigger callbacks sooner)
108
150
  *
151
+ * @link https://foresightjs.com/docs/getting_started/config#available-global-settings
152
+ *
109
153
  * **This value is clamped between 10 and 200.**
110
154
  * @default 120
111
155
  */
@@ -113,14 +157,32 @@ type BaseForesightManagerSettings = {
113
157
  /**
114
158
  * Whether to enable mouse trajectory prediction.
115
159
  * If false, only direct hover/interaction is considered.
160
+ * @link https://foresightjs.com/docs/getting_started/config#available-global-settings
116
161
  * @default true
117
162
  */
118
163
  enableMousePrediction: boolean;
119
164
  /**
120
165
  * Toggles whether keyboard prediction is on
166
+ *
167
+ * @link https://foresightjs.com/docs/getting_started/config#available-global-settings
121
168
  * @default true
122
169
  */
123
170
  enableTabPrediction: boolean;
171
+ /**
172
+ * Sets the pixel distance to check from the mouse position in the scroll direction.
173
+ *
174
+ * @link https://foresightjs.com/docs/getting_started/config#available-global-settings
175
+ *
176
+ * **This value is clamped between 30 and 300.**
177
+ * @default 150
178
+ */
179
+ scrollMargin: number;
180
+ /**
181
+ * Toggles whether scroll prediction is on
182
+ * @link https://foresightjs.com/docs/getting_started/config#available-global-settings
183
+ * @default true
184
+ */
185
+ enableScrollPrediction: boolean;
124
186
  /**
125
187
  * Tab stops away from an element to trigger callback. Only works when @argument enableTabPrediction is true
126
188
  *
@@ -151,13 +213,21 @@ type BaseForesightManagerSettings = {
151
213
  };
152
214
  /**
153
215
  * Configuration options for the ForesightManager
216
+ * @link https://foresightjs.com/docs/getting_started/config#available-global-settings
154
217
  */
155
218
  type ForesightManagerSettings = BaseForesightManagerSettings & {
156
219
  defaultHitSlop: Exclude<HitSlop, number>;
157
220
  };
221
+ /**
222
+ * Update options for the ForesightManager
223
+ * @link https://foresightjs.com/docs/getting_started/config#available-global-settings
224
+ */
158
225
  type UpdateForsightManagerSettings = BaseForesightManagerSettings & {
159
226
  defaultHitSlop: HitSlop;
160
227
  };
228
+ /**
229
+ * Type used to register elements to the foresight manager
230
+ */
161
231
  type ForesightRegisterOptions = {
162
232
  element: ForesightElement;
163
233
  callback: ForesightCallback;
@@ -165,7 +235,18 @@ type ForesightRegisterOptions = {
165
235
  unregisterOnCallback?: boolean;
166
236
  name?: string;
167
237
  };
238
+ /**
239
+ * Usefull for if you want to create a custom button component in a modern framework (for example React).
240
+ * And you want to have the ForesightRegisterOptions used in ForesightManager.instance.register({})
241
+ * without the element as the element will be the ref of the component.
242
+ *
243
+ * @link https://foresightjs.com/docs/getting_started/typescript#foresightregisteroptionswithoutelement
244
+ */
168
245
  type ForesightRegisterOptionsWithoutElement = Omit<ForesightRegisterOptions, "element">;
246
+ /**
247
+ * Fully invisible "slop" around the element.
248
+ * Basically increases the hover hitbox
249
+ */
169
250
  type HitSlop = Rect | number;
170
251
 
171
252
  /**
@@ -194,6 +275,10 @@ declare class ForesightManager {
194
275
  private _globalCallbackHits;
195
276
  private _globalSettings;
196
277
  private trajectoryPositions;
278
+ private tabbableElementsCache;
279
+ private lastFocusedIndex;
280
+ private predictedScrollPoint;
281
+ private scrollDirection;
197
282
  private domObserver;
198
283
  private positionObserver;
199
284
  private lastKeyDown;
@@ -256,6 +341,7 @@ declare class ForesightManager {
256
341
  */
257
342
  private forceUpdateElementBounds;
258
343
  private updateElementBounds;
344
+ private handleScrollPrefetch;
259
345
  private handlePositionChange;
260
346
  private initializeGlobalListeners;
261
347
  private removeGlobalListeners;