@msobiecki/react-marauders-path 1.23.0 → 1.24.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 CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![License](https://img.shields.io/badge/license-%20%20GNU%20GPLv3%20-green.svg)](https://github.com/msobiecki/react-marauders-path/blob/master/LICENSE)
4
4
 
5
- A lightweight, type-safe React library for handling keyboard, wheel, swipe, and drag events. Perfect for games, interactive applications, and input-driven interfaces.
5
+ A lightweight, type-safe React library for handling keyboard, wheel, swipe, drag, and pinch events. Perfect for games, interactive applications, and input-driven interfaces.
6
6
 
7
7
  ![react-marauders-path](./docs/images/logotype.png)
8
8
 
@@ -12,6 +12,7 @@ A lightweight, type-safe React library for handling keyboard, wheel, swipe, and
12
12
  - 🎡 **Wheel Event Handling** - Track wheel, delta values with optional `requestAnimationFrame` batching for smoother updates
13
13
  - 🖐️ **Swipe Gesture Handling** - Detect directional swipes with configurable distance and velocity with pointer type filtering
14
14
  - 🖱️ **Drag Event Handling** - Track movement, delta values, duration, start/end positions with pointer type filtering and optional `requestAnimationFrame` batching for smoother updates
15
+ - 🤏 **Pinch Gesture Handling** - Track two-finger distance, delta, and scale with pointer type filtering and optional `requestAnimationFrame` batching for smoother updates
15
16
 
16
17
  ## Installation
17
18
 
@@ -122,11 +123,25 @@ function MyComponent() {
122
123
  }
123
124
  ```
124
125
 
126
+ ### Pinch Event Hook
127
+
128
+ ```typescript
129
+ import { usePinch } from '@msobiecki/react-marauders-path';
130
+
131
+ function MyComponent() {
132
+ usePinch((event, data) => {
133
+ console.log(`Pinch scale: ${data.scale}, delta: ${data.delta}`);
134
+ });
135
+
136
+ return <div>Pinch to zoom</div>;
137
+ }
138
+ ```
139
+
125
140
  ## API
126
141
 
127
142
  ### `useKey(keyEvent, callback, options?)`
128
143
 
129
- Main hook for keyboard event handling.
144
+ Hook for keyboard event handling with support for single keys, combinations, and sequences.
130
145
 
131
146
  **Parameters:**
132
147
 
@@ -256,6 +271,40 @@ interface DragData {
256
271
  }
257
272
  ```
258
273
 
274
+ ### `usePinch(callback, options?)`
275
+
276
+ Hook for handling two-pointer pinch gestures with distance and scale tracking.
277
+
278
+ **Parameters:**
279
+
280
+ - `callback: (event: PointerEvent, data: PinchData) => void | boolean` - Called when pinch event occurs
281
+ - `options?: UsePinchOptions` - Optional configuration
282
+
283
+ **Options:**
284
+
285
+ ```typescript
286
+ interface UsePinchOptions {
287
+ eventPointerTypes?: Array<"touch" | "mouse" | "pen">; // Default: ["touch"]
288
+ eventCapture?: boolean; // Default: false
289
+ eventOnce?: boolean; // Default: false
290
+ eventStopImmediatePropagation?: boolean; // Default: false
291
+ threshold?: number; // Default: 0 (px) - Minimum pinch distance change
292
+ container?: RefObject<HTMLElement>; // Default: window
293
+ raf?: boolean; // Default: false - Use requestAnimationFrame for batching
294
+ }
295
+ ```
296
+
297
+ **Pinch Data:**
298
+
299
+ ```typescript
300
+ interface PinchData {
301
+ distance: number; // Current distance between active pointers
302
+ delta: number; // Distance change since previous pinch update
303
+ totalDelta: number; // Distance change since pinch start
304
+ scale: number; // Current scale ratio (distance / startDistance)
305
+ }
306
+ ```
307
+
259
308
  ## Advanced Examples
260
309
 
261
310
  ### Using Options for Event Type and Propagation Control
@@ -370,7 +419,6 @@ npm run lint
370
419
  - 🚧 **`useTap`** – single tap / click
371
420
  - 🚧 **`useDoubleTap`** – quick double tap
372
421
  - 🚧 **`usePress`** – press and hold (longPress)
373
- - 🚧 **`usePinch`** – two-finger pinch / zoom
374
422
 
375
423
  ### Pointer / Mouse Hooks (Unified)
376
424
 
@@ -0,0 +1,276 @@
1
+ import { RefObject } from 'react';
2
+
3
+ export declare interface CombinationActiveKey {
4
+ pressedAt: number;
5
+ releasedAt?: number;
6
+ }
7
+
8
+ export declare interface CombinationState {
9
+ activeKeys: Map<Key, CombinationActiveKey>;
10
+ }
11
+
12
+ export declare interface DragData {
13
+ deltaX: number;
14
+ deltaY: number;
15
+ movementX: number;
16
+ movementY: number;
17
+ duration: number;
18
+ startX: number;
19
+ startY: number;
20
+ endX: number;
21
+ endY: number;
22
+ }
23
+
24
+ export declare type DragEventPointerType = (typeof DragEventPointerTypes)[keyof typeof DragEventPointerTypes];
25
+
26
+ export declare const DragEventPointerTypes: {
27
+ readonly Touch: "touch";
28
+ readonly Mouse: "mouse";
29
+ readonly Pen: "pen";
30
+ };
31
+
32
+ export declare interface DragOptions {
33
+ eventPointerTypes: DragEventPointerType[];
34
+ eventCapture: boolean;
35
+ eventOnce: boolean;
36
+ eventStopImmediatePropagation: boolean;
37
+ threshold: number;
38
+ container: {
39
+ current: HTMLElement | null;
40
+ };
41
+ raf: boolean;
42
+ }
43
+
44
+ export declare interface DragState {
45
+ startX: number;
46
+ startY: number;
47
+ lastX: number;
48
+ lastY: number;
49
+ startTime: number;
50
+ active: boolean;
51
+ }
52
+
53
+ export declare type Key = string;
54
+
55
+ export declare type KeyChord = (Key | Key[])[];
56
+
57
+ export declare type KeyEvent = Key | Key[];
58
+
59
+ export declare type KeyEventType = (typeof KeyEventTypes)[keyof typeof KeyEventTypes];
60
+
61
+ export declare const KeyEventTypes: {
62
+ readonly KeyUp: "keyup";
63
+ readonly KeyDown: "keydown";
64
+ };
65
+
66
+ export declare interface KeyOptions {
67
+ eventType: KeyEventType;
68
+ eventRepeat: boolean;
69
+ eventCapture: boolean;
70
+ eventOnce: boolean;
71
+ eventStopImmediatePropagation: boolean;
72
+ sequenceThreshold: number;
73
+ combinationThreshold: number;
74
+ container: RefObject<HTMLElement | null>;
75
+ }
76
+
77
+ export declare interface PinchData {
78
+ distance: number;
79
+ delta: number;
80
+ totalDelta: number;
81
+ scale: number;
82
+ }
83
+
84
+ export declare type PinchEventPointerType = (typeof PinchEventPointerTypes)[keyof typeof PinchEventPointerTypes];
85
+
86
+ export declare const PinchEventPointerTypes: {
87
+ readonly Touch: "touch";
88
+ readonly Mouse: "mouse";
89
+ readonly Pen: "pen";
90
+ };
91
+
92
+ export declare interface PinchOptions {
93
+ eventPointerTypes: PinchEventPointerType[];
94
+ eventCapture: boolean;
95
+ eventOnce: boolean;
96
+ eventStopImmediatePropagation: boolean;
97
+ threshold: number;
98
+ container: {
99
+ current: HTMLElement | null;
100
+ };
101
+ raf: boolean;
102
+ }
103
+
104
+ export declare interface PinchState {
105
+ pointers: Map<number, PointerEvent>;
106
+ startDistance: number;
107
+ lastDistance: number;
108
+ active: boolean;
109
+ }
110
+
111
+ export declare interface SequenceState {
112
+ key: Key;
113
+ chord: KeyChord;
114
+ index: number;
115
+ sequenceTimeout: ReturnType<typeof setTimeout> | null;
116
+ }
117
+
118
+ export declare interface SwipeData {
119
+ deltaX: number;
120
+ deltaY: number;
121
+ velocity: number;
122
+ duration: number;
123
+ }
124
+
125
+ export declare type SwipeDirection = (typeof SwipeDirections)[keyof typeof SwipeDirections];
126
+
127
+ export declare const SwipeDirections: {
128
+ readonly Left: "left";
129
+ readonly Right: "right";
130
+ readonly Up: "up";
131
+ readonly Down: "down";
132
+ readonly Horizontal: "horizontal";
133
+ readonly Vertical: "vertical";
134
+ readonly Both: "both";
135
+ };
136
+
137
+ export declare type SwipeEventPointerType = (typeof SwipeEventPointerTypes)[keyof typeof SwipeEventPointerTypes];
138
+
139
+ export declare const SwipeEventPointerTypes: {
140
+ readonly Touch: "touch";
141
+ readonly Mouse: "mouse";
142
+ readonly Pen: "pen";
143
+ };
144
+
145
+ export declare interface SwipeOptions {
146
+ eventPointerTypes: SwipeEventPointerType[];
147
+ eventCapture: boolean;
148
+ eventOnce: boolean;
149
+ eventStopImmediatePropagation: boolean;
150
+ threshold: number;
151
+ velocity: number;
152
+ container: {
153
+ current: HTMLElement | null;
154
+ };
155
+ }
156
+
157
+ export declare interface SwipeState {
158
+ startX: number;
159
+ startY: number;
160
+ startTime: number;
161
+ active: boolean;
162
+ }
163
+
164
+ export declare const useDrag: (dragCallback: UseDragCallback, options?: UseDragOptions) => void;
165
+
166
+ export declare type UseDragCallback = ((event: PointerEvent, data: DragData) => boolean) | ((event: PointerEvent, data: DragData) => void);
167
+
168
+ export declare type UseDragOptions = Partial<DragOptions>;
169
+
170
+ /**
171
+ * React hook for handling keyboard events with support for key sequences and combinations.
172
+ *
173
+ * Enables listening for single key presses, key combinations, and sequential key presses.
174
+ * Supports customizable options like event type, repeat handling, and one-time listeners.
175
+ *
176
+ * @template T - The callback function type
177
+ * @param {UseKeySchema} key - Single key, combination, sequence, or array of patterns to listen for
178
+ * @param {UseKeyCallback} keyCallback - Callback function invoked when key pattern matches
179
+ * @param {UseKeyOptions} [options] - Configuration options for the hook
180
+ * @param {KeyEventType} [options.eventType=KeyEventTypes.KeyUp] - Type of keyboard event ('keydown' or 'keyup')
181
+ * @param {boolean} [options.eventRepeat=false] - Allow repeated key presses to trigger callback
182
+ * @param {boolean} [options.eventCapture=false] - Use event capture phase instead of bubbling
183
+ * @param {boolean} [options.eventOnce=false] - Trigger callback only once
184
+ * @param {boolean} [options.eventStopImmediatePropagation=false] - Stop immediate propagation
185
+ * @param {number} [options.sequenceThreshold=1000] - Timeout in ms between sequence keys
186
+ * @param {number} [options.combinationThreshold=200] - Timeout in ms between combination keys
187
+ * @param {RefObject<HTMLElement>} [options.container] - DOM element to attach listener to (default: window)
188
+ *
189
+ * @example
190
+ * // Single key schema
191
+ * useKey('a', (event, key) => console.log(`Pressed ${key}`));
192
+ *
193
+ * @example
194
+ * // Multiple patterns of single key schema
195
+ * useKey(['a', 'b', 'c'], (event, key) => console.log(`Pressed ${key}`));
196
+ *
197
+ * @example
198
+ * // Combination key schema
199
+ * useKey('a+b', (event, key) => {
200
+ * console.log(`Pressed ${key}`);
201
+ * });
202
+ *
203
+ * @example
204
+ * // Multiple patterns of combination key schema
205
+ * useKey(['a+b', 'c+d'], (event, key) => {
206
+ * console.log(`Pressed ${key}`);
207
+ * });
208
+ *
209
+ * @example
210
+ * // Sequential key schema
211
+ * useKey('ArrowUp ArrowUp ArrowDown ArrowDown', (event, key) => {
212
+ * console.log(`Pressed ${key}`);
213
+ * });
214
+ *
215
+ * @example
216
+ * // Multiple patterns of sequential key schema
217
+ * useKey(['ArrowUp ArrowUp ArrowDown ArrowDown', 'ArrowLeft ArrowRight'], (event, key) => {
218
+ * console.log(`Pressed ${key}`);
219
+ * });
220
+ *
221
+ * @example
222
+ * // Using options to listen for a key on keydown event and stop propagation
223
+ * useKey('Any', handleSubmit, {
224
+ * eventType: KeyEventTypes.KeyDown,
225
+ * eventStopImmediatePropagation: true,
226
+ * container: inputRef
227
+ * });
228
+ *
229
+ */
230
+ export declare const useKey: (key: UseKeySchema, keyCallback: UseKeyCallback, options?: UseKeyOptions) => void;
231
+
232
+ export declare type UseKeyCallback = ((event: KeyboardEvent, key: Key, ...properties: unknown[]) => boolean) | ((event: KeyboardEvent, key: Key, ...properties: unknown[]) => void);
233
+
234
+ export declare type UseKeyOptions = Partial<KeyOptions>;
235
+
236
+ export declare type UseKeySchema = KeyEvent;
237
+
238
+ export declare const usePinch: (pinchCallback: UsePinchCallback, options?: UsePinchOptions) => void;
239
+
240
+ export declare type UsePinchCallback = ((event: PointerEvent, data: PinchData) => boolean) | ((event: PointerEvent, data: PinchData) => void);
241
+
242
+ export declare type UsePinchOptions = Partial<PinchOptions>;
243
+
244
+ export declare const useSwipe: (swipe: UseSwipeSchema, swipeCallback: UseSwipeCallback, options?: UseSwipeOptions) => void;
245
+
246
+ export declare type UseSwipeCallback = ((event: PointerEvent, direction: SwipeDirection, data: SwipeData, ...properties: unknown[]) => boolean) | ((event: PointerEvent, direction: SwipeDirection, data: SwipeData, ...properties: unknown[]) => void);
247
+
248
+ export declare type UseSwipeOptions = Partial<SwipeOptions>;
249
+
250
+ export declare type UseSwipeSchema = SwipeDirection | SwipeDirection[];
251
+
252
+ export declare const useWheel: (wheelCallback: UseWheelCallback, options?: UseWheelOptions) => void;
253
+
254
+ export declare type UseWheelCallback = ((event: WheelEvent, delta: WheelData, ...properties: unknown[]) => void) | ((event: WheelEvent, delta: WheelData, ...properties: unknown[]) => boolean);
255
+
256
+ export declare type UseWheelOptions = Partial<WheelOptions>;
257
+
258
+ export declare interface WheelData {
259
+ deltaX: number;
260
+ deltaY: number;
261
+ deltaZ: number;
262
+ deltaMode: number;
263
+ }
264
+
265
+ export declare interface WheelOptions {
266
+ eventPassive: boolean;
267
+ eventCapture: boolean;
268
+ eventOnce: boolean;
269
+ eventStopImmediatePropagation: boolean;
270
+ container: {
271
+ current: HTMLElement | null;
272
+ };
273
+ raf: boolean;
274
+ }
275
+
276
+ export { }
@@ -0,0 +1,761 @@
1
+ import { useRef as E, useCallback as u, useEffect as x } from "react";
2
+ const k = (n, s, c, r) => {
3
+ r.stopImmediate && n.stopImmediatePropagation(), c(n, s) && n.preventDefault(), r.once && r.onOnce?.();
4
+ }, Z = {
5
+ eventPointerTypes: ["touch", "mouse", "pen"],
6
+ eventCapture: !1,
7
+ eventOnce: !1,
8
+ eventStopImmediatePropagation: !1,
9
+ threshold: 0,
10
+ container: { current: null },
11
+ raf: !1
12
+ }, ie = (n, s = {}) => {
13
+ const {
14
+ eventPointerTypes: c,
15
+ eventCapture: r,
16
+ eventOnce: a,
17
+ eventStopImmediatePropagation: p,
18
+ threshold: f,
19
+ container: R,
20
+ raf: O
21
+ } = { ...Z, ...s }, T = E(null), m = E(null), y = E(null), h = E(null), A = E(null), d = E({
22
+ startX: 0,
23
+ startY: 0,
24
+ lastX: 0,
25
+ lastY: 0,
26
+ startTime: 0,
27
+ active: !1
28
+ }), S = u(() => {
29
+ m.current?.abort();
30
+ }, []), L = u(() => {
31
+ y.current = null;
32
+ const e = h.current, t = A.current;
33
+ !e || !t || (k(t, e, n, {
34
+ stopImmediate: p,
35
+ once: a,
36
+ onOnce: () => {
37
+ S();
38
+ }
39
+ }), h.current = null, A.current = null);
40
+ }, [n, p, a, S]), g = u(
41
+ (e) => {
42
+ e.isPrimary && c.includes(e.pointerType) && (d.current = {
43
+ startX: e.clientX,
44
+ startY: e.clientY,
45
+ lastX: e.clientX,
46
+ lastY: e.clientY,
47
+ startTime: Date.now(),
48
+ active: !0
49
+ });
50
+ },
51
+ [c]
52
+ ), Y = u(
53
+ (e) => {
54
+ const t = d.current;
55
+ if (!t.active || !e.isPrimary || !c.includes(e.pointerType))
56
+ return;
57
+ const D = e.clientX - t.startX, P = e.clientY - t.startY, v = e.clientX - t.lastX, M = e.clientY - t.lastY;
58
+ if (Math.hypot(D, P) < f) return;
59
+ const i = Date.now() - t.startTime, l = {
60
+ deltaX: D,
61
+ deltaY: P,
62
+ movementX: v,
63
+ movementY: M,
64
+ duration: i,
65
+ startX: t.startX,
66
+ startY: t.startY,
67
+ endX: e.clientX,
68
+ endY: e.clientY
69
+ };
70
+ if (t.lastX = e.clientX, t.lastY = e.clientY, !O) {
71
+ k(e, l, n, {
72
+ stopImmediate: p,
73
+ once: a,
74
+ onOnce: () => {
75
+ S();
76
+ }
77
+ });
78
+ return;
79
+ }
80
+ h.current = l, A.current = e, y.current === null && (y.current = requestAnimationFrame(L));
81
+ },
82
+ [
83
+ c,
84
+ f,
85
+ O,
86
+ n,
87
+ p,
88
+ a,
89
+ S,
90
+ L
91
+ ]
92
+ ), X = u(
93
+ (e) => {
94
+ e.isPrimary && c.includes(e.pointerType) && (d.current.active = !1);
95
+ },
96
+ [c]
97
+ ), K = u(() => {
98
+ d.current.active = !1;
99
+ }, []);
100
+ x(() => {
101
+ T.current = R?.current ?? globalThis, m.current = new AbortController();
102
+ const e = (v) => g(v), t = (v) => Y(v), D = (v) => X(v), P = () => K();
103
+ return T.current.addEventListener(
104
+ "pointerdown",
105
+ e,
106
+ {
107
+ capture: r,
108
+ signal: m.current.signal
109
+ }
110
+ ), T.current.addEventListener(
111
+ "pointermove",
112
+ t,
113
+ {
114
+ capture: r,
115
+ signal: m.current.signal
116
+ }
117
+ ), T.current.addEventListener("pointerup", D, {
118
+ capture: r,
119
+ signal: m.current.signal
120
+ }), T.current.addEventListener(
121
+ "pointercancel",
122
+ P,
123
+ {
124
+ capture: r,
125
+ signal: m.current.signal
126
+ }
127
+ ), () => {
128
+ m.current?.abort(), d.current.active = !1, y.current !== null && cancelAnimationFrame(y.current);
129
+ };
130
+ }, [
131
+ R,
132
+ r,
133
+ g,
134
+ Y,
135
+ X,
136
+ K
137
+ ]);
138
+ }, ue = {
139
+ Touch: "touch",
140
+ Mouse: "mouse",
141
+ Pen: "pen"
142
+ }, F = {
143
+ KeyUp: "keyup",
144
+ KeyDown: "keydown"
145
+ }, U = {
146
+ ENTER: "Enter",
147
+ ESC: "Escape",
148
+ ESCAPE: "Escape",
149
+ SPACE: "Space",
150
+ TAB: "Tab",
151
+ SHIFT: "Shift",
152
+ CONTROL: "Control",
153
+ CTRL: "Control",
154
+ ALT: "Alt",
155
+ META: "Meta",
156
+ ARROWUP: "ArrowUp",
157
+ ARROWDOWN: "ArrowDown",
158
+ ARROWLEFT: "ArrowLeft",
159
+ ARROWRIGHT: "ArrowRight",
160
+ BACKSPACE: "Backspace",
161
+ DELETE: "Delete",
162
+ DEL: "Delete",
163
+ INSERT: "Insert",
164
+ HOME: "Home",
165
+ END: "End",
166
+ PAGEUP: "PageUp",
167
+ PAGEDOWN: "PageDown",
168
+ CONTEXTMENU: "ContextMenu",
169
+ CAPSLOCK: "CapsLock",
170
+ NUMLOCK: "NumLock",
171
+ SCROLLLOCK: "ScrollLock",
172
+ ANY: "Any"
173
+ // Special key to match any key in sequences
174
+ }, _ = (n) => {
175
+ if (!n)
176
+ return n;
177
+ const s = n.trim(), c = s.toUpperCase();
178
+ return c in U ? U[c] : s.length === 1 ? s.toLowerCase() : s[0].toUpperCase() + s.slice(1).toLowerCase();
179
+ }, J = (n) => n.split(" ").filter(Boolean).map(
180
+ (s) => s.split("+").map((c) => _(c)).join("+")
181
+ ).join(" "), Q = (n) => (Array.isArray(n) ? n : [n]).map((c) => {
182
+ const r = J(c), p = r.split(" ").filter((f) => f.length > 0).map(
183
+ (f) => f.includes("+") ? f.split("+") : f
184
+ );
185
+ return {
186
+ key: r,
187
+ chord: p,
188
+ index: 0,
189
+ sequenceTimeout: null
190
+ };
191
+ }), ee = (n, s) => {
192
+ const c = { ...n, index: 0, sequenceTimeout: null };
193
+ return n.sequenceTimeout && clearTimeout(n.sequenceTimeout), s.map(
194
+ (r) => r === n ? c : r
195
+ );
196
+ }, B = (n, s, c, r) => {
197
+ const a = {
198
+ ...n,
199
+ index: n.index + 1,
200
+ sequenceTimeout: n.sequenceTimeout
201
+ };
202
+ c && (a.sequenceTimeout && clearTimeout(a.sequenceTimeout), a.sequenceTimeout = setTimeout(
203
+ () => r(a),
204
+ c
205
+ ));
206
+ const p = s.map(
207
+ (f) => f === n ? a : f
208
+ );
209
+ return [a, p];
210
+ }, q = (n, s, c, r) => {
211
+ r.stopImmediate && n.stopImmediatePropagation(), c(n, s) && n.preventDefault(), r.once && r.onOnce?.();
212
+ }, te = (n, s) => !(!s.repeat && n.repeat), $ = {
213
+ eventType: F.KeyUp,
214
+ eventRepeat: !1,
215
+ eventCapture: !1,
216
+ eventOnce: !1,
217
+ eventStopImmediatePropagation: !1,
218
+ sequenceThreshold: 1e3,
219
+ combinationThreshold: 200,
220
+ container: { current: null }
221
+ }, le = (n, s, c = $) => {
222
+ const {
223
+ eventType: r,
224
+ eventRepeat: a,
225
+ eventCapture: p,
226
+ eventOnce: f,
227
+ eventStopImmediatePropagation: R,
228
+ sequenceThreshold: O,
229
+ combinationThreshold: T,
230
+ container: m
231
+ } = { ...$, ...c }, y = E(null), h = E(null), A = E({
232
+ activeKeys: /* @__PURE__ */ new Map()
233
+ }), d = E([]), S = u(() => {
234
+ h.current && h.current.abort();
235
+ }, []), L = u(() => {
236
+ A.current.activeKeys.clear();
237
+ }, []), g = u((o) => {
238
+ d.current = ee(
239
+ o,
240
+ d.current
241
+ );
242
+ }, []), Y = u(
243
+ (o) => te(o, {
244
+ repeat: a
245
+ }),
246
+ [a]
247
+ ), X = u((o) => {
248
+ const i = o.key === " " ? U.SPACE : o.key;
249
+ A.current.activeKeys.set(i, {
250
+ pressedAt: Date.now()
251
+ });
252
+ }, []), K = u((o) => {
253
+ const i = o.key === " " ? U.SPACE : o.key, l = A.current.activeKeys.get(i);
254
+ l && (l.releasedAt = Date.now());
255
+ }, []), e = u(() => {
256
+ const o = Date.now(), i = A.current;
257
+ [...i.activeKeys.entries()].forEach(([l, w]) => {
258
+ r === F.KeyDown ? w.releasedAt && i.activeKeys.delete(l) : w.releasedAt && o - w.releasedAt > T && i.activeKeys.delete(l);
259
+ });
260
+ }, [r, T]), t = u(
261
+ (o, i) => {
262
+ if (r === F.KeyDown)
263
+ return i.size === o.length && o.every((l) => l === U.ANY ? i.size > 0 : i.has(l));
264
+ if (r === F.KeyUp) {
265
+ const l = o.map((C) => {
266
+ if (C === U.ANY) {
267
+ const H = [...i.entries()].at(-1);
268
+ return H ? H[1] : void 0;
269
+ }
270
+ return i.get(C);
271
+ });
272
+ if (l.some((C) => !C?.releasedAt))
273
+ return !1;
274
+ const w = l.map((C) => C?.pressedAt).filter((C) => C !== void 0), I = l.map((C) => C?.releasedAt).filter((C) => C !== void 0), N = Math.min(...I), W = Math.max(...I);
275
+ return !(Math.max(...w) > N || W - N > T);
276
+ }
277
+ return !1;
278
+ },
279
+ [r, T]
280
+ ), D = u(
281
+ (o, i) => {
282
+ const l = i.chord[0];
283
+ if (Array.isArray(l)) {
284
+ const { activeKeys: I } = A.current;
285
+ if (!t(l, I))
286
+ return;
287
+ q(o, i.key, s, {
288
+ stopImmediate: R,
289
+ once: f,
290
+ onOnce: () => {
291
+ S();
292
+ }
293
+ });
294
+ return;
295
+ }
296
+ const w = o.key === " " ? U.SPACE : o.key;
297
+ l !== U.ANY && l !== w || q(o, i.key, s, {
298
+ stopImmediate: R,
299
+ once: f,
300
+ onOnce: () => {
301
+ S();
302
+ }
303
+ });
304
+ },
305
+ [
306
+ R,
307
+ f,
308
+ s,
309
+ t,
310
+ S
311
+ ]
312
+ ), P = u(
313
+ (o, i) => {
314
+ const l = i.chord[i.index];
315
+ if (Array.isArray(l)) {
316
+ const { activeKeys: W } = A.current;
317
+ if (!t(l, W))
318
+ return;
319
+ const [z, C] = B(
320
+ i,
321
+ d.current,
322
+ O,
323
+ g
324
+ );
325
+ d.current = C, z.index === z.chord.length && (q(o, z.key, s, {
326
+ stopImmediate: R,
327
+ once: f,
328
+ onOnce: () => {
329
+ S();
330
+ }
331
+ }), g(z));
332
+ return;
333
+ }
334
+ const w = o.key === " " ? U.SPACE : o.key;
335
+ if (l !== U.ANY && l !== w) {
336
+ g(i);
337
+ return;
338
+ }
339
+ const [I, N] = B(
340
+ i,
341
+ d.current,
342
+ O,
343
+ g
344
+ );
345
+ d.current = N, I.index === I.chord.length && (q(o, I.key, s, {
346
+ stopImmediate: R,
347
+ once: f,
348
+ onOnce: () => {
349
+ S();
350
+ }
351
+ }), g(I));
352
+ },
353
+ [
354
+ f,
355
+ R,
356
+ O,
357
+ s,
358
+ g,
359
+ t,
360
+ S
361
+ ]
362
+ ), v = u(
363
+ (o) => {
364
+ d.current.forEach((i) => {
365
+ i.chord.length === 1 ? D(o, i) : P(o, i);
366
+ });
367
+ },
368
+ [D, P]
369
+ ), M = u(
370
+ (o) => {
371
+ Y(o) && (e(), v(o));
372
+ },
373
+ [Y, e, v]
374
+ );
375
+ x(() => {
376
+ d.current = Q(n);
377
+ }, [n]), x(() => {
378
+ y.current = m?.current ?? globalThis, h.current = new AbortController();
379
+ const o = (w) => X(w);
380
+ y.current.addEventListener("keydown", o, {
381
+ capture: p,
382
+ signal: h.current.signal
383
+ });
384
+ const i = (w) => K(w);
385
+ y.current.addEventListener("keyup", i, {
386
+ capture: p,
387
+ signal: h.current.signal
388
+ });
389
+ const l = (w) => M(w);
390
+ return y.current.addEventListener(r, l, {
391
+ capture: p,
392
+ signal: h.current.signal
393
+ }), () => {
394
+ h.current?.abort(), L(), d.current.forEach((w) => g(w));
395
+ };
396
+ }, [
397
+ r,
398
+ p,
399
+ m,
400
+ X,
401
+ K,
402
+ M,
403
+ L,
404
+ g
405
+ ]);
406
+ }, G = (n, s, c, r) => {
407
+ r.stopImmediate && n.stopImmediatePropagation(), c(n, s) && n.preventDefault(), r.once && r.onOnce?.();
408
+ }, ne = {
409
+ eventPointerTypes: ["touch"],
410
+ eventCapture: !1,
411
+ eventOnce: !1,
412
+ eventStopImmediatePropagation: !1,
413
+ threshold: 0,
414
+ container: { current: null },
415
+ raf: !1
416
+ }, de = (n, s = {}) => {
417
+ const {
418
+ eventPointerTypes: c,
419
+ eventCapture: r,
420
+ eventOnce: a,
421
+ eventStopImmediatePropagation: p,
422
+ threshold: f,
423
+ container: R,
424
+ raf: O
425
+ } = { ...ne, ...s }, T = E(null), m = E(null), y = E(null), h = E(null), A = E(null), d = E({
426
+ pointers: /* @__PURE__ */ new Map(),
427
+ startDistance: 0,
428
+ lastDistance: 0,
429
+ active: !1
430
+ }), S = u(() => {
431
+ m.current?.abort();
432
+ }, []), L = u(() => {
433
+ y.current = null;
434
+ const e = h.current, t = A.current;
435
+ !e || !t || (G(t, e, n, {
436
+ stopImmediate: p,
437
+ once: a,
438
+ onOnce: () => S()
439
+ }), h.current = null, A.current = null);
440
+ }, [
441
+ n,
442
+ p,
443
+ a,
444
+ S
445
+ ]), g = u(
446
+ (e) => {
447
+ if (!c.includes(e.pointerType))
448
+ return;
449
+ const t = d.current;
450
+ if (t.pointers.set(e.pointerId, e), t.pointers.size === 2) {
451
+ const [D, P] = [...t.pointers.values()], v = P.clientX - D.clientX, M = P.clientY - D.clientY, o = Math.hypot(v, M);
452
+ t.startDistance = o, t.lastDistance = o, t.active = !0;
453
+ }
454
+ },
455
+ [c]
456
+ ), Y = u(
457
+ (e) => {
458
+ const t = d.current;
459
+ if (!t.active || !t.pointers.has(e.pointerId) || (t.pointers.set(e.pointerId, e), t.pointers.size < 2)) return;
460
+ const [D, P] = [...t.pointers.values()], v = P.clientX - D.clientX, M = P.clientY - D.clientY, o = Math.hypot(v, M), i = o - t.lastDistance, l = o - t.startDistance;
461
+ if (Math.abs(l) < f) return;
462
+ const w = o / t.startDistance, I = {
463
+ distance: o,
464
+ delta: i,
465
+ totalDelta: l,
466
+ scale: w
467
+ };
468
+ if (t.lastDistance = o, !O) {
469
+ G(e, I, n, {
470
+ stopImmediate: p,
471
+ once: a,
472
+ onOnce: () => S()
473
+ });
474
+ return;
475
+ }
476
+ h.current = I, A.current = e, y.current === null && (y.current = requestAnimationFrame(L));
477
+ },
478
+ [
479
+ f,
480
+ O,
481
+ n,
482
+ p,
483
+ a,
484
+ S,
485
+ L
486
+ ]
487
+ ), X = u((e) => {
488
+ const t = d.current;
489
+ t.pointers.delete(e.pointerId), t.pointers.size < 2 && (t.active = !1, t.startDistance = 0, t.lastDistance = 0);
490
+ }, []), K = u((e) => {
491
+ const t = d.current;
492
+ t.pointers.delete(e.pointerId), t.active = !1;
493
+ }, []);
494
+ x(() => {
495
+ T.current = R?.current ?? globalThis, m.current = new AbortController();
496
+ const e = (v) => g(v), t = (v) => Y(v), D = (v) => X(v), P = (v) => K(v);
497
+ return T.current.addEventListener(
498
+ "pointerdown",
499
+ e,
500
+ {
501
+ capture: r,
502
+ signal: m.current.signal
503
+ }
504
+ ), T.current.addEventListener(
505
+ "pointermove",
506
+ t,
507
+ {
508
+ capture: r,
509
+ signal: m.current.signal
510
+ }
511
+ ), T.current.addEventListener("pointerup", D, {
512
+ capture: r,
513
+ signal: m.current.signal
514
+ }), T.current.addEventListener(
515
+ "pointercancel",
516
+ P,
517
+ {
518
+ capture: r,
519
+ signal: m.current.signal
520
+ }
521
+ ), () => {
522
+ m.current?.abort(), d.current.active = !1, d.current.pointers.clear(), y.current !== null && cancelAnimationFrame(y.current);
523
+ };
524
+ }, [
525
+ R,
526
+ r,
527
+ g,
528
+ Y,
529
+ X,
530
+ K
531
+ ]);
532
+ }, pe = {
533
+ Touch: "touch",
534
+ Mouse: "mouse",
535
+ Pen: "pen"
536
+ }, b = {
537
+ Left: "left",
538
+ Right: "right",
539
+ Up: "up",
540
+ Down: "down",
541
+ Horizontal: "horizontal",
542
+ Vertical: "vertical",
543
+ Both: "both"
544
+ }, fe = {
545
+ Touch: "touch",
546
+ Mouse: "mouse",
547
+ Pen: "pen"
548
+ }, re = (n) => Array.isArray(n) ? n : [n], oe = (n, s, c, r, a) => {
549
+ a.stopImmediate && n.stopImmediatePropagation(), r(n, s, c) && n.preventDefault(), a.once && a.onOnce?.();
550
+ }, se = {
551
+ eventPointerTypes: ["touch", "mouse", "pen"],
552
+ eventCapture: !1,
553
+ eventOnce: !1,
554
+ eventStopImmediatePropagation: !1,
555
+ threshold: 50,
556
+ velocity: 0.3,
557
+ container: { current: null }
558
+ }, me = (n, s, c = {}) => {
559
+ const {
560
+ eventPointerTypes: r,
561
+ eventCapture: a,
562
+ eventOnce: p,
563
+ eventStopImmediatePropagation: f,
564
+ threshold: R,
565
+ velocity: O,
566
+ container: T
567
+ } = { ...se, ...c }, m = E([]), y = E(null), h = E(null), A = E({
568
+ startX: 0,
569
+ startY: 0,
570
+ startTime: 0,
571
+ active: !1
572
+ }), d = u(() => {
573
+ h.current?.abort();
574
+ }, []), S = u(
575
+ (e, t) => {
576
+ const D = Math.abs(e), P = Math.abs(t);
577
+ return D > P ? e > 0 ? b.Right : b.Left : t > 0 ? b.Down : b.Up;
578
+ },
579
+ []
580
+ ), L = u((e) => {
581
+ const t = m.current;
582
+ return t.includes(b.Both) || t.includes(b.Horizontal) && (e === b.Left || e === b.Right) || t.includes(b.Vertical) && (e === b.Up || e === b.Down) ? !0 : t.includes(e);
583
+ }, []), g = u(
584
+ (e) => {
585
+ e.isPrimary && r.includes(e.pointerType) && (A.current = {
586
+ startX: e.clientX,
587
+ startY: e.clientY,
588
+ startTime: Date.now(),
589
+ active: !0
590
+ });
591
+ },
592
+ [r]
593
+ ), Y = u(
594
+ (e) => {
595
+ const t = A.current;
596
+ if (!t.active)
597
+ return;
598
+ t.active = !1;
599
+ const D = Date.now() - t.startTime;
600
+ if (D === 0)
601
+ return;
602
+ const P = e.clientX - t.startX, v = e.clientY - t.startY, M = Math.hypot(P, v), o = M / D;
603
+ if (M < R || o < O)
604
+ return;
605
+ const i = S(P, v);
606
+ if (!L(i))
607
+ return;
608
+ oe(e, i, {
609
+ deltaX: P,
610
+ deltaY: v,
611
+ velocity: o,
612
+ duration: D
613
+ }, s, {
614
+ stopImmediate: f,
615
+ once: p,
616
+ onOnce: () => {
617
+ d();
618
+ }
619
+ });
620
+ },
621
+ [
622
+ R,
623
+ O,
624
+ S,
625
+ L,
626
+ s,
627
+ p,
628
+ f,
629
+ d
630
+ ]
631
+ ), X = u(
632
+ (e) => {
633
+ e.isPrimary && r.includes(e.pointerType) && Y(e);
634
+ },
635
+ [Y, r]
636
+ ), K = u(() => {
637
+ A.current.active = !1;
638
+ }, []);
639
+ x(() => {
640
+ m.current = re(n);
641
+ }, [n]), x(() => {
642
+ y.current = T?.current ?? globalThis, h.current = new AbortController();
643
+ const e = (P) => g(P);
644
+ y.current.addEventListener(
645
+ "pointerdown",
646
+ e,
647
+ {
648
+ capture: a,
649
+ signal: h.current.signal
650
+ }
651
+ );
652
+ const t = (P) => X(P);
653
+ y.current.addEventListener("pointerup", t, {
654
+ capture: a,
655
+ signal: h.current.signal
656
+ });
657
+ const D = () => K();
658
+ return y.current.addEventListener(
659
+ "pointercancel",
660
+ D,
661
+ {
662
+ capture: a,
663
+ signal: h.current.signal
664
+ }
665
+ ), () => {
666
+ h.current?.abort(), A.current.active = !1;
667
+ };
668
+ }, [
669
+ T,
670
+ a,
671
+ g,
672
+ X,
673
+ K
674
+ ]);
675
+ }, V = (n, s, c, r) => {
676
+ r.stopImmediate && n.stopImmediatePropagation(), c(n, s) && n.preventDefault(), r.once && r.onOnce?.();
677
+ }, j = {
678
+ eventPassive: !0,
679
+ eventCapture: !1,
680
+ eventOnce: !1,
681
+ eventStopImmediatePropagation: !1,
682
+ container: { current: null },
683
+ raf: !1
684
+ }, he = (n, s = j) => {
685
+ const {
686
+ eventPassive: c,
687
+ eventCapture: r,
688
+ eventOnce: a,
689
+ eventStopImmediatePropagation: p,
690
+ container: f,
691
+ raf: R
692
+ } = { ...j, ...s }, O = E(null), T = E(null), m = E(null), y = E(null), h = E(null), A = u(() => {
693
+ T.current?.abort();
694
+ }, []), d = u(() => {
695
+ m.current = null;
696
+ const L = y.current, g = h.current;
697
+ !L || !g || (V(g, L, n, {
698
+ stopImmediate: p,
699
+ once: a,
700
+ onOnce: () => {
701
+ A();
702
+ }
703
+ }), y.current = null, h.current = null);
704
+ }, [
705
+ n,
706
+ p,
707
+ a,
708
+ A
709
+ ]), S = u(
710
+ (L) => {
711
+ const g = {
712
+ deltaX: L.deltaX,
713
+ deltaY: L.deltaY,
714
+ deltaZ: L.deltaZ,
715
+ deltaMode: L.deltaMode
716
+ };
717
+ if (!R) {
718
+ V(L, g, n, {
719
+ stopImmediate: p,
720
+ once: a,
721
+ onOnce: () => {
722
+ A();
723
+ }
724
+ });
725
+ return;
726
+ }
727
+ y.current = g, h.current = L, m.current === null && (m.current = requestAnimationFrame(d));
728
+ },
729
+ [
730
+ R,
731
+ n,
732
+ p,
733
+ a,
734
+ A,
735
+ d
736
+ ]
737
+ );
738
+ x(() => {
739
+ O.current = f?.current ?? globalThis, T.current = new AbortController();
740
+ const L = (g) => S(g);
741
+ return O.current.addEventListener("wheel", L, {
742
+ passive: c,
743
+ capture: r,
744
+ signal: T.current.signal
745
+ }), () => {
746
+ T.current?.abort(), m.current !== null && cancelAnimationFrame(m.current);
747
+ };
748
+ }, [f, c, r, S]);
749
+ };
750
+ export {
751
+ ue as DragEventPointerTypes,
752
+ F as KeyEventTypes,
753
+ pe as PinchEventPointerTypes,
754
+ b as SwipeDirections,
755
+ fe as SwipeEventPointerTypes,
756
+ ie as useDrag,
757
+ le as useKey,
758
+ de as usePinch,
759
+ me as useSwipe,
760
+ he as useWheel
761
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@msobiecki/react-marauders-path",
3
- "version": "1.23.0",
3
+ "version": "1.24.1",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=22.17.1"