react-native-gesture-handler 2.19.0 → 2.20.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. package/android/build.gradle +7 -12
  2. package/android/paper/src/main/java/com/swmansion/gesturehandler/ReactContextExtensions.kt +1 -1
  3. package/android/src/main/java/com/swmansion/gesturehandler/core/GestureUtils.kt +1 -0
  4. package/android/src/main/java/com/swmansion/gesturehandler/core/HoverGestureHandler.kt +11 -0
  5. package/android/src/main/java/com/swmansion/gesturehandler/core/PanGestureHandler.kt +8 -0
  6. package/android/src/main/java/com/swmansion/gesturehandler/core/StylusData.kt +103 -0
  7. package/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonViewManager.kt +24 -15
  8. package/android/src/main/java/com/swmansion/gesturehandler/react/eventbuilders/HoverGestureHandlerEventDataBuilder.kt +7 -0
  9. package/android/src/main/java/com/swmansion/gesturehandler/react/eventbuilders/PanGestureHandlerEventDataBuilder.kt +7 -0
  10. package/android/src/main/jni/CMakeLists.txt +18 -9
  11. package/apple/Handlers/RNLongPressHandler.m +2 -0
  12. package/apple/Handlers/RNPanHandler.m +57 -7
  13. package/apple/Handlers/RNRotationHandler.m +1 -1
  14. package/apple/RNGHStylusData.h +77 -0
  15. package/apple/RNGHStylusData.m +37 -0
  16. package/apple/RNGestureHandlerButtonComponentView.mm +35 -0
  17. package/apple/RNGestureHandlerEvents.h +3 -1
  18. package/apple/RNGestureHandlerEvents.m +11 -3
  19. package/lib/commonjs/components/GestureButtons.js +5 -1
  20. package/lib/commonjs/components/GestureButtons.js.map +1 -1
  21. package/lib/commonjs/components/GestureComponents.js.map +1 -1
  22. package/lib/commonjs/components/Pressable/Pressable.js +5 -14
  23. package/lib/commonjs/components/Pressable/Pressable.js.map +1 -1
  24. package/lib/commonjs/components/Pressable/utils.js +1 -23
  25. package/lib/commonjs/components/Pressable/utils.js.map +1 -1
  26. package/lib/commonjs/handlers/GestureHandlerEventPayload.js +4 -0
  27. package/lib/commonjs/handlers/createHandler.js +2 -1
  28. package/lib/commonjs/handlers/createHandler.js.map +1 -1
  29. package/lib/commonjs/handlers/gestures/gesture.js.map +1 -1
  30. package/lib/commonjs/handlers/gestures/hoverGesture.js.map +1 -1
  31. package/lib/commonjs/jestUtils/jestUtils.js +12 -4
  32. package/lib/commonjs/jestUtils/jestUtils.js.map +1 -1
  33. package/lib/commonjs/web/handlers/GestureHandler.js +1 -3
  34. package/lib/commonjs/web/handlers/GestureHandler.js.map +1 -1
  35. package/lib/commonjs/web/handlers/HoverGestureHandler.js +18 -1
  36. package/lib/commonjs/web/handlers/HoverGestureHandler.js.map +1 -1
  37. package/lib/commonjs/web/handlers/PanGestureHandler.js +8 -1
  38. package/lib/commonjs/web/handlers/PanGestureHandler.js.map +1 -1
  39. package/lib/commonjs/web/interfaces.js.map +1 -1
  40. package/lib/commonjs/web/tools/EventManager.js.map +1 -1
  41. package/lib/commonjs/web/tools/GestureHandlerWebDelegate.js +0 -3
  42. package/lib/commonjs/web/tools/GestureHandlerWebDelegate.js.map +1 -1
  43. package/lib/commonjs/web/tools/PointerEventManager.js +3 -37
  44. package/lib/commonjs/web/tools/PointerEventManager.js.map +1 -1
  45. package/lib/commonjs/web/utils.js +173 -0
  46. package/lib/commonjs/web/utils.js.map +1 -1
  47. package/lib/module/components/GestureButtons.js +5 -1
  48. package/lib/module/components/GestureButtons.js.map +1 -1
  49. package/lib/module/components/GestureComponents.js.map +1 -1
  50. package/lib/module/components/Pressable/Pressable.js +7 -14
  51. package/lib/module/components/Pressable/Pressable.js.map +1 -1
  52. package/lib/module/components/Pressable/utils.js +1 -22
  53. package/lib/module/components/Pressable/utils.js.map +1 -1
  54. package/lib/module/handlers/GestureHandlerEventPayload.js +1 -1
  55. package/lib/module/handlers/createHandler.js +2 -1
  56. package/lib/module/handlers/createHandler.js.map +1 -1
  57. package/lib/module/handlers/gestures/gesture.js.map +1 -1
  58. package/lib/module/handlers/gestures/hoverGesture.js.map +1 -1
  59. package/lib/module/jestUtils/jestUtils.js +12 -4
  60. package/lib/module/jestUtils/jestUtils.js.map +1 -1
  61. package/lib/module/web/handlers/GestureHandler.js +1 -3
  62. package/lib/module/web/handlers/GestureHandler.js.map +1 -1
  63. package/lib/module/web/handlers/HoverGestureHandler.js +18 -1
  64. package/lib/module/web/handlers/HoverGestureHandler.js.map +1 -1
  65. package/lib/module/web/handlers/PanGestureHandler.js +8 -1
  66. package/lib/module/web/handlers/PanGestureHandler.js.map +1 -1
  67. package/lib/module/web/interfaces.js.map +1 -1
  68. package/lib/module/web/tools/EventManager.js.map +1 -1
  69. package/lib/module/web/tools/GestureHandlerWebDelegate.js +0 -2
  70. package/lib/module/web/tools/GestureHandlerWebDelegate.js.map +1 -1
  71. package/lib/module/web/tools/PointerEventManager.js +4 -38
  72. package/lib/module/web/tools/PointerEventManager.js.map +1 -1
  73. package/lib/module/web/utils.js +170 -0
  74. package/lib/module/web/utils.js.map +1 -1
  75. package/lib/typescript/components/GestureComponents.d.ts +1 -1
  76. package/lib/typescript/components/Pressable/utils.d.ts +3 -5
  77. package/lib/typescript/handlers/GestureHandlerEventPayload.d.ts +35 -0
  78. package/lib/typescript/handlers/gestures/gesture.d.ts +2 -2
  79. package/lib/typescript/handlers/gestures/hoverGesture.d.ts +1 -6
  80. package/lib/typescript/handlers/handlersRegistry.d.ts +1 -1
  81. package/lib/typescript/jestUtils/jestUtils.d.ts +1 -1
  82. package/lib/typescript/web/handlers/HoverGestureHandler.d.ts +2 -0
  83. package/lib/typescript/web/handlers/PanGestureHandler.d.ts +3 -1
  84. package/lib/typescript/web/interfaces.d.ts +8 -3
  85. package/lib/typescript/web/tools/EventManager.d.ts +2 -2
  86. package/lib/typescript/web/utils.d.ts +2 -1
  87. package/package.json +1 -1
  88. package/src/components/GestureButtons.tsx +2 -1
  89. package/src/components/GestureComponents.tsx +1 -1
  90. package/src/components/Pressable/Pressable.tsx +16 -29
  91. package/src/components/Pressable/utils.ts +5 -49
  92. package/src/handlers/GestureHandlerEventPayload.ts +42 -0
  93. package/src/handlers/createHandler.tsx +1 -0
  94. package/src/handlers/gestures/gesture.ts +3 -1
  95. package/src/handlers/gestures/hoverGesture.ts +1 -7
  96. package/src/jestUtils/jestUtils.ts +9 -1
  97. package/src/web/handlers/GestureHandler.ts +1 -1
  98. package/src/web/handlers/HoverGestureHandler.ts +16 -2
  99. package/src/web/handlers/PanGestureHandler.ts +10 -1
  100. package/src/web/interfaces.ts +9 -3
  101. package/src/web/tools/EventManager.ts +2 -4
  102. package/src/web/tools/GestureHandlerWebDelegate.ts +0 -2
  103. package/src/web/tools/PointerEventManager.ts +2 -38
  104. package/src/web/utils.ts +174 -1
  105. package/lib/commonjs/web/tools/TouchEventManager.js +0 -164
  106. package/lib/commonjs/web/tools/TouchEventManager.js.map +0 -1
  107. package/lib/module/web/tools/TouchEventManager.js +0 -149
  108. package/lib/module/web/tools/TouchEventManager.js.map +0 -1
  109. package/lib/typescript/web/tools/TouchEventManager.d.ts +0 -11
  110. package/src/web/tools/TouchEventManager.ts +0 -175
@@ -1,3 +1,5 @@
1
+ import { StylusData } from '../web/interfaces';
2
+
1
3
  export type FlingGestureHandlerEventPayload = {
2
4
  x: number;
3
5
  y: number;
@@ -120,6 +122,11 @@ export type PanGestureHandlerEventPayload = {
120
122
  * value is expressed in point units per second.
121
123
  */
122
124
  velocityY: number;
125
+
126
+ /**
127
+ * Object containing additional stylus data.
128
+ */
129
+ stylusData: StylusData | undefined;
123
130
  };
124
131
 
125
132
  export type PinchGestureHandlerEventPayload = {
@@ -182,3 +189,38 @@ export type RotationGestureHandlerEventPayload = {
182
189
  */
183
190
  velocity: number;
184
191
  };
192
+
193
+ export type HoverGestureHandlerEventPayload = {
194
+ /**
195
+ * X coordinate of the current position of the pointer relative to the view
196
+ * attached to the handler. Expressed in point units.
197
+ */
198
+ x: number;
199
+
200
+ /**
201
+ * Y coordinate of the current position of the pointer relative to the view
202
+ * attached to the handler. Expressed in point units.
203
+ */
204
+ y: number;
205
+
206
+ /**
207
+ * X coordinate of the current position of the pointer relative to the window.
208
+ * The value is expressed in point units. It is recommended to use it instead
209
+ * of `x` in cases when the original view can be transformed as an
210
+ * effect of the gesture.
211
+ */
212
+ absoluteX: number;
213
+
214
+ /**
215
+ * Y coordinate of the current position of the pointer relative to the window.
216
+ * The value is expressed in point units. It is recommended to use it instead
217
+ * of `y` in cases when the original view can be transformed as an
218
+ * effect of the gesture.
219
+ */
220
+ absoluteY: number;
221
+
222
+ /**
223
+ * Object containing additional stylus data.
224
+ */
225
+ stylusData: StylusData | undefined;
226
+ };
@@ -529,6 +529,7 @@ export default function createHandler<
529
529
  ? {
530
530
  handlerType: name,
531
531
  handlerTag: this.handlerTag,
532
+ enabled: this.props.enabled,
532
533
  }
533
534
  : {}),
534
535
  testID: this.props.testID ?? child.props.testID,
@@ -18,6 +18,7 @@ import type {
18
18
  RotationGestureHandlerEventPayload,
19
19
  TapGestureHandlerEventPayload,
20
20
  NativeViewGestureHandlerPayload,
21
+ HoverGestureHandlerEventPayload,
21
22
  } from '../GestureHandlerEventPayload';
22
23
  import { isRemoteDebuggingEnabled } from '../../utils';
23
24
 
@@ -31,7 +32,8 @@ export type GestureType =
31
32
  | BaseGesture<PinchGestureHandlerEventPayload>
32
33
  | BaseGesture<FlingGestureHandlerEventPayload>
33
34
  | BaseGesture<ForceTouchGestureHandlerEventPayload>
34
- | BaseGesture<NativeViewGestureHandlerPayload>;
35
+ | BaseGesture<NativeViewGestureHandlerPayload>
36
+ | BaseGesture<HoverGestureHandlerEventPayload>;
35
37
 
36
38
  export type GestureRef =
37
39
  | number
@@ -1,12 +1,6 @@
1
1
  import { BaseGestureConfig, ContinousBaseGesture } from './gesture';
2
2
  import { GestureUpdateEvent } from '../gestureHandlerCommon';
3
-
4
- export type HoverGestureHandlerEventPayload = {
5
- x: number;
6
- y: number;
7
- absoluteX: number;
8
- absoluteY: number;
9
- };
3
+ import type { HoverGestureHandlerEventPayload } from '../GestureHandlerEventPayload';
10
4
 
11
5
  export type HoverGestureChangeEventPayload = {
12
6
  changeX: number;
@@ -138,6 +138,7 @@ const handlersDefaultEvents: DefaultEventsMapping = {
138
138
  velocityX: 3,
139
139
  velocityY: 0,
140
140
  numberOfPointers: 1,
141
+ stylusData: undefined,
141
142
  },
142
143
  [pinchHandlerName]: {
143
144
  focalX: 0,
@@ -404,6 +405,7 @@ interface HandlerData {
404
405
  emitEvent: EventEmitter;
405
406
  handlerType: HandlerNames;
406
407
  handlerTag: number;
408
+ enabled: boolean | undefined;
407
409
  }
408
410
  function getHandlerData(
409
411
  componentOrGesture: ReactTestInstance | GestureType
@@ -416,6 +418,7 @@ function getHandlerData(
416
418
  },
417
419
  handlerType: gesture.handlerName as HandlerNames,
418
420
  handlerTag: gesture.handlerTag,
421
+ enabled: gesture.config.enabled,
419
422
  };
420
423
  }
421
424
  const gestureHandlerComponent = componentOrGesture;
@@ -425,6 +428,7 @@ function getHandlerData(
425
428
  },
426
429
  handlerType: gestureHandlerComponent.props.handlerType as HandlerNames,
427
430
  handlerTag: gestureHandlerComponent.props.handlerTag as number,
431
+ enabled: gestureHandlerComponent.props.enabled,
428
432
  };
429
433
  }
430
434
  type AllGestures =
@@ -466,9 +470,13 @@ export function fireGestureHandler<THandler extends AllGestures | AllHandlers>(
466
470
  componentOrGesture: ReactTestInstance | GestureType,
467
471
  eventList: Partial<GestureHandlerTestEvent<ExtractConfig<THandler>>>[] = []
468
472
  ): void {
469
- const { emitEvent, handlerType, handlerTag } =
473
+ const { emitEvent, handlerType, handlerTag, enabled } =
470
474
  getHandlerData(componentOrGesture);
471
475
 
476
+ if (enabled === false) {
477
+ return;
478
+ }
479
+
472
480
  let _ = fillMissingStatesTransitions(
473
481
  eventList,
474
482
  isDiscreteHandler(handlerType)
@@ -507,7 +507,7 @@ export default abstract class GestureHandler implements IGestureHandler {
507
507
  nativeEvent: {
508
508
  handlerTag: this.handlerTag,
509
509
  state: this.currentState,
510
- eventType: event.touchEventType ?? eventType,
510
+ eventType: eventType,
511
511
  changedTouches: changed,
512
512
  allTouches: all,
513
513
  numberOfTouches: numberOfTouches,
@@ -1,13 +1,22 @@
1
1
  import { State } from '../../State';
2
- import { AdaptedEvent, Config } from '../interfaces';
2
+ import { AdaptedEvent, Config, StylusData } from '../interfaces';
3
3
  import GestureHandlerOrchestrator from '../tools/GestureHandlerOrchestrator';
4
4
  import GestureHandler from './GestureHandler';
5
5
 
6
6
  export default class HoverGestureHandler extends GestureHandler {
7
+ private stylusData: StylusData | undefined;
8
+
7
9
  public init(ref: number, propsRef: React.RefObject<unknown>) {
8
10
  super.init(ref, propsRef);
9
11
  }
10
12
 
13
+ protected transformNativeEvent(): Record<string, unknown> {
14
+ return {
15
+ ...super.transformNativeEvent(),
16
+ stylusData: this.stylusData,
17
+ };
18
+ }
19
+
11
20
  public updateGestureConfig({ enabled = true, ...props }: Config): void {
12
21
  super.updateGestureConfig({ enabled: enabled, ...props });
13
22
  }
@@ -16,6 +25,7 @@ export default class HoverGestureHandler extends GestureHandler {
16
25
  GestureHandlerOrchestrator.getInstance().recordHandlerIfNotPresent(this);
17
26
 
18
27
  this.tracker.addToTracker(event);
28
+ this.stylusData = event.stylusData;
19
29
  super.onPointerMoveOver(event);
20
30
 
21
31
  if (this.getState() === State.UNDETERMINED) {
@@ -25,7 +35,9 @@ export default class HoverGestureHandler extends GestureHandler {
25
35
  }
26
36
 
27
37
  protected onPointerMoveOut(event: AdaptedEvent): void {
28
- this.tracker.addToTracker(event);
38
+ this.tracker.removeFromTracker(event.pointerId);
39
+ this.stylusData = event.stylusData;
40
+
29
41
  super.onPointerMoveOut(event);
30
42
 
31
43
  this.end();
@@ -33,6 +45,8 @@ export default class HoverGestureHandler extends GestureHandler {
33
45
 
34
46
  protected onPointerMove(event: AdaptedEvent): void {
35
47
  this.tracker.track(event);
48
+ this.stylusData = event.stylusData;
49
+
36
50
  super.onPointerMove(event);
37
51
  }
38
52
 
@@ -1,6 +1,6 @@
1
1
  import { State } from '../../State';
2
2
  import { DEFAULT_TOUCH_SLOP } from '../constants';
3
- import { AdaptedEvent, Config } from '../interfaces';
3
+ import { AdaptedEvent, Config, StylusData } from '../interfaces';
4
4
 
5
5
  import GestureHandler from './GestureHandler';
6
6
 
@@ -52,6 +52,8 @@ export default class PanGestureHandler extends GestureHandler {
52
52
  private lastX = 0;
53
53
  private lastY = 0;
54
54
 
55
+ private stylusData: StylusData | undefined;
56
+
55
57
  private activateAfterLongPress = 0;
56
58
  private activationTimeout = 0;
57
59
 
@@ -196,6 +198,7 @@ export default class PanGestureHandler extends GestureHandler {
196
198
  translationY: isNaN(translationY) ? 0 : translationY,
197
199
  velocityX: this.velocityX,
198
200
  velocityY: this.velocityY,
201
+ stylusData: this.stylusData,
199
202
  };
200
203
  }
201
204
 
@@ -217,6 +220,8 @@ export default class PanGestureHandler extends GestureHandler {
217
220
  }
218
221
 
219
222
  this.tracker.addToTracker(event);
223
+ this.stylusData = event.stylusData;
224
+
220
225
  super.onPointerDown(event);
221
226
 
222
227
  const lastCoords = this.tracker.getAbsoluteCoordsAverage();
@@ -259,6 +264,8 @@ export default class PanGestureHandler extends GestureHandler {
259
264
  }
260
265
 
261
266
  protected onPointerUp(event: AdaptedEvent): void {
267
+ this.stylusData = event.stylusData;
268
+
262
269
  super.onPointerUp(event);
263
270
  if (this.currentState === State.ACTIVE) {
264
271
  const lastCoords = this.tracker.getAbsoluteCoordsAverage();
@@ -306,6 +313,7 @@ export default class PanGestureHandler extends GestureHandler {
306
313
 
307
314
  protected onPointerMove(event: AdaptedEvent): void {
308
315
  this.tracker.track(event);
316
+ this.stylusData = event.stylusData;
309
317
 
310
318
  const lastCoords = this.tracker.getAbsoluteCoordsAverage();
311
319
  this.lastX = lastCoords.x;
@@ -326,6 +334,7 @@ export default class PanGestureHandler extends GestureHandler {
326
334
  }
327
335
 
328
336
  this.tracker.track(event);
337
+ this.stylusData = event.stylusData;
329
338
 
330
339
  const lastCoords = this.tracker.getAbsoluteCoordsAverage();
331
340
  this.lastX = lastCoords.x;
@@ -132,6 +132,14 @@ export interface PropsRef {
132
132
  onGestureHandlerStateChange: () => void;
133
133
  }
134
134
 
135
+ export interface StylusData {
136
+ tiltX: number;
137
+ tiltY: number;
138
+ azimuthAngle: number;
139
+ altitudeAngle: number;
140
+ pressure: number;
141
+ }
142
+
135
143
  export interface AdaptedEvent {
136
144
  x: number;
137
145
  y: number;
@@ -142,9 +150,7 @@ export interface AdaptedEvent {
142
150
  pointerType: PointerType;
143
151
  time: number;
144
152
  button?: MouseButton;
145
- allTouches?: TouchList;
146
- changedTouches?: TouchList;
147
- touchEventType?: TouchEventType;
153
+ stylusData?: StylusData;
148
154
  }
149
155
 
150
156
  export enum EventTypes {
@@ -1,5 +1,5 @@
1
1
  /* eslint-disable @typescript-eslint/no-empty-function */
2
- import { AdaptedEvent, EventTypes, TouchEventType } from '../interfaces';
2
+ import { AdaptedEvent, EventTypes } from '../interfaces';
3
3
 
4
4
  type PointerEventCallback = (event: AdaptedEvent) => void;
5
5
 
@@ -18,9 +18,7 @@ export default abstract class EventManager<T> {
18
18
 
19
19
  protected abstract mapEvent(
20
20
  event: Event,
21
- eventType: EventTypes,
22
- index?: number,
23
- touchEventType?: TouchEventType
21
+ eventType: EventTypes
24
22
  ): AdaptedEvent;
25
23
 
26
24
  protected onPointerDown(_event: AdaptedEvent): void {}
@@ -5,7 +5,6 @@ import {
5
5
  MeasureResult,
6
6
  } from './GestureHandlerDelegate';
7
7
  import PointerEventManager from './PointerEventManager';
8
- import TouchEventManager from './TouchEventManager';
9
8
  import { State } from '../../State';
10
9
  import { isPointerInBounds } from '../utils';
11
10
  import EventManager from './EventManager';
@@ -58,7 +57,6 @@ export class GestureHandlerWebDelegate
58
57
  this.setContextMenu(config.enabled);
59
58
 
60
59
  this.eventManagers.push(new PointerEventManager(this.view));
61
- this.eventManagers.push(new TouchEventManager(this.view));
62
60
  this.eventManagers.push(new KeyboardEventManager(this.view));
63
61
 
64
62
  this.eventManagers.forEach((manager) =>
@@ -4,15 +4,12 @@ import { AdaptedEvent, EventTypes, Point } from '../interfaces';
4
4
  import {
5
5
  PointerTypeMapping,
6
6
  calculateViewScale,
7
+ tryExtractStylusData,
7
8
  isPointerInBounds,
8
9
  } from '../utils';
9
10
  import { PointerType } from '../../PointerType';
10
11
 
11
12
  const POINTER_CAPTURE_EXCLUDE_LIST = new Set<string>(['SELECT', 'INPUT']);
12
- const PointerTypes = {
13
- Touch: 'touch',
14
- Stylus: 'pen',
15
- };
16
13
 
17
14
  export default class PointerEventManager extends EventManager<HTMLElement> {
18
15
  private trackedPointers = new Set<number>();
@@ -35,9 +32,6 @@ export default class PointerEventManager extends EventManager<HTMLElement> {
35
32
  }
36
33
 
37
34
  private pointerDownCallback = (event: PointerEvent) => {
38
- if (event.pointerType === PointerTypes.Touch) {
39
- return;
40
- }
41
35
  if (!isPointerInBounds(this.view, { x: event.clientX, y: event.clientY })) {
42
36
  return;
43
37
  }
@@ -61,10 +55,6 @@ export default class PointerEventManager extends EventManager<HTMLElement> {
61
55
  };
62
56
 
63
57
  private pointerUpCallback = (event: PointerEvent) => {
64
- if (event.pointerType === PointerTypes.Touch) {
65
- return;
66
- }
67
-
68
58
  // When we call reset on gesture handlers, it also resets their event managers
69
59
  // In some handlers (like RotationGestureHandler) reset is called before all pointers leave view
70
60
  // This means, that activePointersCounter will be set to 0, while there are still remaining pointers on view
@@ -92,21 +82,6 @@ export default class PointerEventManager extends EventManager<HTMLElement> {
92
82
  };
93
83
 
94
84
  private pointerMoveCallback = (event: PointerEvent) => {
95
- if (event.pointerType === PointerTypes.Touch) {
96
- return;
97
- }
98
-
99
- // Stylus triggers `pointermove` event when it detects changes in pressure. Since it is very sensitive to those changes,
100
- // it constantly sends events, even though there was no change in position. To fix that we check whether
101
- // pointer has actually moved and if not, we do not send event.
102
- if (
103
- event.pointerType === PointerTypes.Stylus &&
104
- event.x === this.lastPosition.x &&
105
- event.y === this.lastPosition.y
106
- ) {
107
- return;
108
- }
109
-
110
85
  const adaptedEvent: AdaptedEvent = this.mapEvent(event, EventTypes.MOVE);
111
86
  const target = event.target as HTMLElement;
112
87
 
@@ -161,10 +136,6 @@ export default class PointerEventManager extends EventManager<HTMLElement> {
161
136
  };
162
137
 
163
138
  private pointerCancelCallback = (event: PointerEvent) => {
164
- if (event.pointerType === PointerTypes.Touch) {
165
- return;
166
- }
167
-
168
139
  const adaptedEvent: AdaptedEvent = this.mapEvent(event, EventTypes.CANCEL);
169
140
 
170
141
  this.onPointerCancel(adaptedEvent);
@@ -174,20 +145,12 @@ export default class PointerEventManager extends EventManager<HTMLElement> {
174
145
  };
175
146
 
176
147
  private pointerEnterCallback = (event: PointerEvent) => {
177
- if (event.pointerType === PointerTypes.Touch) {
178
- return;
179
- }
180
-
181
148
  const adaptedEvent: AdaptedEvent = this.mapEvent(event, EventTypes.ENTER);
182
149
 
183
150
  this.onPointerMoveOver(adaptedEvent);
184
151
  };
185
152
 
186
153
  private pointerLeaveCallback = (event: PointerEvent) => {
187
- if (event.pointerType === PointerTypes.Touch) {
188
- return;
189
- }
190
-
191
154
  const adaptedEvent: AdaptedEvent = this.mapEvent(event, EventTypes.LEAVE);
192
155
 
193
156
  this.onPointerMoveOut(adaptedEvent);
@@ -252,6 +215,7 @@ export default class PointerEventManager extends EventManager<HTMLElement> {
252
215
  PointerTypeMapping.get(event.pointerType) ?? PointerType.OTHER,
253
216
  button: this.mouseButtonsMapper.get(event.button),
254
217
  time: event.timeStamp,
218
+ stylusData: tryExtractStylusData(event),
255
219
  };
256
220
  }
257
221
 
package/src/web/utils.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { PointerType } from '../PointerType';
2
- import { Point } from './interfaces';
2
+ import type { Point, StylusData } from './interfaces';
3
3
 
4
4
  export function isPointerInBounds(view: HTMLElement, { x, y }: Point): boolean {
5
5
  const rect: DOMRect = view.getBoundingClientRect();
@@ -54,3 +54,176 @@ export function calculateViewScale(view: HTMLElement) {
54
54
 
55
55
  return resultScales;
56
56
  }
57
+
58
+ export function tryExtractStylusData(
59
+ event: PointerEvent
60
+ ): StylusData | undefined {
61
+ const pointerType = PointerTypeMapping.get(event.pointerType);
62
+
63
+ if (pointerType !== PointerType.STYLUS) {
64
+ return;
65
+ }
66
+
67
+ // @ts-ignore This property exists (https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent#instance_properties)
68
+ const eventAzimuthAngle: number | undefined = event.azimuthAngle;
69
+ // @ts-ignore This property exists (https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent#instance_properties)
70
+ const eventAltitudeAngle: number | undefined = event.altitudeAngle;
71
+
72
+ if (event.tiltX === 0 && event.tiltY === 0) {
73
+ // If we are in this branch, it means that either tilt properties are not supported and we have to calculate them from altitude and azimuth angles,
74
+ // or stylus is perpendicular to the screen and we can use altitude / azimuth instead of tilt
75
+
76
+ // If azimuth and altitude are undefined in this branch, it means that we are either perpendicular to the screen,
77
+ // or that none of the position sets is supported. In that case, we can treat stylus as perpendicular
78
+ if (eventAzimuthAngle === undefined || eventAltitudeAngle === undefined) {
79
+ return {
80
+ tiltX: 0,
81
+ tiltY: 0,
82
+ azimuthAngle: Math.PI / 2,
83
+ altitudeAngle: Math.PI / 2,
84
+ pressure: event.pressure,
85
+ };
86
+ }
87
+
88
+ const { tiltX, tiltY } = spherical2tilt(
89
+ eventAltitudeAngle,
90
+ eventAzimuthAngle
91
+ );
92
+
93
+ return {
94
+ tiltX,
95
+ tiltY,
96
+ azimuthAngle: eventAzimuthAngle,
97
+ altitudeAngle: eventAltitudeAngle,
98
+ pressure: event.pressure,
99
+ };
100
+ }
101
+
102
+ const { altitudeAngle, azimuthAngle } = tilt2spherical(
103
+ event.tiltX,
104
+ event.tiltY
105
+ );
106
+
107
+ return {
108
+ tiltX: event.tiltX,
109
+ tiltY: event.tiltY,
110
+ azimuthAngle,
111
+ altitudeAngle,
112
+ pressure: event.pressure,
113
+ };
114
+ }
115
+
116
+ // `altitudeAngle` and `azimuthAngle` are experimental properties, which are not supported on Firefox and Safari.
117
+ // Given that, we use `tilt` properties and algorithm that converts one value to another.
118
+ //
119
+ // Source: https://w3c.github.io/pointerevents/#converting-between-tiltx-tilty-and-altitudeangle-azimuthangle
120
+ function tilt2spherical(tiltX: number, tiltY: number) {
121
+ const tiltXrad = (tiltX * Math.PI) / 180;
122
+ const tiltYrad = (tiltY * Math.PI) / 180;
123
+
124
+ // calculate azimuth angle
125
+ let azimuthAngle = 0;
126
+
127
+ if (tiltX === 0) {
128
+ if (tiltY > 0) {
129
+ azimuthAngle = Math.PI / 2;
130
+ } else if (tiltY < 0) {
131
+ azimuthAngle = (3 * Math.PI) / 2;
132
+ }
133
+ } else if (tiltY === 0) {
134
+ if (tiltX < 0) {
135
+ azimuthAngle = Math.PI;
136
+ }
137
+ } else if (Math.abs(tiltX) === 90 || Math.abs(tiltY) === 90) {
138
+ // not enough information to calculate azimuth
139
+ azimuthAngle = 0;
140
+ } else {
141
+ // Non-boundary case: neither tiltX nor tiltY is equal to 0 or +-90
142
+ const tanX = Math.tan(tiltXrad);
143
+ const tanY = Math.tan(tiltYrad);
144
+
145
+ azimuthAngle = Math.atan2(tanY, tanX);
146
+ if (azimuthAngle < 0) {
147
+ azimuthAngle += 2 * Math.PI;
148
+ }
149
+ }
150
+
151
+ // calculate altitude angle
152
+ let altitudeAngle = 0;
153
+
154
+ if (Math.abs(tiltX) === 90 || Math.abs(tiltY) === 90) {
155
+ altitudeAngle = 0;
156
+ } else if (tiltX === 0) {
157
+ altitudeAngle = Math.PI / 2 - Math.abs(tiltYrad);
158
+ } else if (tiltY === 0) {
159
+ altitudeAngle = Math.PI / 2 - Math.abs(tiltXrad);
160
+ } else {
161
+ // Non-boundary case: neither tiltX nor tiltY is equal to 0 or +-90
162
+ altitudeAngle = Math.atan(
163
+ 1.0 /
164
+ Math.sqrt(
165
+ Math.pow(Math.tan(tiltXrad), 2) + Math.pow(Math.tan(tiltYrad), 2)
166
+ )
167
+ );
168
+ }
169
+
170
+ return { altitudeAngle: altitudeAngle, azimuthAngle: azimuthAngle };
171
+ }
172
+
173
+ // If we are on a platform that doesn't support `tiltX` and `tiltY`, we have to calculate them from `altitude` and `azimuth` angles.
174
+ //
175
+ // Source: https://w3c.github.io/pointerevents/#converting-between-tiltx-tilty-and-altitudeangle-azimuthangle
176
+ function spherical2tilt(altitudeAngle: number, azimuthAngle: number) {
177
+ const radToDeg = 180 / Math.PI;
178
+
179
+ let tiltXrad = 0;
180
+ let tiltYrad = 0;
181
+
182
+ if (altitudeAngle === 0) {
183
+ // the pen is in the X-Y plane
184
+ if (azimuthAngle === 0 || azimuthAngle === 2 * Math.PI) {
185
+ // pen is on positive X axis
186
+ tiltXrad = Math.PI / 2;
187
+ }
188
+ if (azimuthAngle === Math.PI / 2) {
189
+ // pen is on positive Y axis
190
+ tiltYrad = Math.PI / 2;
191
+ }
192
+ if (azimuthAngle === Math.PI) {
193
+ // pen is on negative X axis
194
+ tiltXrad = -Math.PI / 2;
195
+ }
196
+ if (azimuthAngle === (3 * Math.PI) / 2) {
197
+ // pen is on negative Y axis
198
+ tiltYrad = -Math.PI / 2;
199
+ }
200
+ if (azimuthAngle > 0 && azimuthAngle < Math.PI / 2) {
201
+ tiltXrad = Math.PI / 2;
202
+ tiltYrad = Math.PI / 2;
203
+ }
204
+ if (azimuthAngle > Math.PI / 2 && azimuthAngle < Math.PI) {
205
+ tiltXrad = -Math.PI / 2;
206
+ tiltYrad = Math.PI / 2;
207
+ }
208
+ if (azimuthAngle > Math.PI && azimuthAngle < (3 * Math.PI) / 2) {
209
+ tiltXrad = -Math.PI / 2;
210
+ tiltYrad = -Math.PI / 2;
211
+ }
212
+ if (azimuthAngle > (3 * Math.PI) / 2 && azimuthAngle < 2 * Math.PI) {
213
+ tiltXrad = Math.PI / 2;
214
+ tiltYrad = -Math.PI / 2;
215
+ }
216
+ }
217
+
218
+ if (altitudeAngle !== 0) {
219
+ const tanAlt = Math.tan(altitudeAngle);
220
+
221
+ tiltXrad = Math.atan(Math.cos(azimuthAngle) / tanAlt);
222
+ tiltYrad = Math.atan(Math.sin(azimuthAngle) / tanAlt);
223
+ }
224
+
225
+ const tiltX = Math.round(tiltXrad * radToDeg);
226
+ const tiltY = Math.round(tiltYrad * radToDeg);
227
+
228
+ return { tiltX, tiltY };
229
+ }