gesturx 1.0.0 → 1.2.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 (41) hide show
  1. package/README.md +17 -1
  2. package/dist/index.d.ts +192 -0
  3. package/dist/index.js +627 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/index.umd.cjs +2 -0
  6. package/dist/index.umd.cjs.map +1 -0
  7. package/package.json +12 -1
  8. package/.github/workflows/publish.yml +0 -17
  9. package/.prettierrc.json +0 -3
  10. package/eslint.config.js +0 -18
  11. package/index.html +0 -13
  12. package/src/core/index.ts +0 -4
  13. package/src/core/manager.ts +0 -46
  14. package/src/core/pan/PanRecognizer.ts +0 -192
  15. package/src/core/pinch/PinchRecognizer.ts +0 -131
  16. package/src/core/recognizer/Recognizer.ts +0 -54
  17. package/src/core/recognizer/RecognizerEvent.ts +0 -39
  18. package/src/core/recognizer/mapToRecognizerEvent.ts +0 -142
  19. package/src/core/rotate/RotateRecognizer.ts +0 -115
  20. package/src/core/swipe/SwipeRecognizer.test.ts +0 -160
  21. package/src/core/swipe/SwipeRecognizer.ts +0 -82
  22. package/src/core/swipe/isRecognizedAsSwipe.ts +0 -13
  23. package/src/core/tap/TapRecognizer.test.ts +0 -567
  24. package/src/core/tap/TapRecognizer.ts +0 -172
  25. package/src/core/utils/events.ts +0 -186
  26. package/src/core/utils/geometry.ts +0 -115
  27. package/src/core/utils/utils.test.ts +0 -26
  28. package/src/core/utils/utils.ts +0 -58
  29. package/src/demo/App.tsx +0 -171
  30. package/src/demo/DebugBox.tsx +0 -10
  31. package/src/demo/useManager.ts +0 -40
  32. package/src/demo/useTap.tsx +0 -87
  33. package/src/index.css +0 -66
  34. package/src/main.tsx +0 -14
  35. package/src/theme.ts +0 -11
  36. package/src/vite-env.d.ts +0 -1
  37. package/tsconfig.json +0 -26
  38. package/tsconfig.node.json +0 -11
  39. package/vite.config.ts +0 -38
  40. package/vitest.config.ts +0 -8
  41. /package/{public → dist}/vite.svg +0 -0
@@ -1,82 +0,0 @@
1
- import {
2
- Observable,
3
- exhaustMap,
4
- filter,
5
- first,
6
- map,
7
- share,
8
- switchMap,
9
- takeUntil,
10
- } from "rxjs"
11
- import { Recognizer } from "../recognizer/Recognizer"
12
- import { PanRecognizer } from "../pan/PanRecognizer"
13
- import { RecognizerEvent } from "../recognizer/RecognizerEvent"
14
- import { calculateDegreeAngleBetweenPoints } from "../utils/geometry"
15
- import { isRecognizedAsSwipe } from "./isRecognizedAsSwipe"
16
-
17
- export interface SwipeEvent extends RecognizerEvent {
18
- type: "swipe"
19
- /**
20
- * Angle between first pointer and last one in degree
21
- *
22
- * (-90)
23
- * (+/-180) <- | -> (+/-0)
24
- * (+90)
25
- */
26
- angle: number
27
- }
28
-
29
- type Options = {
30
- /**
31
- * The minimum velocity (px/ms) that the gesture has to obtain by the end event.
32
- */
33
- escapeVelocity?: number
34
- // @todo
35
- numInputs?: number
36
- // @todo
37
- maxRestTime?: number
38
- posThreshold?: number
39
- }
40
-
41
- export class SwipeRecognizer extends Recognizer<Options> {
42
- public events$: Observable<SwipeEvent>
43
-
44
- constructor(protected options: Options = {}) {
45
- super(options)
46
-
47
- this.events$ = this.validConfig$.pipe(
48
- switchMap((initializedWith) => {
49
- const { escapeVelocity = 0.9, posThreshold } =
50
- initializedWith.options ?? {}
51
- const panRecognizer = new PanRecognizer({ posThreshold })
52
-
53
- panRecognizer.initialize(initializedWith)
54
-
55
- const hasMoreThanOneFinger$ = panRecognizer.events$.pipe(
56
- filter(({ pointers }) => pointers.length > 1),
57
- )
58
-
59
- return panRecognizer.start$.pipe(
60
- exhaustMap((startEvent) => {
61
- return panRecognizer.end$.pipe(
62
- first(),
63
- isRecognizedAsSwipe(escapeVelocity),
64
- map(({ type, ...rest }) => {
65
- return {
66
- type: "swipe" as const,
67
- angle: calculateDegreeAngleBetweenPoints(
68
- startEvent.event,
69
- rest.event,
70
- ),
71
- ...rest,
72
- }
73
- }),
74
- takeUntil(hasMoreThanOneFinger$),
75
- )
76
- }),
77
- share(),
78
- )
79
- }),
80
- )
81
- }
82
- }
@@ -1,13 +0,0 @@
1
- import { Observable, filter } from "rxjs"
2
- import { PanEvent } from "../pan/PanRecognizer"
3
-
4
- export const isRecognizedAsSwipe =
5
- (escapeVelocity: number) => (stream: Observable<PanEvent>) =>
6
- stream.pipe(
7
- filter((event) => {
8
- return (
9
- Math.abs(event.velocityX) >= escapeVelocity ||
10
- Math.abs(event.velocityY) >= escapeVelocity
11
- )
12
- }),
13
- )
@@ -1,567 +0,0 @@
1
- import { describe, it, expect, beforeEach } from "vitest"
2
- import { TapRecognizer } from "./TapRecognizer"
3
- import { buffer, first, lastValueFrom, tap, timer } from "rxjs"
4
-
5
- const waitFor = (time: number) =>
6
- new Promise((resolve) => setTimeout(resolve, time))
7
-
8
- describe("TapGestureRecognizer", () => {
9
- let container = document.createElement("div")
10
-
11
- beforeEach(() => {
12
- container = document.createElement("div")
13
- document.body.appendChild(container)
14
- })
15
-
16
- function createPointerEvent({
17
- type,
18
- x,
19
- y,
20
- identifier,
21
- }: {
22
- type: string
23
- x: number
24
- y: number
25
- identifier: number
26
- }) {
27
- const event = new PointerEvent(type, {
28
- clientX: x,
29
- clientY: y,
30
- pointerId: identifier,
31
- pointerType: "touch",
32
- bubbles: true,
33
- })
34
-
35
- // @ts-ignore
36
- event.x = x
37
- // @ts-ignore
38
- event.y = y
39
-
40
- return event
41
- }
42
-
43
- function sendPointerEvent({
44
- type,
45
- x,
46
- y,
47
- container,
48
- identifier,
49
- }: {
50
- container: HTMLElement
51
- type: string
52
- x: number
53
- y: number
54
- identifier: number
55
- }) {
56
- const event = createPointerEvent({
57
- type,
58
- x,
59
- y,
60
- identifier,
61
- })
62
-
63
- sendPointerEventFromEvent({ event, container })
64
- }
65
-
66
- function sendPointerEventFromEvent({
67
- event,
68
- container,
69
- }: {
70
- event: PointerEvent
71
- container: HTMLElement
72
- }) {
73
- container.dispatchEvent(event)
74
- }
75
-
76
- it("should detect a single tap", async () => {
77
- const multiTapThreshold = 5
78
- const waitLongEnough$ = timer(100)
79
- const recognizer = new TapRecognizer({ multiTapThreshold })
80
- recognizer.initialize({
81
- container,
82
- })
83
-
84
- const values = await lastValueFrom(
85
- recognizer.events$.pipe(
86
- tap({
87
- subscribe: async () => {
88
- await waitFor(1)
89
-
90
- sendPointerEvent({
91
- container,
92
- identifier: 1,
93
- type: "pointerdown",
94
- x: 0,
95
- y: 0,
96
- })
97
- sendPointerEvent({
98
- container,
99
- identifier: 1,
100
- type: "pointerup",
101
- x: 0,
102
- y: 0,
103
- })
104
- },
105
- }),
106
- buffer(waitLongEnough$),
107
- first(),
108
- ),
109
- )
110
-
111
- expect(values).toMatchObject([{ type: "tap", taps: 1 }])
112
- })
113
-
114
- it("should detect a double tap", async () => {
115
- const multiTapThreshold = 5
116
- const waitLongEnough$ = timer(multiTapThreshold * 2)
117
- const recognizer = new TapRecognizer({ multiTapThreshold, maxTaps: 2 })
118
- recognizer.initialize({
119
- container,
120
- })
121
-
122
- const values = await lastValueFrom(
123
- recognizer.events$.pipe(
124
- tap({
125
- subscribe: async () => {
126
- await waitFor(1)
127
-
128
- sendPointerEvent({
129
- container,
130
- identifier: 1,
131
- type: "pointerdown",
132
- x: 0,
133
- y: 0,
134
- })
135
- sendPointerEvent({
136
- container,
137
- identifier: 1,
138
- type: "pointerup",
139
- x: 0,
140
- y: 0,
141
- })
142
- sendPointerEvent({
143
- container,
144
- identifier: 2,
145
- type: "pointerdown",
146
- x: 0,
147
- y: 0,
148
- })
149
- sendPointerEvent({
150
- container,
151
- identifier: 2,
152
- type: "pointerup",
153
- x: 0,
154
- y: 0,
155
- })
156
- },
157
- }),
158
- buffer(waitLongEnough$),
159
- first(),
160
- ),
161
- )
162
-
163
- expect(values).toMatchObject([{ type: "tap", taps: 2 }])
164
- })
165
-
166
- it("should detect a triple tap", async () => {
167
- const multiTapThreshold = 5
168
- const recognizer = new TapRecognizer({ multiTapThreshold, maxTaps: 3 })
169
- recognizer.initialize({
170
- container,
171
- })
172
- const waitLongEnough$ = timer(100)
173
-
174
- const values = await lastValueFrom(
175
- recognizer.events$.pipe(
176
- tap({
177
- subscribe: () => {
178
- setTimeout(() => {
179
- sendPointerEvent({
180
- container,
181
- identifier: 1,
182
- type: "pointerdown",
183
- x: 0,
184
- y: 0,
185
- })
186
- sendPointerEvent({
187
- container,
188
- identifier: 1,
189
- type: "pointerup",
190
- x: 0,
191
- y: 0,
192
- })
193
- sendPointerEvent({
194
- container,
195
- identifier: 1,
196
- type: "pointerdown",
197
- x: 0,
198
- y: 0,
199
- })
200
- sendPointerEvent({
201
- container,
202
- identifier: 1,
203
- type: "pointerup",
204
- x: 0,
205
- y: 0,
206
- })
207
- sendPointerEvent({
208
- container,
209
- identifier: 1,
210
- type: "pointerdown",
211
- x: 0,
212
- y: 0,
213
- })
214
- sendPointerEvent({
215
- container,
216
- identifier: 1,
217
- type: "pointerup",
218
- x: 0,
219
- y: 0,
220
- })
221
- })
222
- },
223
- }),
224
- buffer(waitLongEnough$),
225
- first(),
226
- ),
227
- )
228
-
229
- expect(values).toMatchObject([{ type: "tap", taps: 3 }])
230
- })
231
-
232
- it("should not detect a double tap if the second tap is after threshold", async () => {
233
- const multiTapThreshold = 5
234
- const waitLongEnough$ = timer(100)
235
- const recognizer = new TapRecognizer({ multiTapThreshold, maxTaps: 2 })
236
- recognizer.initialize({
237
- container,
238
- })
239
-
240
- const values = await lastValueFrom(
241
- recognizer.events$.pipe(
242
- tap({
243
- subscribe: async () => {
244
- await waitFor(1)
245
-
246
- sendPointerEvent({
247
- container,
248
- identifier: 1,
249
- type: "pointerdown",
250
- x: 0,
251
- y: 0,
252
- })
253
- sendPointerEvent({
254
- container,
255
- identifier: 1,
256
- type: "pointerup",
257
- x: 0,
258
- y: 0,
259
- })
260
-
261
- await waitFor(multiTapThreshold)
262
-
263
- sendPointerEvent({
264
- container,
265
- identifier: 2,
266
- type: "pointerdown",
267
- x: 0,
268
- y: 0,
269
- })
270
- sendPointerEvent({
271
- container,
272
- identifier: 2,
273
- type: "pointerup",
274
- x: 0,
275
- y: 0,
276
- })
277
- },
278
- }),
279
- buffer(waitLongEnough$),
280
- first(),
281
- ),
282
- )
283
-
284
- expect(values.length).toBe(2)
285
- expect(values).toMatchObject([
286
- { type: "tap", taps: 1, pointers: [{ pointerId: 1 }] },
287
- { type: "tap", taps: 1, pointers: [{ pointerId: 2 }] },
288
- ])
289
- })
290
-
291
- it("should not trigger tap if moved too much between down and up", async () => {
292
- const multiTapThreshold = 5
293
- const waitLongEnough$ = timer(100)
294
- const recognizer = new TapRecognizer({
295
- multiTapThreshold,
296
- posThreshold: 5,
297
- })
298
- recognizer.initialize({
299
- container,
300
- })
301
-
302
- const values = await lastValueFrom(
303
- recognizer.events$.pipe(
304
- tap({
305
- subscribe: async () => {
306
- await waitFor(1)
307
-
308
- sendPointerEvent({
309
- container,
310
- identifier: 1,
311
- type: "pointerdown",
312
- x: 0,
313
- y: 0,
314
- })
315
-
316
- sendPointerEvent({
317
- container,
318
- identifier: 1,
319
- type: "pointerup",
320
- x: 50,
321
- y: 50,
322
- })
323
- },
324
- }),
325
- buffer(waitLongEnough$),
326
- first(),
327
- ),
328
- )
329
-
330
- expect(values.length).toBe(0)
331
- })
332
-
333
- it("should not detect a tap if the pointer is held down too long", async () => {
334
- const multiTapThreshold = 5
335
- const maximumPressTime = 10
336
- const waitLongEnough$ = timer(100)
337
- const recognizer = new TapRecognizer({
338
- multiTapThreshold,
339
- posThreshold: 5,
340
- maximumPressTime,
341
- })
342
- recognizer.initialize({
343
- container,
344
- })
345
-
346
- const values = await lastValueFrom(
347
- recognizer.events$.pipe(
348
- tap({
349
- subscribe: async () => {
350
- await waitFor(1)
351
-
352
- sendPointerEvent({
353
- container,
354
- identifier: 1,
355
- type: "pointerdown",
356
- x: 0,
357
- y: 0,
358
- })
359
-
360
- await waitFor(maximumPressTime - 5)
361
-
362
- sendPointerEvent({
363
- container,
364
- identifier: 1,
365
- type: "pointerup",
366
- x: 0,
367
- y: 0,
368
- })
369
-
370
- await waitFor(multiTapThreshold * 2)
371
-
372
- sendPointerEvent({
373
- container,
374
- identifier: 2,
375
- type: "pointerdown",
376
- x: 0,
377
- y: 0,
378
- })
379
-
380
- await waitFor(maximumPressTime)
381
-
382
- sendPointerEvent({
383
- container,
384
- identifier: 2,
385
- type: "pointerup",
386
- x: 0,
387
- y: 0,
388
- })
389
- },
390
- }),
391
- buffer(waitLongEnough$),
392
- first(),
393
- ),
394
- )
395
-
396
- expect(values.length).toBe(1)
397
- expect(values).toMatchObject([
398
- { type: "tap", taps: 1, pointers: [{ pointerId: 1 }] },
399
- ])
400
- })
401
-
402
- it("should ignore taps when there are simultaneous pointers active", async () => {
403
- const recognizer = new TapRecognizer({})
404
- recognizer.initialize({
405
- container,
406
- })
407
- const waitLongEnough$ = timer(100)
408
-
409
- const values = await lastValueFrom(
410
- recognizer.events$.pipe(
411
- tap({
412
- subscribe: () => {
413
- setTimeout(() => {
414
- sendPointerEvent({
415
- container,
416
- identifier: 1,
417
- type: "pointerdown",
418
- x: 0,
419
- y: 0,
420
- })
421
- sendPointerEvent({
422
- container,
423
- identifier: 2,
424
- type: "pointerdown",
425
- x: 10,
426
- y: 10,
427
- })
428
- sendPointerEvent({
429
- container,
430
- identifier: 1,
431
- type: "pointerup",
432
- x: 0,
433
- y: 0,
434
- })
435
- sendPointerEvent({
436
- container,
437
- identifier: 2,
438
- type: "pointerup",
439
- x: 10,
440
- y: 10,
441
- })
442
- })
443
- },
444
- }),
445
- buffer(waitLongEnough$),
446
- first(),
447
- ),
448
- )
449
-
450
- expect(values.length).toBe(0) // Assuming the recognizer should ignore simultaneous taps
451
- })
452
-
453
- it("should ignore one tap if there is more than one tap", async () => {
454
- const recognizer = new TapRecognizer({
455
- multiTapThreshold: 10,
456
- maxTaps: 1,
457
- })
458
- recognizer.initialize({
459
- container,
460
- })
461
- const waitLongEnough$ = timer(100)
462
-
463
- const values = await lastValueFrom(
464
- recognizer.events$.pipe(
465
- tap({
466
- subscribe: async () => {
467
- await waitFor(1)
468
-
469
- sendPointerEvent({
470
- container,
471
- identifier: 1,
472
- type: "pointerdown",
473
- x: 0,
474
- y: 0,
475
- })
476
- sendPointerEvent({
477
- container,
478
- identifier: 1,
479
- type: "pointerup",
480
- x: 0,
481
- y: 0,
482
- })
483
-
484
- await waitFor(1)
485
-
486
- sendPointerEvent({
487
- container,
488
- identifier: 2,
489
- type: "pointerdown",
490
- x: 0,
491
- y: 0,
492
- })
493
- sendPointerEvent({
494
- container,
495
- identifier: 2,
496
- type: "pointerup",
497
- x: 0,
498
- y: 0,
499
- })
500
- },
501
- }),
502
- buffer(waitLongEnough$),
503
- first(),
504
- ),
505
- )
506
-
507
- expect(values.length).toBe(0)
508
- })
509
-
510
- it("should ignore taps when a second tap is outside of position threshold", async () => {
511
- const recognizer = new TapRecognizer({
512
- posThreshold: 5,
513
- multiTapThreshold: 10,
514
- maxTaps: Infinity,
515
- })
516
- recognizer.initialize({
517
- container,
518
- })
519
- const waitLongEnough$ = timer(100)
520
-
521
- const values = await lastValueFrom(
522
- recognizer.events$.pipe(
523
- tap({
524
- subscribe: async () => {
525
- // await waitFor(1)
526
-
527
- sendPointerEvent({
528
- container,
529
- identifier: 1,
530
- type: "pointerdown",
531
- x: 0,
532
- y: 0,
533
- })
534
- sendPointerEvent({
535
- container,
536
- identifier: 1,
537
- type: "pointerup",
538
- x: 0,
539
- y: 0,
540
- })
541
-
542
- // await waitFor(1)
543
-
544
- sendPointerEvent({
545
- container,
546
- identifier: 2,
547
- type: "pointerdown",
548
- x: 10,
549
- y: 10,
550
- })
551
- sendPointerEvent({
552
- container,
553
- identifier: 2,
554
- type: "pointerup",
555
- x: 10,
556
- y: 10,
557
- })
558
- },
559
- }),
560
- buffer(waitLongEnough$),
561
- first(),
562
- ),
563
- )
564
-
565
- expect(values.length).toBe(0)
566
- })
567
- })