@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-design-system",
3
- "version": "2.6.123",
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,7 @@
1
+ /**
2
+ * Haptics Exports
3
+ *
4
+ * Haptic feedback utilities for React Native
5
+ */
6
+
7
+ export * from '../haptics';
@@ -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
- if (intensity === 'Light') {
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