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 +17 -10
- package/dist/index.d.ts +92 -6
- package/dist/index.js +2 -18
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -18
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
[](https://opensource.org/licenses/MIT)
|
|
11
11
|
[](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
|
-
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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:
|
|
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;
|