@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.
|
|
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
|
|
92
|
-
if (__DEV__) console.log('[DesignSystemProvider] All systems ready - calling onInitialized');
|
|
74
|
+
if (isInitialized && fontsLoaded) {
|
|
93
75
|
onInitialized?.();
|
|
94
76
|
}
|
|
95
|
-
}, [isInitialized, fontsLoaded,
|
|
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
|
-
//
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
isInitialized,
|
|
127
|
-
fontsLoaded,
|
|
128
|
-
minDisplayTimeMet
|
|
129
|
-
});
|
|
89
|
+
if (isLoading) {
|
|
90
|
+
if (loadingComponent) {
|
|
91
|
+
return <>{loadingComponent}</>;
|
|
130
92
|
}
|
|
131
93
|
|
|
132
|
-
|
|
133
|
-
|
|
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
|
-
|
|
156
|
-
|
|
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
|
-
{
|
|
108
|
+
{children}
|
|
163
109
|
</SafeAreaProvider>
|
|
164
110
|
</GestureHandlerRootView>
|
|
165
111
|
);
|