@umituz/react-native-design-system 2.3.14 → 2.3.16

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.
Files changed (93) hide show
  1. package/package.json +19 -2
  2. package/src/index.ts +105 -0
  3. package/src/layouts/ScreenLayout/ScreenLayout.example.tsx +2 -2
  4. package/src/layouts/ScreenLayout/ScreenLayout.tsx +1 -1
  5. package/src/molecules/animation/core/AnimationCore.ts +29 -0
  6. package/src/molecules/animation/domain/entities/Animation.ts +81 -0
  7. package/src/molecules/animation/domain/entities/Fireworks.ts +44 -0
  8. package/src/molecules/animation/domain/entities/Theme.ts +76 -0
  9. package/src/molecules/animation/index.ts +146 -0
  10. package/src/molecules/animation/infrastructure/services/AnimationConfigService.ts +35 -0
  11. package/src/molecules/animation/infrastructure/services/SpringAnimationConfigService.ts +67 -0
  12. package/src/molecules/animation/infrastructure/services/TimingAnimationConfigService.ts +57 -0
  13. package/src/molecules/animation/infrastructure/services/__tests__/SpringAnimationConfigService.test.ts +114 -0
  14. package/src/molecules/animation/infrastructure/services/__tests__/TimingAnimationConfigService.test.ts +105 -0
  15. package/src/molecules/animation/presentation/components/Fireworks.tsx +126 -0
  16. package/src/molecules/animation/presentation/components/__tests__/Fireworks.test.tsx +189 -0
  17. package/src/molecules/animation/presentation/hooks/__tests__/useAnimation.integration.test.ts +216 -0
  18. package/src/molecules/animation/presentation/hooks/__tests__/useFireworks.test.ts +242 -0
  19. package/src/molecules/animation/presentation/hooks/__tests__/useGesture.test.ts +111 -0
  20. package/src/molecules/animation/presentation/hooks/__tests__/useSpringAnimation.test.ts +131 -0
  21. package/src/molecules/animation/presentation/hooks/__tests__/useTimingAnimation.test.ts +175 -0
  22. package/src/molecules/animation/presentation/hooks/__tests__/useTransformAnimation.test.ts +137 -0
  23. package/src/molecules/animation/presentation/hooks/useAnimation.ts +77 -0
  24. package/src/molecules/animation/presentation/hooks/useFireworks.ts +141 -0
  25. package/src/molecules/animation/presentation/hooks/useGesture.ts +61 -0
  26. package/src/molecules/animation/presentation/hooks/useGestureCreators.ts +163 -0
  27. package/src/molecules/animation/presentation/hooks/useGestureState.ts +53 -0
  28. package/src/molecules/animation/presentation/hooks/useIconAnimations.ts +119 -0
  29. package/src/molecules/animation/presentation/hooks/useModalAnimations.ts +124 -0
  30. package/src/molecules/animation/presentation/hooks/useReanimatedReady.ts +60 -0
  31. package/src/molecules/animation/presentation/hooks/useSpringAnimation.ts +69 -0
  32. package/src/molecules/animation/presentation/hooks/useTimingAnimation.ts +111 -0
  33. package/src/molecules/animation/presentation/hooks/useTransformAnimation.ts +57 -0
  34. package/src/molecules/animation/presentation/providers/AnimationThemeProvider.tsx +62 -0
  35. package/src/molecules/animation/presentation/providers/__tests__/AnimationThemeProvider.test.tsx +165 -0
  36. package/src/molecules/animation/types/global.d.ts +97 -0
  37. package/src/molecules/celebration/domain/entities/CelebrationConfig.ts +17 -0
  38. package/src/molecules/celebration/domain/entities/FireworksConfig.ts +32 -0
  39. package/src/molecules/celebration/index.ts +93 -0
  40. package/src/molecules/celebration/infrastructure/services/FireworksConfigService.ts +49 -0
  41. package/src/molecules/celebration/presentation/components/CelebrationFireworksOverlay.tsx +33 -0
  42. package/src/molecules/celebration/presentation/components/CelebrationModal.tsx +78 -0
  43. package/src/molecules/celebration/presentation/components/CelebrationModalContent.tsx +90 -0
  44. package/src/molecules/celebration/presentation/hooks/useCelebrationModalAnimation.ts +49 -0
  45. package/src/molecules/celebration/presentation/hooks/useCelebrationState.ts +45 -0
  46. package/src/molecules/celebration/presentation/styles/CelebrationModalStyles.ts +65 -0
  47. package/src/molecules/countdown/components/Countdown.tsx +128 -0
  48. package/src/molecules/countdown/components/CountdownHeader.tsx +84 -0
  49. package/src/molecules/countdown/components/TimeUnit.tsx +73 -0
  50. package/src/molecules/countdown/hooks/useCountdown.ts +107 -0
  51. package/src/molecules/countdown/index.ts +25 -0
  52. package/src/molecules/countdown/types/CountdownTypes.ts +31 -0
  53. package/src/molecules/countdown/utils/TimeCalculator.ts +46 -0
  54. package/src/molecules/emoji/domain/entities/Emoji.ts +129 -0
  55. package/src/molecules/emoji/index.ts +177 -0
  56. package/src/molecules/emoji/presentation/components/EmojiPicker.tsx +102 -0
  57. package/src/molecules/emoji/presentation/hooks/useEmojiPicker.ts +171 -0
  58. package/src/molecules/index.ts +21 -0
  59. package/src/molecules/long-press-menu/domain/entities/MenuAction.ts +37 -0
  60. package/src/molecules/long-press-menu/index.ts +16 -0
  61. package/src/molecules/navigation/StackNavigator.tsx +75 -0
  62. package/src/molecules/navigation/TabsNavigator.tsx +94 -0
  63. package/src/molecules/navigation/components/FabButton.tsx +45 -0
  64. package/src/molecules/navigation/components/TabLabel.tsx +47 -0
  65. package/src/molecules/navigation/createStackNavigator.ts +20 -0
  66. package/src/molecules/navigation/createTabNavigator.ts +20 -0
  67. package/src/molecules/navigation/hooks/useTabBarStyles.ts +54 -0
  68. package/src/molecules/navigation/index.ts +37 -0
  69. package/src/molecules/navigation/types.ts +118 -0
  70. package/src/molecules/navigation/utils/AppNavigation.ts +101 -0
  71. package/src/molecules/navigation/utils/IconRenderer.ts +50 -0
  72. package/src/molecules/navigation/utils/LabelProcessor.ts +70 -0
  73. package/src/molecules/navigation/utils/NavigationCleanup.ts +62 -0
  74. package/src/molecules/navigation/utils/NavigationTheme.ts +21 -0
  75. package/src/molecules/navigation/utils/NavigationValidator.ts +61 -0
  76. package/src/molecules/navigation/utils/ScreenFactory.ts +115 -0
  77. package/src/molecules/navigation/utils/__tests__/IconRenderer.getIconName.test.ts +109 -0
  78. package/src/molecules/navigation/utils/__tests__/IconRenderer.renderIcon.test.ts +116 -0
  79. package/src/molecules/navigation/utils/__tests__/LabelProcessor.processLabel.test.ts +116 -0
  80. package/src/molecules/navigation/utils/__tests__/LabelProcessor.processTitle.test.ts +59 -0
  81. package/src/molecules/navigation/utils/__tests__/NavigationCleanup.test.ts +271 -0
  82. package/src/molecules/navigation/utils/__tests__/NavigationValidator.test.ts +252 -0
  83. package/src/molecules/swipe-actions/domain/entities/SwipeAction.ts +194 -0
  84. package/src/molecules/swipe-actions/index.ts +6 -0
  85. package/src/molecules/swipe-actions/presentation/components/SwipeActionButton.tsx +131 -0
  86. package/src/theme/hooks/useResponsiveDesignTokens.ts +1 -1
  87. package/src/utilities/clipboard/ClipboardUtils.ts +71 -0
  88. package/src/utilities/clipboard/index.ts +5 -0
  89. package/src/utilities/index.ts +6 -0
  90. package/src/utilities/sharing/domain/entities/Share.ts +210 -0
  91. package/src/utilities/sharing/index.ts +205 -0
  92. package/src/utilities/sharing/infrastructure/services/SharingService.ts +165 -0
  93. package/src/utilities/sharing/presentation/hooks/useSharing.ts +154 -0
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Swipe Actions Domain - Entity Layer
3
+ *
4
+ * Core swipe action types and configurations.
5
+ * Defines pre-built action types, colors, icons, and utilities.
6
+ *
7
+ * @domain swipe-actions
8
+ * @layer domain/entities
9
+ */
10
+
11
+ import type { IconName } from '../../../../index';
12
+
13
+ /**
14
+ * Pre-built swipe action types
15
+ */
16
+ export type SwipeActionType =
17
+ | 'delete'
18
+ | 'archive'
19
+ | 'edit'
20
+ | 'share'
21
+ | 'favorite'
22
+ | 'more'
23
+ | 'custom';
24
+
25
+ /**
26
+ * Swipe action configuration
27
+ */
28
+ export interface SwipeActionConfig {
29
+ /** Action type */
30
+ type: SwipeActionType;
31
+ /** Action label (optional, defaults from type) */
32
+ label?: string;
33
+ /** Icon name (optional, defaults from type) */
34
+ icon?: IconName;
35
+ /** Custom color (optional, defaults from type) */
36
+ color?: string;
37
+ /** Action handler */
38
+ onPress: () => void | Promise<void>;
39
+ /** Enable haptic feedback (default: true) */
40
+ enableHaptics?: boolean;
41
+ }
42
+
43
+ /**
44
+ * Swipe direction
45
+ */
46
+ export type SwipeDirection = 'left' | 'right';
47
+
48
+ /**
49
+ * Swipeable item configuration
50
+ */
51
+ export interface SwipeableConfig {
52
+ /** Actions revealed when swiping right (left side) */
53
+ leftActions?: SwipeActionConfig[];
54
+ /** Actions revealed when swiping left (right side) */
55
+ rightActions?: SwipeActionConfig[];
56
+ /** Swipe threshold in points (default: 80) */
57
+ threshold?: number;
58
+ /** Disable overshoot animation (default: true) */
59
+ disableOvershoot?: boolean;
60
+ /** Friction value (default: 2) */
61
+ friction?: number;
62
+ }
63
+
64
+ /**
65
+ * Pre-built action type configurations
66
+ */
67
+ export const ACTION_PRESETS: Record<Exclude<SwipeActionType, 'custom'>, {
68
+ label: string;
69
+ icon: IconName;
70
+ colorKey: 'error' | 'success' | 'primary' | 'secondary' | 'warning' | 'textSecondary';
71
+ hapticsIntensity: 'Light' | 'Medium' | 'Heavy';
72
+ }> = {
73
+ delete: {
74
+ label: 'Delete',
75
+ icon: 'Trash2',
76
+ colorKey: 'error',
77
+ hapticsIntensity: 'Heavy',
78
+ },
79
+ archive: {
80
+ label: 'Archive',
81
+ icon: 'Archive',
82
+ colorKey: 'success',
83
+ hapticsIntensity: 'Medium',
84
+ },
85
+ edit: {
86
+ label: 'Edit',
87
+ icon: 'Pencil',
88
+ colorKey: 'primary',
89
+ hapticsIntensity: 'Light',
90
+ },
91
+ share: {
92
+ label: 'Share',
93
+ icon: 'Share2',
94
+ colorKey: 'secondary',
95
+ hapticsIntensity: 'Light',
96
+ },
97
+ favorite: {
98
+ label: 'Favorite',
99
+ icon: 'Heart',
100
+ colorKey: 'warning',
101
+ hapticsIntensity: 'Light',
102
+ },
103
+ more: {
104
+ label: 'More',
105
+ icon: 'MoveHorizontal',
106
+ colorKey: 'textSecondary',
107
+ hapticsIntensity: 'Light',
108
+ },
109
+ };
110
+
111
+ /**
112
+ * Default swipe configuration
113
+ */
114
+ export const DEFAULT_SWIPE_CONFIG: Required<Omit<SwipeableConfig, 'leftActions' | 'rightActions'>> = {
115
+ threshold: 80,
116
+ disableOvershoot: true,
117
+ friction: 2,
118
+ };
119
+
120
+ /**
121
+ * Swipe action utility functions
122
+ */
123
+ export class SwipeActionUtils {
124
+ /**
125
+ * Gets preset configuration for action type
126
+ */
127
+ static getPreset(type: SwipeActionType) {
128
+ if (type === 'custom') {
129
+ return null;
130
+ }
131
+ return ACTION_PRESETS[type];
132
+ }
133
+
134
+ /**
135
+ * Validates swipe action configuration
136
+ */
137
+ static validateAction(action: SwipeActionConfig): boolean {
138
+ // Must have onPress handler
139
+ if (!action.onPress || typeof action.onPress !== 'function') {
140
+ return false;
141
+ }
142
+
143
+ // Custom actions must have label, icon, and color
144
+ if (action.type === 'custom') {
145
+ return !!(action.label && action.icon && action.color);
146
+ }
147
+
148
+ return true;
149
+ }
150
+
151
+ /**
152
+ * Gets action display label
153
+ */
154
+ static getLabel(action: SwipeActionConfig): string {
155
+ if (action.label) {
156
+ return action.label;
157
+ }
158
+
159
+ const preset = this.getPreset(action.type);
160
+ return preset?.label || 'Action';
161
+ }
162
+
163
+ /**
164
+ * Gets action icon name
165
+ */
166
+ static getIcon(action: SwipeActionConfig): IconName {
167
+ if (action.icon) {
168
+ return action.icon;
169
+ }
170
+
171
+ const preset = this.getPreset(action.type);
172
+ return preset?.icon || 'MoveHorizontal';
173
+ }
174
+
175
+ /**
176
+ * Gets action color key for theme
177
+ */
178
+ static getColorKey(action: SwipeActionConfig): string | null {
179
+ if (action.color) {
180
+ return null; // Use custom color
181
+ }
182
+
183
+ const preset = this.getPreset(action.type);
184
+ return preset?.colorKey || null;
185
+ }
186
+
187
+ /**
188
+ * Gets haptics intensity for action
189
+ */
190
+ static getHapticsIntensity(action: SwipeActionConfig): 'Light' | 'Medium' | 'Heavy' {
191
+ const preset = this.getPreset(action.type);
192
+ return preset?.hapticsIntensity || 'Light';
193
+ }
194
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Swipe Actions Domain - Barrel Export
3
+ */
4
+
5
+ export * from './domain/entities/SwipeAction';
6
+ export * from './presentation/components/SwipeActionButton';
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Swipe Actions Domain - SwipeActionButton Component
3
+ *
4
+ * Individual swipe action button with icon and label.
5
+ * Rendered inside SwipeableItem when swiping.
6
+ *
7
+ * @domain swipe-actions
8
+ * @layer presentation/components
9
+ */
10
+
11
+ import React from 'react';
12
+ import { StyleSheet, TouchableOpacity, View, type StyleProp, type ViewStyle } from 'react-native';
13
+ import { AtomicText, AtomicIcon } from '../../../../index';
14
+ import { useAppDesignTokens } from '../../../../index';
15
+ import { HapticService } from '@umituz/react-native-haptics';
16
+ import type { SwipeActionConfig } from '../../domain/entities/SwipeAction';
17
+ import { SwipeActionUtils } from '../../domain/entities/SwipeAction';
18
+
19
+ /**
20
+ * SwipeActionButton component props
21
+ */
22
+ export interface SwipeActionButtonProps {
23
+ /** Action configuration */
24
+ action: SwipeActionConfig;
25
+ /** Action position (index) */
26
+ position: number;
27
+ /** Total actions count */
28
+ totalActions: number;
29
+ /** Swipe direction */
30
+ direction: 'left' | 'right';
31
+ /** Custom container style */
32
+ style?: StyleProp<ViewStyle>;
33
+ }
34
+
35
+ /**
36
+ * SwipeActionButton Component
37
+ *
38
+ * Displays an individual swipe action with icon and label.
39
+ * Animates based on swipe progress.
40
+ *
41
+ * USAGE:
42
+ * ```typescript
43
+ * // Used internally by SwipeableItem
44
+ * <SwipeActionButton
45
+ * action={deleteAction}
46
+ * progress={progressValue}
47
+ * drag={dragValue}
48
+ * position={0}
49
+ * totalActions={1}
50
+ * direction="right"
51
+ * />
52
+ * ```
53
+ */
54
+ export const SwipeActionButton: React.FC<SwipeActionButtonProps> = ({
55
+ action,
56
+ style,
57
+ }) => {
58
+ const tokens = useAppDesignTokens();
59
+
60
+ // Get action properties
61
+ const label = SwipeActionUtils.getLabel(action);
62
+ const iconName = SwipeActionUtils.getIcon(action);
63
+ const colorKey = SwipeActionUtils.getColorKey(action);
64
+ const customColor = action.color;
65
+ const enableHaptics = action.enableHaptics !== false;
66
+
67
+ // Get background color from theme or custom
68
+ const backgroundColor = customColor || (colorKey ? (tokens.colors[colorKey as keyof typeof tokens.colors] as string) : tokens.colors.primary);
69
+
70
+ const handlePress = async () => {
71
+ // Trigger haptic feedback
72
+ if (enableHaptics) {
73
+ const intensity = SwipeActionUtils.getHapticsIntensity(action);
74
+ if (intensity === 'Light') {
75
+ await HapticService.impact('Light');
76
+ } else if (intensity === 'Medium') {
77
+ await HapticService.impact('Medium');
78
+ } else {
79
+ await HapticService.impact('Heavy');
80
+ }
81
+ }
82
+
83
+ // Execute action
84
+ await action.onPress();
85
+ };
86
+
87
+ return (
88
+ <TouchableOpacity
89
+ style={[
90
+ styles.container,
91
+ {
92
+ backgroundColor,
93
+ paddingHorizontal: tokens.spacing.lg,
94
+ minWidth: 80,
95
+ justifyContent: 'center',
96
+ alignItems: 'center',
97
+ },
98
+ style,
99
+ ]}
100
+ onPress={handlePress}
101
+ activeOpacity={0.7}
102
+ >
103
+ <View style={styles.content}>
104
+ <AtomicIcon
105
+ name={iconName}
106
+ size="md"
107
+ customColor="#FFFFFF"
108
+ />
109
+ <AtomicText
110
+ type="bodySmall"
111
+ style={[styles.label, { color: '#FFFFFF' }]}
112
+ >
113
+ {label}
114
+ </AtomicText>
115
+ </View>
116
+ </TouchableOpacity>
117
+ );
118
+ };
119
+
120
+ const styles = StyleSheet.create({
121
+ container: {
122
+ height: '100%',
123
+ },
124
+ content: {
125
+ alignItems: 'center',
126
+ gap: 4,
127
+ },
128
+ label: {
129
+ textAlign: 'center',
130
+ },
131
+ });
@@ -24,7 +24,7 @@ import { useResponsive } from '../../responsive/useResponsive';
24
24
  *
25
25
  * @example
26
26
  * ```typescript
27
- * import { useResponsiveDesignTokens } from '@umituz/react-native-design-system';
27
+ * import { useResponsiveDesignTokens } from '../../index';
28
28
  *
29
29
  * const MyComponent = () => {
30
30
  * const tokens = useResponsiveDesignTokens();
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Clipboard Utilities
3
+ *
4
+ * Simple wrapper around expo-clipboard for copy/paste functionality
5
+ *
6
+ * Usage:
7
+ * ```typescript
8
+ * import { ClipboardUtils } from '@umituz/react-native-design-system';
9
+ *
10
+ * // Copy text
11
+ * await ClipboardUtils.copyToClipboard('Hello World');
12
+ *
13
+ * // Paste text
14
+ * const text = await ClipboardUtils.getFromClipboard();
15
+ *
16
+ * // Check if clipboard has content
17
+ * const hasContent = await ClipboardUtils.hasContent();
18
+ * ```
19
+ */
20
+
21
+ import * as Clipboard from 'expo-clipboard';
22
+
23
+ export class ClipboardUtils {
24
+ /**
25
+ * Copy text to clipboard
26
+ */
27
+ static async copyToClipboard(text: string): Promise<void> {
28
+ try {
29
+ await Clipboard.setStringAsync(text);
30
+ } catch (error) {
31
+ console.error('[ClipboardUtils] Failed to copy to clipboard:', error);
32
+ throw error;
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Get text from clipboard
38
+ */
39
+ static async getFromClipboard(): Promise<string> {
40
+ try {
41
+ return await Clipboard.getStringAsync();
42
+ } catch (error) {
43
+ console.error('[ClipboardUtils] Failed to get from clipboard:', error);
44
+ throw error;
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Check if clipboard has content
50
+ */
51
+ static async hasContent(): Promise<boolean> {
52
+ try {
53
+ return await Clipboard.hasStringAsync();
54
+ } catch (error) {
55
+ console.error('[ClipboardUtils] Failed to check clipboard:', error);
56
+ return false;
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Clear clipboard
62
+ */
63
+ static async clear(): Promise<void> {
64
+ try {
65
+ await Clipboard.setStringAsync('');
66
+ } catch (error) {
67
+ console.error('[ClipboardUtils] Failed to clear clipboard:', error);
68
+ throw error;
69
+ }
70
+ }
71
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Clipboard - Public API
3
+ */
4
+
5
+ export { ClipboardUtils } from './ClipboardUtils';
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Utilities - Public API
3
+ */
4
+
5
+ export * from './clipboard';
6
+ export * from './sharing';
@@ -0,0 +1,210 @@
1
+ /**
2
+ * Sharing Domain - Core Entities
3
+ *
4
+ * This file defines core types and interfaces for sharing functionality.
5
+ * Handles system share sheet using expo-sharing.
6
+ *
7
+ * @domain sharing
8
+ * @layer domain/entities
9
+ */
10
+
11
+ /**
12
+ * Share options for sharing content
13
+ */
14
+ export interface ShareOptions {
15
+ /**
16
+ * Dialog title (Android only)
17
+ */
18
+ dialogTitle?: string;
19
+
20
+ /**
21
+ * MIME type of the file being shared
22
+ */
23
+ mimeType?: string;
24
+
25
+ /**
26
+ * UTI (Uniform Type Identifier) for the file (iOS only)
27
+ */
28
+ UTI?: string;
29
+ }
30
+
31
+ /**
32
+ * Share result
33
+ */
34
+ export interface ShareResult {
35
+ success: boolean;
36
+ error?: string;
37
+ }
38
+
39
+ /**
40
+ * Common MIME types for sharing
41
+ */
42
+ export const MIME_TYPES = {
43
+ // Images
44
+ IMAGE_JPEG: 'image/jpeg',
45
+ IMAGE_PNG: 'image/png',
46
+ IMAGE_GIF: 'image/gif',
47
+ IMAGE_WEBP: 'image/webp',
48
+
49
+ // Videos
50
+ VIDEO_MP4: 'video/mp4',
51
+ VIDEO_QUICKTIME: 'video/quicktime',
52
+ VIDEO_AVI: 'video/avi',
53
+
54
+ // Audio
55
+ AUDIO_MP3: 'audio/mpeg',
56
+ AUDIO_WAV: 'audio/wav',
57
+ AUDIO_AAC: 'audio/aac',
58
+
59
+ // Documents
60
+ PDF: 'application/pdf',
61
+ TEXT: 'text/plain',
62
+ JSON: 'application/json',
63
+ ZIP: 'application/zip',
64
+
65
+ // Generic
66
+ OCTET_STREAM: 'application/octet-stream',
67
+ } as const;
68
+
69
+ /**
70
+ * iOS UTI (Uniform Type Identifiers)
71
+ */
72
+ export const UTI_TYPES = {
73
+ // Images
74
+ IMAGE: 'public.image',
75
+ JPEG: 'public.jpeg',
76
+ PNG: 'public.png',
77
+
78
+ // Videos
79
+ VIDEO: 'public.video',
80
+ MOVIE: 'public.movie',
81
+
82
+ // Audio
83
+ AUDIO: 'public.audio',
84
+ MP3: 'public.mp3',
85
+
86
+ // Documents
87
+ PDF: 'com.adobe.pdf',
88
+ TEXT: 'public.text',
89
+ JSON: 'public.json',
90
+
91
+ // Generic
92
+ DATA: 'public.data',
93
+ CONTENT: 'public.content',
94
+ } as const;
95
+
96
+ /**
97
+ * Sharing constants
98
+ */
99
+ export const SHARING_CONSTANTS = {
100
+ DEFAULT_DIALOG_TITLE: 'Share',
101
+ DEFAULT_MIME_TYPE: MIME_TYPES.OCTET_STREAM,
102
+ } as const;
103
+
104
+ /**
105
+ * Sharing utilities
106
+ */
107
+ export class SharingUtils {
108
+ /**
109
+ * Get MIME type from file extension
110
+ */
111
+ static getMimeTypeFromExtension(filename: string): string {
112
+ const extension = filename.split('.').pop()?.toLowerCase();
113
+
114
+ switch (extension) {
115
+ // Images
116
+ case 'jpg':
117
+ case 'jpeg':
118
+ return MIME_TYPES.IMAGE_JPEG;
119
+ case 'png':
120
+ return MIME_TYPES.IMAGE_PNG;
121
+ case 'gif':
122
+ return MIME_TYPES.IMAGE_GIF;
123
+ case 'webp':
124
+ return MIME_TYPES.IMAGE_WEBP;
125
+
126
+ // Videos
127
+ case 'mp4':
128
+ return MIME_TYPES.VIDEO_MP4;
129
+ case 'mov':
130
+ return MIME_TYPES.VIDEO_QUICKTIME;
131
+ case 'avi':
132
+ return MIME_TYPES.VIDEO_AVI;
133
+
134
+ // Audio
135
+ case 'mp3':
136
+ return MIME_TYPES.AUDIO_MP3;
137
+ case 'wav':
138
+ return MIME_TYPES.AUDIO_WAV;
139
+ case 'aac':
140
+ return MIME_TYPES.AUDIO_AAC;
141
+
142
+ // Documents
143
+ case 'pdf':
144
+ return MIME_TYPES.PDF;
145
+ case 'txt':
146
+ return MIME_TYPES.TEXT;
147
+ case 'json':
148
+ return MIME_TYPES.JSON;
149
+ case 'zip':
150
+ return MIME_TYPES.ZIP;
151
+
152
+ default:
153
+ return MIME_TYPES.OCTET_STREAM;
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Get UTI from file extension (iOS)
159
+ */
160
+ static getUTIFromExtension(filename: string): string {
161
+ const extension = filename.split('.').pop()?.toLowerCase();
162
+
163
+ switch (extension) {
164
+ // Images
165
+ case 'jpg':
166
+ case 'jpeg':
167
+ return UTI_TYPES.JPEG;
168
+ case 'png':
169
+ return UTI_TYPES.PNG;
170
+ case 'gif':
171
+ case 'webp':
172
+ return UTI_TYPES.IMAGE;
173
+
174
+ // Videos
175
+ case 'mp4':
176
+ case 'mov':
177
+ case 'avi':
178
+ return UTI_TYPES.VIDEO;
179
+
180
+ // Audio
181
+ case 'mp3':
182
+ return UTI_TYPES.MP3;
183
+ case 'wav':
184
+ case 'aac':
185
+ return UTI_TYPES.AUDIO;
186
+
187
+ // Documents
188
+ case 'pdf':
189
+ return UTI_TYPES.PDF;
190
+ case 'txt':
191
+ return UTI_TYPES.TEXT;
192
+ case 'json':
193
+ return UTI_TYPES.JSON;
194
+
195
+ default:
196
+ return UTI_TYPES.DATA;
197
+ }
198
+ }
199
+
200
+ /**
201
+ * Prepare share options from filename
202
+ */
203
+ static prepareShareOptions(filename: string, dialogTitle?: string): ShareOptions {
204
+ return {
205
+ dialogTitle: dialogTitle || SHARING_CONSTANTS.DEFAULT_DIALOG_TITLE,
206
+ mimeType: SharingUtils.getMimeTypeFromExtension(filename),
207
+ UTI: SharingUtils.getUTIFromExtension(filename),
208
+ };
209
+ }
210
+ }