@croct/sdk 0.21.1 → 0.22.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/constants.cjs CHANGED
@@ -25,7 +25,7 @@ __export(constants_exports, {
25
25
  module.exports = __toCommonJS(constants_exports);
26
26
  const BASE_ENDPOINT_URL = "https://api.croct.io";
27
27
  const MAX_QUERY_LENGTH = parseInt("<@maxQueryLength@>", 10);
28
- const VERSION = "0.21.1";
28
+ const VERSION = "0.22.1";
29
29
  const CLIENT_LIBRARY = `Croct SDK JS v${VERSION}`;
30
30
  // Annotate the CommonJS export names for ESM import in node:
31
31
  0 && (module.exports = {
package/constants.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  declare const BASE_ENDPOINT_URL = "https://api.croct.io";
2
2
  declare const MAX_QUERY_LENGTH: number;
3
- declare const VERSION = "0.21.1";
4
- declare const CLIENT_LIBRARY = "Croct SDK JS v0.21.1";
3
+ declare const VERSION = "0.22.1";
4
+ declare const CLIENT_LIBRARY = "Croct SDK JS v0.22.1";
5
5
 
6
6
  export { BASE_ENDPOINT_URL, CLIENT_LIBRARY, MAX_QUERY_LENGTH, VERSION };
package/constants.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  declare const BASE_ENDPOINT_URL = "https://api.croct.io";
2
2
  declare const MAX_QUERY_LENGTH: number;
3
- declare const VERSION = "0.21.1";
4
- declare const CLIENT_LIBRARY = "Croct SDK JS v0.21.1";
3
+ declare const VERSION = "0.22.1";
4
+ declare const CLIENT_LIBRARY = "Croct SDK JS v0.22.1";
5
5
 
6
6
  export { BASE_ENDPOINT_URL, CLIENT_LIBRARY, MAX_QUERY_LENGTH, VERSION };
package/constants.js CHANGED
@@ -1,6 +1,6 @@
1
1
  const BASE_ENDPOINT_URL = "https://api.croct.io";
2
2
  const MAX_QUERY_LENGTH = parseInt("<@maxQueryLength@>", 10);
3
- const VERSION = "0.21.1";
3
+ const VERSION = "0.22.1";
4
4
  const CLIENT_LIBRARY = `Croct SDK JS v${VERSION}`;
5
5
  export {
6
6
  BASE_ENDPOINT_URL,
package/container.cjs CHANGED
@@ -47,6 +47,12 @@ const _Container = class _Container {
47
47
  getConfiguration() {
48
48
  return this.configuration;
49
49
  }
50
+ getTimeZone() {
51
+ if (this.timeZone === void 0) {
52
+ this.timeZone = _Container.detectTimeZone();
53
+ }
54
+ return this.timeZone;
55
+ }
50
56
  getEvaluator() {
51
57
  if (this.evaluator === void 0) {
52
58
  this.evaluator = this.createEvaluator();
@@ -98,7 +104,8 @@ const _Container = class _Container {
98
104
  inactivityRetryPolicy: new import_retry.ArbitraryPolicy([3e4, 3e4, 12e4, 12e4, 3e5, 3e5, 9e5]),
99
105
  logger: this.getLogger("Tracker"),
100
106
  channel: this.getBeaconChannel(),
101
- eventMetadata: this.configuration.eventMetadata
107
+ eventMetadata: this.configuration.eventMetadata,
108
+ timeZone: this.getTimeZone() ?? void 0
102
109
  });
103
110
  const queue = this.getBeaconQueue();
104
111
  queue.addCallback("halfEmpty", tracker.unsuspend);
@@ -297,6 +304,13 @@ const _Container = class _Container {
297
304
  delete this.removeTokenSyncListener;
298
305
  logger.debug("Container resources released.");
299
306
  }
307
+ static detectTimeZone() {
308
+ const { timeZone } = Intl.DateTimeFormat().resolvedOptions();
309
+ if (timeZone === void 0 || timeZone === "Etc/Unknown") {
310
+ return null;
311
+ }
312
+ return timeZone;
313
+ }
300
314
  };
301
315
  _Container.DEFAULT_FETCH_TIMEOUT = 5e3;
302
316
  let Container = _Container;
package/container.d.cts CHANGED
@@ -61,8 +61,10 @@ declare class Container {
61
61
  private beaconQueue?;
62
62
  private removeTokenSyncListener?;
63
63
  private readonly eventManager;
64
+ private timeZone?;
64
65
  constructor(configuration: Configuration);
65
66
  getConfiguration(): Configuration;
67
+ getTimeZone(): string | null;
66
68
  getEvaluator(): Evaluator;
67
69
  private createEvaluator;
68
70
  getContentFetcher(): ContentFetcher;
@@ -89,6 +91,7 @@ declare class Container {
89
91
  private getSessionStorage;
90
92
  getEventManager(): EventManager<SdkEventMap>;
91
93
  dispose(): Promise<void>;
94
+ private static detectTimeZone;
92
95
  }
93
96
 
94
97
  export { type Configuration, Container };
package/container.d.ts CHANGED
@@ -61,8 +61,10 @@ declare class Container {
61
61
  private beaconQueue?;
62
62
  private removeTokenSyncListener?;
63
63
  private readonly eventManager;
64
+ private timeZone?;
64
65
  constructor(configuration: Configuration);
65
66
  getConfiguration(): Configuration;
67
+ getTimeZone(): string | null;
66
68
  getEvaluator(): Evaluator;
67
69
  private createEvaluator;
68
70
  getContentFetcher(): ContentFetcher;
@@ -89,6 +91,7 @@ declare class Container {
89
91
  private getSessionStorage;
90
92
  getEventManager(): EventManager<SdkEventMap>;
91
93
  dispose(): Promise<void>;
94
+ private static detectTimeZone;
92
95
  }
93
96
 
94
97
  export { type Configuration, Container };
package/container.js CHANGED
@@ -25,6 +25,12 @@ const _Container = class _Container {
25
25
  getConfiguration() {
26
26
  return this.configuration;
27
27
  }
28
+ getTimeZone() {
29
+ if (this.timeZone === void 0) {
30
+ this.timeZone = _Container.detectTimeZone();
31
+ }
32
+ return this.timeZone;
33
+ }
28
34
  getEvaluator() {
29
35
  if (this.evaluator === void 0) {
30
36
  this.evaluator = this.createEvaluator();
@@ -76,7 +82,8 @@ const _Container = class _Container {
76
82
  inactivityRetryPolicy: new ArbitraryPolicy([3e4, 3e4, 12e4, 12e4, 3e5, 3e5, 9e5]),
77
83
  logger: this.getLogger("Tracker"),
78
84
  channel: this.getBeaconChannel(),
79
- eventMetadata: this.configuration.eventMetadata
85
+ eventMetadata: this.configuration.eventMetadata,
86
+ timeZone: this.getTimeZone() ?? void 0
80
87
  });
81
88
  const queue = this.getBeaconQueue();
82
89
  queue.addCallback("halfEmpty", tracker.unsuspend);
@@ -275,6 +282,13 @@ const _Container = class _Container {
275
282
  delete this.removeTokenSyncListener;
276
283
  logger.debug("Container resources released.");
277
284
  }
285
+ static detectTimeZone() {
286
+ const { timeZone } = Intl.DateTimeFormat().resolvedOptions();
287
+ if (timeZone === void 0 || timeZone === "Etc/Unknown") {
288
+ return null;
289
+ }
290
+ return timeZone;
291
+ }
278
292
  };
279
293
  _Container.DEFAULT_FETCH_TIMEOUT = 5e3;
280
294
  let Container = _Container;
@@ -60,8 +60,9 @@ class MinimalContextFactory {
60
60
  }
61
61
  }
62
62
  class TabContextFactory {
63
- constructor(tab) {
63
+ constructor(tab, timeZone) {
64
64
  this.tab = tab;
65
+ this.timeZone = timeZone;
65
66
  }
66
67
  createContext(attributes) {
67
68
  const url = new URL(this.tab.url);
@@ -75,9 +76,8 @@ class TabContextFactory {
75
76
  page.referrer = referrer;
76
77
  }
77
78
  context.page = page;
78
- const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone ?? null;
79
- if (timeZone !== null) {
80
- context.timeZone = timeZone;
79
+ if (this.timeZone !== void 0) {
80
+ context.timeZone = this.timeZone;
81
81
  }
82
82
  if (attributes !== void 0 && Object.keys(attributes).length > 0) {
83
83
  context.attributes = attributes;
@@ -34,7 +34,8 @@ declare class MinimalContextFactory implements ContextFactory {
34
34
  }
35
35
  declare class TabContextFactory implements ContextFactory {
36
36
  private readonly tab;
37
- constructor(tab: Tab);
37
+ private readonly timeZone?;
38
+ constructor(tab: Tab, timeZone?: string);
38
39
  createContext(attributes?: JsonObject): EvaluationContext;
39
40
  }
40
41
 
@@ -34,7 +34,8 @@ declare class MinimalContextFactory implements ContextFactory {
34
34
  }
35
35
  declare class TabContextFactory implements ContextFactory {
36
36
  private readonly tab;
37
- constructor(tab: Tab);
37
+ private readonly timeZone?;
38
+ constructor(tab: Tab, timeZone?: string);
38
39
  createContext(attributes?: JsonObject): EvaluationContext;
39
40
  }
40
41
 
@@ -36,8 +36,9 @@ class MinimalContextFactory {
36
36
  }
37
37
  }
38
38
  class TabContextFactory {
39
- constructor(tab) {
39
+ constructor(tab, timeZone) {
40
40
  this.tab = tab;
41
+ this.timeZone = timeZone;
41
42
  }
42
43
  createContext(attributes) {
43
44
  const url = new URL(this.tab.url);
@@ -51,9 +52,8 @@ class TabContextFactory {
51
52
  page.referrer = referrer;
52
53
  }
53
54
  context.page = page;
54
- const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone ?? null;
55
- if (timeZone !== null) {
56
- context.timeZone = timeZone;
55
+ if (this.timeZone !== void 0) {
56
+ context.timeZone = this.timeZone;
57
57
  }
58
58
  if (attributes !== void 0 && Object.keys(attributes).length > 0) {
59
59
  context.attributes = attributes;
@@ -132,7 +132,10 @@ class SdkFacade {
132
132
  if (this.evaluatorFacade === void 0) {
133
133
  this.evaluatorFacade = new import_evaluatorFacade.EvaluatorFacade({
134
134
  evaluator: this.sdk.evaluator,
135
- contextFactory: new import_evaluatorFacade.TabContextFactory(this.sdk.context.getTab()),
135
+ contextFactory: new import_evaluatorFacade.TabContextFactory(
136
+ this.sdk.context.getTab(),
137
+ this.sdk.timeZone ?? void 0
138
+ ),
136
139
  cidAssigner: this.sdk.cidAssigner,
137
140
  userTokenProvider: this.sdk.userTokenStore
138
141
  });
@@ -143,7 +146,10 @@ class SdkFacade {
143
146
  if (this.contentFetcherFacade === void 0) {
144
147
  this.contentFetcherFacade = new import_contentFetcherFacade.ContentFetcherFacade({
145
148
  contentFetcher: this.sdk.contentFetcher,
146
- contextFactory: new import_evaluatorFacade.TabContextFactory(this.sdk.context.getTab()),
149
+ contextFactory: new import_evaluatorFacade.TabContextFactory(
150
+ this.sdk.context.getTab(),
151
+ this.sdk.timeZone ?? void 0
152
+ ),
147
153
  cidAssigner: this.sdk.cidAssigner,
148
154
  previewTokenProvider: this.sdk.previewTokenStore,
149
155
  userTokenProvider: this.sdk.userTokenStore
@@ -110,7 +110,10 @@ class SdkFacade {
110
110
  if (this.evaluatorFacade === void 0) {
111
111
  this.evaluatorFacade = new EvaluatorFacade({
112
112
  evaluator: this.sdk.evaluator,
113
- contextFactory: new TabContextFactory(this.sdk.context.getTab()),
113
+ contextFactory: new TabContextFactory(
114
+ this.sdk.context.getTab(),
115
+ this.sdk.timeZone ?? void 0
116
+ ),
114
117
  cidAssigner: this.sdk.cidAssigner,
115
118
  userTokenProvider: this.sdk.userTokenStore
116
119
  });
@@ -121,7 +124,10 @@ class SdkFacade {
121
124
  if (this.contentFetcherFacade === void 0) {
122
125
  this.contentFetcherFacade = new ContentFetcherFacade({
123
126
  contentFetcher: this.sdk.contentFetcher,
124
- contextFactory: new TabContextFactory(this.sdk.context.getTab()),
127
+ contextFactory: new TabContextFactory(
128
+ this.sdk.context.getTab(),
129
+ this.sdk.timeZone ?? void 0
130
+ ),
125
131
  cidAssigner: this.sdk.cidAssigner,
126
132
  previewTokenProvider: this.sdk.previewTokenStore,
127
133
  userTokenProvider: this.sdk.userTokenStore
@@ -0,0 +1,181 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+ var interactionMonitor_exports = {};
19
+ __export(interactionMonitor_exports, {
20
+ InteractionMonitor: () => InteractionMonitor
21
+ });
22
+ module.exports = __toCommonJS(interactionMonitor_exports);
23
+ var import_eventManager = require('./eventManager.cjs');
24
+ class InteractionMonitor {
25
+ constructor(options = {}) {
26
+ this.eventManager = new import_eventManager.SynchronousEventManager();
27
+ this.lastClickTime = 0;
28
+ this.enabled = false;
29
+ this.clickThrottleInterval = options.clickThrottleInterval ?? 500;
30
+ this.scrollDebounceInterval = options.scrollDebounceInterval ?? 250;
31
+ this.handleClick = this.handleClick.bind(this);
32
+ this.handleScroll = this.handleScroll.bind(this);
33
+ }
34
+ addListener(type, listener) {
35
+ this.eventManager.addListener(type, listener);
36
+ }
37
+ removeListener(type, listener) {
38
+ this.eventManager.removeListener(type, listener);
39
+ }
40
+ isEnabled() {
41
+ return this.enabled;
42
+ }
43
+ enable() {
44
+ if (this.enabled) {
45
+ return;
46
+ }
47
+ this.enabled = true;
48
+ window.addEventListener("click", this.handleClick, true);
49
+ window.addEventListener("scroll", this.handleScroll, true);
50
+ }
51
+ disable() {
52
+ if (!this.enabled) {
53
+ return;
54
+ }
55
+ this.enabled = false;
56
+ window.removeEventListener("click", this.handleClick, true);
57
+ window.removeEventListener("scroll", this.handleScroll, true);
58
+ this.flushPendingScroll();
59
+ }
60
+ handleClick(event) {
61
+ const currentTime = Date.now();
62
+ if (currentTime - this.lastClickTime < this.clickThrottleInterval) {
63
+ return;
64
+ }
65
+ this.lastClickTime = currentTime;
66
+ this.eventManager.dispatch("userClicked", {
67
+ type: "userClicked",
68
+ point: {
69
+ x: Math.max(0, Math.round(event.pageX)),
70
+ y: Math.max(0, Math.round(event.pageY))
71
+ },
72
+ surfaceSize: {
73
+ width: document.documentElement.scrollWidth,
74
+ height: document.documentElement.scrollHeight
75
+ }
76
+ });
77
+ }
78
+ handleScroll() {
79
+ const currentPosition = {
80
+ x: Math.max(0, Math.round(window.scrollX)),
81
+ y: Math.max(0, Math.round(window.scrollY))
82
+ };
83
+ if (this.scrollState === void 0) {
84
+ this.scrollState = {
85
+ start: currentPosition,
86
+ lastPosition: currentPosition
87
+ };
88
+ } else if (this.hasDirectionChanged(this.scrollState, currentPosition)) {
89
+ const turningPoint = this.scrollState.lastPosition;
90
+ this.flushPendingScroll(turningPoint);
91
+ this.scrollState = {
92
+ start: turningPoint,
93
+ lastPosition: currentPosition
94
+ };
95
+ } else {
96
+ this.scrollState.lastPosition = currentPosition;
97
+ }
98
+ if (this.scrollDebounceTimer !== void 0) {
99
+ window.clearTimeout(this.scrollDebounceTimer);
100
+ }
101
+ this.scrollDebounceTimer = window.setTimeout(
102
+ () => this.flushPendingScroll(),
103
+ this.scrollDebounceInterval
104
+ );
105
+ }
106
+ /**
107
+ * Determines whether the scroll direction has reversed on either axis.
108
+ *
109
+ * It compares two vectors:
110
+ * - The scroll direction: the overall direction from start to the previous position (start -> previous)
111
+ * - The movement direction: the direction of the latest movement (previous -> current)
112
+ *
113
+ * A reversal is detected when these two vectors point in opposite directions on the same axis.
114
+ * Axes with no movement (sign = 0) are ignored, so scrolling that simply stops on one axis
115
+ * without reversing does not trigger a flush.
116
+ *
117
+ * Example of vertical reversal:
118
+ * start = {y: 0}, previous = {y: 500}, current = {y: 400}
119
+ * scrollDirectionY = sign(500 - 0) = +1 (downward)
120
+ * movementDirectionY = sign(400 - 500) = -1 (upward)
121
+ * +1 !== -1 => reversed
122
+ *
123
+ * Example of no reversal (same direction):
124
+ * start = {y: 0}, previous = {y: 200}, current = {y: 500}
125
+ * scrollDirectionY = sign(200 - 0) = +1 (downward)
126
+ * movementDirectionY = sign(500 - 200) = +1 (downward)
127
+ * +1 === +1 => not reversed
128
+ *
129
+ * Example of horizontal stop ignored:
130
+ * start = {x: 0}, previous = {x: 300}, current = {x: 300}
131
+ * scrollDirectionX = sign(300 - 0) = +1
132
+ * movementDirectionX = sign(300 - 300) = 0 (no movement)
133
+ * movementDirectionX is 0 => ignored, not a reversal
134
+ */
135
+ hasDirectionChanged(state, current) {
136
+ const { start, lastPosition: previous } = state;
137
+ const scrollDirectionX = Math.sign(previous.x - start.x);
138
+ const scrollDirectionY = Math.sign(previous.y - start.y);
139
+ const movementDirectionX = Math.sign(current.x - previous.x);
140
+ const movementDirectionY = Math.sign(current.y - previous.y);
141
+ return scrollDirectionX !== 0 && movementDirectionX !== 0 && scrollDirectionX !== movementDirectionX || scrollDirectionY !== 0 && movementDirectionY !== 0 && scrollDirectionY !== movementDirectionY;
142
+ }
143
+ flushPendingScroll(end) {
144
+ if (this.scrollDebounceTimer !== void 0) {
145
+ window.clearTimeout(this.scrollDebounceTimer);
146
+ this.scrollDebounceTimer = void 0;
147
+ }
148
+ if (this.scrollState === void 0) {
149
+ return;
150
+ }
151
+ const { start } = this.scrollState;
152
+ const destination = end ?? {
153
+ x: Math.max(0, Math.round(window.scrollX)),
154
+ y: Math.max(0, Math.round(window.scrollY))
155
+ };
156
+ this.scrollState = void 0;
157
+ if (start.x === destination.x && start.y === destination.y) {
158
+ return;
159
+ }
160
+ this.eventManager.dispatch("userScrolled", {
161
+ type: "userScrolled",
162
+ start,
163
+ end: destination,
164
+ surfaceSize: {
165
+ width: document.documentElement.scrollWidth,
166
+ height: document.documentElement.scrollHeight
167
+ },
168
+ // Uses clientWidth/clientHeight instead of innerWidth/innerHeight to get the
169
+ // layout viewport size, which remains stable regardless of pinch-to-zoom level
170
+ // on mobile devices.
171
+ viewportSize: {
172
+ width: document.documentElement.clientWidth,
173
+ height: document.documentElement.clientHeight
174
+ }
175
+ });
176
+ }
177
+ }
178
+ // Annotate the CommonJS export names for ESM import in node:
179
+ 0 && (module.exports = {
180
+ InteractionMonitor
181
+ });
@@ -0,0 +1,64 @@
1
+ import { EventListener } from './eventManager.cjs';
2
+ import { UserClicked, UserScrolled } from './trackingEvents.cjs';
3
+ import './patch.cjs';
4
+ import '@croct/json';
5
+ import './utilityTypes.cjs';
6
+
7
+ type InteractionEventMap = {
8
+ userClicked: UserClicked;
9
+ userScrolled: UserScrolled;
10
+ };
11
+ type Options = {
12
+ clickThrottleInterval?: number;
13
+ scrollDebounceInterval?: number;
14
+ };
15
+ declare class InteractionMonitor {
16
+ private readonly eventManager;
17
+ private readonly clickThrottleInterval;
18
+ private readonly scrollDebounceInterval;
19
+ private lastClickTime;
20
+ private scrollState?;
21
+ private scrollDebounceTimer?;
22
+ private enabled;
23
+ constructor(options?: Options);
24
+ addListener<T extends keyof InteractionEventMap>(type: T, listener: EventListener<InteractionEventMap[T]>): void;
25
+ removeListener<T extends keyof InteractionEventMap>(type: T, listener: EventListener<InteractionEventMap[T]>): void;
26
+ isEnabled(): boolean;
27
+ enable(): void;
28
+ disable(): void;
29
+ private handleClick;
30
+ private handleScroll;
31
+ /**
32
+ * Determines whether the scroll direction has reversed on either axis.
33
+ *
34
+ * It compares two vectors:
35
+ * - The scroll direction: the overall direction from start to the previous position (start -> previous)
36
+ * - The movement direction: the direction of the latest movement (previous -> current)
37
+ *
38
+ * A reversal is detected when these two vectors point in opposite directions on the same axis.
39
+ * Axes with no movement (sign = 0) are ignored, so scrolling that simply stops on one axis
40
+ * without reversing does not trigger a flush.
41
+ *
42
+ * Example of vertical reversal:
43
+ * start = {y: 0}, previous = {y: 500}, current = {y: 400}
44
+ * scrollDirectionY = sign(500 - 0) = +1 (downward)
45
+ * movementDirectionY = sign(400 - 500) = -1 (upward)
46
+ * +1 !== -1 => reversed
47
+ *
48
+ * Example of no reversal (same direction):
49
+ * start = {y: 0}, previous = {y: 200}, current = {y: 500}
50
+ * scrollDirectionY = sign(200 - 0) = +1 (downward)
51
+ * movementDirectionY = sign(500 - 200) = +1 (downward)
52
+ * +1 === +1 => not reversed
53
+ *
54
+ * Example of horizontal stop ignored:
55
+ * start = {x: 0}, previous = {x: 300}, current = {x: 300}
56
+ * scrollDirectionX = sign(300 - 0) = +1
57
+ * movementDirectionX = sign(300 - 300) = 0 (no movement)
58
+ * movementDirectionX is 0 => ignored, not a reversal
59
+ */
60
+ private hasDirectionChanged;
61
+ private flushPendingScroll;
62
+ }
63
+
64
+ export { InteractionMonitor };
@@ -0,0 +1,64 @@
1
+ import { EventListener } from './eventManager.js';
2
+ import { UserClicked, UserScrolled } from './trackingEvents.js';
3
+ import './patch.js';
4
+ import '@croct/json';
5
+ import './utilityTypes.js';
6
+
7
+ type InteractionEventMap = {
8
+ userClicked: UserClicked;
9
+ userScrolled: UserScrolled;
10
+ };
11
+ type Options = {
12
+ clickThrottleInterval?: number;
13
+ scrollDebounceInterval?: number;
14
+ };
15
+ declare class InteractionMonitor {
16
+ private readonly eventManager;
17
+ private readonly clickThrottleInterval;
18
+ private readonly scrollDebounceInterval;
19
+ private lastClickTime;
20
+ private scrollState?;
21
+ private scrollDebounceTimer?;
22
+ private enabled;
23
+ constructor(options?: Options);
24
+ addListener<T extends keyof InteractionEventMap>(type: T, listener: EventListener<InteractionEventMap[T]>): void;
25
+ removeListener<T extends keyof InteractionEventMap>(type: T, listener: EventListener<InteractionEventMap[T]>): void;
26
+ isEnabled(): boolean;
27
+ enable(): void;
28
+ disable(): void;
29
+ private handleClick;
30
+ private handleScroll;
31
+ /**
32
+ * Determines whether the scroll direction has reversed on either axis.
33
+ *
34
+ * It compares two vectors:
35
+ * - The scroll direction: the overall direction from start to the previous position (start -> previous)
36
+ * - The movement direction: the direction of the latest movement (previous -> current)
37
+ *
38
+ * A reversal is detected when these two vectors point in opposite directions on the same axis.
39
+ * Axes with no movement (sign = 0) are ignored, so scrolling that simply stops on one axis
40
+ * without reversing does not trigger a flush.
41
+ *
42
+ * Example of vertical reversal:
43
+ * start = {y: 0}, previous = {y: 500}, current = {y: 400}
44
+ * scrollDirectionY = sign(500 - 0) = +1 (downward)
45
+ * movementDirectionY = sign(400 - 500) = -1 (upward)
46
+ * +1 !== -1 => reversed
47
+ *
48
+ * Example of no reversal (same direction):
49
+ * start = {y: 0}, previous = {y: 200}, current = {y: 500}
50
+ * scrollDirectionY = sign(200 - 0) = +1 (downward)
51
+ * movementDirectionY = sign(500 - 200) = +1 (downward)
52
+ * +1 === +1 => not reversed
53
+ *
54
+ * Example of horizontal stop ignored:
55
+ * start = {x: 0}, previous = {x: 300}, current = {x: 300}
56
+ * scrollDirectionX = sign(300 - 0) = +1
57
+ * movementDirectionX = sign(300 - 300) = 0 (no movement)
58
+ * movementDirectionX is 0 => ignored, not a reversal
59
+ */
60
+ private hasDirectionChanged;
61
+ private flushPendingScroll;
62
+ }
63
+
64
+ export { InteractionMonitor };