nexa-mobile 0.6.4 → 0.7.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.
@@ -0,0 +1,5 @@
1
+ export * from './GestureManager.js';
2
+ export * from './usePan.js';
3
+ export * from './usePinch.js';
4
+ export * from './useLongPress.js';
5
+ export * from './useDoubleTap.js';
@@ -0,0 +1,5 @@
1
+ export * from './GestureManager.js';
2
+ export * from './usePan.js';
3
+ export * from './usePinch.js';
4
+ export * from './useLongPress.js';
5
+ export * from './useDoubleTap.js';
@@ -0,0 +1,8 @@
1
+ export interface DoubleTapOptions {
2
+ interval?: number;
3
+ radius?: number;
4
+ }
5
+ export interface DoubleTapHandle {
6
+ destroy: () => void;
7
+ }
8
+ export declare function useDoubleTap(el: HTMLElement, callback: (e: TouchEvent) => void, options?: DoubleTapOptions): DoubleTapHandle;
@@ -0,0 +1,32 @@
1
+ export function useDoubleTap(el, callback, options = {}) {
2
+ const interval = options.interval ?? 300;
3
+ const radius = options.radius ?? 30;
4
+ let lastTapTime = 0;
5
+ let lastX = 0;
6
+ let lastY = 0;
7
+ const onTouchStart = (e) => {
8
+ const t = e.touches[0];
9
+ const now = Date.now();
10
+ const dt = now - lastTapTime;
11
+ const dx = Math.abs(t.clientX - lastX);
12
+ const dy = Math.abs(t.clientY - lastY);
13
+ if (dt < interval && dx <= radius && dy <= radius) {
14
+ callback(e);
15
+ lastTapTime = 0;
16
+ }
17
+ else {
18
+ lastTapTime = now;
19
+ lastX = t.clientX;
20
+ lastY = t.clientY;
21
+ }
22
+ };
23
+ const onTouchEnd = () => { };
24
+ el.addEventListener('touchstart', onTouchStart, { passive: true });
25
+ el.addEventListener('touchend', onTouchEnd, { passive: true });
26
+ return {
27
+ destroy() {
28
+ el.removeEventListener('touchstart', onTouchStart);
29
+ el.removeEventListener('touchend', onTouchEnd);
30
+ },
31
+ };
32
+ }
@@ -0,0 +1,8 @@
1
+ export interface LongPressOptions {
2
+ threshold?: number;
3
+ moveTolerance?: number;
4
+ }
5
+ export interface LongPressHandle {
6
+ destroy: () => void;
7
+ }
8
+ export declare function useLongPress(el: HTMLElement, callback: (e: TouchEvent | MouseEvent) => void, options?: LongPressOptions): LongPressHandle;
@@ -0,0 +1,52 @@
1
+ export function useLongPress(el, callback, options = {}) {
2
+ const threshold = options.threshold ?? 500;
3
+ const moveTolerance = options.moveTolerance ?? 10;
4
+ let timer = null;
5
+ let startX = 0;
6
+ let startY = 0;
7
+ let currentEvent = null;
8
+ const clear = () => {
9
+ if (timer !== null) {
10
+ clearTimeout(timer);
11
+ timer = null;
12
+ }
13
+ };
14
+ const start = (x, y, e) => {
15
+ clear();
16
+ startX = x;
17
+ startY = y;
18
+ currentEvent = e;
19
+ timer = setTimeout(() => {
20
+ if (currentEvent)
21
+ callback(currentEvent);
22
+ timer = null;
23
+ }, threshold);
24
+ };
25
+ const move = (x, y) => {
26
+ const dx = Math.abs(x - startX);
27
+ const dy = Math.abs(y - startY);
28
+ if (dx > moveTolerance || dy > moveTolerance)
29
+ clear();
30
+ };
31
+ const onTouchStart = (e) => {
32
+ const t = e.touches[0];
33
+ start(t.clientX, t.clientY, e);
34
+ };
35
+ const onTouchMove = (e) => {
36
+ const t = e.touches[0];
37
+ move(t.clientX, t.clientY);
38
+ };
39
+ el.addEventListener('touchstart', onTouchStart, { passive: true });
40
+ el.addEventListener('touchmove', onTouchMove, { passive: true });
41
+ el.addEventListener('touchend', clear, { passive: true });
42
+ el.addEventListener('touchcancel', clear, { passive: true });
43
+ return {
44
+ destroy() {
45
+ clear();
46
+ el.removeEventListener('touchstart', onTouchStart);
47
+ el.removeEventListener('touchmove', onTouchMove);
48
+ el.removeEventListener('touchend', clear);
49
+ el.removeEventListener('touchcancel', clear);
50
+ },
51
+ };
52
+ }
@@ -0,0 +1,10 @@
1
+ import { signal } from 'nexa-reactivity';
2
+ export interface PanState {
3
+ active: ReturnType<typeof signal<boolean>>;
4
+ deltaX: ReturnType<typeof signal<number>>;
5
+ deltaY: ReturnType<typeof signal<number>>;
6
+ velocityX: ReturnType<typeof signal<number>>;
7
+ velocityY: ReturnType<typeof signal<number>>;
8
+ destroy: () => void;
9
+ }
10
+ export declare function usePan(el: HTMLElement): PanState;
@@ -0,0 +1,88 @@
1
+ import { signal, batch } from 'nexa-reactivity';
2
+ export function usePan(el) {
3
+ const active = signal(false);
4
+ const deltaX = signal(0);
5
+ const deltaY = signal(0);
6
+ const velocityX = signal(0);
7
+ const velocityY = signal(0);
8
+ let startX = 0;
9
+ let startY = 0;
10
+ let lastX = 0;
11
+ let lastY = 0;
12
+ let lastTime = 0;
13
+ const onStart = (x, y) => {
14
+ startX = x;
15
+ startY = y;
16
+ lastX = x;
17
+ lastY = y;
18
+ lastTime = Date.now();
19
+ batch(() => {
20
+ active.value = true;
21
+ deltaX.value = 0;
22
+ deltaY.value = 0;
23
+ velocityX.value = 0;
24
+ velocityY.value = 0;
25
+ });
26
+ };
27
+ const onMove = (x, y) => {
28
+ if (!active.value)
29
+ return;
30
+ const now = Date.now();
31
+ batch(() => {
32
+ deltaX.value = x - startX;
33
+ deltaY.value = y - startY;
34
+ });
35
+ lastX = x;
36
+ lastY = y;
37
+ lastTime = now;
38
+ };
39
+ const onEnd = (x, y) => {
40
+ const dt = Date.now() - lastTime;
41
+ if (dt > 0) {
42
+ batch(() => {
43
+ velocityX.value = (x - lastX) / dt;
44
+ velocityY.value = (y - lastY) / dt;
45
+ active.value = false;
46
+ });
47
+ }
48
+ else {
49
+ active.value = false;
50
+ }
51
+ };
52
+ const onTouchStart = (e) => {
53
+ const t = e.touches[0];
54
+ onStart(t.clientX, t.clientY);
55
+ };
56
+ const onTouchMove = (e) => {
57
+ const t = e.touches[0];
58
+ onMove(t.clientX, t.clientY);
59
+ };
60
+ const onTouchEnd = (e) => {
61
+ const t = e.changedTouches[0];
62
+ onEnd(t ? t.clientX : lastX, t ? t.clientY : lastY);
63
+ };
64
+ const onMouseDown = (e) => onStart(e.clientX, e.clientY);
65
+ const onMouseMove = (e) => onMove(e.clientX, e.clientY);
66
+ const onMouseUp = (e) => onEnd(e.clientX, e.clientY);
67
+ el.addEventListener('touchstart', onTouchStart, { passive: true });
68
+ el.addEventListener('touchmove', onTouchMove, { passive: false });
69
+ el.addEventListener('touchend', onTouchEnd, { passive: true });
70
+ el.addEventListener('mousedown', onMouseDown);
71
+ document.addEventListener('mousemove', onMouseMove);
72
+ document.addEventListener('mouseup', onMouseUp);
73
+ return {
74
+ active,
75
+ deltaX,
76
+ deltaY,
77
+ velocityX,
78
+ velocityY,
79
+ destroy() {
80
+ el.removeEventListener('touchstart', onTouchStart);
81
+ el.removeEventListener('touchmove', onTouchMove);
82
+ el.removeEventListener('touchend', onTouchEnd);
83
+ el.removeEventListener('mousedown', onMouseDown);
84
+ document.removeEventListener('mousemove', onMouseMove);
85
+ document.removeEventListener('mouseup', onMouseUp);
86
+ },
87
+ };
88
+ }
@@ -0,0 +1,9 @@
1
+ import { signal } from 'nexa-reactivity';
2
+ export interface PinchState {
3
+ active: ReturnType<typeof signal<boolean>>;
4
+ scale: ReturnType<typeof signal<number>>;
5
+ originX: ReturnType<typeof signal<number>>;
6
+ originY: ReturnType<typeof signal<number>>;
7
+ destroy: () => void;
8
+ }
9
+ export declare function usePinch(el: HTMLElement): PinchState;
@@ -0,0 +1,57 @@
1
+ import { signal, batch } from 'nexa-reactivity';
2
+ const getDistance = (t1, t2) => {
3
+ const dx = t1.clientX - t2.clientX;
4
+ const dy = t1.clientY - t2.clientY;
5
+ return Math.sqrt(dx * dx + dy * dy);
6
+ };
7
+ const getMidpoint = (t1, t2) => ({
8
+ x: (t1.clientX + t2.clientX) / 2,
9
+ y: (t1.clientY + t2.clientY) / 2,
10
+ });
11
+ export function usePinch(el) {
12
+ const active = signal(false);
13
+ const scale = signal(1);
14
+ const originX = signal(0);
15
+ const originY = signal(0);
16
+ let startDistance = 0;
17
+ let startScale = 1;
18
+ const onTouchStart = (e) => {
19
+ if (e.touches.length < 2)
20
+ return;
21
+ startDistance = getDistance(e.touches[0], e.touches[1]);
22
+ startScale = scale.value;
23
+ const mid = getMidpoint(e.touches[0], e.touches[1]);
24
+ batch(() => {
25
+ active.value = true;
26
+ originX.value = mid.x;
27
+ originY.value = mid.y;
28
+ });
29
+ };
30
+ const onTouchMove = (e) => {
31
+ if (e.touches.length < 2 || !active.value)
32
+ return;
33
+ const currentDistance = getDistance(e.touches[0], e.touches[1]);
34
+ if (startDistance === 0)
35
+ return;
36
+ scale.value = startScale * (currentDistance / startDistance);
37
+ };
38
+ const onTouchEnd = (e) => {
39
+ if (e.touches.length < 2) {
40
+ active.value = false;
41
+ }
42
+ };
43
+ el.addEventListener('touchstart', onTouchStart, { passive: true });
44
+ el.addEventListener('touchmove', onTouchMove, { passive: false });
45
+ el.addEventListener('touchend', onTouchEnd, { passive: true });
46
+ return {
47
+ active,
48
+ scale,
49
+ originX,
50
+ originY,
51
+ destroy() {
52
+ el.removeEventListener('touchstart', onTouchStart);
53
+ el.removeEventListener('touchmove', onTouchMove);
54
+ el.removeEventListener('touchend', onTouchEnd);
55
+ },
56
+ };
57
+ }
package/dist/index.d.ts CHANGED
@@ -1 +1 @@
1
- export * from './gestures/GestureManager.js';
1
+ export * from './gestures/index.js';
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- export * from './gestures/GestureManager.js';
1
+ export * from './gestures/index.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexa-mobile",
3
- "version": "0.6.4",
3
+ "version": "0.7.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -13,8 +13,8 @@
13
13
  }
14
14
  },
15
15
  "dependencies": {
16
- "nexa-reactivity": "0.6.4",
17
- "nexa-runtime": "0.6.4"
16
+ "nexa-reactivity": "0.7.0",
17
+ "nexa-runtime": "0.7.0"
18
18
  },
19
19
  "files": [
20
20
  "dist"