@object-ui/mobile 16.1.0 → 17.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.
- package/CHANGELOG.md +52 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/useGesture.d.ts.map +1 -1
- package/dist/useGesture.js +57 -3
- package/dist/useSpecGesture.d.ts +27 -1
- package/dist/useSpecGesture.d.ts.map +1 -1
- package/dist/useSpecGesture.js +69 -17
- package/package.json +5 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,57 @@
|
|
|
1
1
|
# @object-ui/mobile
|
|
2
2
|
|
|
3
|
+
## 17.1.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [9e7349e]
|
|
8
|
+
- Updated dependencies [8864971]
|
|
9
|
+
- Updated dependencies [b41f401]
|
|
10
|
+
- Updated dependencies [19e9fa0]
|
|
11
|
+
- Updated dependencies [38ca8be]
|
|
12
|
+
- Updated dependencies [4952edf]
|
|
13
|
+
- Updated dependencies [7f0252e]
|
|
14
|
+
- Updated dependencies [7639a61]
|
|
15
|
+
- Updated dependencies [94e63ef]
|
|
16
|
+
- Updated dependencies [c4db402]
|
|
17
|
+
- Updated dependencies [5319bf1]
|
|
18
|
+
- Updated dependencies [49e5671]
|
|
19
|
+
- Updated dependencies [b5b97e2]
|
|
20
|
+
- Updated dependencies [f59f2c1]
|
|
21
|
+
- Updated dependencies [4874117]
|
|
22
|
+
- Updated dependencies [ce08d55]
|
|
23
|
+
- Updated dependencies [2374a49]
|
|
24
|
+
- Updated dependencies [ea7f477]
|
|
25
|
+
- Updated dependencies [7f23cd0]
|
|
26
|
+
- Updated dependencies [24e0e0a]
|
|
27
|
+
- Updated dependencies [3a6cf24]
|
|
28
|
+
- Updated dependencies [aa35561]
|
|
29
|
+
- Updated dependencies [03bd53b]
|
|
30
|
+
- Updated dependencies [3c1f321]
|
|
31
|
+
- Updated dependencies [a045a32]
|
|
32
|
+
- Updated dependencies [912496d]
|
|
33
|
+
- Updated dependencies [9867281]
|
|
34
|
+
- @object-ui/types@17.1.0
|
|
35
|
+
|
|
36
|
+
## 17.0.0
|
|
37
|
+
|
|
38
|
+
### Patch Changes
|
|
39
|
+
|
|
40
|
+
- Updated dependencies [1767124]
|
|
41
|
+
- Updated dependencies [8ecf5a6]
|
|
42
|
+
- Updated dependencies [dfd3705]
|
|
43
|
+
- Updated dependencies [6dee2cb]
|
|
44
|
+
- Updated dependencies [c7cff19]
|
|
45
|
+
- Updated dependencies [cd09a7b]
|
|
46
|
+
- Updated dependencies [f1abf0e]
|
|
47
|
+
- Updated dependencies [f05b84e]
|
|
48
|
+
- Updated dependencies [662bdf9]
|
|
49
|
+
- Updated dependencies [059a052]
|
|
50
|
+
- Updated dependencies [53642d4]
|
|
51
|
+
- Updated dependencies [8aae006]
|
|
52
|
+
- Updated dependencies [d147a13]
|
|
53
|
+
- @object-ui/types@17.0.0
|
|
54
|
+
|
|
3
55
|
## 16.1.0
|
|
4
56
|
|
|
5
57
|
### Patch Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -33,5 +33,5 @@ export { createOfflineDataSource, type OfflineDataSource, type OfflineDataSource
|
|
|
33
33
|
export { useOfflineSync, type OfflineSyncState } from './useOfflineSync';
|
|
34
34
|
export { getServiceWorkerSource, requestBackgroundSync, type ServiceWorkerSourceOptions, } from './serviceWorkerSource';
|
|
35
35
|
export { BREAKPOINTS, resolveResponsiveValue } from './breakpoints';
|
|
36
|
-
export type { BreakpointName, ResponsiveValue, ResponsiveConfig, MobileOverrides, PWAConfig, PWAIcon,
|
|
36
|
+
export type { BreakpointName, ResponsiveValue, ResponsiveConfig, MobileOverrides, PWAConfig, PWAIcon, FetchCacheStrategy, OfflineConfig, OfflineRoute, GestureType, GestureConfig, GestureContext, MobileComponentConfig, SpecGestureConfig, SwipeGestureConfig, PinchGestureConfig, LongPressGestureConfig, TouchInteraction, TouchTargetConfig, } from '@object-ui/types';
|
|
37
37
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,aAAa,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,mBAAmB,EAAE,KAAK,oBAAoB,EAAE,KAAK,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AACrH,OAAO,EAAE,UAAU,EAAE,KAAK,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAC9E,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACtG,OAAO,EAAE,gBAAgB,EAAE,KAAK,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5E,OAAO,EAAE,mBAAmB,EAAE,KAAK,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AAC3F,OAAO,EAAE,mBAAmB,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAE,qBAAqB,EAAE,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAClF,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,kBAAkB,EAClB,YAAY,EACZ,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,GACzB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,uBAAuB,EACvB,KAAK,iBAAiB,EACtB,KAAK,wBAAwB,EAC7B,KAAK,mBAAmB,GACzB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,cAAc,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzE,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACrB,KAAK,0BAA0B,GAChC,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAGpE,YAAY,EACV,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,SAAS,EACT,OAAO,EACP,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,aAAa,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,mBAAmB,EAAE,KAAK,oBAAoB,EAAE,KAAK,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AACrH,OAAO,EAAE,UAAU,EAAE,KAAK,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAC9E,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACtG,OAAO,EAAE,gBAAgB,EAAE,KAAK,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5E,OAAO,EAAE,mBAAmB,EAAE,KAAK,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AAC3F,OAAO,EAAE,mBAAmB,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAE,qBAAqB,EAAE,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAClF,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,kBAAkB,EAClB,YAAY,EACZ,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,GACzB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,uBAAuB,EACvB,KAAK,iBAAiB,EACtB,KAAK,wBAAwB,EAC7B,KAAK,mBAAmB,GACzB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,cAAc,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzE,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACrB,KAAK,0BAA0B,GAChC,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAGpE,YAAY,EACV,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,SAAS,EACT,OAAO,EACP,kBAAkB,EAClB,aAAa,EACb,YAAY,EACZ,WAAW,EACX,aAAa,EACb,cAAc,EACd,qBAAqB,EACrB,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,sBAAsB,EACtB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,kBAAkB,CAAC"}
|
package/dist/useGesture.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useGesture.d.ts","sourceRoot":"","sources":["../src/useGesture.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEpE,MAAM,WAAW,iBAAiB;IAChC,6BAA6B;IAC7B,IAAI,EAAE,WAAW,CAAC;IAClB,wCAAwC;IACxC,SAAS,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;IAC7C,oDAAoD;IACpD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uDAAuD;IACvD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,2CAA2C;IAC3C,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAC5D,OAAO,EAAE,iBAAiB,
|
|
1
|
+
{"version":3,"file":"useGesture.d.ts","sourceRoot":"","sources":["../src/useGesture.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEpE,MAAM,WAAW,iBAAiB;IAChC,6BAA6B;IAC7B,IAAI,EAAE,WAAW,CAAC;IAClB,wCAAwC;IACxC,SAAS,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;IAC7C,oDAAoD;IACpD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uDAAuD;IACvD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,2CAA2C;IAC3C,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAC5D,OAAO,EAAE,iBAAiB,uCAqK3B"}
|
package/dist/useGesture.js
CHANGED
|
@@ -23,12 +23,27 @@ export function useGesture(options) {
|
|
|
23
23
|
const ref = useRef(null);
|
|
24
24
|
const startRef = useRef(null);
|
|
25
25
|
const longPressTimerRef = useRef(undefined);
|
|
26
|
+
// Real double-tap needs the PREVIOUS tap's timestamp — the old
|
|
27
|
+
// implementation fired on every single qualifying tap (#2942).
|
|
28
|
+
const lastTapRef = useRef(0);
|
|
29
|
+
// Two-touch tracking for pinch/rotate: initial + latest distance/angle
|
|
30
|
+
// between the two touch points.
|
|
31
|
+
const twoTouchRef = useRef(null);
|
|
26
32
|
const { type, onGesture, threshold = 50, longPressDuration = 500, enabled = true } = options;
|
|
27
33
|
const handleTouchStart = useCallback((e) => {
|
|
28
34
|
if (!enabled)
|
|
29
35
|
return;
|
|
30
36
|
const touch = e.touches[0];
|
|
31
37
|
startRef.current = { x: touch.clientX, y: touch.clientY, time: Date.now() };
|
|
38
|
+
// Pinch/rotate baseline — distance and angle between the two touches.
|
|
39
|
+
if ((type === 'pinch' || type === 'rotate') && e.touches.length >= 2) {
|
|
40
|
+
const [t1, t2] = [e.touches[0], e.touches[1]];
|
|
41
|
+
const dx2 = t2.clientX - t1.clientX;
|
|
42
|
+
const dy2 = t2.clientY - t1.clientY;
|
|
43
|
+
const d = Math.hypot(dx2, dy2);
|
|
44
|
+
const a = (Math.atan2(dy2, dx2) * 180) / Math.PI;
|
|
45
|
+
twoTouchRef.current = { d0: d, a0: a, d1: d, a1: a };
|
|
46
|
+
}
|
|
32
47
|
if (type === 'long-press') {
|
|
33
48
|
longPressTimerRef.current = setTimeout(() => {
|
|
34
49
|
if (startRef.current) {
|
|
@@ -62,14 +77,45 @@ export function useGesture(options) {
|
|
|
62
77
|
duration,
|
|
63
78
|
velocity,
|
|
64
79
|
};
|
|
80
|
+
// Pinch / rotate resolve from the two-touch delta, not the single-touch
|
|
81
|
+
// path below. Fired once on release, like the other discrete gestures.
|
|
82
|
+
if (type === 'pinch' || type === 'rotate') {
|
|
83
|
+
const t2 = twoTouchRef.current;
|
|
84
|
+
twoTouchRef.current = null;
|
|
85
|
+
startRef.current = null;
|
|
86
|
+
if (!t2 || t2.d0 <= 0)
|
|
87
|
+
return;
|
|
88
|
+
const scale = t2.d1 / t2.d0;
|
|
89
|
+
// Normalize the angle delta into (-180, 180].
|
|
90
|
+
let rotation = t2.a1 - t2.a0;
|
|
91
|
+
if (rotation > 180)
|
|
92
|
+
rotation -= 360;
|
|
93
|
+
if (rotation <= -180)
|
|
94
|
+
rotation += 360;
|
|
95
|
+
if (type === 'pinch' && Math.abs(scale - 1) >= 0.1) {
|
|
96
|
+
onGesture({ ...context, scale });
|
|
97
|
+
}
|
|
98
|
+
else if (type === 'rotate' && Math.abs(rotation) >= 15) {
|
|
99
|
+
onGesture({ ...context, rotation });
|
|
100
|
+
}
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
65
103
|
// Detect gesture type
|
|
66
104
|
if (type === 'tap' && distance < 10 && duration < 300) {
|
|
67
105
|
onGesture(context);
|
|
68
106
|
}
|
|
69
107
|
else if (type === 'double-tap') {
|
|
70
|
-
//
|
|
108
|
+
// Two qualifying taps within 350ms — a single tap must NOT fire
|
|
109
|
+
// (it used to, collapsing double-tap into tap, #2942).
|
|
71
110
|
if (distance < 10 && duration < 300) {
|
|
72
|
-
|
|
111
|
+
const now = Date.now();
|
|
112
|
+
if (now - lastTapRef.current <= 350) {
|
|
113
|
+
lastTapRef.current = 0;
|
|
114
|
+
onGesture(context);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
lastTapRef.current = now;
|
|
118
|
+
}
|
|
73
119
|
}
|
|
74
120
|
}
|
|
75
121
|
else if (distance >= threshold) {
|
|
@@ -93,11 +139,19 @@ export function useGesture(options) {
|
|
|
93
139
|
}
|
|
94
140
|
startRef.current = null;
|
|
95
141
|
}, [type, onGesture, threshold, enabled]);
|
|
96
|
-
const handleTouchMove = useCallback(() => {
|
|
142
|
+
const handleTouchMove = useCallback((e) => {
|
|
97
143
|
// Cancel long-press if finger moves
|
|
98
144
|
if (type === 'long-press') {
|
|
99
145
|
clearTimeout(longPressTimerRef.current);
|
|
100
146
|
}
|
|
147
|
+
// Track the latest two-touch distance/angle for pinch/rotate.
|
|
148
|
+
if ((type === 'pinch' || type === 'rotate') && twoTouchRef.current && e.touches.length >= 2) {
|
|
149
|
+
const [t1, t2] = [e.touches[0], e.touches[1]];
|
|
150
|
+
const dx2 = t2.clientX - t1.clientX;
|
|
151
|
+
const dy2 = t2.clientY - t1.clientY;
|
|
152
|
+
twoTouchRef.current.d1 = Math.hypot(dx2, dy2);
|
|
153
|
+
twoTouchRef.current.a1 = (Math.atan2(dy2, dx2) * 180) / Math.PI;
|
|
154
|
+
}
|
|
101
155
|
}, [type]);
|
|
102
156
|
useEffect(() => {
|
|
103
157
|
const el = ref.current;
|
package/dist/useSpecGesture.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* This source code is licensed under the MIT license found in the
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*/
|
|
8
|
-
import type { SpecGestureConfig } from '@object-ui/types';
|
|
8
|
+
import type { GestureType, SpecGestureConfig } from '@object-ui/types';
|
|
9
9
|
export interface UseSpecGestureOptions {
|
|
10
10
|
/** Spec gesture configuration */
|
|
11
11
|
config: SpecGestureConfig;
|
|
@@ -15,7 +15,33 @@ export interface UseSpecGestureOptions {
|
|
|
15
15
|
onPinch?: (scale: number) => void;
|
|
16
16
|
/** Callback when a long-press gesture is detected */
|
|
17
17
|
onLongPress?: () => void;
|
|
18
|
+
/** Callback when a double-tap gesture is detected */
|
|
19
|
+
onDoubleTap?: () => void;
|
|
20
|
+
/** Callback when a pan/drag gesture is detected (any-direction move) */
|
|
21
|
+
onPan?: (direction: string) => void;
|
|
22
|
+
/** Callback when a rotate gesture is detected (degrees, CW positive) */
|
|
23
|
+
onRotate?: (rotation: number) => void;
|
|
24
|
+
/** Fallback for gestures without a dedicated callback */
|
|
25
|
+
onGesture?: (context: {
|
|
26
|
+
type: string;
|
|
27
|
+
direction?: string;
|
|
28
|
+
scale?: number;
|
|
29
|
+
rotation?: number;
|
|
30
|
+
}) => void;
|
|
18
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Spec `GestureTypeSchema` (`ui/touch.zod.ts`) → the recognizer type
|
|
34
|
+
* `useGesture` implements. The spec's `drag` and `pan` are one recognizer
|
|
35
|
+
* (any-direction move past the threshold); `swipe` resolves per configured
|
|
36
|
+
* direction, so it maps through {@link SWIPE_DIRECTION_MAP} instead.
|
|
37
|
+
* Exported for the spec-parity test.
|
|
38
|
+
*
|
|
39
|
+
* Before #2942 the hook never read `config.type` at all — it branched on
|
|
40
|
+
* which SUB-OBJECT was present (`swipe` / `pinch` / `longPress`), so
|
|
41
|
+
* `pan` / `drag` / `rotate` / `double_tap` (types with no sub-object) all
|
|
42
|
+
* fell through to the `'tap'` initializer and fired on a tap.
|
|
43
|
+
*/
|
|
44
|
+
export declare const SPEC_GESTURE_TYPE_MAP: Record<string, GestureType>;
|
|
19
45
|
/**
|
|
20
46
|
* Spec-aware gesture hook that maps an @objectstack/spec GestureConfig
|
|
21
47
|
* to the existing useGesture hook.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSpecGesture.d.ts","sourceRoot":"","sources":["../src/useSpecGesture.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"useSpecGesture.d.ts","sourceRoot":"","sources":["../src/useSpecGesture.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAEvE,MAAM,WAAW,qBAAqB;IACpC,iCAAiC;IACjC,MAAM,EAAE,iBAAiB,CAAC;IAC1B,gDAAgD;IAChD,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,gDAAgD;IAChD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,qDAAqD;IACrD,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB,qDAAqD;IACrD,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB,wEAAwE;IACxE,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,wEAAwE;IACxE,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,yDAAyD;IACzD,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CACxG;AASD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAQ7D,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAChE,OAAO,EAAE,qBAAqB,uCAoE/B"}
|
package/dist/useSpecGesture.js
CHANGED
|
@@ -12,6 +12,27 @@ const SWIPE_DIRECTION_MAP = {
|
|
|
12
12
|
up: 'swipe-up',
|
|
13
13
|
down: 'swipe-down',
|
|
14
14
|
};
|
|
15
|
+
/**
|
|
16
|
+
* Spec `GestureTypeSchema` (`ui/touch.zod.ts`) → the recognizer type
|
|
17
|
+
* `useGesture` implements. The spec's `drag` and `pan` are one recognizer
|
|
18
|
+
* (any-direction move past the threshold); `swipe` resolves per configured
|
|
19
|
+
* direction, so it maps through {@link SWIPE_DIRECTION_MAP} instead.
|
|
20
|
+
* Exported for the spec-parity test.
|
|
21
|
+
*
|
|
22
|
+
* Before #2942 the hook never read `config.type` at all — it branched on
|
|
23
|
+
* which SUB-OBJECT was present (`swipe` / `pinch` / `longPress`), so
|
|
24
|
+
* `pan` / `drag` / `rotate` / `double_tap` (types with no sub-object) all
|
|
25
|
+
* fell through to the `'tap'` initializer and fired on a tap.
|
|
26
|
+
*/
|
|
27
|
+
export const SPEC_GESTURE_TYPE_MAP = {
|
|
28
|
+
swipe: 'swipe-left', // per-direction; resolved via SWIPE_DIRECTION_MAP
|
|
29
|
+
pinch: 'pinch',
|
|
30
|
+
long_press: 'long-press',
|
|
31
|
+
double_tap: 'double-tap',
|
|
32
|
+
drag: 'pan',
|
|
33
|
+
pan: 'pan',
|
|
34
|
+
rotate: 'rotate',
|
|
35
|
+
};
|
|
15
36
|
/**
|
|
16
37
|
* Spec-aware gesture hook that maps an @objectstack/spec GestureConfig
|
|
17
38
|
* to the existing useGesture hook.
|
|
@@ -26,28 +47,59 @@ const SWIPE_DIRECTION_MAP = {
|
|
|
26
47
|
* ```
|
|
27
48
|
*/
|
|
28
49
|
export function useSpecGesture(options) {
|
|
29
|
-
const { config, onSwipe, onPinch, onLongPress } = options;
|
|
50
|
+
const { config, onSwipe, onPinch, onLongPress, onDoubleTap, onPan, onRotate, onGesture: onAny } = options;
|
|
30
51
|
const enabled = config.enabled ?? true;
|
|
52
|
+
// The DECLARED type drives recognition (#2942) — `config.type` is required
|
|
53
|
+
// by the spec's `GestureConfigSchema`. The sub-object presence checks below
|
|
54
|
+
// only back-fill legacy configs that predate the type field.
|
|
55
|
+
const declared = typeof config.type === 'string'
|
|
56
|
+
? config.type
|
|
57
|
+
: config.swipe
|
|
58
|
+
? 'swipe'
|
|
59
|
+
: config.longPress
|
|
60
|
+
? 'long_press'
|
|
61
|
+
: config.pinch
|
|
62
|
+
? 'pinch'
|
|
63
|
+
: undefined;
|
|
31
64
|
let gestureType = 'tap';
|
|
32
65
|
let threshold;
|
|
33
66
|
let longPressDuration;
|
|
34
67
|
let onGesture = () => { };
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
68
|
+
const fallback = (ctx) => onAny?.(ctx);
|
|
69
|
+
switch (declared) {
|
|
70
|
+
case 'swipe': {
|
|
71
|
+
const dir = Array.isArray(config.swipe?.direction)
|
|
72
|
+
? config.swipe?.direction[0]
|
|
73
|
+
: config.swipe?.direction;
|
|
74
|
+
gestureType = (dir ? SWIPE_DIRECTION_MAP[dir] : undefined) ?? 'swipe-left';
|
|
75
|
+
threshold = config.swipe?.threshold;
|
|
76
|
+
onGesture = (ctx) => (onSwipe ? onSwipe(ctx.direction ?? dir ?? 'left') : fallback({ type: 'swipe', ...ctx }));
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
case 'long_press':
|
|
80
|
+
gestureType = 'long-press';
|
|
81
|
+
longPressDuration = config.longPress?.duration;
|
|
82
|
+
onGesture = () => (onLongPress ? onLongPress() : fallback({ type: 'long_press' }));
|
|
83
|
+
break;
|
|
84
|
+
case 'pinch':
|
|
85
|
+
gestureType = 'pinch';
|
|
86
|
+
onGesture = (ctx) => (onPinch ? onPinch(ctx.scale ?? 1) : fallback({ type: 'pinch', ...ctx }));
|
|
87
|
+
break;
|
|
88
|
+
case 'double_tap':
|
|
89
|
+
gestureType = 'double-tap';
|
|
90
|
+
onGesture = () => (onDoubleTap ? onDoubleTap() : fallback({ type: 'double_tap' }));
|
|
91
|
+
break;
|
|
92
|
+
case 'pan':
|
|
93
|
+
case 'drag':
|
|
94
|
+
gestureType = 'pan';
|
|
95
|
+
onGesture = (ctx) => (onPan ? onPan(ctx.direction ?? 'left') : fallback({ type: declared, ...ctx }));
|
|
96
|
+
break;
|
|
97
|
+
case 'rotate':
|
|
98
|
+
gestureType = 'rotate';
|
|
99
|
+
onGesture = (ctx) => (onRotate ? onRotate(ctx.rotation ?? 0) : fallback({ type: 'rotate', ...ctx }));
|
|
100
|
+
break;
|
|
101
|
+
default:
|
|
102
|
+
break;
|
|
51
103
|
}
|
|
52
104
|
return useGesture({
|
|
53
105
|
type: gestureType,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@object-ui/mobile",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "17.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Mobile optimization for Object UI with responsive components, PWA support, and touch gesture handling.",
|
|
@@ -30,11 +30,12 @@
|
|
|
30
30
|
"react": "^18.0.0 || ^19.0.0"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@object-ui/types": "
|
|
33
|
+
"@object-ui/types": "17.1.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
+
"@objectstack/spec": "^17.0.0-rc.0",
|
|
36
37
|
"@types/react": "19.2.17",
|
|
37
|
-
"react": "19.2.
|
|
38
|
+
"react": "19.2.8",
|
|
38
39
|
"typescript": "^6.0.3",
|
|
39
40
|
"vitest": "^4.1.10"
|
|
40
41
|
},
|
|
@@ -63,7 +64,7 @@
|
|
|
63
64
|
"build": "tsc",
|
|
64
65
|
"clean": "rm -rf dist",
|
|
65
66
|
"test": "vitest run",
|
|
66
|
-
"type-check": "tsc --noEmit",
|
|
67
|
+
"type-check": "tsc --noEmit && tsc -p tsconfig.test.json",
|
|
67
68
|
"lint": "eslint ."
|
|
68
69
|
}
|
|
69
70
|
}
|