intent-click 0.1.0

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.
Files changed (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +126 -0
  3. package/dist/angular.d.ts +18 -0
  4. package/dist/angular.d.ts.map +1 -0
  5. package/dist/angular.js +29 -0
  6. package/dist/angular.js.map +1 -0
  7. package/dist/core/engine.d.ts +3 -0
  8. package/dist/core/engine.d.ts.map +1 -0
  9. package/dist/core/engine.js +241 -0
  10. package/dist/core/engine.js.map +1 -0
  11. package/dist/core/env.d.ts +9 -0
  12. package/dist/core/env.d.ts.map +1 -0
  13. package/dist/core/env.js +38 -0
  14. package/dist/core/env.js.map +1 -0
  15. package/dist/core/index.d.ts +3 -0
  16. package/dist/core/index.d.ts.map +1 -0
  17. package/dist/core/index.js +2 -0
  18. package/dist/core/index.js.map +1 -0
  19. package/dist/core/scheduler.d.ts +6 -0
  20. package/dist/core/scheduler.d.ts.map +1 -0
  21. package/dist/core/scheduler.js +31 -0
  22. package/dist/core/scheduler.js.map +1 -0
  23. package/dist/core/types.d.ts +30 -0
  24. package/dist/core/types.d.ts.map +1 -0
  25. package/dist/core/types.js +2 -0
  26. package/dist/core/types.js.map +1 -0
  27. package/dist/index.d.ts +8 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +3 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/react.d.ts +5 -0
  32. package/dist/react.d.ts.map +1 -0
  33. package/dist/react.js +25 -0
  34. package/dist/react.js.map +1 -0
  35. package/dist/solid.d.ts +8 -0
  36. package/dist/solid.d.ts.map +1 -0
  37. package/dist/solid.js +33 -0
  38. package/dist/solid.js.map +1 -0
  39. package/dist/svelte.d.ts +14 -0
  40. package/dist/svelte.d.ts.map +1 -0
  41. package/dist/svelte.js +28 -0
  42. package/dist/svelte.js.map +1 -0
  43. package/dist/vanilla.d.ts +6 -0
  44. package/dist/vanilla.d.ts.map +1 -0
  45. package/dist/vanilla.js +8 -0
  46. package/dist/vanilla.js.map +1 -0
  47. package/dist/vue.d.ts +8 -0
  48. package/dist/vue.d.ts.map +1 -0
  49. package/dist/vue.js +32 -0
  50. package/dist/vue.js.map +1 -0
  51. package/package.json +164 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 AntonVoronezh <anton.voronezh.1990@gmail.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,126 @@
1
+ # intent-click
2
+
3
+ [![npm version](https://img.shields.io/npm/v/intent-click.svg)](https://www.npmjs.com/package/intent-click)
4
+ [![npm downloads](https://img.shields.io/npm/dm/intent-click.svg)](https://www.npmjs.com/package/intent-click)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+
7
+ Detect intentional clicks vs accidental taps on touchscreens. Adaptive threshold, ghost click suppression, scroll intent detection.
8
+
9
+ ## Why?
10
+
11
+ On touchscreens, accidental taps are common:
12
+ - User scrolls, finger lands on a button → **click fires**
13
+ - Drag gesture slightly moved → **browser still fires click**
14
+ - iOS Safari ghost clicks 300ms after touchend → **hits element underneath**
15
+
16
+ Standard solutions don't work:
17
+ - `e.preventDefault()` — breaks scrolling
18
+ - `touch-action: manipulation` — doesn't solve drag-vs-click
19
+ - `fastclick` — deprecated, only solved 300ms delay
20
+
21
+ `intent-click` distinguishes **intentional** clicks from:
22
+ - Drag gestures (movement > threshold)
23
+ - Scroll intents (vertical movement > 10px)
24
+ - Ghost clicks (same coords within 400ms)
25
+ - Long presses (>500ms without movement)
26
+ - Concurrent pointers (pinch-zoom started)
27
+
28
+ ## Install
29
+
30
+ ```bash
31
+ npm install intent-click
32
+ ```
33
+
34
+ ## Quick Start
35
+
36
+ ```typescript
37
+ import { useIntentClick } from 'intent-click/react';
38
+
39
+ // React
40
+ const ref = useIntentClick<HTMLButtonElement>((event) => {
41
+ // Guaranteed: intentional click
42
+ deleteAccount();
43
+ }, { threshold: 'auto' });
44
+
45
+ <button ref={ref}>Delete Account</button>
46
+ ```
47
+
48
+ ```typescript
49
+ // Vanilla JS
50
+ import { onIntentClick } from 'intent-click/vanilla';
51
+
52
+ const off = onIntentClick(buttonEl, () => {
53
+ deleteAccount();
54
+ }, { threshold: 'auto' });
55
+ ```
56
+
57
+ ## Features
58
+
59
+ - **~800 bytes gzip**
60
+ - **Zero runtime dependencies**
61
+ - **Adaptive threshold**: 4px × devicePixelRatio on desktop, 6px × devicePixelRatio on mobile
62
+ - **Ghost click suppression**: 400ms dead zone on same coords ±5px
63
+ - **Scroll intent detection**: vertical movement > 10px → scroll, not click
64
+ - **Long press discrimination**: >500ms without movement → `onLongPress`
65
+ - **Concurrent pointer rejection**: multi-touch → cancel
66
+ - **SSR-safe**: returns null on server, hydrates in browser
67
+ - **TypeScript-first**: full type inference
68
+ - **Framework adapters**: React, Vue, Svelte, Solid, Angular, vanilla JS
69
+
70
+ ## API
71
+
72
+ ```typescript
73
+ interface IntentClickOptions {
74
+ // Movement threshold in CSS pixels. 'auto' = adaptive
75
+ threshold?: number | 'auto';
76
+
77
+ // Duration in ms after which it's considered a long press
78
+ longPressDelay?: number;
79
+
80
+ // Callback when long press is detected
81
+ onLongPress?: (event: PointerEvent) => void;
82
+
83
+ // Callback when intent is cancelled
84
+ onIntentCancel?: (reason: IntentCancelReason) => void;
85
+ }
86
+
87
+ type IntentCancelReason =
88
+ | 'drag' // Movement exceeded threshold
89
+ | 'scroll' // Vertical movement > 10px
90
+ | 'concurrent-pointer' // Second pointer appeared
91
+ | 'pointercancel' // OS intercepted gesture
92
+ | 'timeout'; // (reserved)
93
+ ```
94
+
95
+ ## How It Works
96
+
97
+ 1. **PointerEvent pipeline**: single entry point for mouse/touch/pen
98
+ 2. **FSM**: IDLE → PENDING → MOVING → DECIDED
99
+ 3. **Adaptive threshold**: `baseThreshold × devicePixelRatio`
100
+ 4. **Ghost click suppression**: track last coords, block ±5px for 400ms
101
+ 5. **Scroll intent**: vertical movement > 10px → cancel
102
+ 6. **Long press**: >500ms without movement → `onLongPress`
103
+
104
+ ## Framework Adapters
105
+
106
+ - [React](https://antonvoronezh.github.io/intent-click/react.html)
107
+ - [Vue](https://antonvoronezh.github.io/intent-click/vue.html)
108
+ - [Svelte](https://antonvoronezh.github.io/intent-click/svelte.html)
109
+ - [Solid](https://antonvoronezh.github.io/intent-click/solid.html)
110
+ - [Angular](https://antonvoronezh.github.io/intent-click/angular.html)
111
+
112
+ ## Docs
113
+
114
+ - [npm Package](https://antonvoronezh.github.io/intent-click/npm.html)
115
+ - [FAQ](https://antonvoronezh.github.io/intent-click/faq.html)
116
+ - [Common Pitfalls](https://antonvoronezh.github.io/intent-click/common-pitfalls.html)
117
+ - [Smoke Test](https://antonvoronezh.github.io/intent-click/smoke-test.html)
118
+ - [SemVer Policy](./SEMVER_POLICY.md)
119
+
120
+ ## Support
121
+
122
+ See [Funding](https://antonvoronezh.github.io/intent-click/funding.html).
123
+
124
+ ## License
125
+
126
+ MIT
@@ -0,0 +1,18 @@
1
+ import { type OnInit } from '@angular/core';
2
+ import type { IntentClickCallback } from './core/types.js';
3
+ import * as i0 from "@angular/core";
4
+ export declare class IntentClickDirective implements OnInit {
5
+ private readonly destroyRef;
6
+ private readonly elementRef;
7
+ readonly intentClickCallback: import("@angular/core").InputSignal<IntentClickCallback>;
8
+ readonly intentClickOptions: import("@angular/core").InputSignal<Readonly<{
9
+ threshold?: number | "auto";
10
+ longPressDelay?: number;
11
+ onLongPress?: import("./core/types.js").IntentClickLongPressCallback;
12
+ onIntentCancel?: (reason: import("./core/types.js").IntentCancelReason) => void;
13
+ }> | undefined>;
14
+ ngOnInit(): void;
15
+ static ɵfac: i0.ɵɵFactoryDeclaration<IntentClickDirective, never>;
16
+ static ɵdir: i0.ɵɵDirectiveDeclaration<IntentClickDirective, "[intentClickCallback]", ["intentClick"], { "intentClickCallback": { "alias": "intentClickCallback"; "required": true; "isSignal": true; }; "intentClickOptions": { "alias": "intentClickOptions"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
17
+ }
18
+ //# sourceMappingURL=angular.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"angular.d.ts","sourceRoot":"","sources":["../src/angular.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoD,KAAK,MAAM,EAAE,MAAM,eAAe,CAAC;AAC9F,OAAO,KAAK,EAAsB,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;;AAG/E,qBAKa,oBAAqB,YAAW,MAAM;IAC/C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAsB;IACjD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA+C;IAE1E,QAAQ,CAAC,mBAAmB,2DAAyC;IACrE,QAAQ,CAAC,kBAAkB;;;;;oBAA2C;IAEtE,QAAQ,IAAI,IAAI;yCAPP,oBAAoB;2CAApB,oBAAoB;CAuBhC"}
@@ -0,0 +1,29 @@
1
+ import { Directive, DestroyRef, ElementRef, inject, input } from '@angular/core';
2
+ import { createIntentClickStore } from './core/engine.js';
3
+ import * as i0 from "@angular/core";
4
+ export class IntentClickDirective {
5
+ destroyRef = inject(DestroyRef);
6
+ elementRef = inject(ElementRef);
7
+ intentClickCallback = input.required();
8
+ intentClickOptions = input();
9
+ ngOnInit() {
10
+ const element = this.elementRef.nativeElement;
11
+ const store = createIntentClickStore(element, this.intentClickCallback(), this.intentClickOptions());
12
+ const unsub = store.subscribe(() => { });
13
+ this.destroyRef.onDestroy(() => {
14
+ unsub();
15
+ store.destroy();
16
+ });
17
+ }
18
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: IntentClickDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
19
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "17.3.12", type: IntentClickDirective, isStandalone: true, selector: "[intentClickCallback]", inputs: { intentClickCallback: { classPropertyName: "intentClickCallback", publicName: "intentClickCallback", isSignal: true, isRequired: true, transformFunction: null }, intentClickOptions: { classPropertyName: "intentClickOptions", publicName: "intentClickOptions", isSignal: true, isRequired: false, transformFunction: null } }, exportAs: ["intentClick"], ngImport: i0 });
20
+ }
21
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: IntentClickDirective, decorators: [{
22
+ type: Directive,
23
+ args: [{
24
+ selector: '[intentClickCallback]',
25
+ standalone: true,
26
+ exportAs: 'intentClick',
27
+ }]
28
+ }] });
29
+ //# sourceMappingURL=angular.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"angular.js","sourceRoot":"","sources":["../src/angular.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAe,MAAM,eAAe,CAAC;AAE9F,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;;AAO1D,MAAM,OAAO,oBAAoB;IACZ,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;IAChC,UAAU,GAAG,MAAM,CAA0B,UAAU,CAAC,CAAC;IAEjE,mBAAmB,GAAG,KAAK,CAAC,QAAQ,EAAuB,CAAC;IAC5D,kBAAkB,GAAG,KAAK,EAAkC,CAAC;IAEtE,QAAQ;QACJ,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC;QAE9C,MAAM,KAAK,GAAG,sBAAsB,CAChC,OAAO,EACP,IAAI,CAAC,mBAAmB,EAAE,EAC1B,IAAI,CAAC,kBAAkB,EAAE,CAC5B,CAAC;QAEF,MAAM,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAExC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,GAAG,EAAE;YAC3B,KAAK,EAAE,CAAC;YACR,KAAK,CAAC,OAAO,EAAE,CAAC;QACpB,CAAC,CAAC,CAAC;IACP,CAAC;wGAtBQ,oBAAoB;4FAApB,oBAAoB;;4FAApB,oBAAoB;kBALhC,SAAS;mBAAC;oBACP,QAAQ,EAAE,uBAAuB;oBACjC,UAAU,EAAE,IAAI;oBAChB,QAAQ,EAAE,aAAa;iBAC1B"}
@@ -0,0 +1,3 @@
1
+ import type { IntentClickOptions, IntentClickStore } from './types.js';
2
+ export declare const createIntentClickStore: (element: HTMLElement | null | undefined, callback: (event: PointerEvent) => void, options?: IntentClickOptions) => IntentClickStore;
3
+ //# sourceMappingURL=engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/core/engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAuB,gBAAgB,EAAsB,MAAM,YAAY,CAAC;AA4DhH,eAAO,MAAM,sBAAsB,YACtB,WAAW,GAAG,IAAI,GAAG,SAAS,YAC7B,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,YAC7B,kBAAkB,KAC7B,gBAoOF,CAAC"}
@@ -0,0 +1,241 @@
1
+ import { canUseDOM, getDevicePixelRatio, getBaseThreshold, now } from './env.js';
2
+ import { createScheduler } from './scheduler.js';
3
+ const GHOST_CLICK_BLOCK_DURATION = 400;
4
+ const GHOST_CLICK_COORD_THRESHOLD = 5;
5
+ const MIN_CLICK_DURATION = 50;
6
+ const DEFAULT_LONG_PRESS_DELAY = 500;
7
+ const SCROLL_INTENT_THRESHOLD = 10;
8
+ const calculateAdaptiveThreshold = (options) => {
9
+ if (typeof options.threshold === 'number')
10
+ return options.threshold;
11
+ const baseThreshold = getBaseThreshold();
12
+ const dpr = getDevicePixelRatio();
13
+ return baseThreshold * dpr;
14
+ };
15
+ const isGhostClick = (state, x, y) => {
16
+ if (state.lastClickTime === null || state.lastClickX === null || state.lastClickY === null) {
17
+ return false;
18
+ }
19
+ const timeSinceLastClick = now() - state.lastClickTime;
20
+ if (timeSinceLastClick > GHOST_CLICK_BLOCK_DURATION)
21
+ return false;
22
+ const dx = Math.abs(x - state.lastClickX);
23
+ const dy = Math.abs(y - state.lastClickY);
24
+ return dx <= GHOST_CLICK_COORD_THRESHOLD && dy <= GHOST_CLICK_COORD_THRESHOLD;
25
+ };
26
+ const detectScrollIntent = (movementX, movementY) => {
27
+ const absX = Math.abs(movementX);
28
+ const absY = Math.abs(movementY);
29
+ if (absY > SCROLL_INTENT_THRESHOLD && absX < absY * 0.5)
30
+ return true;
31
+ if (absX > SCROLL_INTENT_THRESHOLD && absY < absX * 0.5)
32
+ return true;
33
+ return false;
34
+ };
35
+ export const createIntentClickStore = (element, callback, options) => {
36
+ if (!canUseDOM() || !element) {
37
+ return {
38
+ getSnapshot: () => null,
39
+ getServerSnapshot: () => null,
40
+ subscribe: () => () => { },
41
+ destroy: () => { },
42
+ };
43
+ }
44
+ const opts = {
45
+ threshold: 'auto',
46
+ longPressDelay: DEFAULT_LONG_PRESS_DELAY,
47
+ ...options,
48
+ };
49
+ const adaptiveThreshold = calculateAdaptiveThreshold(opts);
50
+ let state = {
51
+ status: 'IDLE',
52
+ pointerId: null,
53
+ startX: 0,
54
+ startY: 0,
55
+ startTime: 0,
56
+ currentX: 0,
57
+ currentY: 0,
58
+ movementX: 0,
59
+ movementY: 0,
60
+ maxMovement: 0,
61
+ devicePixelRatio: getDevicePixelRatio(),
62
+ lastClickX: null,
63
+ lastClickY: null,
64
+ lastClickTime: null,
65
+ rafId: null,
66
+ longPressTimer: null,
67
+ ghostClickBlockTimer: null,
68
+ };
69
+ const scheduler = createScheduler();
70
+ const listeners = new Set();
71
+ let snapshot = null;
72
+ const updateSnapshot = (isIntent, reason) => {
73
+ snapshot = {
74
+ isIntent,
75
+ pointerId: state.pointerId ?? 0,
76
+ startX: state.startX,
77
+ startY: state.startY,
78
+ endX: state.currentX,
79
+ endY: state.currentY,
80
+ duration: now() - state.startTime,
81
+ movementX: state.movementX,
82
+ movementY: state.movementY,
83
+ devicePixelRatio: state.devicePixelRatio,
84
+ reason,
85
+ };
86
+ listeners.forEach((l) => l());
87
+ };
88
+ const resetState = () => {
89
+ if (state.longPressTimer !== null) {
90
+ clearTimeout(state.longPressTimer);
91
+ }
92
+ scheduler.cancelAll();
93
+ state = {
94
+ ...state,
95
+ status: 'IDLE',
96
+ pointerId: null,
97
+ startX: 0,
98
+ startY: 0,
99
+ startTime: 0,
100
+ currentX: 0,
101
+ currentY: 0,
102
+ movementX: 0,
103
+ movementY: 0,
104
+ maxMovement: 0,
105
+ rafId: null,
106
+ longPressTimer: null,
107
+ };
108
+ };
109
+ const handlePointerDown = (event) => {
110
+ if (state.status !== 'IDLE') {
111
+ state.status = 'IDLE';
112
+ if (state.longPressTimer !== null)
113
+ clearTimeout(state.longPressTimer);
114
+ }
115
+ state.status = 'PENDING';
116
+ state.pointerId = event.pointerId;
117
+ state.startX = event.clientX;
118
+ state.startY = event.clientY;
119
+ state.startTime = now();
120
+ state.currentX = event.clientX;
121
+ state.currentY = event.clientY;
122
+ state.movementX = 0;
123
+ state.movementY = 0;
124
+ state.maxMovement = 0;
125
+ state.devicePixelRatio = getDevicePixelRatio();
126
+ if (opts.onLongPress) {
127
+ state.longPressTimer = setTimeout(() => {
128
+ if (state.status === 'PENDING' && state.maxMovement < adaptiveThreshold) {
129
+ state.status = 'DECIDED';
130
+ opts.onLongPress(event);
131
+ resetState();
132
+ }
133
+ }, opts.longPressDelay ?? DEFAULT_LONG_PRESS_DELAY);
134
+ }
135
+ };
136
+ const handlePointerMove = (event) => {
137
+ if (state.status !== 'PENDING' && state.status !== 'MOVING')
138
+ return;
139
+ if (state.pointerId !== event.pointerId)
140
+ return;
141
+ state.currentX = event.clientX;
142
+ state.currentY = event.clientY;
143
+ state.movementX = event.clientX - state.startX;
144
+ state.movementY = event.clientY - state.startY;
145
+ state.maxMovement = Math.max(Math.abs(state.movementX), Math.abs(state.movementY));
146
+ if (state.maxMovement > adaptiveThreshold) {
147
+ state.status = 'MOVING';
148
+ if (state.longPressTimer !== null) {
149
+ clearTimeout(state.longPressTimer);
150
+ state.longPressTimer = null;
151
+ }
152
+ }
153
+ };
154
+ const handlePointerUp = (event) => {
155
+ if (state.status !== 'PENDING' && state.status !== 'MOVING')
156
+ return;
157
+ if (state.pointerId !== event.pointerId)
158
+ return;
159
+ const duration = now() - state.startTime;
160
+ if (duration < MIN_CLICK_DURATION) {
161
+ resetState();
162
+ return;
163
+ }
164
+ if (state.maxMovement > adaptiveThreshold) {
165
+ opts.onIntentCancel?.('drag');
166
+ resetState();
167
+ return;
168
+ }
169
+ if (detectScrollIntent(state.movementX, state.movementY)) {
170
+ opts.onIntentCancel?.('scroll');
171
+ resetState();
172
+ return;
173
+ }
174
+ if (isGhostClick(state, event.clientX, event.clientY)) {
175
+ resetState();
176
+ return;
177
+ }
178
+ state.status = 'DECIDED';
179
+ state.lastClickX = event.clientX;
180
+ state.lastClickY = event.clientY;
181
+ state.lastClickTime = now();
182
+ updateSnapshot(true, null);
183
+ callback(event);
184
+ state.ghostClickBlockTimer = setTimeout(() => {
185
+ state.lastClickX = null;
186
+ state.lastClickY = null;
187
+ state.lastClickTime = null;
188
+ }, GHOST_CLICK_BLOCK_DURATION);
189
+ resetState();
190
+ };
191
+ const handlePointerCancel = () => {
192
+ if (state.status === 'IDLE')
193
+ return;
194
+ opts.onIntentCancel?.('pointercancel');
195
+ resetState();
196
+ };
197
+ const handleConcurrentPointer = () => {
198
+ if (state.status === 'IDLE')
199
+ return;
200
+ opts.onIntentCancel?.('concurrent-pointer');
201
+ resetState();
202
+ };
203
+ element.addEventListener('pointerdown', handlePointerDown, { passive: true });
204
+ element.addEventListener('pointermove', handlePointerMove, { passive: true });
205
+ element.addEventListener('pointerup', handlePointerUp, { passive: true });
206
+ element.addEventListener('pointercancel', handlePointerCancel, { passive: true });
207
+ element.addEventListener('pointerleave', handlePointerCancel, { passive: true });
208
+ const globalConcurrentHandler = (event) => {
209
+ if (state.status !== 'IDLE' && event.pointerId !== state.pointerId) {
210
+ handleConcurrentPointer();
211
+ }
212
+ };
213
+ document.addEventListener('pointerdown', globalConcurrentHandler, { passive: true });
214
+ const destroy = () => {
215
+ element.removeEventListener('pointerdown', handlePointerDown);
216
+ element.removeEventListener('pointermove', handlePointerMove);
217
+ element.removeEventListener('pointerup', handlePointerUp);
218
+ element.removeEventListener('pointercancel', handlePointerCancel);
219
+ element.removeEventListener('pointerleave', handlePointerCancel);
220
+ document.removeEventListener('pointerdown', globalConcurrentHandler);
221
+ if (state.longPressTimer !== null)
222
+ clearTimeout(state.longPressTimer);
223
+ if (state.ghostClickBlockTimer !== null)
224
+ clearTimeout(state.ghostClickBlockTimer);
225
+ scheduler.cancelAll();
226
+ listeners.clear();
227
+ resetState();
228
+ };
229
+ return {
230
+ getSnapshot: () => snapshot,
231
+ getServerSnapshot: () => null,
232
+ subscribe: (onStoreChange) => {
233
+ listeners.add(onStoreChange);
234
+ return () => {
235
+ listeners.delete(onStoreChange);
236
+ };
237
+ },
238
+ destroy,
239
+ };
240
+ };
241
+ //# sourceMappingURL=engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/core/engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export declare const canUseDOM: () => boolean;
2
+ export declare const now: () => number;
3
+ export declare const raf: (cb: FrameRequestCallback) => number;
4
+ export declare const caf: (id: number) => void;
5
+ export declare const queueMicrotaskSafe: (cb: () => void) => void;
6
+ export declare const getDevicePixelRatio: () => number;
7
+ export declare const isTouchDevice: () => boolean;
8
+ export declare const getBaseThreshold: () => number;
9
+ //# sourceMappingURL=env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/core/env.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,SAAS,QAAO,OAGO,CAAC;AAErC,eAAO,MAAM,GAAG,QAAO,MAKtB,CAAC;AAEF,eAAO,MAAM,GAAG,OAAQ,oBAAoB,KAAG,MAG9C,CAAC;AAEF,eAAO,MAAM,GAAG,OAAQ,MAAM,KAAG,IAGhC,CAAC;AAEF,eAAO,MAAM,kBAAkB,OAAQ,MAAM,IAAI,KAAG,IAGnD,CAAC;AAEF,eAAO,MAAM,mBAAmB,QAAO,MAGtC,CAAC;AAEF,eAAO,MAAM,aAAa,QAAO,OAGhC,CAAC;AAEF,eAAO,MAAM,gBAAgB,QAAO,MAEnC,CAAC"}
@@ -0,0 +1,38 @@
1
+ export const canUseDOM = () => typeof window !== 'undefined' &&
2
+ typeof document !== 'undefined' &&
3
+ typeof navigator !== 'undefined';
4
+ export const now = () => {
5
+ const p = (typeof performance !== 'undefined' ? performance : undefined);
6
+ return typeof p?.now === 'function' ? p.now() : Date.now();
7
+ };
8
+ export const raf = (cb) => {
9
+ if (typeof requestAnimationFrame === 'function')
10
+ return requestAnimationFrame(cb);
11
+ return setTimeout(() => cb(now()), 16);
12
+ };
13
+ export const caf = (id) => {
14
+ if (typeof cancelAnimationFrame === 'function')
15
+ cancelAnimationFrame(id);
16
+ else
17
+ clearTimeout(id);
18
+ };
19
+ export const queueMicrotaskSafe = (cb) => {
20
+ if (typeof queueMicrotask === 'function')
21
+ queueMicrotask(cb);
22
+ else
23
+ Promise.resolve().then(cb).catch(() => void 0);
24
+ };
25
+ export const getDevicePixelRatio = () => {
26
+ if (typeof window === 'undefined')
27
+ return 1;
28
+ return window.devicePixelRatio ?? 1;
29
+ };
30
+ export const isTouchDevice = () => {
31
+ if (typeof window === 'undefined')
32
+ return false;
33
+ return window.matchMedia('(hover: none)').matches;
34
+ };
35
+ export const getBaseThreshold = () => {
36
+ return isTouchDevice() ? 6 : 4;
37
+ };
38
+ //# sourceMappingURL=env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/core/env.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,SAAS,GAAG,GAAY,EAAE,CACnC,OAAO,MAAM,KAAK,WAAW;IAC7B,OAAO,QAAQ,KAAK,WAAW;IAC/B,OAAO,SAAS,KAAK,WAAW,CAAC;AAErC,MAAM,CAAC,MAAM,GAAG,GAAG,GAAW,EAAE;IAC5B,MAAM,CAAC,GAAG,CAAC,OAAO,WAAW,KAAK,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAExD,CAAC;IAChB,OAAO,OAAO,CAAC,EAAE,GAAG,KAAK,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;AAC/D,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,EAAwB,EAAU,EAAE;IACpD,IAAI,OAAO,qBAAqB,KAAK,UAAU;QAAE,OAAO,qBAAqB,CAAC,EAAE,CAAC,CAAC;IAClF,OAAO,UAAU,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,GAAG,EAAuB,CAAC,EAAE,EAAE,CAAsB,CAAC;AACrF,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,EAAU,EAAQ,EAAE;IACpC,IAAI,OAAO,oBAAoB,KAAK,UAAU;QAAE,oBAAoB,CAAC,EAAE,CAAC,CAAC;;QACpE,YAAY,CAAC,EAAuB,CAAC,CAAC;AAC/C,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,EAAc,EAAQ,EAAE;IACvD,IAAI,OAAO,cAAc,KAAK,UAAU;QAAE,cAAc,CAAC,EAAE,CAAC,CAAC;;QACxD,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;AACxD,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAW,EAAE;IAC5C,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO,CAAC,CAAC;IAC5C,OAAO,MAAM,CAAC,gBAAgB,IAAI,CAAC,CAAC;AACxC,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG,GAAY,EAAE;IACvC,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO,KAAK,CAAC;IAChD,OAAO,MAAM,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC,OAAO,CAAC;AACtD,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAW,EAAE;IACzC,OAAO,aAAa,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACnC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ export type { IntentClickCallback, IntentClickLongPressCallback, IntentClickOptions, IntentCancelReason, IntentClickSnapshot, IntentClickStore, IntentClickSubscribe, } from './types.js';
2
+ export { createIntentClickStore } from './engine.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACR,mBAAmB,EACnB,4BAA4B,EAC5B,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EAChB,oBAAoB,GACvB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { createIntentClickStore } from './engine.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,6 @@
1
+ export type Scheduler = Readonly<{
2
+ scheduleFrame: (fn: () => void) => void;
3
+ cancelAll: () => void;
4
+ }>;
5
+ export declare const createScheduler: () => Scheduler;
6
+ //# sourceMappingURL=scheduler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../src/core/scheduler.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,SAAS,GAAG,QAAQ,CAAC;IAC7B,aAAa,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;IACxC,SAAS,EAAE,MAAM,IAAI,CAAC;CACzB,CAAC,CAAC;AAEH,eAAO,MAAM,eAAe,QAAO,SA2BlC,CAAC"}
@@ -0,0 +1,31 @@
1
+ import { caf, raf } from './env.js';
2
+ export const createScheduler = () => {
3
+ let rafId = null;
4
+ let frameQueued = false;
5
+ let pendingFn = null;
6
+ const execute = () => {
7
+ rafId = null;
8
+ frameQueued = false;
9
+ const fn = pendingFn;
10
+ pendingFn = null;
11
+ if (fn)
12
+ fn();
13
+ };
14
+ return {
15
+ scheduleFrame(fn) {
16
+ pendingFn = fn;
17
+ if (frameQueued)
18
+ return;
19
+ frameQueued = true;
20
+ rafId = raf(execute);
21
+ },
22
+ cancelAll() {
23
+ if (rafId != null)
24
+ caf(rafId);
25
+ rafId = null;
26
+ frameQueued = false;
27
+ pendingFn = null;
28
+ },
29
+ };
30
+ };
31
+ //# sourceMappingURL=scheduler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler.js","sourceRoot":"","sources":["../../src/core/scheduler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAOpC,MAAM,CAAC,MAAM,eAAe,GAAG,GAAc,EAAE;IAC3C,IAAI,KAAK,GAAkB,IAAI,CAAC;IAChC,IAAI,WAAW,GAAG,KAAK,CAAC;IACxB,IAAI,SAAS,GAAwB,IAAI,CAAC;IAE1C,MAAM,OAAO,GAAG,GAAG,EAAE;QACjB,KAAK,GAAG,IAAI,CAAC;QACb,WAAW,GAAG,KAAK,CAAC;QACpB,MAAM,EAAE,GAAG,SAAS,CAAC;QACrB,SAAS,GAAG,IAAI,CAAC;QACjB,IAAI,EAAE;YAAE,EAAE,EAAE,CAAC;IACjB,CAAC,CAAC;IAEF,OAAO;QACH,aAAa,CAAC,EAAE;YACZ,SAAS,GAAG,EAAE,CAAC;YACf,IAAI,WAAW;gBAAE,OAAO;YACxB,WAAW,GAAG,IAAI,CAAC;YACnB,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC;QACzB,CAAC;QACD,SAAS;YACL,IAAI,KAAK,IAAI,IAAI;gBAAE,GAAG,CAAC,KAAK,CAAC,CAAC;YAC9B,KAAK,GAAG,IAAI,CAAC;YACb,WAAW,GAAG,KAAK,CAAC;YACpB,SAAS,GAAG,IAAI,CAAC;QACrB,CAAC;KACJ,CAAC;AACN,CAAC,CAAC"}
@@ -0,0 +1,30 @@
1
+ export type IntentClickCallback = (event: PointerEvent) => void;
2
+ export type IntentClickLongPressCallback = (event: PointerEvent) => void;
3
+ export type IntentCancelReason = 'drag' | 'scroll' | 'concurrent-pointer' | 'pointercancel' | 'timeout';
4
+ export type IntentClickOptions = Readonly<{
5
+ threshold?: number | 'auto';
6
+ longPressDelay?: number;
7
+ onLongPress?: IntentClickLongPressCallback;
8
+ onIntentCancel?: (reason: IntentCancelReason) => void;
9
+ }>;
10
+ export type IntentClickSnapshot = Readonly<{
11
+ isIntent: boolean;
12
+ pointerId: number;
13
+ startX: number;
14
+ startY: number;
15
+ endX: number;
16
+ endY: number;
17
+ duration: number;
18
+ movementX: number;
19
+ movementY: number;
20
+ devicePixelRatio: number;
21
+ reason: IntentCancelReason | null;
22
+ }>;
23
+ export type IntentClickSubscribe = (onStoreChange: () => void) => () => void;
24
+ export type IntentClickStore = Readonly<{
25
+ getSnapshot: () => IntentClickSnapshot | null;
26
+ getServerSnapshot: () => IntentClickSnapshot | null;
27
+ subscribe: IntentClickSubscribe;
28
+ destroy: () => void;
29
+ }>;
30
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,mBAAmB,GAAG,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;AAChE,MAAM,MAAM,4BAA4B,GAAG,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;AAEzE,MAAM,MAAM,kBAAkB,GACxB,MAAM,GACN,QAAQ,GACR,oBAAoB,GACpB,eAAe,GACf,SAAS,CAAC;AAEhB,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IACtC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,4BAA4B,CAAC;IAC3C,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAC;CACzD,CAAC,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC;IACvC,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,kBAAkB,GAAG,IAAI,CAAC;CACrC,CAAC,CAAC;AAEH,MAAM,MAAM,oBAAoB,GAAG,CAAC,aAAa,EAAE,MAAM,IAAI,KAAK,MAAM,IAAI,CAAC;AAE7E,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC;IACpC,WAAW,EAAE,MAAM,mBAAmB,GAAG,IAAI,CAAC;IAC9C,iBAAiB,EAAE,MAAM,mBAAmB,GAAG,IAAI,CAAC;IACpD,SAAS,EAAE,oBAAoB,CAAC;IAChC,OAAO,EAAE,MAAM,IAAI,CAAC;CACvB,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,8 @@
1
+ export * from './core/index.js';
2
+ export * as vanilla from './vanilla.js';
3
+ export type * as react from './react.js';
4
+ export type * as vue from './vue.js';
5
+ export type * as svelte from './svelte.js';
6
+ export type * as solid from './solid.js';
7
+ export type * as angular from './angular.js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AAExC,YAAY,KAAK,KAAK,MAAM,YAAY,CAAC;AACzC,YAAY,KAAK,GAAG,MAAM,UAAU,CAAC;AACrC,YAAY,KAAK,MAAM,MAAM,aAAa,CAAC;AAC3C,YAAY,KAAK,KAAK,MAAM,YAAY,CAAC;AACzC,YAAY,KAAK,OAAO,MAAM,cAAc,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export * from './core/index.js';
2
+ export * as vanilla from './vanilla.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { IntentClickOptions, IntentClickCallback } from './core/types.js';
2
+ type UseIntentClickResult<T extends HTMLElement> = (element: T | null) => void;
3
+ export declare const useIntentClick: <T extends HTMLElement>(callback: IntentClickCallback, options?: IntentClickOptions) => UseIntentClickResult<T>;
4
+ export {};
5
+ //# sourceMappingURL=react.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAG/E,KAAK,oBAAoB,CAAC,CAAC,SAAS,WAAW,IAAI,CAAC,OAAO,EAAE,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC;AAE/E,eAAO,MAAM,cAAc,oCACb,mBAAmB,YACnB,kBAAkB,KAC7B,qBAAqB,CAAC,CAyBxB,CAAC"}
package/dist/react.js ADDED
@@ -0,0 +1,25 @@
1
+ import { useEffect, useRef, useCallback, useMemo } from 'react';
2
+ import { createIntentClickStore } from './core/engine.js';
3
+ export const useIntentClick = (callback, options) => {
4
+ const opts = useMemo(() => options, [options]);
5
+ const storeRef = useRef(null);
6
+ const refCallback = useCallback((element) => {
7
+ if (storeRef.current) {
8
+ storeRef.current.destroy();
9
+ storeRef.current = null;
10
+ }
11
+ if (!element)
12
+ return;
13
+ storeRef.current = createIntentClickStore(element, callback, opts);
14
+ }, [callback, opts]);
15
+ useEffect(() => {
16
+ return () => {
17
+ if (storeRef.current) {
18
+ storeRef.current.destroy();
19
+ storeRef.current = null;
20
+ }
21
+ };
22
+ }, []);
23
+ return refCallback;
24
+ };
25
+ //# sourceMappingURL=react.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.js","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAEhE,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAI1D,MAAM,CAAC,MAAM,cAAc,GAAG,CAC1B,QAA6B,EAC7B,OAA4B,EACL,EAAE;IACzB,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAC/C,MAAM,QAAQ,GAAG,MAAM,CAAmD,IAAI,CAAC,CAAC;IAEhF,MAAM,WAAW,GAAG,WAAW,CAAC,CAAC,OAAiB,EAAE,EAAE;QAClD,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;YACnB,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;YAC3B,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC;QAC5B,CAAC;QAED,IAAI,CAAC,OAAO;YAAE,OAAO;QAErB,QAAQ,CAAC,OAAO,GAAG,sBAAsB,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;IACvE,CAAC,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC;IAErB,SAAS,CAAC,GAAG,EAAE;QACX,OAAO,GAAG,EAAE;YACR,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;gBACnB,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;gBAC3B,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC;YAC5B,CAAC;QACL,CAAC,CAAC;IACN,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,WAAW,CAAC;AACvB,CAAC,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { IntentClickOptions, IntentClickCallback } from './core/types.js';
2
+ export type IntentClickRef<T extends HTMLElement> = (element: T) => void;
3
+ export declare const createIntentClick: <T extends HTMLElement>(callback: IntentClickCallback, options?: IntentClickOptions) => IntentClickRef<T>;
4
+ export declare const intentClickDirective: (el: HTMLElement, accessor: () => {
5
+ callback: IntentClickCallback;
6
+ options?: IntentClickOptions;
7
+ }) => void;
8
+ //# sourceMappingURL=solid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"solid.d.ts","sourceRoot":"","sources":["../src/solid.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAG/E,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,WAAW,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,IAAI,CAAC;AAEzE,eAAO,MAAM,iBAAiB,oCAChB,mBAAmB,YACnB,kBAAkB,KAC7B,eAAe,CAAC,CAkBlB,CAAC;AAEF,eAAO,MAAM,oBAAoB,OACzB,WAAW,YACL,MAAM;IAAE,QAAQ,EAAE,mBAAmB,CAAC;IAAC,OAAO,CAAC,EAAE,kBAAkB,CAAA;CAAE,SAiBlF,CAAC"}
package/dist/solid.js ADDED
@@ -0,0 +1,33 @@
1
+ import { onCleanup } from 'solid-js';
2
+ import { createIntentClickStore } from './core/engine.js';
3
+ export const createIntentClick = (callback, options) => {
4
+ let store = null;
5
+ let unsub = null;
6
+ const cleanup = () => {
7
+ unsub?.();
8
+ store?.destroy();
9
+ unsub = null;
10
+ store = null;
11
+ };
12
+ onCleanup(cleanup);
13
+ return (element) => {
14
+ cleanup();
15
+ store = createIntentClickStore(element, callback, options);
16
+ unsub = store.subscribe(() => { });
17
+ };
18
+ };
19
+ export const intentClickDirective = (el, accessor) => {
20
+ let store = null;
21
+ let unsub = null;
22
+ const cleanup = () => {
23
+ unsub?.();
24
+ store?.destroy();
25
+ unsub = null;
26
+ store = null;
27
+ };
28
+ const cfg = accessor();
29
+ store = createIntentClickStore(el, cfg.callback, cfg.options);
30
+ unsub = store.subscribe(() => { });
31
+ onCleanup(cleanup);
32
+ };
33
+ //# sourceMappingURL=solid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"solid.js","sourceRoot":"","sources":["../src/solid.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAErC,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAI1D,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAC7B,QAA6B,EAC7B,OAA4B,EACX,EAAE;IACnB,IAAI,KAAK,GAAqD,IAAI,CAAC;IACnE,IAAI,KAAK,GAAwB,IAAI,CAAC;IAEtC,MAAM,OAAO,GAAG,GAAS,EAAE;QACvB,KAAK,EAAE,EAAE,CAAC;QACV,KAAK,EAAE,OAAO,EAAE,CAAC;QACjB,KAAK,GAAG,IAAI,CAAC;QACb,KAAK,GAAG,IAAI,CAAC;IACjB,CAAC,CAAC;IAEF,SAAS,CAAC,OAAO,CAAC,CAAC;IAEnB,OAAO,CAAC,OAAU,EAAE,EAAE;QAClB,OAAO,EAAE,CAAC;QACV,KAAK,GAAG,sBAAsB,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC3D,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACtC,CAAC,CAAC;AACN,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAChC,EAAe,EACf,QAA+E,EACjF,EAAE;IACA,IAAI,KAAK,GAAqD,IAAI,CAAC;IACnE,IAAI,KAAK,GAAwB,IAAI,CAAC;IAEtC,MAAM,OAAO,GAAG,GAAS,EAAE;QACvB,KAAK,EAAE,EAAE,CAAC;QACV,KAAK,EAAE,OAAO,EAAE,CAAC;QACjB,KAAK,GAAG,IAAI,CAAC;QACb,KAAK,GAAG,IAAI,CAAC;IACjB,CAAC,CAAC;IAEF,MAAM,GAAG,GAAG,QAAQ,EAAE,CAAC;IACvB,KAAK,GAAG,sBAAsB,CAAC,EAAE,EAAE,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IAC9D,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAElC,SAAS,CAAC,OAAO,CAAC,CAAC;AACvB,CAAC,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { IntentClickOptions, IntentClickCallback } from './core/types.js';
2
+ export type IntentClickActionParams = {
3
+ callback: IntentClickCallback;
4
+ options?: IntentClickOptions;
5
+ };
6
+ export declare const intentClick: (node: HTMLElement, params: IntentClickActionParams) => {
7
+ update(next: IntentClickActionParams): void;
8
+ destroy(): void;
9
+ };
10
+ export declare const intentClickAction: (node: HTMLElement, params: IntentClickActionParams) => {
11
+ update(next: IntentClickActionParams): void;
12
+ destroy(): void;
13
+ };
14
+ //# sourceMappingURL=svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"svelte.d.ts","sourceRoot":"","sources":["../src/svelte.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAG/E,MAAM,MAAM,uBAAuB,GAAG;IAClC,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,OAAO,CAAC,EAAE,kBAAkB,CAAC;CAChC,CAAC;AAkCF,eAAO,MAAM,WAAW,SA/Bd,WAAW,UACT,uBAAuB;iBAqBd,uBAAuB;;CASM,CAAC;AACnD,eAAO,MAAM,iBAAiB,SAhCpB,WAAW,UACT,uBAAuB;iBAqBd,uBAAuB;;CAUY,CAAC"}
package/dist/svelte.js ADDED
@@ -0,0 +1,28 @@
1
+ import { createIntentClickStore } from './core/engine.js';
2
+ const createIntentClickAction = (node, params) => {
3
+ let store = null;
4
+ let unsub = null;
5
+ const cleanup = () => {
6
+ unsub?.();
7
+ store?.destroy();
8
+ unsub = null;
9
+ store = null;
10
+ };
11
+ const setup = (next) => {
12
+ cleanup();
13
+ store = createIntentClickStore(node, next.callback, next.options);
14
+ unsub = store.subscribe(() => { });
15
+ };
16
+ setup(params);
17
+ return {
18
+ update(next) {
19
+ setup(next);
20
+ },
21
+ destroy() {
22
+ cleanup();
23
+ },
24
+ };
25
+ };
26
+ export const intentClick = createIntentClickAction;
27
+ export const intentClickAction = createIntentClickAction;
28
+ //# sourceMappingURL=svelte.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"svelte.js","sourceRoot":"","sources":["../src/svelte.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAO1D,MAAM,uBAAuB,GAAG,CAC5B,IAAiB,EACjB,MAA+B,EACjC,EAAE;IACA,IAAI,KAAK,GAAqD,IAAI,CAAC;IACnE,IAAI,KAAK,GAAwB,IAAI,CAAC;IAEtC,MAAM,OAAO,GAAG,GAAS,EAAE;QACvB,KAAK,EAAE,EAAE,CAAC;QACV,KAAK,EAAE,OAAO,EAAE,CAAC;QACjB,KAAK,GAAG,IAAI,CAAC;QACb,KAAK,GAAG,IAAI,CAAC;IACjB,CAAC,CAAC;IAEF,MAAM,KAAK,GAAG,CAAC,IAA6B,EAAQ,EAAE;QAClD,OAAO,EAAE,CAAC;QACV,KAAK,GAAG,sBAAsB,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAClE,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACtC,CAAC,CAAC;IAEF,KAAK,CAAC,MAAM,CAAC,CAAC;IAEd,OAAO;QACH,MAAM,CAAC,IAA6B;YAChC,KAAK,CAAC,IAAI,CAAC,CAAC;QAChB,CAAC;QACD,OAAO;YACH,OAAO,EAAE,CAAC;QACd,CAAC;KACJ,CAAC;AACN,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAG,uBAAuB,CAAC;AACnD,MAAM,CAAC,MAAM,iBAAiB,GAAG,uBAAuB,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { IntentClickOptions, IntentClickCallback } from './core/types.js';
2
+ export type IntentClickController = Readonly<{
3
+ destroy: () => void;
4
+ }>;
5
+ export declare const onIntentClick: (element: HTMLElement, callback: IntentClickCallback, options?: IntentClickOptions) => (() => void);
6
+ //# sourceMappingURL=vanilla.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vanilla.d.ts","sourceRoot":"","sources":["../src/vanilla.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAG/E,MAAM,MAAM,qBAAqB,GAAG,QAAQ,CAAC;IACzC,OAAO,EAAE,MAAM,IAAI,CAAC;CACvB,CAAC,CAAC;AAEH,eAAO,MAAM,aAAa,YACb,WAAW,YACV,mBAAmB,YACnB,kBAAkB,KAC7B,CAAC,MAAM,IAAI,CAMb,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { createIntentClickStore } from './core/engine.js';
2
+ export const onIntentClick = (element, callback, options) => {
3
+ const store = createIntentClickStore(element, callback, options);
4
+ return () => {
5
+ store.destroy();
6
+ };
7
+ };
8
+ //# sourceMappingURL=vanilla.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vanilla.js","sourceRoot":"","sources":["../src/vanilla.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAM1D,MAAM,CAAC,MAAM,aAAa,GAAG,CACzB,OAAoB,EACpB,QAA6B,EAC7B,OAA4B,EAChB,EAAE;IACd,MAAM,KAAK,GAAG,sBAAsB,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAEjE,OAAO,GAAG,EAAE;QACR,KAAK,CAAC,OAAO,EAAE,CAAC;IACpB,CAAC,CAAC;AACN,CAAC,CAAC"}
package/dist/vue.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ import { type ShallowRef, type Directive } from 'vue';
2
+ import type { IntentClickOptions, IntentClickCallback } from './core/types.js';
3
+ export declare const useIntentClick: <T extends HTMLElement>(callback: IntentClickCallback, options?: IntentClickOptions) => ShallowRef<T | null>;
4
+ export declare const vIntentClick: Directive<HTMLElement, {
5
+ callback: IntentClickCallback;
6
+ options?: IntentClickOptions;
7
+ }>;
8
+ //# sourceMappingURL=vue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vue.d.ts","sourceRoot":"","sources":["../src/vue.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0C,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,MAAM,KAAK,CAAC;AAC9F,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAG/E,eAAO,MAAM,cAAc,oCACb,mBAAmB,YACnB,kBAAkB,KAC7B,WAAW,CAAC,GAAG,IAAI,CAmBrB,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,SAAS,CAChC,WAAW,EACX;IAAE,QAAQ,EAAE,mBAAmB,CAAC;IAAC,OAAO,CAAC,EAAE,kBAAkB,CAAA;CAAE,CAYlE,CAAC"}
package/dist/vue.js ADDED
@@ -0,0 +1,32 @@
1
+ import { onBeforeUnmount, onMounted, shallowRef } from 'vue';
2
+ import { createIntentClickStore } from './core/engine.js';
3
+ export const useIntentClick = (callback, options) => {
4
+ const elRef = shallowRef(null);
5
+ let unsub = null;
6
+ let store = null;
7
+ onMounted(() => {
8
+ const el = elRef.value;
9
+ if (!el)
10
+ return;
11
+ store = createIntentClickStore(el, callback, options);
12
+ unsub = store.subscribe(() => { });
13
+ });
14
+ onBeforeUnmount(() => {
15
+ unsub?.();
16
+ store?.destroy();
17
+ });
18
+ return elRef;
19
+ };
20
+ export const vIntentClick = {
21
+ mounted(el, binding) {
22
+ const { callback, options } = binding.value;
23
+ const store = createIntentClickStore(el, callback, options);
24
+ el.__intentClick = { store };
25
+ },
26
+ unmounted(el) {
27
+ const bag = el.__intentClick;
28
+ bag?.store?.destroy();
29
+ delete el.__intentClick;
30
+ },
31
+ };
32
+ //# sourceMappingURL=vue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vue.js","sourceRoot":"","sources":["../src/vue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,UAAU,EAAmC,MAAM,KAAK,CAAC;AAE9F,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,MAAM,CAAC,MAAM,cAAc,GAAG,CAC1B,QAA6B,EAC7B,OAA4B,EACR,EAAE;IACtB,MAAM,KAAK,GAAG,UAAU,CAAW,IAAI,CAAyB,CAAC;IACjE,IAAI,KAAK,GAAwB,IAAI,CAAC;IACtC,IAAI,KAAK,GAAqD,IAAI,CAAC;IAEnE,SAAS,CAAC,GAAG,EAAE;QACX,MAAM,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC;QACvB,IAAI,CAAC,EAAE;YAAE,OAAO;QAEhB,KAAK,GAAG,sBAAsB,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;QACtD,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,eAAe,CAAC,GAAG,EAAE;QACjB,KAAK,EAAE,EAAE,CAAC;QACV,KAAK,EAAE,OAAO,EAAE,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,OAAO,KAAK,CAAC;AACjB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,YAAY,GAGrB;IACA,OAAO,CAAC,EAAE,EAAE,OAAO;QACf,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC;QAC5C,MAAM,KAAK,GAAG,sBAAsB,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC3D,EAAU,CAAC,aAAa,GAAG,EAAE,KAAK,EAAE,CAAC;IAC1C,CAAC;IACD,SAAS,CAAC,EAAE;QACR,MAAM,GAAG,GAAI,EAAU,CAAC,aAAiF,CAAC;QAC1G,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;QACtB,OAAQ,EAAU,CAAC,aAAa,CAAC;IACrC,CAAC;CACJ,CAAC"}
package/package.json ADDED
@@ -0,0 +1,164 @@
1
+ {
2
+ "name": "intent-click",
3
+ "version": "0.1.0",
4
+ "description": "Detect intentional clicks vs accidental taps on touchscreens. Adaptive threshold, ghost click suppression, scroll intent detection.",
5
+ "author": {
6
+ "name": "AntonVoronezh",
7
+ "email": "anton.voronezh.1990@gmail.com",
8
+ "url": "https://github.com/AntonVoronezh"
9
+ },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/AntonVoronezh/intent-click.git"
13
+ },
14
+ "bugs": {
15
+ "url": "https://github.com/AntonVoronezh/intent-click/issues"
16
+ },
17
+ "homepage": "https://github.com/AntonVoronezh/intent-click#readme",
18
+ "funding": [
19
+ {
20
+ "type": "boosty",
21
+ "url": "https://boosty.to/antonvoronezh/donate"
22
+ },
23
+ {
24
+ "type": "crypto",
25
+ "url": "https://t.me/AntonVoronezhh/4"
26
+ }
27
+ ],
28
+ "files": [
29
+ "dist",
30
+ "README.md",
31
+ "LICENSE"
32
+ ],
33
+ "scripts": {
34
+ "build": "npm run build:ts && npm run build:angular",
35
+ "build:ts": "tsc -p tsconfig.build.json",
36
+ "build:angular": "ngc -p tsconfig.angular.json",
37
+ "clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
38
+ "prepack": "npm run build",
39
+ "typecheck": "tsc -p tsconfig.json --noEmit",
40
+ "test": "vitest run",
41
+ "test:watch": "vitest",
42
+ "docs:dev": "vitepress dev docs",
43
+ "docs:build": "vitepress build docs",
44
+ "docs:preview": "vitepress preview docs"
45
+ },
46
+ "keywords": [
47
+ "click",
48
+ "touch",
49
+ "tap",
50
+ "intent",
51
+ "pointer",
52
+ "touchscreen",
53
+ "mobile",
54
+ "ghost-click",
55
+ "scroll-detection",
56
+ "adaptive-threshold",
57
+ "react",
58
+ "vue",
59
+ "svelte",
60
+ "solid",
61
+ "angular"
62
+ ],
63
+ "engines": {
64
+ "node": ">=18"
65
+ },
66
+ "license": "MIT",
67
+ "sideEffects": false,
68
+ "type": "module",
69
+ "peerDependencies": {
70
+ "@angular/core": ">=17.0.0",
71
+ "react": ">=18.0.0",
72
+ "solid-js": ">=1.7.0",
73
+ "svelte": ">=4.0.0",
74
+ "vue": ">=3.3.0"
75
+ },
76
+ "peerDependenciesMeta": {
77
+ "react": {
78
+ "optional": true
79
+ },
80
+ "vue": {
81
+ "optional": true
82
+ },
83
+ "svelte": {
84
+ "optional": true
85
+ },
86
+ "solid-js": {
87
+ "optional": true
88
+ },
89
+ "@angular/core": {
90
+ "optional": true
91
+ }
92
+ },
93
+ "devDependencies": {
94
+ "@angular/compiler": "^17.3.12",
95
+ "@angular/compiler-cli": "^17.3.12",
96
+ "@angular/core": "^17.3.0",
97
+ "@semantic-release/changelog": "^6.0.3",
98
+ "@semantic-release/commit-analyzer": "^11.1.0",
99
+ "@semantic-release/git": "^10.0.1",
100
+ "@semantic-release/github": "^10.3.5",
101
+ "@semantic-release/npm": "^13.1.3",
102
+ "@semantic-release/release-notes-generator": "^12.1.0",
103
+ "@types/node": "^22.0.0",
104
+ "@types/react": "^18.3.0",
105
+ "@types/react-dom": "^18.3.0",
106
+ "happy-dom": "^20.5.0",
107
+ "jsdom": "^28.0.0",
108
+ "react": "^18.3.0",
109
+ "react-dom": "^18.3.0",
110
+ "semantic-release": "^23.1.1",
111
+ "solid-js": "^1.9.0",
112
+ "svelte": "^5.56.4",
113
+ "typescript": "~5.4.5",
114
+ "vitepress": "^1.6.4",
115
+ "vitest": "^4.0.18",
116
+ "vue": "^3.5.0"
117
+ },
118
+ "main": "./dist/index.js",
119
+ "module": "./dist/index.js",
120
+ "types": "./dist/index.d.ts",
121
+ "exports": {
122
+ ".": {
123
+ "types": "./dist/index.d.ts",
124
+ "import": "./dist/index.js",
125
+ "default": "./dist/index.js"
126
+ },
127
+ "./core": {
128
+ "types": "./dist/core/index.d.ts",
129
+ "import": "./dist/core/index.js",
130
+ "default": "./dist/core/index.js"
131
+ },
132
+ "./react": {
133
+ "types": "./dist/react.d.ts",
134
+ "import": "./dist/react.js",
135
+ "default": "./dist/react.js"
136
+ },
137
+ "./vue": {
138
+ "types": "./dist/vue.d.ts",
139
+ "import": "./dist/vue.js",
140
+ "default": "./dist/vue.js"
141
+ },
142
+ "./svelte": {
143
+ "types": "./dist/svelte.d.ts",
144
+ "import": "./dist/svelte.js",
145
+ "default": "./dist/svelte.js"
146
+ },
147
+ "./solid": {
148
+ "types": "./dist/solid.d.ts",
149
+ "import": "./dist/solid.js",
150
+ "default": "./dist/solid.js"
151
+ },
152
+ "./angular": {
153
+ "types": "./dist/angular.d.ts",
154
+ "import": "./dist/angular.js",
155
+ "default": "./dist/angular.js"
156
+ },
157
+ "./vanilla": {
158
+ "types": "./dist/vanilla.d.ts",
159
+ "import": "./dist/vanilla.js",
160
+ "default": "./dist/vanilla.js"
161
+ },
162
+ "./package.json": "./package.json"
163
+ }
164
+ }