@umituz/react-native-design-system 2.6.89 → 2.6.91

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.89",
3
+ "version": "2.6.91",
4
4
  "description": "Universal design system for React Native apps - Consolidated package with atoms, molecules, organisms, theme, typography, responsive and safe area utilities",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -0,0 +1,499 @@
1
+ # ListItem
2
+
3
+ ListItem is a versatile list item component with title, subtitle, and optional icons. Perfect for menus, settings, navigation lists, and more.
4
+
5
+ ## Features
6
+
7
+ - 📝 **Title & Subtitle**: Display primary and secondary text
8
+ - 🎭 **Left Icon**: Icon on the left side
9
+ - 🔄 **Right Icon**: Icon on the right side (for navigation)
10
+ - 👆 **Pressable**: Optional press handler
11
+ - ♿ **Accessible**: Full accessibility support
12
+ - 🎨 **Theme-Aware**: Design token integration
13
+ - ⚡ **Disabled State**: Disabled styling support
14
+
15
+ ## Installation
16
+
17
+ ```tsx
18
+ import { ListItem } from 'react-native-design-system';
19
+ ```
20
+
21
+ ## Basic Usage
22
+
23
+ ```tsx
24
+ import React from 'react';
25
+ import { View } from 'react-native';
26
+ import { ListItem } from 'react-native-design-system';
27
+
28
+ export const BasicExample = () => {
29
+ return (
30
+ <View>
31
+ <ListItem
32
+ title="Settings"
33
+ onPress={() => console.log('Settings pressed')}
34
+ />
35
+ </View>
36
+ );
37
+ };
38
+ ```
39
+
40
+ ## Basic Item
41
+
42
+ ```tsx
43
+ <ListItem
44
+ title="Profile"
45
+ />
46
+ ```
47
+
48
+ ## With Subtitle
49
+
50
+ ```tsx
51
+ <ListItem
52
+ title="John Doe"
53
+ subtitle="Software Engineer"
54
+ />
55
+ ```
56
+
57
+ ## With Left Icon
58
+
59
+ ```tsx
60
+ <ListItem
61
+ title="Settings"
62
+ leftIcon="settings-outline"
63
+ />
64
+ ```
65
+
66
+ ## Pressable with Right Icon
67
+
68
+ ```tsx
69
+ <ListItem
70
+ title="Notifications"
71
+ rightIcon="chevron-forward-outline"
72
+ onPress={() => navigation.navigate('Notifications')}
73
+ />
74
+ ```
75
+
76
+ ## With Both Icons
77
+
78
+ ```tsx
79
+ <ListItem
80
+ title="Dark Mode"
81
+ leftIcon="moon-outline"
82
+ rightIcon="chevron-forward-outline"
83
+ onPress={() => {}}
84
+ />
85
+ ```
86
+
87
+ ## Disabled Item
88
+
89
+ ```tsx
90
+ <ListItem
91
+ title="Premium Feature"
92
+ subtitle="Upgrade to access"
93
+ leftIcon="diamond-outline"
94
+ disabled
95
+ />
96
+ ```
97
+
98
+ ## Example Usages
99
+
100
+ ### Settings Menu
101
+
102
+ ```tsx
103
+ export const SettingsMenu = () => {
104
+ const menuItems = [
105
+ {
106
+ title: 'Account',
107
+ subtitle: 'Personal information',
108
+ leftIcon: 'person-outline',
109
+ route: 'Account',
110
+ },
111
+ {
112
+ title: 'Notifications',
113
+ subtitle: 'Push notifications',
114
+ leftIcon: 'notifications-outline',
115
+ route: 'Notifications',
116
+ },
117
+ {
118
+ title: 'Privacy',
119
+ subtitle: 'Privacy settings',
120
+ leftIcon: 'shield-checkmark-outline',
121
+ route: 'Privacy',
122
+ },
123
+ {
124
+ title: 'Help',
125
+ subtitle: 'FAQ and support',
126
+ leftIcon: 'help-circle-outline',
127
+ route: 'Help',
128
+ },
129
+ ];
130
+
131
+ return (
132
+ <View>
133
+ {menuItems.map((item, index) => (
134
+ <ListItem
135
+ key={index}
136
+ title={item.title}
137
+ subtitle={item.subtitle}
138
+ leftIcon={item.leftIcon}
139
+ rightIcon="chevron-forward-outline"
140
+ onPress={() => navigation.navigate(item.route)}
141
+ />
142
+ ))}
143
+ </View>
144
+ );
145
+ };
146
+ ```
147
+
148
+ ### User List
149
+
150
+ ```tsx
151
+ export const UserList = ({ users }) => {
152
+ return (
153
+ <View>
154
+ {users.map((user) => (
155
+ <ListItem
156
+ key={user.id}
157
+ title={user.name}
158
+ subtitle={user.email}
159
+ leftIcon="person-outline"
160
+ onPress={() => navigation.navigate('UserProfile', { userId: user.id })}
161
+ />
162
+ ))}
163
+ </View>
164
+ );
165
+ };
166
+ ```
167
+
168
+ ### Navigation Menu
169
+
170
+ ```tsx
171
+ export const NavigationMenu = () => {
172
+ const menuItems = [
173
+ { title: 'Home', icon: 'home-outline', route: 'Home' },
174
+ { title: 'Search', icon: 'search-outline', route: 'Search' },
175
+ { title: 'Notifications', icon: 'notifications-outline', route: 'Notifications' },
176
+ { title: 'Messages', icon: 'chatbubble-outline', route: 'Messages' },
177
+ { title: 'Profile', icon: 'person-outline', route: 'Profile' },
178
+ ];
179
+
180
+ return (
181
+ <View>
182
+ {menuItems.map((item) => (
183
+ <ListItem
184
+ key={item.route}
185
+ title={item.title}
186
+ leftIcon={item.icon}
187
+ onPress={() => navigation.navigate(item.route)}
188
+ />
189
+ ))}
190
+ </View>
191
+ );
192
+ };
193
+ ```
194
+
195
+ ### Feature List
196
+
197
+ ```tsx
198
+ export const FeatureList = () => {
199
+ const features = [
200
+ {
201
+ title: 'Dark Mode',
202
+ subtitle: 'Reduce eye strain',
203
+ icon: 'moon-outline',
204
+ available: true,
205
+ },
206
+ {
207
+ title: 'Biometric Login',
208
+ subtitle: 'Face ID / Touch ID',
209
+ icon: 'finger-print-outline',
210
+ available: true,
211
+ },
212
+ {
213
+ title: 'Cloud Sync',
214
+ subtitle: 'Sync across devices',
215
+ icon: 'cloud-outline',
216
+ available: false,
217
+ },
218
+ ];
219
+
220
+ return (
221
+ <View>
222
+ {features.map((feature, index) => (
223
+ <ListItem
224
+ key={index}
225
+ title={feature.title}
226
+ subtitle={feature.subtitle}
227
+ leftIcon={feature.icon}
228
+ disabled={!feature.available}
229
+ onPress={feature.available ? () => enableFeature(feature) : undefined}
230
+ rightIcon={feature.available ? 'chevron-forward-outline' : undefined}
231
+ />
232
+ ))}
233
+ </View>
234
+ );
235
+ };
236
+ ```
237
+
238
+ ### Action Menu
239
+
240
+ ```tsx
241
+ export const ActionMenu = ({ onAction }) => {
242
+ const actions = [
243
+ { title: 'Edit', icon: 'create-outline', action: 'edit' },
244
+ { title: 'Share', icon: 'share-outline', action: 'share' },
245
+ { title: 'Delete', icon: 'trash-outline', action: 'delete' },
246
+ ];
247
+
248
+ return (
249
+ <View>
250
+ {actions.map((action) => (
251
+ <ListItem
252
+ key={action.action}
253
+ title={action.title}
254
+ leftIcon={action.icon}
255
+ onPress={() => onAction(action.action)}
256
+ rightIcon="chevron-forward-outline"
257
+ />
258
+ ))}
259
+ </View>
260
+ );
261
+ };
262
+ ```
263
+
264
+ ### Contact List
265
+
266
+ ```tsx
267
+ export const ContactList = ({ contacts }) => {
268
+ return (
269
+ <View>
270
+ {contacts.map((contact) => (
271
+ <ListItem
272
+ key={contact.id}
273
+ title={contact.name}
274
+ subtitle={contact.phone}
275
+ leftIcon="person-outline"
276
+ rightIcon="call-outline"
277
+ onPress={() => Linking.openURL(`tel:${contact.phone}`)}
278
+ />
279
+ ))}
280
+ </View>
281
+ );
282
+ };
283
+ ```
284
+
285
+ ### File List
286
+
287
+ ```tsx
288
+ export const FileList = ({ files }) => {
289
+ const getFileIcon = (fileName) => {
290
+ if (fileName.endsWith('.pdf')) return 'document-text-outline';
291
+ if (fileName.endsWith('.jpg') || fileName.endsWith('.png')) return 'image-outline';
292
+ if (fileName.endsWith('.mp4')) return 'videocam-outline';
293
+ return 'document-outline';
294
+ };
295
+
296
+ return (
297
+ <View>
298
+ {files.map((file) => (
299
+ <ListItem
300
+ key={file.id}
301
+ title={file.name}
302
+ subtitle={`${file.size} • ${file.date}`}
303
+ leftIcon={getFileIcon(file.name)}
304
+ onPress={() => openFile(file)}
305
+ rightIcon="ellipsis-vertical-outline"
306
+ />
307
+ ))}
308
+ </View>
309
+ );
310
+ };
311
+ ```
312
+
313
+ ### Download List
314
+
315
+ ```tsx
316
+ export const DownloadList = ({ downloads }) => {
317
+ return (
318
+ <View>
319
+ {downloads.map((download) => (
320
+ <ListItem
321
+ key={download.id}
322
+ title={download.fileName}
323
+ subtitle={`${download.progress}% • ${download.status}`}
324
+ leftIcon="download-outline"
325
+ disabled={download.status === 'downloading'}
326
+ onPress={() => {
327
+ if (download.status === 'completed') {
328
+ openDownload(download);
329
+ } else if (download.status === 'pending') {
330
+ startDownload(download);
331
+ }
332
+ }}
333
+ rightIcon={download.status === 'completed' ? 'checkmark-circle' : undefined}
334
+ />
335
+ ))}
336
+ </View>
337
+ );
338
+ };
339
+ ```
340
+
341
+ ### Selection List
342
+
343
+ ```tsx
344
+ export const SelectionList = ({ options, selectedOption, onSelect }) => {
345
+ return (
346
+ <View>
347
+ {options.map((option) => (
348
+ <ListItem
349
+ key={option.id}
350
+ title={option.title}
351
+ subtitle={option.subtitle}
352
+ leftIcon={option.icon}
353
+ onPress={() => onSelect(option)}
354
+ rightIcon={selectedOption?.id === option.id ? 'checkmark' : undefined}
355
+ />
356
+ ))}
357
+ </View>
358
+ );
359
+ };
360
+ ```
361
+
362
+ ### Grouped List
363
+
364
+ ```tsx
365
+ export const GroupedList = () => {
366
+ const groups = [
367
+ {
368
+ title: 'Account',
369
+ items: [
370
+ { title: 'Profile', icon: 'person-outline' },
371
+ { title: 'Security', icon: 'shield-outline' },
372
+ ],
373
+ },
374
+ {
375
+ title: 'Preferences',
376
+ items: [
377
+ { title: 'Appearance', icon: 'color-palette-outline' },
378
+ { title: 'Language', icon: 'globe-outline' },
379
+ ],
380
+ },
381
+ ];
382
+
383
+ return (
384
+ <View>
385
+ {groups.map((group, groupIndex) => (
386
+ <View key={groupIndex}>
387
+ <AtomicText
388
+ type="labelLarge"
389
+ color="primary"
390
+ style={{ paddingHorizontal: 16, paddingVertical: 8 }}
391
+ >
392
+ {group.title}
393
+ </AtomicText>
394
+
395
+ {group.items.map((item, itemIndex) => (
396
+ <ListItem
397
+ key={itemIndex}
398
+ title={item.title}
399
+ leftIcon={item.icon}
400
+ onPress={() => handleNavigation(item.title)}
401
+ rightIcon="chevron-forward-outline"
402
+ />
403
+ ))}
404
+ </View>
405
+ ))}
406
+ </View>
407
+ );
408
+ };
409
+ ```
410
+
411
+ ## Props
412
+
413
+ ### ListItemProps
414
+
415
+ | Prop | Type | Default | Description |
416
+ |------|------|---------|-------------|
417
+ | `title` | `string` | - **(Required)** | Primary text |
418
+ | `subtitle` | `string` | - | Secondary text |
419
+ | `leftIcon` | `string` | - | Left icon name (Ionicons) |
420
+ | `rightIcon` | `string` | - | Right icon name (Ionicons) |
421
+ | `onPress` | `() => void` | - | Press callback (makes item pressable) |
422
+ | `disabled` | `boolean` | `false` | Disable the item |
423
+ | `style` | `ViewStyle` | - | Custom container style |
424
+
425
+ ## Best Practices
426
+
427
+ ### 1. Icon Selection
428
+
429
+ ```tsx
430
+ // ✅ Good: Descriptive icons
431
+ <ListItem
432
+ title="Settings"
433
+ leftIcon="settings-outline"
434
+ />
435
+
436
+ // ❌ Bad: Generic icons
437
+ <ListItem
438
+ title="Settings"
439
+ leftIcon="ellipse-outline"
440
+ />
441
+ ```
442
+
443
+ ### 2. Right Icon Usage
444
+
445
+ ```tsx
446
+ // ✅ Good: Navigation indicator
447
+ <ListItem
448
+ title="Settings"
449
+ rightIcon="chevron-forward-outline"
450
+ onPress={() => navigate('Settings')}
451
+ />
452
+
453
+ // ❌ Bad: No press handler
454
+ <ListItem
455
+ title="Settings"
456
+ rightIcon="chevron-forward-outline"
457
+ />
458
+ ```
459
+
460
+ ### 3. Subtitle Length
461
+
462
+ ```tsx
463
+ // ✅ Good: Concise
464
+ <ListItem
465
+ subtitle="Software Engineer at Google"
466
+ />
467
+
468
+ // ❌ Bad: Too long
469
+ <ListItem
470
+ subtitle="Lorem ipsum dolor sit amet consectetur adipiscing elit sed do eiusmod tempor"
471
+ />
472
+ ```
473
+
474
+ ## Accessibility
475
+
476
+ ListItem provides full accessibility support:
477
+
478
+ - ✅ Screen reader support
479
+ - ✅ Touch target size
480
+ - ✅ Accessibility labels
481
+ - ✅ Disabled state announcements
482
+ - ✅ Semantic roles
483
+
484
+ ## Performance Tips
485
+
486
+ 1. **Memoization**: Memo list items for large lists
487
+ 2. **Key Prop**: Always use unique keys
488
+ 3. **Optimization**: Use `removeClippedSubviews` for long lists
489
+
490
+ ## Related Components
491
+
492
+ - [`List`](../List/README.md) - List container component
493
+ - [`Avatar`](../avatar/README.md) - User avatar component
494
+ - [`AtomicIcon`](../../atoms/AtomicIcon/README.md) - Icon component
495
+ - [`Divider`](../Divider/README.md) - List divider component
496
+
497
+ ## License
498
+
499
+ MIT
@@ -9,7 +9,6 @@ import type { CustomThemeColors } from '../../core/CustomColors';
9
9
  import { SplashScreen } from '../../../molecules/splash';
10
10
  import type { SplashScreenProps } from '../../../molecules/splash/types';
11
11
 
12
-
13
12
  declare const __DEV__: boolean;
14
13
 
15
14
  interface DesignSystemProviderProps {
@@ -45,9 +44,7 @@ export const DesignSystemProvider: React.FC<DesignSystemProviderProps> = ({
45
44
  onInitialized,
46
45
  onError,
47
46
  }: DesignSystemProviderProps) => {
48
- // ALL HOOKS MUST BE AT THE TOP (Rules of Hooks)
49
47
  const [isInitialized, setIsInitialized] = useState(false);
50
- const [minDisplayTimeMet, setMinDisplayTimeMet] = useState(false);
51
48
 
52
49
  // Load fonts if provided
53
50
  const [fontsLoaded, fontError] = fonts ? useFonts(fonts) : [true, null];
@@ -56,110 +53,59 @@ export const DesignSystemProvider: React.FC<DesignSystemProviderProps> = ({
56
53
  const setCustomColors = useDesignSystemTheme((state) => state.setCustomColors);
57
54
 
58
55
  useEffect(() => {
59
- if (__DEV__) console.log('[DesignSystemProvider] Initializing...');
60
-
61
56
  // Apply custom colors if provided
62
57
  if (customColors) {
63
- if (__DEV__) console.log('[DesignSystemProvider] Applying custom colors');
64
58
  setCustomColors(customColors);
65
59
  }
66
60
 
67
- // Start minimum display timer (1.5 seconds)
68
- const MIN_SPLASH_DISPLAY_TIME = 1500;
69
- const displayTimer = setTimeout(() => {
70
- if (__DEV__) console.log('[DesignSystemProvider] Minimum display time met (1.5s)');
71
- setMinDisplayTimeMet(true);
72
- }, MIN_SPLASH_DISPLAY_TIME);
73
-
74
61
  // Initialize theme store
75
62
  initialize()
76
63
  .then(() => {
77
- if (__DEV__) console.log('[DesignSystemProvider] Theme initialized successfully');
78
64
  setIsInitialized(true);
79
65
  })
80
66
  .catch((error) => {
81
- if (__DEV__) console.error('[DesignSystemProvider] Initialization failed:', error);
82
67
  setIsInitialized(true); // Still render app even on error
83
68
  onError?.(error);
84
69
  });
85
-
86
- return () => clearTimeout(displayTimer);
87
70
  }, [initialize, customColors, setCustomColors, onError]);
88
71
 
89
72
  // Handle initialization completion when both theme and fonts are ready
90
73
  useEffect(() => {
91
- if (isInitialized && fontsLoaded && minDisplayTimeMet) {
92
- if (__DEV__) console.log('[DesignSystemProvider] All systems ready - calling onInitialized');
74
+ if (isInitialized && fontsLoaded) {
93
75
  onInitialized?.();
94
76
  }
95
- }, [isInitialized, fontsLoaded, minDisplayTimeMet, onInitialized]);
77
+ }, [isInitialized, fontsLoaded, onInitialized]);
96
78
 
97
79
  // Handle font errors
98
80
  useEffect(() => {
99
81
  if (fontError) {
100
- if (__DEV__) console.error('[DesignSystemProvider] Font loading failed:', fontError);
101
82
  onError?.(fontError);
102
83
  }
103
84
  }, [fontError, onError]);
104
85
 
105
- // ALL HOOKS ABOVE - NOW SAFE TO USE OTHER LOGIC
106
-
107
- if (__DEV__) {
108
- console.log('[DesignSystemProvider] Component render:', {
109
- isInitialized,
110
- fontsLoaded,
111
- minDisplayTimeMet,
112
- showLoadingIndicator,
113
- hasSplashConfig: !!splashConfig,
114
- });
115
- }
116
-
117
- const renderContent = () => {
118
- // Show splash if:
119
- // 1. Loading indicator is enabled AND
120
- // 2. Either theme not init OR fonts not loaded OR min time not met
121
- const shouldShowSplash = showLoadingIndicator && (!isInitialized || !fontsLoaded || !minDisplayTimeMet);
86
+ // Determine if we should show loading state
87
+ const isLoading = showLoadingIndicator && (!isInitialized || !fontsLoaded);
122
88
 
123
- if (__DEV__) {
124
- console.log('[DesignSystemProvider] renderContent:', {
125
- shouldShowSplash,
126
- isInitialized,
127
- fontsLoaded,
128
- minDisplayTimeMet
129
- });
89
+ if (isLoading) {
90
+ if (loadingComponent) {
91
+ return <>{loadingComponent}</>;
130
92
  }
131
93
 
132
- // Show loading indicator if requested and not yet ready (both conditions must be met)
133
- if (shouldShowSplash) {
134
- if (__DEV__) console.log('[DesignSystemProvider] Showing loading state');
135
-
136
- if (loadingComponent) {
137
- if (__DEV__) console.log('[DesignSystemProvider] Rendering custom loading component');
138
- return <>{loadingComponent}</>;
139
- }
140
-
141
- // Use SplashScreen if config provided, otherwise fallback to ActivityIndicator
142
- if (splashConfig) {
143
- if (__DEV__) console.log('[DesignSystemProvider] Rendering SplashScreen with config:', splashConfig);
144
- return <SplashScreen {...splashConfig} visible={shouldShowSplash} />;
145
- }
146
-
147
- if (__DEV__) console.log('[DesignSystemProvider] Rendering fallback ActivityIndicator');
148
- return (
149
- <View style={styles.loadingContainer}>
150
- <ActivityIndicator size="large" />
151
- </View>
152
- );
94
+ if (splashConfig) {
95
+ return <SplashScreen {...splashConfig} visible={true} />;
153
96
  }
154
97
 
155
- if (__DEV__) console.log('[DesignSystemProvider] Rendering children (app initialized)');
156
- return <>{children}</>;
157
- };
98
+ return (
99
+ <View style={styles.loadingContainer}>
100
+ <ActivityIndicator size="large" />
101
+ </View>
102
+ );
103
+ }
158
104
 
159
105
  return (
160
106
  <GestureHandlerRootView style={{ flex: 1 }}>
161
107
  <SafeAreaProvider initialMetrics={initialWindowMetrics}>
162
- {renderContent()}
108
+ {children}
163
109
  </SafeAreaProvider>
164
110
  </GestureHandlerRootView>
165
111
  );