Skip to content

Commit 885743b

Browse files
committed
removed unneeded import
1 parent 767c2e8 commit 885743b

File tree

4 files changed

+102
-38
lines changed

4 files changed

+102
-38
lines changed

dist/ForesightManager/ForesightDebugger.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export class ForesightDebugger {
3333
transition: opacity 0.2s ease, border-color 0.2s ease, background-color 0.2s ease, box-shadow 0.2s ease;
3434
}
3535
.jsforesight-link-overlay.active {
36-
border-color: red; background-color: rgba(255, 0, 0, 0.1);
36+
border-color: orange; background-color: rgba(255, 0, 0, 0.1);
3737
}
3838
.jsforesight-link-overlay.trajectory-hit {
3939
border-color: lime; background-color: rgba(0, 255, 0, 0.3);

dist/ForesightManager/ForesightManager.d.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { ForesightCallback, ForesightManagerProps, ForesightElement, Rect } from "../types/types";
22
export declare class ForesightManager {
3-
private static instance;
3+
private static manager;
44
private links;
55
private isSetup;
66
private debugMode;
@@ -13,7 +13,7 @@ export declare class ForesightManager {
1313
private resizeScrollThrottleTimeoutId;
1414
private constructor();
1515
static initialize(props?: Partial<ForesightManagerProps>): ForesightManager;
16-
static getInstance(): ForesightManager;
16+
static get instance(): ForesightManager;
1717
private checkTrajectoryHitExpiration;
1818
private normalizeHitSlop;
1919
register(element: ForesightElement, callback: ForesightCallback, hitSlop?: number | Rect): () => void;
@@ -24,7 +24,15 @@ export declare class ForesightManager {
2424
private updateExpandedRect;
2525
private updateAllRects;
2626
private predictMousePosition;
27-
private pointIntersectsRect;
27+
/**
28+
* Checks if a line segment intersects with an axis-aligned rectangle.
29+
* Uses the Liang-Barsky line clipping algorithm.
30+
* @param p1 Start point of the line segment.
31+
* @param p2 End point of the line segment.
32+
* @param rect The rectangle to check against.
33+
* @returns True if the line segment intersects the rectangle, false otherwise.
34+
*/
35+
private lineSegmentIntersectsRect;
2836
private isMouseInExpandedArea;
2937
private handleMouseMove;
3038
private handleResizeOrScroll;

dist/ForesightManager/ForesightManager.js

Lines changed: 88 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
"use client";
22
import { ForesightDebugger } from "./ForesightDebugger";
33
export class ForesightManager {
4-
static instance;
4+
static manager;
55
links = new Map();
66
isSetup = false;
77
debugMode = false; // Synced with globalSettings.debug
88
debugger = null;
99
globalSettings = {
1010
debug: false,
1111
enableMouseTrajectory: true,
12-
positionHistorySize: 6,
13-
trajectoryPredictionTime: 50,
12+
positionHistorySize: 8,
13+
trajectoryPredictionTime: 80,
1414
defaultHitSlop: { top: 0, left: 0, right: 0, bottom: 0 },
1515
resizeScrollThrottleDelay: 50,
1616
};
@@ -20,44 +20,40 @@ export class ForesightManager {
2020
lastResizeScrollCallTimestamp = 0;
2121
resizeScrollThrottleTimeoutId = null;
2222
constructor() {
23-
// Ensure defaultHitSlop is normalized if it's a number initially
2423
this.globalSettings.defaultHitSlop = this.normalizeHitSlop(this.globalSettings.defaultHitSlop);
2524
setInterval(this.checkTrajectoryHitExpiration.bind(this), 100);
2625
}
2726
static initialize(props) {
28-
if (!ForesightManager.instance) {
29-
ForesightManager.instance = new ForesightManager();
30-
// Apply initial props, which also handles initial debugger setup
27+
if (!ForesightManager.manager) {
28+
ForesightManager.manager = new ForesightManager();
3129
if (props) {
32-
ForesightManager.instance.alterGlobalSettings(props);
30+
ForesightManager.manager.alterGlobalSettings(props);
3331
}
3432
else {
35-
// If no props, but default globalSettings.debug is true, ensure debugger is on
36-
if (ForesightManager.instance.globalSettings.debug) {
37-
ForesightManager.instance.turnOnDebugMode();
33+
if (ForesightManager.manager.globalSettings.debug) {
34+
ForesightManager.manager.turnOnDebugMode();
3835
}
3936
}
4037
}
4138
else if (props) {
42-
// Instance exists, apply new props (handles debugger lifecycle and UI updates)
43-
ForesightManager.instance.alterGlobalSettings(props);
39+
console.error("ForesightManager is already initialized. Use alterGlobalSettings to update settings. Make sure to not put the ForesightManager.initialize() in a place that rerenders often.");
40+
ForesightManager.manager.alterGlobalSettings(props);
4441
}
45-
// Ensure internal debugMode flag is synced
46-
ForesightManager.instance.debugMode = ForesightManager.instance.globalSettings.debug;
47-
return ForesightManager.instance;
42+
ForesightManager.manager.debugMode = ForesightManager.manager.globalSettings.debug;
43+
return ForesightManager.manager;
4844
}
49-
static getInstance() {
50-
if (!ForesightManager.instance) {
51-
return this.initialize(); // Initialize with defaults
45+
static get instance() {
46+
if (!ForesightManager.manager) {
47+
return this.initialize();
5248
}
53-
return ForesightManager.instance;
49+
return ForesightManager.manager;
5450
}
5551
checkTrajectoryHitExpiration() {
5652
const now = performance.now();
5753
let needsVisualUpdate = false;
5854
const updatedForesightElements = [];
5955
this.links.forEach((elementData, element) => {
60-
if (elementData.isTrajectoryHit && now - elementData.trajectoryHitTime > 300) {
56+
if (elementData.isTrajectoryHit && now - elementData.trajectoryHitTime > 100) {
6157
this.links.set(element, {
6258
...elementData,
6359
isTrajectoryHit: false,
@@ -89,7 +85,7 @@ export class ForesightManager {
8985
register(element, callback, hitSlop) {
9086
const normalizedHitSlop = hitSlop
9187
? this.normalizeHitSlop(hitSlop)
92-
: this.globalSettings.defaultHitSlop; // Already normalized in constructor/alterGlobalSettings
88+
: this.globalSettings.defaultHitSlop; // Already normalized in constructor
9389
const originalRect = element.getBoundingClientRect();
9490
const newElementData = {
9591
callback,
@@ -145,7 +141,6 @@ export class ForesightManager {
145141
if (JSON.stringify(this.globalSettings.defaultHitSlop) !== JSON.stringify(newSlop)) {
146142
this.globalSettings.defaultHitSlop = newSlop;
147143
settingsActuallyChanged = true;
148-
// Note: This won't update existing elements' hitSlop unless they are re-registered.
149144
}
150145
}
151146
if (props?.resizeScrollThrottleDelay !== undefined &&
@@ -172,14 +167,12 @@ export class ForesightManager {
172167
}
173168
}
174169
turnOnDebugMode() {
175-
this.debugMode = true; // Ensure this is true when method is called
170+
this.debugMode = true;
176171
if (!this.debugger) {
177172
this.debugger = new ForesightDebugger(this);
178173
this.debugger.initialize(this.links, this.globalSettings, this.currentPoint, this.predictedPoint);
179174
}
180175
else {
181-
// If debugger exists, ensure its controls are up-to-date with current globalSettings
182-
// This could happen if debug was false, then true again, or settings changed.
183176
this.debugger.updateControlsState(this.globalSettings);
184177
this.debugger.updateTrajectoryVisuals(this.currentPoint, this.predictedPoint, this.globalSettings.enableMouseTrajectory);
185178
}
@@ -243,9 +236,67 @@ export class ForesightManager {
243236
const predictedY = y + vy * trajectoryPredictionTimeInSeconds;
244237
return { x: predictedX, y: predictedY };
245238
};
246-
pointIntersectsRect = (x, y, rect) => {
247-
return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom;
248-
};
239+
/**
240+
* Checks if a line segment intersects with an axis-aligned rectangle.
241+
* Uses the Liang-Barsky line clipping algorithm.
242+
* @param p1 Start point of the line segment.
243+
* @param p2 End point of the line segment.
244+
* @param rect The rectangle to check against.
245+
* @returns True if the line segment intersects the rectangle, false otherwise.
246+
*/
247+
lineSegmentIntersectsRect(p1, p2, rect) {
248+
let t0 = 0.0;
249+
let t1 = 1.0;
250+
const dx = p2.x - p1.x;
251+
const dy = p2.y - p1.y;
252+
// Helper function for Liang-Barsky algorithm
253+
// p: parameter related to edge normal and line direction
254+
// q: parameter related to distance from p1 to edge
255+
const clipTest = (p, q) => {
256+
if (p === 0) {
257+
// Line is parallel to the clip edge
258+
if (q < 0) {
259+
// Line is outside the clip edge (p1 is on the "wrong" side)
260+
return false;
261+
}
262+
}
263+
else {
264+
const r = q / p;
265+
if (p < 0) {
266+
// Line proceeds from outside to inside (potential entry)
267+
if (r > t1)
268+
return false; // Enters after already exited
269+
if (r > t0)
270+
t0 = r; // Update latest entry time
271+
}
272+
else {
273+
// Line proceeds from inside to outside (potential exit) (p > 0)
274+
if (r < t0)
275+
return false; // Exits before already entered
276+
if (r < t1)
277+
t1 = r; // Update earliest exit time
278+
}
279+
}
280+
return true;
281+
};
282+
// Left edge: rect.left
283+
if (!clipTest(-dx, p1.x - rect.left))
284+
return false;
285+
// Right edge: rect.right
286+
if (!clipTest(dx, rect.right - p1.x))
287+
return false;
288+
// Top edge: rect.top
289+
if (!clipTest(-dy, p1.y - rect.top))
290+
return false;
291+
// Bottom edge: rect.bottom
292+
if (!clipTest(dy, rect.bottom - p1.y))
293+
return false;
294+
// If t0 > t1, the segment is completely outside or misses the clip window.
295+
// Also, the valid intersection must be within the segment [0,1].
296+
// Since t0 and t1 are initialized to 0 and 1, and clamped,
297+
// this also ensures the intersection lies on the segment.
298+
return t0 <= t1;
299+
}
249300
isMouseInExpandedArea = (area, clientPoint, isAlreadyHovering) => {
250301
const isInExpandedArea = clientPoint.x >= area.left &&
251302
clientPoint.x <= area.right &&
@@ -268,24 +319,29 @@ export class ForesightManager {
268319
const { isHoveringInArea, shouldRunCallback } = this.isMouseInExpandedArea(elementData.elementBounds.expandedRect, this.currentPoint, elementData.isHovering);
269320
let linkStateChanged = false;
270321
if (this.globalSettings.enableMouseTrajectory && !isHoveringInArea) {
271-
if (this.pointIntersectsRect(this.predictedPoint.x, this.predictedPoint.y, elementData.elementBounds.expandedRect)) {
322+
// Check if the trajectory line segment intersects the expanded rect
323+
if (this.lineSegmentIntersectsRect(this.currentPoint, this.predictedPoint, elementData.elementBounds.expandedRect)) {
272324
if (!elementData.isTrajectoryHit) {
273325
elementData.callback();
274326
this.links.set(element, {
275327
...elementData,
276328
isTrajectoryHit: true,
277329
trajectoryHitTime: performance.now(),
278-
isHovering: isHoveringInArea,
330+
isHovering: isHoveringInArea, // isHoveringInArea is false here
279331
});
280332
linkStateChanged = true;
281333
}
282334
}
335+
// Note: No 'else' here to turn off isTrajectoryHit immediately.
336+
// It's managed by checkTrajectoryHitExpiration or when actual hover occurs.
283337
}
284338
if (elementData.isHovering !== isHoveringInArea) {
285339
this.links.set(element, {
286340
...elementData,
287341
isHovering: isHoveringInArea,
288-
// Preserve trajectory hit state if it was already hit
342+
// Preserve trajectory hit state if it was already hit,
343+
// unless actual hover is now false and trajectory also doesn't hit
344+
// (though trajectory hit is primarily for non-hovering states)
289345
isTrajectoryHit: this.links.get(element).isTrajectoryHit,
290346
trajectoryHitTime: this.links.get(element).trajectoryHitTime,
291347
});

dist/types/types.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,13 @@ export type ForesightManagerProps = {
6868
/**
6969
* Number of mouse positions to keep in history for trajectory calculation.
7070
* A higher number might lead to smoother but slightly delayed predictions.
71-
* @default 6
71+
* @default 8
7272
*/
7373
positionHistorySize: number;
7474
/**
7575
* How far ahead (in milliseconds) to predict the mouse trajectory.
7676
* A larger value means the prediction extends further into the future. (meaning it will trigger callbacks sooner)
77-
* @default 50
77+
* @default 80
7878
*/
7979
trajectoryPredictionTime: number;
8080
/**

0 commit comments

Comments
 (0)