@umituz/react-native-design-system 2.6.123 → 2.6.124
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/package.json +1 -1
- package/src/exports/haptics.ts +7 -0
- package/src/haptics/domain/entities/Haptic.ts +66 -0
- package/src/haptics/index.ts +181 -0
- package/src/haptics/infrastructure/services/HapticService.ts +130 -0
- package/src/haptics/presentation/hooks/useHaptics.ts +173 -0
- package/src/index.ts +5 -0
- package/src/molecules/swipe-actions/presentation/components/SwipeActionButton.tsx +2 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-design-system",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.124",
|
|
4
4
|
"description": "Universal design system for React Native apps - Consolidated package with atoms, molecules, organisms, theme, typography, responsive, safe area, exception, infinite scroll, UUID, image, timezone and offline utilities",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Haptics Domain - Core Entities
|
|
3
|
+
*
|
|
4
|
+
* This file defines core types and interfaces for haptic feedback.
|
|
5
|
+
* Handles vibration patterns and feedback types using expo-haptics.
|
|
6
|
+
*
|
|
7
|
+
* @domain haptics
|
|
8
|
+
* @layer domain/entities
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Impact feedback style (compatible with expo-haptics)
|
|
13
|
+
*/
|
|
14
|
+
export type ImpactStyle = 'Light' | 'Medium' | 'Heavy';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Notification feedback type (compatible with expo-haptics)
|
|
18
|
+
*/
|
|
19
|
+
export type NotificationType = 'Success' | 'Warning' | 'Error';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Haptic patterns for common interactions
|
|
23
|
+
*/
|
|
24
|
+
export type HapticPattern =
|
|
25
|
+
| 'success'
|
|
26
|
+
| 'warning'
|
|
27
|
+
| 'error'
|
|
28
|
+
| 'selection';
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Haptic constants
|
|
32
|
+
*/
|
|
33
|
+
export const HAPTIC_CONSTANTS = {
|
|
34
|
+
DEFAULT_IMPACT: 'Light' as ImpactStyle,
|
|
35
|
+
BUTTON_IMPACT: 'Light' as ImpactStyle,
|
|
36
|
+
DELETE_IMPACT: 'Medium' as ImpactStyle,
|
|
37
|
+
ERROR_IMPACT: 'Heavy' as ImpactStyle,
|
|
38
|
+
} as const;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Type guards for runtime type safety
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Check if value is a valid ImpactStyle
|
|
46
|
+
*/
|
|
47
|
+
export function isImpactStyle(value: unknown): value is ImpactStyle {
|
|
48
|
+
return typeof value === 'string' &&
|
|
49
|
+
['Light', 'Medium', 'Heavy'].includes(value);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Check if value is a valid NotificationType
|
|
54
|
+
*/
|
|
55
|
+
export function isNotificationType(value: unknown): value is NotificationType {
|
|
56
|
+
return typeof value === 'string' &&
|
|
57
|
+
['Success', 'Warning', 'Error'].includes(value);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Check if value is a valid HapticPattern
|
|
62
|
+
*/
|
|
63
|
+
export function isHapticPattern(value: unknown): value is HapticPattern {
|
|
64
|
+
return typeof value === 'string' &&
|
|
65
|
+
['success', 'warning', 'error', 'selection'].includes(value);
|
|
66
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Haptics Domain - Barrel Export
|
|
3
|
+
*
|
|
4
|
+
* Provides haptic feedback (vibration) using expo-haptics.
|
|
5
|
+
* Universal domain - enabled by default for UX improvement.
|
|
6
|
+
*
|
|
7
|
+
* @domain haptics
|
|
8
|
+
* @enabled true (All apps - UX improvement)
|
|
9
|
+
*
|
|
10
|
+
* ARCHITECTURE:
|
|
11
|
+
* - Domain Layer: Entities (haptic types, patterns, utilities)
|
|
12
|
+
* - Infrastructure Layer: Services (HapticService)
|
|
13
|
+
* - Presentation Layer: Hooks (useHaptics)
|
|
14
|
+
*
|
|
15
|
+
* DEPENDENCIES:
|
|
16
|
+
* - expo-haptics (vibration patterns)
|
|
17
|
+
*
|
|
18
|
+
* FEATURES:
|
|
19
|
+
* - Impact feedback (light, medium, heavy)
|
|
20
|
+
* - Notification feedback (success, warning, error)
|
|
21
|
+
* - Selection feedback (pickers, sliders)
|
|
22
|
+
* - Custom haptic patterns
|
|
23
|
+
* - Convenience methods for common interactions
|
|
24
|
+
*
|
|
25
|
+
* USAGE:
|
|
26
|
+
*
|
|
27
|
+
* Basic Haptics:
|
|
28
|
+
* ```typescript
|
|
29
|
+
* import { useHaptics } from '@umituz/react-native-haptics';
|
|
30
|
+
*
|
|
31
|
+
* const haptics = useHaptics();
|
|
32
|
+
*
|
|
33
|
+
* // Button press
|
|
34
|
+
* <TouchableOpacity onPress={() => haptics.buttonPress()}>
|
|
35
|
+
* <Text>Click Me</Text>
|
|
36
|
+
* </TouchableOpacity>
|
|
37
|
+
*
|
|
38
|
+
* // Success feedback
|
|
39
|
+
* const handleSave = async () => {
|
|
40
|
+
* await saveData();
|
|
41
|
+
* haptics.success();
|
|
42
|
+
* };
|
|
43
|
+
*
|
|
44
|
+
* // Error feedback
|
|
45
|
+
* haptics.error();
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* Selection Change (Sliders, Pickers):
|
|
49
|
+
* ```typescript
|
|
50
|
+
* import { useHaptics } from '@umituz/react-native-haptics';
|
|
51
|
+
*
|
|
52
|
+
* const haptics = useHaptics();
|
|
53
|
+
*
|
|
54
|
+
* <Slider
|
|
55
|
+
* value={value}
|
|
56
|
+
* onValueChange={(val) => {
|
|
57
|
+
* setValue(val);
|
|
58
|
+
* haptics.selectionChange();
|
|
59
|
+
* }}
|
|
60
|
+
* />
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
* Custom Patterns:
|
|
64
|
+
* ```typescript
|
|
65
|
+
* import { useHaptics } from '@umituz/react-native-haptics';
|
|
66
|
+
*
|
|
67
|
+
* const haptics = useHaptics();
|
|
68
|
+
*
|
|
69
|
+
* // Impact with specific style
|
|
70
|
+
* haptics.impact('heavy');
|
|
71
|
+
*
|
|
72
|
+
* // Notification with specific type
|
|
73
|
+
* haptics.notification('warning');
|
|
74
|
+
*
|
|
75
|
+
* // Custom pattern
|
|
76
|
+
* haptics.pattern('long_press');
|
|
77
|
+
* ```
|
|
78
|
+
*
|
|
79
|
+
* Direct Service Usage (Rare):
|
|
80
|
+
* ```typescript
|
|
81
|
+
* import { HapticService } from '@umituz/react-native-haptics';
|
|
82
|
+
*
|
|
83
|
+
* // Use when you can't use hooks (outside components)
|
|
84
|
+
* await HapticService.buttonPress();
|
|
85
|
+
* await HapticService.success();
|
|
86
|
+
* ```
|
|
87
|
+
*
|
|
88
|
+
* Common Patterns:
|
|
89
|
+
* ```typescript
|
|
90
|
+
* import { useHaptics } from '@umituz/react-native-haptics';
|
|
91
|
+
*
|
|
92
|
+
* const haptics = useHaptics();
|
|
93
|
+
*
|
|
94
|
+
* // Button press (light impact)
|
|
95
|
+
* haptics.buttonPress();
|
|
96
|
+
*
|
|
97
|
+
* // Success (notification feedback)
|
|
98
|
+
* haptics.success();
|
|
99
|
+
*
|
|
100
|
+
* // Error (notification feedback)
|
|
101
|
+
* haptics.error();
|
|
102
|
+
*
|
|
103
|
+
* // Warning (notification feedback)
|
|
104
|
+
* haptics.warning();
|
|
105
|
+
*
|
|
106
|
+
* // Delete (medium impact)
|
|
107
|
+
* haptics.delete();
|
|
108
|
+
*
|
|
109
|
+
* // Refresh (medium impact)
|
|
110
|
+
* haptics.refresh();
|
|
111
|
+
*
|
|
112
|
+
* // Selection change (selection feedback)
|
|
113
|
+
* haptics.selectionChange();
|
|
114
|
+
*
|
|
115
|
+
* // Long press (heavy impact)
|
|
116
|
+
* haptics.longPress();
|
|
117
|
+
* ```
|
|
118
|
+
*
|
|
119
|
+
* Utilities:
|
|
120
|
+
* ```typescript
|
|
121
|
+
* import { HapticUtils } from '@umituz/react-native-haptics';
|
|
122
|
+
*
|
|
123
|
+
* // Get impact style for pattern
|
|
124
|
+
* const style = HapticUtils.getImpactForPattern('button_press'); // 'light'
|
|
125
|
+
*
|
|
126
|
+
* // Get notification type for pattern
|
|
127
|
+
* const type = HapticUtils.getNotificationForPattern('success'); // 'success'
|
|
128
|
+
*
|
|
129
|
+
* // Check if pattern should use notification
|
|
130
|
+
* const shouldUse = HapticUtils.shouldUseNotification('success'); // true
|
|
131
|
+
* ```
|
|
132
|
+
*
|
|
133
|
+
* BENEFITS:
|
|
134
|
+
* - Improved UX with tactile feedback
|
|
135
|
+
* - Consistent haptic patterns across app
|
|
136
|
+
* - Silent failure (no crashes if unsupported)
|
|
137
|
+
* - Platform-agnostic (iOS + Android)
|
|
138
|
+
* - Easy integration in any component
|
|
139
|
+
* - Works across all apps
|
|
140
|
+
*
|
|
141
|
+
* USE CASES:
|
|
142
|
+
* - Button presses
|
|
143
|
+
* - Success confirmations
|
|
144
|
+
* - Error alerts
|
|
145
|
+
* - Selection changes (sliders, pickers)
|
|
146
|
+
* - Delete actions
|
|
147
|
+
* - Refresh actions
|
|
148
|
+
* - Long press gestures
|
|
149
|
+
* - Form validation feedback
|
|
150
|
+
*
|
|
151
|
+
* @see https://docs.expo.dev/versions/latest/sdk/haptics/
|
|
152
|
+
*/
|
|
153
|
+
|
|
154
|
+
// ============================================================================
|
|
155
|
+
// DOMAIN LAYER - ENTITIES
|
|
156
|
+
// ============================================================================
|
|
157
|
+
|
|
158
|
+
export type {
|
|
159
|
+
ImpactStyle,
|
|
160
|
+
NotificationType,
|
|
161
|
+
HapticPattern,
|
|
162
|
+
} from './domain/entities/Haptic';
|
|
163
|
+
|
|
164
|
+
export {
|
|
165
|
+
HAPTIC_CONSTANTS,
|
|
166
|
+
isImpactStyle,
|
|
167
|
+
isNotificationType,
|
|
168
|
+
isHapticPattern,
|
|
169
|
+
} from './domain/entities/Haptic';
|
|
170
|
+
|
|
171
|
+
// ============================================================================
|
|
172
|
+
// INFRASTRUCTURE LAYER - SERVICES
|
|
173
|
+
// ============================================================================
|
|
174
|
+
|
|
175
|
+
export { HapticService } from './infrastructure/services/HapticService';
|
|
176
|
+
|
|
177
|
+
// ============================================================================
|
|
178
|
+
// PRESENTATION LAYER - HOOKS
|
|
179
|
+
// ============================================================================
|
|
180
|
+
|
|
181
|
+
export { useHaptics } from './presentation/hooks/useHaptics';
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Haptics Domain - Haptic Service
|
|
3
|
+
*
|
|
4
|
+
* Service for haptic feedback using expo-haptics.
|
|
5
|
+
* Provides abstraction layer for vibration and feedback.
|
|
6
|
+
*
|
|
7
|
+
* @domain haptics
|
|
8
|
+
* @layer infrastructure/services
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import * as Haptics from 'expo-haptics';
|
|
12
|
+
import type { ImpactStyle, NotificationType, HapticPattern } from '../../domain/entities/Haptic';
|
|
13
|
+
import { isImpactStyle, isNotificationType, isHapticPattern } from '../../domain/entities/Haptic';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Log error in development mode only
|
|
17
|
+
*/
|
|
18
|
+
function logError(method: string, error: unknown): void {
|
|
19
|
+
if (process.env.NODE_ENV === 'development') {
|
|
20
|
+
console.error(`[HapticService.${method}]`, error);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Haptic feedback service
|
|
27
|
+
*/
|
|
28
|
+
export class HapticService {
|
|
29
|
+
/**
|
|
30
|
+
* Trigger impact feedback (Light, Medium, Heavy)
|
|
31
|
+
*/
|
|
32
|
+
static async impact(style: ImpactStyle = 'Light'): Promise<void> {
|
|
33
|
+
try {
|
|
34
|
+
await Haptics.impactAsync(
|
|
35
|
+
style === 'Light' ? Haptics.ImpactFeedbackStyle.Light :
|
|
36
|
+
style === 'Medium' ? Haptics.ImpactFeedbackStyle.Medium :
|
|
37
|
+
Haptics.ImpactFeedbackStyle.Heavy
|
|
38
|
+
);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
logError('impact', error);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Trigger notification feedback (Success, Warning, Error)
|
|
46
|
+
*/
|
|
47
|
+
static async notification(type: NotificationType): Promise<void> {
|
|
48
|
+
try {
|
|
49
|
+
await Haptics.notificationAsync(
|
|
50
|
+
type === 'Success' ? Haptics.NotificationFeedbackType.Success :
|
|
51
|
+
type === 'Warning' ? Haptics.NotificationFeedbackType.Warning :
|
|
52
|
+
Haptics.NotificationFeedbackType.Error
|
|
53
|
+
);
|
|
54
|
+
} catch (error) {
|
|
55
|
+
logError('notification', error);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Trigger selection feedback (for pickers, sliders)
|
|
61
|
+
*/
|
|
62
|
+
static async selection(): Promise<void> {
|
|
63
|
+
try {
|
|
64
|
+
await Haptics.selectionAsync();
|
|
65
|
+
} catch (error) {
|
|
66
|
+
logError('selection', error);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Trigger haptic pattern
|
|
72
|
+
*/
|
|
73
|
+
static async pattern(pattern: HapticPattern): Promise<void> {
|
|
74
|
+
try {
|
|
75
|
+
switch (pattern) {
|
|
76
|
+
case 'selection':
|
|
77
|
+
await HapticService.selection();
|
|
78
|
+
break;
|
|
79
|
+
case 'success':
|
|
80
|
+
await HapticService.notification('Success');
|
|
81
|
+
break;
|
|
82
|
+
case 'warning':
|
|
83
|
+
await HapticService.notification('Warning');
|
|
84
|
+
break;
|
|
85
|
+
case 'error':
|
|
86
|
+
await HapticService.notification('Error');
|
|
87
|
+
break;
|
|
88
|
+
default:
|
|
89
|
+
await HapticService.impact('Light');
|
|
90
|
+
}
|
|
91
|
+
} catch (error) {
|
|
92
|
+
logError('pattern', error);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Common haptic patterns (convenience methods)
|
|
98
|
+
*/
|
|
99
|
+
static async buttonPress(): Promise<void> {
|
|
100
|
+
await HapticService.impact('Light');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
static async success(): Promise<void> {
|
|
104
|
+
await HapticService.pattern('success');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
static async error(): Promise<void> {
|
|
108
|
+
await HapticService.pattern('error');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
static async warning(): Promise<void> {
|
|
112
|
+
await HapticService.pattern('warning');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
static async delete(): Promise<void> {
|
|
116
|
+
await HapticService.impact('Medium');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
static async refresh(): Promise<void> {
|
|
120
|
+
await HapticService.impact('Light');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
static async selectionChange(): Promise<void> {
|
|
124
|
+
await HapticService.pattern('selection');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
static async longPress(): Promise<void> {
|
|
128
|
+
await HapticService.impact('Medium');
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Haptics Domain - useHaptics Hook
|
|
3
|
+
*
|
|
4
|
+
* React hook for haptic feedback.
|
|
5
|
+
* Provides vibration patterns for common interactions.
|
|
6
|
+
*
|
|
7
|
+
* @domain haptics
|
|
8
|
+
* @layer presentation/hooks
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { useCallback, useRef, useMemo } from 'react';
|
|
12
|
+
import { HapticService } from '../../infrastructure/services/HapticService';
|
|
13
|
+
import type { ImpactStyle, NotificationType, HapticPattern } from '../../domain/entities/Haptic';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Minimum interval between haptic feedback (ms)
|
|
17
|
+
* Prevents spam and improves UX
|
|
18
|
+
*/
|
|
19
|
+
const THROTTLE_INTERVAL = 50;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* useHaptics hook for haptic feedback
|
|
23
|
+
*
|
|
24
|
+
* USAGE:
|
|
25
|
+
* ```typescript
|
|
26
|
+
* const haptics = useHaptics();
|
|
27
|
+
*
|
|
28
|
+
* // Common patterns (convenience methods)
|
|
29
|
+
* <TouchableOpacity onPress={() => haptics.buttonPress()}>
|
|
30
|
+
* <Text>Click Me</Text>
|
|
31
|
+
* </TouchableOpacity>
|
|
32
|
+
*
|
|
33
|
+
* // Success feedback
|
|
34
|
+
* const handleSuccess = async () => {
|
|
35
|
+
* await saveData();
|
|
36
|
+
* haptics.success();
|
|
37
|
+
* };
|
|
38
|
+
*
|
|
39
|
+
* // Error feedback
|
|
40
|
+
* const handleError = () => {
|
|
41
|
+
* haptics.error();
|
|
42
|
+
* };
|
|
43
|
+
*
|
|
44
|
+
* // Selection change (sliders, pickers)
|
|
45
|
+
* <Slider onValueChange={() => haptics.selectionChange()} />
|
|
46
|
+
*
|
|
47
|
+
* // Custom patterns
|
|
48
|
+
* haptics.pattern('long_press');
|
|
49
|
+
* haptics.impact('heavy');
|
|
50
|
+
* haptics.notification('warning');
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export const useHaptics = () => {
|
|
54
|
+
const lastExecutionRef = useRef<number>(0);
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Check if enough time has passed since last haptic
|
|
58
|
+
*/
|
|
59
|
+
const canExecute = useCallback((): boolean => {
|
|
60
|
+
const now = Date.now();
|
|
61
|
+
if (now - lastExecutionRef.current < THROTTLE_INTERVAL) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
lastExecutionRef.current = now;
|
|
65
|
+
return true;
|
|
66
|
+
}, []);
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Trigger impact feedback (light, medium, heavy)
|
|
70
|
+
*/
|
|
71
|
+
const impact = useCallback(async (style: ImpactStyle = 'Light') => {
|
|
72
|
+
if (!canExecute()) return;
|
|
73
|
+
await HapticService.impact(style);
|
|
74
|
+
}, [canExecute]);
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Trigger notification feedback (success, warning, error)
|
|
78
|
+
*/
|
|
79
|
+
const notification = useCallback(async (type: NotificationType) => {
|
|
80
|
+
if (!canExecute()) return;
|
|
81
|
+
await HapticService.notification(type);
|
|
82
|
+
}, [canExecute]);
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Trigger selection feedback (for pickers, sliders)
|
|
86
|
+
*/
|
|
87
|
+
const selection = useCallback(async () => {
|
|
88
|
+
if (!canExecute()) return;
|
|
89
|
+
await HapticService.selection();
|
|
90
|
+
}, [canExecute]);
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Trigger custom haptic pattern
|
|
94
|
+
*/
|
|
95
|
+
const pattern = useCallback(async (patternType: HapticPattern) => {
|
|
96
|
+
if (!canExecute()) return;
|
|
97
|
+
await HapticService.pattern(patternType);
|
|
98
|
+
}, [canExecute]);
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Common haptic patterns (convenience methods)
|
|
102
|
+
*/
|
|
103
|
+
const buttonPress = useCallback(async () => {
|
|
104
|
+
if (!canExecute()) return;
|
|
105
|
+
await HapticService.buttonPress();
|
|
106
|
+
}, [canExecute]);
|
|
107
|
+
|
|
108
|
+
const success = useCallback(async () => {
|
|
109
|
+
if (!canExecute()) return;
|
|
110
|
+
await HapticService.success();
|
|
111
|
+
}, [canExecute]);
|
|
112
|
+
|
|
113
|
+
const error = useCallback(async () => {
|
|
114
|
+
if (!canExecute()) return;
|
|
115
|
+
await HapticService.error();
|
|
116
|
+
}, [canExecute]);
|
|
117
|
+
|
|
118
|
+
const warning = useCallback(async () => {
|
|
119
|
+
if (!canExecute()) return;
|
|
120
|
+
await HapticService.warning();
|
|
121
|
+
}, [canExecute]);
|
|
122
|
+
|
|
123
|
+
const deleteItem = useCallback(async () => {
|
|
124
|
+
if (!canExecute()) return;
|
|
125
|
+
await HapticService.delete();
|
|
126
|
+
}, [canExecute]);
|
|
127
|
+
|
|
128
|
+
const refresh = useCallback(async () => {
|
|
129
|
+
if (!canExecute()) return;
|
|
130
|
+
await HapticService.refresh();
|
|
131
|
+
}, [canExecute]);
|
|
132
|
+
|
|
133
|
+
const selectionChange = useCallback(async () => {
|
|
134
|
+
if (!canExecute()) return;
|
|
135
|
+
await HapticService.selectionChange();
|
|
136
|
+
}, [canExecute]);
|
|
137
|
+
|
|
138
|
+
const longPress = useCallback(async () => {
|
|
139
|
+
if (!canExecute()) return;
|
|
140
|
+
await HapticService.longPress();
|
|
141
|
+
}, [canExecute]);
|
|
142
|
+
|
|
143
|
+
return useMemo(() => ({
|
|
144
|
+
// Generic methods
|
|
145
|
+
impact,
|
|
146
|
+
notification,
|
|
147
|
+
selection,
|
|
148
|
+
pattern,
|
|
149
|
+
|
|
150
|
+
// Common patterns (convenience methods)
|
|
151
|
+
buttonPress,
|
|
152
|
+
success,
|
|
153
|
+
error,
|
|
154
|
+
warning,
|
|
155
|
+
delete: deleteItem,
|
|
156
|
+
refresh,
|
|
157
|
+
selectionChange,
|
|
158
|
+
longPress,
|
|
159
|
+
}), [
|
|
160
|
+
impact,
|
|
161
|
+
notification,
|
|
162
|
+
selection,
|
|
163
|
+
pattern,
|
|
164
|
+
buttonPress,
|
|
165
|
+
success,
|
|
166
|
+
error,
|
|
167
|
+
warning,
|
|
168
|
+
deleteItem,
|
|
169
|
+
refresh,
|
|
170
|
+
selectionChange,
|
|
171
|
+
longPress,
|
|
172
|
+
]);
|
|
173
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -87,6 +87,11 @@ export * from './exports/offline';
|
|
|
87
87
|
// =============================================================================
|
|
88
88
|
export * from './exports/image';
|
|
89
89
|
|
|
90
|
+
// =============================================================================
|
|
91
|
+
// HAPTICS EXPORTS
|
|
92
|
+
// =============================================================================
|
|
93
|
+
export * from './exports/haptics';
|
|
94
|
+
|
|
90
95
|
// =============================================================================
|
|
91
96
|
// VARIANT UTILITIES
|
|
92
97
|
// =============================================================================
|
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
|
|
11
11
|
import React from 'react';
|
|
12
12
|
import { StyleSheet, TouchableOpacity, View, type StyleProp, type ViewStyle } from 'react-native';
|
|
13
|
-
import * as Haptics from 'expo-haptics';
|
|
14
13
|
import { AtomicText, AtomicIcon } from '../../../../index';
|
|
15
14
|
import { useAppDesignTokens } from '../../../../index';
|
|
15
|
+
import { HapticService } from '../../../../haptics';
|
|
16
16
|
import type { SwipeActionConfig } from '../../domain/entities/SwipeAction';
|
|
17
17
|
import { SwipeActionUtils } from '../../domain/entities/SwipeAction';
|
|
18
18
|
|
|
@@ -71,13 +71,7 @@ export const SwipeActionButton: React.FC<SwipeActionButtonProps> = ({
|
|
|
71
71
|
// Trigger haptic feedback
|
|
72
72
|
if (enableHaptics) {
|
|
73
73
|
const intensity = SwipeActionUtils.getHapticsIntensity(action);
|
|
74
|
-
|
|
75
|
-
await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
|
76
|
-
} else if (intensity === 'Medium') {
|
|
77
|
-
await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);
|
|
78
|
-
} else {
|
|
79
|
-
await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy);
|
|
80
|
-
}
|
|
74
|
+
await HapticService.impact(intensity);
|
|
81
75
|
}
|
|
82
76
|
|
|
83
77
|
// Execute action
|