js.foresight 3.0.1 → 3.1.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 +120 -5
- package/dist/{src/types/types.d.ts → index.d.mts} +156 -67
- package/dist/index.d.ts +61 -47
- package/dist/index.js +2 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -12
- package/dist/Manager/ForesightManager.d.ts +0 -89
- package/dist/Manager/ForesightManager.d.ts.map +0 -1
- package/dist/Manager/ForesightManager.js +0 -639
- package/dist/Manager/ForesightManager.js.map +0 -1
- package/dist/Manager/constants.d.ts +0 -23
- package/dist/Manager/constants.d.ts.map +0 -1
- package/dist/Manager/constants.js +0 -24
- package/dist/Manager/constants.js.map +0 -1
- package/dist/Manager/helpers/clampNumber.d.ts +0 -2
- package/dist/Manager/helpers/clampNumber.d.ts.map +0 -1
- package/dist/Manager/helpers/clampNumber.js +0 -10
- package/dist/Manager/helpers/clampNumber.js.map +0 -1
- package/dist/Manager/helpers/getFocusedElementIndex.d.ts +0 -15
- package/dist/Manager/helpers/getFocusedElementIndex.d.ts.map +0 -1
- package/dist/Manager/helpers/getFocusedElementIndex.js +0 -27
- package/dist/Manager/helpers/getFocusedElementIndex.js.map +0 -1
- package/dist/Manager/helpers/getScrollDirection.d.ts +0 -3
- package/dist/Manager/helpers/getScrollDirection.d.ts.map +0 -1
- package/dist/Manager/helpers/getScrollDirection.js +0 -21
- package/dist/Manager/helpers/getScrollDirection.js.map +0 -1
- package/dist/Manager/helpers/lineSigmentIntersectsRect.d.ts +0 -12
- package/dist/Manager/helpers/lineSigmentIntersectsRect.d.ts.map +0 -1
- package/dist/Manager/helpers/lineSigmentIntersectsRect.js +0 -52
- package/dist/Manager/helpers/lineSigmentIntersectsRect.js.map +0 -1
- package/dist/Manager/helpers/predictNextMousePosition.d.ts +0 -19
- package/dist/Manager/helpers/predictNextMousePosition.d.ts.map +0 -1
- package/dist/Manager/helpers/predictNextMousePosition.js +0 -43
- package/dist/Manager/helpers/predictNextMousePosition.js.map +0 -1
- package/dist/Manager/helpers/predictNextScrollPosition.d.ts +0 -6
- package/dist/Manager/helpers/predictNextScrollPosition.d.ts.map +0 -1
- package/dist/Manager/helpers/predictNextScrollPosition.js +0 -20
- package/dist/Manager/helpers/predictNextScrollPosition.js.map +0 -1
- package/dist/Manager/helpers/rectAndHitSlop.d.ts +0 -33
- package/dist/Manager/helpers/rectAndHitSlop.d.ts.map +0 -1
- package/dist/Manager/helpers/rectAndHitSlop.js +0 -66
- package/dist/Manager/helpers/rectAndHitSlop.js.map +0 -1
- package/dist/Manager/helpers/shouldUpdateSetting.d.ts +0 -11
- package/dist/Manager/helpers/shouldUpdateSetting.d.ts.map +0 -1
- package/dist/Manager/helpers/shouldUpdateSetting.js +0 -16
- package/dist/Manager/helpers/shouldUpdateSetting.js.map +0 -1
- package/dist/helpers/shouldRegister.d.ts +0 -8
- package/dist/helpers/shouldRegister.d.ts.map +0 -1
- package/dist/helpers/shouldRegister.js +0 -31
- package/dist/helpers/shouldRegister.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/src/Manager/ForesightManager.d.ts +0 -89
- package/dist/src/Manager/ForesightManager.d.ts.map +0 -1
- package/dist/src/Manager/ForesightManager.test.d.ts +0 -2
- package/dist/src/Manager/ForesightManager.test.d.ts.map +0 -1
- package/dist/src/Manager/constants.d.ts +0 -23
- package/dist/src/Manager/constants.d.ts.map +0 -1
- package/dist/src/Manager/helpers/clampNumber.d.ts +0 -2
- package/dist/src/Manager/helpers/clampNumber.d.ts.map +0 -1
- package/dist/src/Manager/helpers/clampNumber.test.d.ts +0 -2
- package/dist/src/Manager/helpers/clampNumber.test.d.ts.map +0 -1
- package/dist/src/Manager/helpers/getFocusedElementIndex.d.ts +0 -15
- package/dist/src/Manager/helpers/getFocusedElementIndex.d.ts.map +0 -1
- package/dist/src/Manager/helpers/getFocusedElementIndex.test.d.ts +0 -2
- package/dist/src/Manager/helpers/getFocusedElementIndex.test.d.ts.map +0 -1
- package/dist/src/Manager/helpers/getScrollDirection.d.ts +0 -3
- package/dist/src/Manager/helpers/getScrollDirection.d.ts.map +0 -1
- package/dist/src/Manager/helpers/lineSegmentIntersectsRect.test.d.ts +0 -2
- package/dist/src/Manager/helpers/lineSegmentIntersectsRect.test.d.ts.map +0 -1
- package/dist/src/Manager/helpers/lineSigmentIntersectsRect.d.ts +0 -12
- package/dist/src/Manager/helpers/lineSigmentIntersectsRect.d.ts.map +0 -1
- package/dist/src/Manager/helpers/predictNextMousePosition.d.ts +0 -19
- package/dist/src/Manager/helpers/predictNextMousePosition.d.ts.map +0 -1
- package/dist/src/Manager/helpers/predictNextMousePosition.test.d.ts +0 -2
- package/dist/src/Manager/helpers/predictNextMousePosition.test.d.ts.map +0 -1
- package/dist/src/Manager/helpers/predictNextScrollPosition.d.ts +0 -6
- package/dist/src/Manager/helpers/predictNextScrollPosition.d.ts.map +0 -1
- package/dist/src/Manager/helpers/rectAndHitSlop.d.ts +0 -33
- package/dist/src/Manager/helpers/rectAndHitSlop.d.ts.map +0 -1
- package/dist/src/Manager/helpers/rectAndHitSlop.test.d.ts +0 -2
- package/dist/src/Manager/helpers/rectAndHitSlop.test.d.ts.map +0 -1
- package/dist/src/Manager/helpers/shouldUpdateSetting.d.ts +0 -11
- package/dist/src/Manager/helpers/shouldUpdateSetting.d.ts.map +0 -1
- package/dist/src/helpers/shouldRegister.d.ts +0 -8
- package/dist/src/helpers/shouldRegister.d.ts.map +0 -1
- package/dist/src/index.d.ts +0 -3
- package/dist/src/index.d.ts.map +0 -1
- package/dist/src/types/types.d.ts.map +0 -1
- package/dist/test-setup.d.ts +0 -17
- package/dist/test-setup.d.ts.map +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/dist/types/types.d.ts +0 -312
- package/dist/types/types.d.ts.map +0 -1
- package/dist/types/types.js +0 -2
- package/dist/types/types.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,12 +1,34 @@
|
|
|
1
|
-
#
|
|
1
|
+
# [ForesightJS](https://foresightjs.com/)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/js.foresight)
|
|
4
|
+
[](https://www.npmjs.com/package/js.foresight)
|
|
5
|
+
[](https://bundlephobia.com/package/js.foresight)
|
|
6
|
+
[](https://github.com/spaansba/ForesightJS)
|
|
7
|
+
[](https://github.com/spaansba/ForesightJS/commits)
|
|
4
8
|
|
|
5
|
-
|
|
9
|
+
[](http://www.typescriptlang.org/)
|
|
10
|
+
[](https://opensource.org/licenses/MIT)
|
|
11
|
+
[](https://foresightjs.com#playground)
|
|
6
12
|
|
|
7
|
-
|
|
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).
|
|
8
14
|
|
|
9
|
-
|
|
15
|
+
### Understanding ForesightJS's Role:
|
|
16
|
+
|
|
17
|
+
When you over simplify prefetching it exists of three parts.
|
|
18
|
+
|
|
19
|
+
- **What** resource or data to load
|
|
20
|
+
- **How** the loading method and caching strategy is
|
|
21
|
+
- **When** the optimal moment to start fetching is
|
|
22
|
+
|
|
23
|
+
ForesightJS takes care of the **When** by predicting user intent with mouse trajectory and tab navigation.
|
|
24
|
+
You supply the **What** and **How** inside your `callback` when you register an element.
|
|
25
|
+
|
|
26
|
+
### [Playground](https://foresightjs.com/)
|
|
27
|
+
|
|
28
|
+

|
|
29
|
+
_In the GIF above, the [ForesightJS DevTools](https://foresightjs.com/docs/getting_started/development_tools) are enabled. Normally, users won't see anything that ForesightJS does except the increased perceived speed from early prefetching._
|
|
30
|
+
|
|
31
|
+
## Download
|
|
10
32
|
|
|
11
33
|
```bash
|
|
12
34
|
pnpm add js.foresight
|
|
@@ -15,3 +37,96 @@ npm install js.foresight
|
|
|
15
37
|
# or
|
|
16
38
|
yarn add js.foresight
|
|
17
39
|
```
|
|
40
|
+
|
|
41
|
+
## Which problems does ForesightJS solve?
|
|
42
|
+
|
|
43
|
+
### Problem 1: On-Hover Prefetching Still Has Latency
|
|
44
|
+
|
|
45
|
+
Traditional hover-based prefetching only triggers after the user's cursor reaches an element. This approach wastes the critical 100-200ms window between when a user begins moving toward a target and when the hover event actually fires.
|
|
46
|
+
|
|
47
|
+
### Problem 2: Viewport-Based Prefetching is Wasteful
|
|
48
|
+
|
|
49
|
+
Many modern frameworks (like Next.js) automatically prefetch resources for all links that enter the viewport. While well-intentioned, this creates significant overhead since users typically interact with only a small fraction of visible elements. Simply scrolling up and down the Next.js homepage can trigger **_1.59MB_** of unnecessary prefetch requests.
|
|
50
|
+
|
|
51
|
+
### Problem 3: Hover-Based Prefetching Excludes Keyboard Users
|
|
52
|
+
|
|
53
|
+
Many routers rely on hover-based prefetching, but this approach completely excludes keyboard users since keyboard navigation never triggers hover events. This means keyboard users miss out on the performance benefits that mouse users get from hover-based prefetching.
|
|
54
|
+
|
|
55
|
+
### The ForesightJS Solution
|
|
56
|
+
|
|
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
|
+
|
|
59
|
+
## Basic Usage Example
|
|
60
|
+
|
|
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
|
+
|
|
63
|
+
```javascript
|
|
64
|
+
import { ForesightManager } from "foresightjs"
|
|
65
|
+
|
|
66
|
+
// Initialize the manager if you want custom global settings (do this once at app startup)
|
|
67
|
+
ForesightManager.initialize({
|
|
68
|
+
// Optional props (see configuration)
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
// Register an element to be tracked
|
|
72
|
+
const myButton = document.getElementById("my-button")
|
|
73
|
+
|
|
74
|
+
const { isTouchDevice, unregister } = ForesightManager.instance.register({
|
|
75
|
+
element: myButton,
|
|
76
|
+
callback: () => {
|
|
77
|
+
// This is where your prefetching logic goes
|
|
78
|
+
},
|
|
79
|
+
hitSlop: 20, // Optional: "hit slop" in pixels. Overwrites defaultHitSlop
|
|
80
|
+
// other optional props (see configuration)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
// Later, when done with this element:
|
|
84
|
+
unregister()
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Integrations
|
|
88
|
+
|
|
89
|
+
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!
|
|
90
|
+
|
|
91
|
+
## Configuration
|
|
92
|
+
|
|
93
|
+
ForesightJS can be used bare-bones but also can be configured. For all configuration possibilities you can reference the [docs](https://foresightjs.com/docs/getting_started/config).
|
|
94
|
+
|
|
95
|
+
## Development Tools
|
|
96
|
+
|
|
97
|
+
ForesightJS has dedicated [Development Tools](https://github.com/spaansba/ForesightJS-DevTools) created with [Foresight Events](https://foresightjs.com/docs/getting_started/events) that help you understand and tune how foresight is working in your application. This standalone development package provides real-time visualization of mouse trajectory predictions, element bounds, and callback execution. It's particularly helpful when setting up ForesightJS for the first time or when fine-tuning for specific UI components.
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
npm install js.foresight-devtools
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
See the [development tools documentation](https://foresightjs.com/docs/getting_started/debug) for more details.
|
|
104
|
+
|
|
105
|
+
## What About Touch Devices and Slow Connections?
|
|
106
|
+
|
|
107
|
+
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.
|
|
108
|
+
|
|
109
|
+
The `ForesightManager.instance.register()` method returns these properties:
|
|
110
|
+
|
|
111
|
+
- `isTouchDevice` - true if user is on a touch device
|
|
112
|
+
- `isLimitedConnection` - true when user is on a 2G connection or has data-saver enabled
|
|
113
|
+
- `isRegistered` - true if element was actually registered
|
|
114
|
+
|
|
115
|
+
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.
|
|
116
|
+
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.
|
|
117
|
+
|
|
118
|
+
## How Does ForesightJS Work?
|
|
119
|
+
|
|
120
|
+
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)**.
|
|
121
|
+
|
|
122
|
+
## Providing Context to AI Tools
|
|
123
|
+
|
|
124
|
+
ForesightJS is a newer library, so most AI assistants and LLMs may not have much built-in knowledge about it. To improve their responses, you can provide the following context:
|
|
125
|
+
|
|
126
|
+
- Use [llms.txt](https://foresightjs.com/llms.txt) for a concise overview of the API and usage patterns.
|
|
127
|
+
- Use [llms-full.txt](https://foresightjs.com/llms-full.txt) for a full markdown version of the docs, ideal for AI tools that support context injection or uploads.
|
|
128
|
+
- All documentation pages are also available in markdown. You can view them by adding .md to the end of any URL, for example: https://foresightjs.com/docs/getting_started.md.
|
|
129
|
+
|
|
130
|
+
# Contributing
|
|
131
|
+
|
|
132
|
+
Please see the [contributing guidelines](/CONTRIBUTING.md)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
type Rect = {
|
|
2
2
|
top: number;
|
|
3
3
|
left: number;
|
|
4
4
|
right: number;
|
|
@@ -8,27 +8,27 @@ export type Rect = {
|
|
|
8
8
|
* A callback function that is executed when a foresight interaction
|
|
9
9
|
* (e.g., hover, trajectory hit) occurs on a registered element.
|
|
10
10
|
*/
|
|
11
|
-
|
|
11
|
+
type ForesightCallback = () => void;
|
|
12
12
|
/**
|
|
13
13
|
* Represents the HTML element that is being tracked by the ForesightManager.
|
|
14
14
|
* This is typically a standard DOM `Element`.
|
|
15
15
|
*/
|
|
16
|
-
|
|
16
|
+
type ForesightElement = Element;
|
|
17
17
|
/**
|
|
18
18
|
* Represents a mouse position captured at a specific point in time.
|
|
19
19
|
* Used for tracking mouse movement history.
|
|
20
20
|
*/
|
|
21
|
-
|
|
21
|
+
type MousePosition = {
|
|
22
22
|
/** The (x, y) coordinates of the mouse. */
|
|
23
23
|
point: Point;
|
|
24
24
|
/** The timestamp (e.g., from `performance.now()`) when this position was recorded. */
|
|
25
25
|
time: number;
|
|
26
26
|
};
|
|
27
|
-
|
|
27
|
+
type Point = {
|
|
28
28
|
x: number;
|
|
29
29
|
y: number;
|
|
30
30
|
};
|
|
31
|
-
|
|
31
|
+
type TrajectoryPositions = {
|
|
32
32
|
positions: MousePosition[];
|
|
33
33
|
currentPoint: Point;
|
|
34
34
|
predictedPoint: Point;
|
|
@@ -37,18 +37,18 @@ export type TrajectoryPositions = {
|
|
|
37
37
|
* Internal type representing the calculated boundaries of a foresight element,
|
|
38
38
|
* including its original dimensions and the expanded hit area.
|
|
39
39
|
*/
|
|
40
|
-
|
|
40
|
+
type ElementBounds = {
|
|
41
41
|
/** The expanded rectangle, including hitSlop, used for interaction detection. */
|
|
42
|
-
expandedRect: Rect
|
|
42
|
+
expandedRect: Readonly<Rect>;
|
|
43
43
|
/** The original bounding rectangle of the element, as returned by `getBoundingClientRect()`. */
|
|
44
|
-
originalRect
|
|
44
|
+
originalRect: DOMRectReadOnly;
|
|
45
45
|
/** The hit slop values applied to this element. */
|
|
46
46
|
hitSlop: Exclude<HitSlop, number>;
|
|
47
47
|
};
|
|
48
48
|
/**
|
|
49
49
|
* Represents trajectory hit related data for a foresight element.
|
|
50
50
|
*/
|
|
51
|
-
|
|
51
|
+
type TrajectoryHitData = {
|
|
52
52
|
/** True if the predicted mouse trajectory has intersected the element's expanded bounds. */
|
|
53
53
|
isTrajectoryHit: boolean;
|
|
54
54
|
/** The timestamp when the last trajectory hit occurred. */
|
|
@@ -56,7 +56,7 @@ export type TrajectoryHitData = {
|
|
|
56
56
|
/** Timeout ID for expiring the trajectory hit state. */
|
|
57
57
|
trajectoryHitExpirationTimeoutId?: ReturnType<typeof setTimeout>;
|
|
58
58
|
};
|
|
59
|
-
|
|
59
|
+
type ForesightRegisterResult = {
|
|
60
60
|
/** 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 */
|
|
61
61
|
isTouchDevice: boolean;
|
|
62
62
|
/** Whether the user has connection limitations (slow network (2g) or data saver enabled) that should prevent prefetching */
|
|
@@ -69,7 +69,7 @@ export type ForesightRegisterResult = {
|
|
|
69
69
|
/**
|
|
70
70
|
* Represents the data associated with a registered foresight element.
|
|
71
71
|
*/
|
|
72
|
-
|
|
72
|
+
type ForesightElementData = Required<Pick<ForesightRegisterOptions, "callback" | "name">> & {
|
|
73
73
|
/** The boundary information for the element. */
|
|
74
74
|
elementBounds: ElementBounds;
|
|
75
75
|
/** True if the mouse cursor is currently hovering over the element's expanded bounds. */
|
|
@@ -80,30 +80,39 @@ export type ForesightElementData = Required<Pick<ForesightRegisterOptions, "call
|
|
|
80
80
|
trajectoryHitData: TrajectoryHitData;
|
|
81
81
|
/**
|
|
82
82
|
* Is the element intersecting with the viewport, usefull to track which element we should observe or not
|
|
83
|
+
* Can be @undefined in the split second the element is registering
|
|
83
84
|
*/
|
|
84
85
|
isIntersectingWithViewport: boolean;
|
|
85
86
|
/**
|
|
86
87
|
* The element you registered
|
|
87
88
|
*/
|
|
88
89
|
element: ForesightElement;
|
|
90
|
+
/**
|
|
91
|
+
* If the element is currently running its callback
|
|
92
|
+
*/
|
|
93
|
+
isRunningCallback: boolean;
|
|
94
|
+
/**
|
|
95
|
+
* For debugging, check if you are registering the same element multiple times.
|
|
96
|
+
*/
|
|
97
|
+
registerCount: number;
|
|
89
98
|
};
|
|
90
|
-
|
|
99
|
+
type MouseCallbackCounts = {
|
|
91
100
|
hover: number;
|
|
92
101
|
trajectory: number;
|
|
93
102
|
};
|
|
94
|
-
|
|
103
|
+
type TabCallbackCounts = {
|
|
95
104
|
reverse: number;
|
|
96
105
|
forwards: number;
|
|
97
106
|
};
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
107
|
+
type ScrollDirection = "down" | "up" | "left" | "right" | "none";
|
|
108
|
+
type ScrollCallbackCounts = Record<`${Exclude<ScrollDirection, "none">}`, number>;
|
|
109
|
+
type CallbackHits = {
|
|
101
110
|
total: number;
|
|
102
111
|
mouse: MouseCallbackCounts;
|
|
103
112
|
tab: TabCallbackCounts;
|
|
104
113
|
scroll: ScrollCallbackCounts;
|
|
105
114
|
};
|
|
106
|
-
|
|
115
|
+
type CallbackHitType = {
|
|
107
116
|
kind: "mouse";
|
|
108
117
|
subType: keyof MouseCallbackCounts;
|
|
109
118
|
} | {
|
|
@@ -116,10 +125,11 @@ export type HitType = {
|
|
|
116
125
|
/**
|
|
117
126
|
* Snapshot of the current ForesightManager state
|
|
118
127
|
*/
|
|
119
|
-
|
|
128
|
+
type ForesightManagerData = {
|
|
120
129
|
registeredElements: ReadonlyMap<ForesightElement, ForesightElementData>;
|
|
121
130
|
globalSettings: Readonly<ForesightManagerSettings>;
|
|
122
131
|
globalCallbackHits: Readonly<CallbackHits>;
|
|
132
|
+
eventListeners: ReadonlyMap<keyof ForesightEventMap, ForesightEventListener[]>;
|
|
123
133
|
};
|
|
124
134
|
type BaseForesightManagerSettings = {
|
|
125
135
|
/**
|
|
@@ -187,33 +197,25 @@ type BaseForesightManagerSettings = {
|
|
|
187
197
|
* @default 2
|
|
188
198
|
*/
|
|
189
199
|
tabOffset: number;
|
|
190
|
-
/**
|
|
191
|
-
* A global callback that runs whenever a callback is fired for any
|
|
192
|
-
* registered element, just after the element's specific callback is fired.
|
|
193
|
-
*
|
|
194
|
-
* @param elementData - The ForesightTarget object for the element that triggered the event.
|
|
195
|
-
* @param managerData - Data about the ForesightManager
|
|
196
|
-
*/
|
|
197
|
-
onAnyCallbackFired: (elementData: ForesightElementData, managerData: ForesightManagerData) => void;
|
|
198
200
|
};
|
|
199
201
|
/**
|
|
200
202
|
* Configuration options for the ForesightManager
|
|
201
203
|
* @link https://foresightjs.com/docs/getting_started/config#available-global-settings
|
|
202
204
|
*/
|
|
203
|
-
|
|
205
|
+
type ForesightManagerSettings = BaseForesightManagerSettings & {
|
|
204
206
|
defaultHitSlop: Exclude<HitSlop, number>;
|
|
205
207
|
};
|
|
206
208
|
/**
|
|
207
209
|
* Update options for the ForesightManager
|
|
208
210
|
* @link https://foresightjs.com/docs/getting_started/config#available-global-settings
|
|
209
211
|
*/
|
|
210
|
-
|
|
212
|
+
type UpdateForsightManagerSettings = BaseForesightManagerSettings & {
|
|
211
213
|
defaultHitSlop: HitSlop;
|
|
212
214
|
};
|
|
213
215
|
/**
|
|
214
216
|
* Type used to register elements to the foresight manager
|
|
215
217
|
*/
|
|
216
|
-
|
|
218
|
+
type ForesightRegisterOptions = {
|
|
217
219
|
element: ForesightElement;
|
|
218
220
|
callback: ForesightCallback;
|
|
219
221
|
hitSlop?: HitSlop;
|
|
@@ -230,43 +232,28 @@ export type ForesightRegisterOptions = {
|
|
|
230
232
|
*
|
|
231
233
|
* @link https://foresightjs.com/docs/getting_started/typescript#foresightregisteroptionswithoutelement
|
|
232
234
|
*/
|
|
233
|
-
|
|
235
|
+
type ForesightRegisterOptionsWithoutElement = Omit<ForesightRegisterOptions, "element">;
|
|
234
236
|
/**
|
|
235
237
|
* Fully invisible "slop" around the element.
|
|
236
238
|
* Basically increases the hover hitbox
|
|
237
239
|
*/
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
* Get all keys in UpdateForsightManagerSettings that are numeric
|
|
241
|
-
*/
|
|
242
|
-
export type NumericSettingKeys = {
|
|
243
|
-
[K in keyof UpdateForsightManagerSettings]: UpdateForsightManagerSettings[K] extends number ? K : never;
|
|
244
|
-
}[keyof UpdateForsightManagerSettings];
|
|
245
|
-
/**
|
|
246
|
-
* Get all keys in UpdateForsightManagerSettings that are boolean
|
|
247
|
-
*/
|
|
248
|
-
export type ManagerBooleanSettingKeys = {
|
|
249
|
-
[K in keyof UpdateForsightManagerSettings]: UpdateForsightManagerSettings[K] extends boolean ? K : never;
|
|
250
|
-
}[keyof UpdateForsightManagerSettings];
|
|
251
|
-
export interface ForesightEventMap {
|
|
240
|
+
type HitSlop = Rect | number;
|
|
241
|
+
interface ForesightEventMap {
|
|
252
242
|
elementRegistered: ElementRegisteredEvent;
|
|
253
243
|
elementUnregistered: ElementUnregisteredEvent;
|
|
254
244
|
elementDataUpdated: ElementDataUpdatedEvent;
|
|
255
|
-
|
|
245
|
+
callbackInvoked: CallbackInvokedEvent;
|
|
246
|
+
callbackCompleted: CallbackCompletedEvent;
|
|
256
247
|
mouseTrajectoryUpdate: MouseTrajectoryUpdateEvent;
|
|
257
248
|
scrollTrajectoryUpdate: ScrollTrajectoryUpdateEvent;
|
|
258
249
|
managerSettingsChanged: ManagerSettingsChangedEvent;
|
|
259
250
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
type: ForesightEventType;
|
|
263
|
-
timestamp: number;
|
|
264
|
-
}
|
|
265
|
-
export interface ElementRegisteredEvent extends ForesightEvent {
|
|
251
|
+
type ForesightEvent = "elementRegistered" | "elementUnregistered" | "elementDataUpdated" | "callbackInvoked" | "callbackCompleted" | "mouseTrajectoryUpdate" | "scrollTrajectoryUpdate" | "managerSettingsChanged";
|
|
252
|
+
interface ElementRegisteredEvent extends ForesightBaseEvent {
|
|
266
253
|
type: "elementRegistered";
|
|
267
254
|
elementData: ForesightElementData;
|
|
268
255
|
}
|
|
269
|
-
|
|
256
|
+
interface ElementUnregisteredEvent extends ForesightBaseEvent {
|
|
270
257
|
type: "elementUnregistered";
|
|
271
258
|
elementData: ForesightElementData;
|
|
272
259
|
unregisterReason: ElementUnregisteredReason;
|
|
@@ -277,33 +264,135 @@ export interface ElementUnregisteredEvent extends ForesightEvent {
|
|
|
277
264
|
* - `disconnected`: The element was automatically unregistered because it was removed from the DOM.
|
|
278
265
|
* - `apiCall`: The developer manually called the `unregister()` function for the element.
|
|
279
266
|
*/
|
|
280
|
-
|
|
281
|
-
|
|
267
|
+
type ElementUnregisteredReason = "callbackHit" | "disconnected" | "apiCall";
|
|
268
|
+
interface ElementDataUpdatedEvent extends Omit<ForesightBaseEvent, "timestamp"> {
|
|
282
269
|
type: "elementDataUpdated";
|
|
283
270
|
elementData: ForesightElementData;
|
|
284
|
-
|
|
271
|
+
updatedProps: UpdatedDataPropertyNames[];
|
|
285
272
|
}
|
|
286
|
-
|
|
287
|
-
|
|
273
|
+
type UpdatedDataPropertyNames = "bounds" | "visibility";
|
|
274
|
+
interface CallbackInvokedEvent extends ForesightBaseEvent {
|
|
275
|
+
type: "callbackInvoked";
|
|
288
276
|
elementData: ForesightElementData;
|
|
289
|
-
hitType:
|
|
290
|
-
|
|
277
|
+
hitType: CallbackHitType;
|
|
278
|
+
}
|
|
279
|
+
interface CallbackCompletedEventBase extends ForesightBaseEvent {
|
|
280
|
+
type: "callbackCompleted";
|
|
281
|
+
elementData: ForesightElementData;
|
|
282
|
+
hitType: CallbackHitType;
|
|
283
|
+
elapsed: number;
|
|
291
284
|
}
|
|
292
|
-
|
|
285
|
+
type CallbackCompletedEvent = CallbackCompletedEventBase & ({
|
|
286
|
+
status: "success";
|
|
287
|
+
} | {
|
|
288
|
+
status: "error";
|
|
289
|
+
errorMessage: string;
|
|
290
|
+
});
|
|
291
|
+
interface MouseTrajectoryUpdateEvent extends Omit<ForesightBaseEvent, "timestamp"> {
|
|
293
292
|
type: "mouseTrajectoryUpdate";
|
|
294
293
|
trajectoryPositions: TrajectoryPositions;
|
|
295
294
|
predictionEnabled: boolean;
|
|
296
295
|
}
|
|
297
|
-
|
|
296
|
+
interface ScrollTrajectoryUpdateEvent extends Omit<ForesightBaseEvent, "timestamp"> {
|
|
298
297
|
type: "scrollTrajectoryUpdate";
|
|
299
298
|
currentPoint: Point;
|
|
300
299
|
predictedPoint: Point;
|
|
300
|
+
scrollDirection: ScrollDirection;
|
|
301
301
|
}
|
|
302
|
-
|
|
302
|
+
interface ManagerSettingsChangedEvent extends ForesightBaseEvent {
|
|
303
303
|
type: "managerSettingsChanged";
|
|
304
304
|
managerData: ForesightManagerData;
|
|
305
|
+
updatedSettings: UpdatedManagerSetting[];
|
|
306
|
+
}
|
|
307
|
+
type UpdatedManagerSetting = {
|
|
308
|
+
[K in keyof ForesightManagerSettings]: {
|
|
309
|
+
setting: K;
|
|
310
|
+
newValue: ForesightManagerSettings[K];
|
|
311
|
+
oldValue: ForesightManagerSettings[K];
|
|
312
|
+
};
|
|
313
|
+
}[keyof ForesightManagerSettings];
|
|
314
|
+
type ForesightEventListener<K extends ForesightEvent = ForesightEvent> = (event: ForesightEventMap[K]) => void;
|
|
315
|
+
interface ForesightBaseEvent {
|
|
316
|
+
type: ForesightEvent;
|
|
317
|
+
timestamp: number;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Manages the prediction of user intent based on mouse trajectory and element interactions.
|
|
322
|
+
*
|
|
323
|
+
* ForesightManager is a singleton class responsible for:
|
|
324
|
+
* - Registering HTML elements to monitor.
|
|
325
|
+
* - Tracking mouse movements and predicting future cursor positions.
|
|
326
|
+
* - Detecting when a predicted trajectory intersects with a registered element's bounds.
|
|
327
|
+
* - Invoking callbacks associated with elements upon predicted or actual interaction.
|
|
328
|
+
* - Optionally unregistering elements after their callback is triggered.
|
|
329
|
+
* - Handling global settings for prediction behavior (e.g., history size, prediction time).
|
|
330
|
+
* - Automatically updating element bounds on resize using {@link ResizeObserver}.
|
|
331
|
+
* - Automatically unregistering elements removed from the DOM using {@link MutationObserver}.
|
|
332
|
+
* - Detecting broader layout shifts via {@link MutationObserver} to update element positions.
|
|
333
|
+
*
|
|
334
|
+
* It should be initialized once using {@link ForesightManager.initialize} and then
|
|
335
|
+
* accessed via the static getter {@link ForesightManager.instance}.
|
|
336
|
+
*/
|
|
337
|
+
declare class ForesightManager {
|
|
338
|
+
private static manager;
|
|
339
|
+
private elements;
|
|
340
|
+
private isSetup;
|
|
341
|
+
private _globalCallbackHits;
|
|
342
|
+
private _globalSettings;
|
|
343
|
+
private trajectoryPositions;
|
|
344
|
+
private tabbableElementsCache;
|
|
345
|
+
private lastFocusedIndex;
|
|
346
|
+
private predictedScrollPoint;
|
|
347
|
+
private scrollDirection;
|
|
348
|
+
private domObserver;
|
|
349
|
+
private positionObserver;
|
|
350
|
+
private lastKeyDown;
|
|
351
|
+
private globalListenersController;
|
|
352
|
+
private rafId;
|
|
353
|
+
private pendingMouseEvent;
|
|
354
|
+
private eventListeners;
|
|
355
|
+
private constructor();
|
|
356
|
+
static initialize(props?: Partial<UpdateForsightManagerSettings>): ForesightManager;
|
|
357
|
+
addEventListener<K extends ForesightEvent>(eventType: K, listener: ForesightEventListener<K>, options?: {
|
|
358
|
+
signal?: AbortSignal;
|
|
359
|
+
}): (() => void) | undefined;
|
|
360
|
+
removeEventListener<K extends ForesightEvent>(eventType: K, listener: ForesightEventListener<K>): void;
|
|
361
|
+
private emit;
|
|
362
|
+
get getManagerData(): Readonly<ForesightManagerData>;
|
|
363
|
+
static get isInitiated(): Readonly<boolean>;
|
|
364
|
+
static get instance(): ForesightManager;
|
|
365
|
+
get registeredElements(): ReadonlyMap<ForesightElement, ForesightElementData>;
|
|
366
|
+
register({ element, callback, hitSlop, name, }: ForesightRegisterOptions): ForesightRegisterResult;
|
|
367
|
+
private unregister;
|
|
368
|
+
private updateNumericSettings;
|
|
369
|
+
private updateBooleanSetting;
|
|
370
|
+
alterGlobalSettings(props?: Partial<UpdateForsightManagerSettings>): void;
|
|
371
|
+
private forceUpdateAllElementBounds;
|
|
372
|
+
private updatePointerState;
|
|
373
|
+
private handleMouseMove;
|
|
374
|
+
private processMouseMovement;
|
|
375
|
+
/**
|
|
376
|
+
* Detects when registered elements are removed from the DOM and automatically unregisters them to prevent stale references.
|
|
377
|
+
*
|
|
378
|
+
* @param mutationsList - Array of MutationRecord objects describing the DOM changes
|
|
379
|
+
*
|
|
380
|
+
*/
|
|
381
|
+
private handleDomMutations;
|
|
382
|
+
private handleKeyDown;
|
|
383
|
+
private handleFocusIn;
|
|
384
|
+
private updateHitCounters;
|
|
385
|
+
private callCallback;
|
|
386
|
+
/**
|
|
387
|
+
* 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.
|
|
388
|
+
* We need an observer for that
|
|
389
|
+
*/
|
|
390
|
+
private forceUpdateElementBounds;
|
|
391
|
+
private handlePositionChange;
|
|
392
|
+
private handlePositionChangeDataUpdates;
|
|
393
|
+
private handleScrollPrefetch;
|
|
394
|
+
private initializeGlobalListeners;
|
|
395
|
+
private removeGlobalListeners;
|
|
305
396
|
}
|
|
306
|
-
|
|
307
|
-
export type
|
|
308
|
-
export {};
|
|
309
|
-
//# sourceMappingURL=types.d.ts.map
|
|
397
|
+
|
|
398
|
+
export { type CallbackCompletedEvent, type CallbackHitType, type CallbackHits, type CallbackInvokedEvent, type ElementDataUpdatedEvent, type ElementRegisteredEvent, type ElementUnregisteredEvent, type ForesightElement, type ForesightElementData, type ForesightEvent, ForesightManager, type ForesightManagerSettings, type Rect as ForesightRect, type ForesightRegisterOptions, type ForesightRegisterOptionsWithoutElement, type ForesightRegisterResult, type HitSlop, type ManagerSettingsChangedEvent, type MouseTrajectoryUpdateEvent, type ScrollTrajectoryUpdateEvent, type UpdateForsightManagerSettings, type UpdatedManagerSetting };
|