@umituz/react-native-design-system 2.6.94 → 2.6.96
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/atoms/AtomicAvatar.README.md +284 -397
- package/src/atoms/AtomicBadge.README.md +123 -358
- package/src/atoms/AtomicCard.README.md +358 -247
- package/src/atoms/AtomicDatePicker.README.md +127 -332
- package/src/atoms/AtomicFab.README.md +194 -352
- package/src/atoms/AtomicIcon.README.md +241 -274
- package/src/atoms/AtomicProgress.README.md +100 -338
- package/src/atoms/AtomicSpinner.README.md +304 -337
- package/src/atoms/AtomicText.README.md +153 -389
- package/src/atoms/AtomicTextArea.README.md +267 -268
- package/src/atoms/EmptyState.README.md +247 -292
- package/src/atoms/GlassView/README.md +313 -444
- package/src/atoms/button/README.md +186 -297
- package/src/atoms/button/STRATEGY.md +252 -0
- package/src/atoms/chip/README.md +242 -290
- package/src/atoms/input/README.md +296 -290
- package/src/atoms/picker/README.md +278 -309
- package/src/atoms/skeleton/AtomicSkeleton.README.md +394 -252
- package/src/molecules/BaseModal/README.md +356 -0
- package/src/molecules/BaseModal.README.md +324 -200
- package/src/molecules/ConfirmationModal.README.md +349 -302
- package/src/molecules/Divider/README.md +293 -376
- package/src/molecules/FormField.README.md +321 -534
- package/src/molecules/GlowingCard/GlowingCard.tsx +1 -1
- package/src/molecules/GlowingCard/README.md +230 -372
- package/src/molecules/List/README.md +281 -488
- package/src/molecules/ListItem.README.md +320 -315
- package/src/molecules/SearchBar/README.md +332 -430
- package/src/molecules/StepHeader/README.md +311 -411
- package/src/molecules/StepProgress/README.md +281 -448
- package/src/molecules/alerts/README.md +272 -355
- package/src/molecules/avatar/README.md +295 -356
- package/src/molecules/bottom-sheet/README.md +303 -340
- package/src/molecules/calendar/README.md +301 -265
- package/src/molecules/countdown/README.md +347 -456
- package/src/molecules/emoji/README.md +281 -514
- package/src/molecules/listitem/README.md +307 -399
- package/src/molecules/media-card/MediaCard.tsx +31 -34
- package/src/molecules/media-card/README.md +217 -319
- package/src/molecules/navigation/README.md +263 -284
- package/src/molecules/navigation/components/NavigationHeader.tsx +77 -0
- package/src/molecules/navigation/index.ts +1 -0
- package/src/molecules/splash/README.md +76 -80
- package/src/molecules/swipe-actions/README.md +376 -588
|
@@ -1,498 +1,406 @@
|
|
|
1
1
|
# ListItem
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A versatile list item component with title, subtitle, and optional icons for menus, settings, and navigation lists.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Import & Usage
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
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';
|
|
7
|
+
```typescript
|
|
8
|
+
import { ListItem } from 'react-native-design-system/src/molecules/listitem';
|
|
19
9
|
```
|
|
20
10
|
|
|
21
|
-
|
|
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
|
-
```
|
|
11
|
+
**Location:** `src/molecules/listitem/ListItem.tsx`
|
|
39
12
|
|
|
40
|
-
## Basic
|
|
13
|
+
## Basic Usage
|
|
41
14
|
|
|
42
15
|
```tsx
|
|
43
16
|
<ListItem
|
|
44
|
-
title="
|
|
17
|
+
title="Settings"
|
|
18
|
+
subtitle="Configure your preferences"
|
|
19
|
+
leftIcon="settings-outline"
|
|
20
|
+
rightIcon="chevron-forward-outline"
|
|
21
|
+
onPress={() => navigateTo('Settings')}
|
|
45
22
|
/>
|
|
46
23
|
```
|
|
47
24
|
|
|
48
|
-
##
|
|
25
|
+
## Strategy
|
|
26
|
+
|
|
27
|
+
**Purpose**: Provide a consistent, accessible list item component for navigation, settings, and content display.
|
|
28
|
+
|
|
29
|
+
**When to Use**:
|
|
30
|
+
- Navigation menus and drawers
|
|
31
|
+
- Settings pages
|
|
32
|
+
- User lists and contacts
|
|
33
|
+
- File/item listings
|
|
34
|
+
- Action menus (edit, share, delete)
|
|
35
|
+
- Selection lists (radio buttons, checkboxes)
|
|
36
|
+
|
|
37
|
+
**When NOT to Use**:
|
|
38
|
+
- For card-based content (use AtomicCard instead)
|
|
39
|
+
- For complex row layouts (use custom View instead)
|
|
40
|
+
- For table data (use Table component instead)
|
|
41
|
+
- For simple dividers (use Divider instead)
|
|
42
|
+
|
|
43
|
+
## Rules
|
|
44
|
+
|
|
45
|
+
### Required
|
|
46
|
+
|
|
47
|
+
1. **MUST** have a `title` prop
|
|
48
|
+
2. **ALWAYS** provide `onPress` when item is interactive
|
|
49
|
+
3. **SHOULD** have descriptive icons (leftIcon for context, rightIcon for navigation)
|
|
50
|
+
4. **MUST** have proper touch feedback when pressable
|
|
51
|
+
5. **SHOULD** keep subtitles concise (1 line max recommended)
|
|
52
|
+
6. **ALWAYS** use unique `key` props when rendering lists
|
|
53
|
+
7. **MUST** respect disabled state (no feedback)
|
|
54
|
+
|
|
55
|
+
### Icon Usage
|
|
56
|
+
|
|
57
|
+
1. **Left icon**: Use for context/category (settings, profile, notifications)
|
|
58
|
+
2. **Right icon**: Use for navigation indication (chevron, checkmark)
|
|
59
|
+
3. **Consistency**: Use same icon style within list
|
|
60
|
+
4. **Size**: Use appropriate icon size (md recommended)
|
|
61
|
+
|
|
62
|
+
### Press Behavior
|
|
63
|
+
|
|
64
|
+
1. **Must have onPress**: Always provide when using rightIcon for navigation
|
|
65
|
+
2. **Visual feedback**: Show press effect
|
|
66
|
+
3. **Disabled state**: No press effect when disabled
|
|
67
|
+
4. **Action delay**: Add confirmation for destructive actions
|
|
68
|
+
|
|
69
|
+
### Content Guidelines
|
|
70
|
+
|
|
71
|
+
1. **Title**: Clear, concise (1-2 words recommended)
|
|
72
|
+
2. **Subtitle**: Additional context, max 1 line
|
|
73
|
+
3. **Truncation**: Titles should not wrap awkwardly
|
|
74
|
+
4. **Grouping**: Use dividers or section headers for groups
|
|
75
|
+
|
|
76
|
+
## Forbidden
|
|
77
|
+
|
|
78
|
+
❌ **NEVER** do these:
|
|
49
79
|
|
|
50
80
|
```tsx
|
|
81
|
+
// ❌ No title
|
|
51
82
|
<ListItem
|
|
52
|
-
title
|
|
53
|
-
subtitle="
|
|
83
|
+
// Missing title prop
|
|
84
|
+
subtitle="No title"
|
|
54
85
|
/>
|
|
55
|
-
```
|
|
56
86
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
```tsx
|
|
87
|
+
// ❌ Right icon without press handler
|
|
60
88
|
<ListItem
|
|
61
89
|
title="Settings"
|
|
62
|
-
|
|
90
|
+
rightIcon="chevron-forward-outline" // ❌ Indicates navigation
|
|
91
|
+
// Missing onPress
|
|
63
92
|
/>
|
|
64
|
-
```
|
|
65
93
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
```tsx
|
|
94
|
+
// ❌ Generic icons
|
|
69
95
|
<ListItem
|
|
70
|
-
title="
|
|
71
|
-
|
|
72
|
-
onPress={() => navigation.navigate('Notifications')}
|
|
96
|
+
title="Settings"
|
|
97
|
+
leftIcon="ellipse-outline" // ❌ Not descriptive
|
|
73
98
|
/>
|
|
74
|
-
```
|
|
75
99
|
|
|
76
|
-
|
|
100
|
+
// ❌ Too long subtitle
|
|
101
|
+
<ListItem
|
|
102
|
+
title="User"
|
|
103
|
+
subtitle="Lorem ipsum dolor sit amet consectetur adipiscing elit sed do eiusmod tempor incididunt ut labore" // ❌ Too long
|
|
104
|
+
/>
|
|
77
105
|
|
|
78
|
-
|
|
106
|
+
// ❌ Destructive action without confirmation
|
|
79
107
|
<ListItem
|
|
80
|
-
title="
|
|
81
|
-
leftIcon="
|
|
82
|
-
|
|
83
|
-
onPress={() => {}}
|
|
108
|
+
title="Delete Account"
|
|
109
|
+
leftIcon="trash-outline"
|
|
110
|
+
onPress={() => deleteAccount()} // ❌ No confirmation
|
|
84
111
|
/>
|
|
85
|
-
```
|
|
86
112
|
|
|
87
|
-
|
|
113
|
+
// ❌ Missing keys in lists
|
|
114
|
+
{items.map((item) => (
|
|
115
|
+
<ListItem
|
|
116
|
+
title={item.title} // ❌ No key prop
|
|
117
|
+
/>
|
|
118
|
+
))}
|
|
88
119
|
|
|
89
|
-
|
|
120
|
+
// ❌ Disabled but pressable
|
|
90
121
|
<ListItem
|
|
91
122
|
title="Premium Feature"
|
|
92
|
-
subtitle="Upgrade to access"
|
|
93
|
-
leftIcon="diamond-outline"
|
|
94
123
|
disabled
|
|
124
|
+
onPress={() => {}} // ❌ Should not have onPress when disabled
|
|
95
125
|
/>
|
|
96
126
|
```
|
|
97
127
|
|
|
98
|
-
##
|
|
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
|
-
```
|
|
128
|
+
## Best Practices
|
|
167
129
|
|
|
168
|
-
### Navigation
|
|
130
|
+
### Navigation Items
|
|
169
131
|
|
|
132
|
+
✅ **DO**:
|
|
170
133
|
```tsx
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
-
};
|
|
134
|
+
<ListItem
|
|
135
|
+
title="Settings"
|
|
136
|
+
leftIcon="settings-outline"
|
|
137
|
+
rightIcon="chevron-forward-outline"
|
|
138
|
+
onPress={() => navigation.navigate('Settings')}
|
|
139
|
+
/>
|
|
193
140
|
```
|
|
194
141
|
|
|
195
|
-
|
|
196
|
-
|
|
142
|
+
❌ **DON'T**:
|
|
197
143
|
```tsx
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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
|
-
};
|
|
144
|
+
// ❌ Navigation indicator without action
|
|
145
|
+
<ListItem
|
|
146
|
+
title="Settings"
|
|
147
|
+
rightIcon="chevron-forward-outline"
|
|
148
|
+
// Missing onPress
|
|
149
|
+
/>
|
|
236
150
|
```
|
|
237
151
|
|
|
238
|
-
###
|
|
152
|
+
### Icon Selection
|
|
239
153
|
|
|
154
|
+
✅ **DO**:
|
|
240
155
|
```tsx
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
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
|
-
};
|
|
156
|
+
// Descriptive icons
|
|
157
|
+
<ListItem
|
|
158
|
+
title="Notifications"
|
|
159
|
+
leftIcon="notifications-outline"
|
|
160
|
+
/>
|
|
161
|
+
<ListItem
|
|
162
|
+
title="Profile"
|
|
163
|
+
leftIcon="person-outline"
|
|
164
|
+
/>
|
|
262
165
|
```
|
|
263
166
|
|
|
264
|
-
|
|
265
|
-
|
|
167
|
+
❌ **DON'T**:
|
|
266
168
|
```tsx
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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
|
-
};
|
|
169
|
+
// Generic icons
|
|
170
|
+
<ListItem
|
|
171
|
+
title="Notifications"
|
|
172
|
+
leftIcon="ellipse-outline" // ❌ Not descriptive
|
|
173
|
+
/>
|
|
283
174
|
```
|
|
284
175
|
|
|
285
|
-
###
|
|
176
|
+
### List Rendering
|
|
286
177
|
|
|
178
|
+
✅ **DO**:
|
|
287
179
|
```tsx
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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
|
-
};
|
|
180
|
+
{users.map((user) => (
|
|
181
|
+
<ListItem
|
|
182
|
+
key={user.id} // ✅ Unique key
|
|
183
|
+
title={user.name}
|
|
184
|
+
subtitle={user.email}
|
|
185
|
+
onPress={() => navigateToUser(user.id)}
|
|
186
|
+
/>
|
|
187
|
+
))}
|
|
311
188
|
```
|
|
312
189
|
|
|
313
|
-
|
|
314
|
-
|
|
190
|
+
❌ **DON'T**:
|
|
315
191
|
```tsx
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
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
|
-
};
|
|
192
|
+
{users.map((user, index) => (
|
|
193
|
+
<ListItem
|
|
194
|
+
key={index} // ❌ Index as key
|
|
195
|
+
title={user.name}
|
|
196
|
+
/>
|
|
197
|
+
))}
|
|
339
198
|
```
|
|
340
199
|
|
|
341
|
-
###
|
|
200
|
+
### Destructive Actions
|
|
342
201
|
|
|
202
|
+
✅ **DO**:
|
|
343
203
|
```tsx
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
/>
|
|
356
|
-
))}
|
|
357
|
-
</View>
|
|
358
|
-
);
|
|
359
|
-
};
|
|
204
|
+
<ListItem
|
|
205
|
+
title="Delete Account"
|
|
206
|
+
leftIcon="trash-outline"
|
|
207
|
+
onPress={() => {
|
|
208
|
+
showConfirmation({
|
|
209
|
+
title: 'Delete Account',
|
|
210
|
+
message: 'This action cannot be undone',
|
|
211
|
+
onConfirm: deleteAccount,
|
|
212
|
+
});
|
|
213
|
+
}}
|
|
214
|
+
/>
|
|
360
215
|
```
|
|
361
216
|
|
|
362
|
-
|
|
363
|
-
|
|
217
|
+
❌ **DON'T**:
|
|
364
218
|
```tsx
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
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
|
-
};
|
|
219
|
+
<ListItem
|
|
220
|
+
title="Delete Account"
|
|
221
|
+
leftIcon="trash-outline"
|
|
222
|
+
onPress={() => deleteAccount()} // ❌ No confirmation
|
|
223
|
+
/>
|
|
409
224
|
```
|
|
410
225
|
|
|
411
|
-
##
|
|
412
|
-
|
|
413
|
-
###
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
226
|
+
## AI Coding Guidelines
|
|
227
|
+
|
|
228
|
+
### For AI Agents
|
|
229
|
+
|
|
230
|
+
When generating ListItem components, follow these rules:
|
|
231
|
+
|
|
232
|
+
1. **Always import from correct path**:
|
|
233
|
+
```typescript
|
|
234
|
+
import { ListItem } from 'react-native-design-system/src/molecules/listitem';
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
2. **Always provide a title**:
|
|
238
|
+
```tsx
|
|
239
|
+
// ✅ Good
|
|
240
|
+
<ListItem
|
|
241
|
+
title="Settings"
|
|
242
|
+
onPress={handlePress}
|
|
243
|
+
/>
|
|
244
|
+
|
|
245
|
+
// ❌ Bad - no title
|
|
246
|
+
<ListItem
|
|
247
|
+
onPress={handlePress}
|
|
248
|
+
/>
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
3. **Always match icons with context**:
|
|
252
|
+
```tsx
|
|
253
|
+
// ✅ Good - descriptive icons
|
|
254
|
+
const iconMap = {
|
|
255
|
+
settings: 'settings-outline',
|
|
256
|
+
profile: 'person-outline',
|
|
257
|
+
notifications: 'notifications-outline',
|
|
258
|
+
privacy: 'shield-checkmark-outline',
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
// ❌ Bad - generic icons
|
|
262
|
+
const icons = ['ellipse-outline', 'circle-outline'];
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
4. **Always use unique keys in lists**:
|
|
266
|
+
```tsx
|
|
267
|
+
// ✅ Good - unique ID as key
|
|
268
|
+
{items.map((item) => (
|
|
269
|
+
<ListItem
|
|
270
|
+
key={item.id}
|
|
271
|
+
title={item.title}
|
|
272
|
+
onPress={() => handleItem(item)}
|
|
273
|
+
/>
|
|
274
|
+
))}
|
|
275
|
+
|
|
276
|
+
// ❌ Bad - index as key
|
|
277
|
+
{items.map((item, index) => (
|
|
278
|
+
<ListItem
|
|
279
|
+
key={index}
|
|
280
|
+
title={item.title}
|
|
281
|
+
/>
|
|
282
|
+
))}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
5. **Always add confirmation for destructive actions**:
|
|
286
|
+
```tsx
|
|
287
|
+
// ✅ Good - confirmation dialog
|
|
288
|
+
<ListItem
|
|
289
|
+
title="Delete"
|
|
290
|
+
leftIcon="trash-outline"
|
|
291
|
+
onPress={() => {
|
|
292
|
+
Alert.alert(
|
|
293
|
+
'Confirm Delete',
|
|
294
|
+
'This action cannot be undone',
|
|
295
|
+
[
|
|
296
|
+
{ text: 'Cancel', style: 'cancel' },
|
|
297
|
+
{ text: 'Delete', style: 'destructive', onPress: handleDelete },
|
|
298
|
+
]
|
|
299
|
+
);
|
|
300
|
+
}}
|
|
301
|
+
/>
|
|
302
|
+
|
|
303
|
+
// ❌ Bad - immediate action
|
|
304
|
+
<ListItem
|
|
305
|
+
title="Delete"
|
|
306
|
+
leftIcon="trash-outline"
|
|
307
|
+
onPress={handleDelete} // No confirmation
|
|
308
|
+
/>
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### Common Patterns
|
|
312
|
+
|
|
313
|
+
#### Settings Menu Item
|
|
429
314
|
```tsx
|
|
430
|
-
// ✅ Good: Descriptive icons
|
|
431
315
|
<ListItem
|
|
432
316
|
title="Settings"
|
|
433
317
|
leftIcon="settings-outline"
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
// ❌ Bad: Generic icons
|
|
437
|
-
<ListItem
|
|
438
|
-
title="Settings"
|
|
439
|
-
leftIcon="ellipse-outline"
|
|
318
|
+
rightIcon="chevron-forward-outline"
|
|
319
|
+
onPress={() => navigation.navigate('Settings')}
|
|
440
320
|
/>
|
|
441
321
|
```
|
|
442
322
|
|
|
443
|
-
|
|
323
|
+
#### User List Item
|
|
324
|
+
```tsx
|
|
325
|
+
{users.map((user) => (
|
|
326
|
+
<ListItem
|
|
327
|
+
key={user.id}
|
|
328
|
+
title={user.name}
|
|
329
|
+
subtitle={user.email}
|
|
330
|
+
leftIcon="person-outline"
|
|
331
|
+
onPress={() => navigation.navigate('UserProfile', { userId: user.id })}
|
|
332
|
+
/>
|
|
333
|
+
))}
|
|
334
|
+
```
|
|
444
335
|
|
|
336
|
+
#### Selection List Item
|
|
445
337
|
```tsx
|
|
446
|
-
|
|
447
|
-
<ListItem
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
338
|
+
{options.map((option) => (
|
|
339
|
+
<ListItem
|
|
340
|
+
key={option.id}
|
|
341
|
+
title={option.title}
|
|
342
|
+
leftIcon={option.icon}
|
|
343
|
+
rightIcon={selectedId === option.id ? 'checkmark' : undefined}
|
|
344
|
+
onPress={() => setSelectedId(option.id)}
|
|
345
|
+
/>
|
|
346
|
+
))}
|
|
347
|
+
```
|
|
452
348
|
|
|
453
|
-
|
|
349
|
+
#### Action Menu Item
|
|
350
|
+
```tsx
|
|
454
351
|
<ListItem
|
|
455
|
-
title="
|
|
352
|
+
title="Edit"
|
|
353
|
+
leftIcon="create-outline"
|
|
456
354
|
rightIcon="chevron-forward-outline"
|
|
355
|
+
onPress={() => onAction('edit')}
|
|
457
356
|
/>
|
|
458
357
|
```
|
|
459
358
|
|
|
460
|
-
|
|
461
|
-
|
|
359
|
+
#### Contact Item
|
|
462
360
|
```tsx
|
|
463
|
-
// ✅ Good: Concise
|
|
464
361
|
<ListItem
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
subtitle="Lorem ipsum dolor sit amet consectetur adipiscing elit sed do eiusmod tempor"
|
|
362
|
+
title={contact.name}
|
|
363
|
+
subtitle={contact.phone}
|
|
364
|
+
leftIcon="person-outline"
|
|
365
|
+
rightIcon="call-outline"
|
|
366
|
+
onPress={() => Linking.openURL(`tel:${contact.phone}`)}
|
|
471
367
|
/>
|
|
472
368
|
```
|
|
473
369
|
|
|
474
|
-
##
|
|
370
|
+
## Props Reference
|
|
371
|
+
|
|
372
|
+
| Prop | Type | Required | Default | Description |
|
|
373
|
+
|------|------|----------|---------|-------------|
|
|
374
|
+
| `title` | `string` | Yes | - | Primary text |
|
|
375
|
+
| `subtitle` | `string` | No | - | Secondary text |
|
|
376
|
+
| `leftIcon` | `string` | No | - | Left icon name (Ionicons) |
|
|
377
|
+
| `rightIcon` | `string` | No | - | Right icon name (Ionicons) |
|
|
378
|
+
| `onPress` | `() => void` | No | - | Press callback |
|
|
379
|
+
| `disabled` | `boolean` | No | `false` | Disable the item |
|
|
380
|
+
| `style` | `ViewStyle` | No | - | Custom container style |
|
|
475
381
|
|
|
476
|
-
|
|
382
|
+
## Accessibility
|
|
477
383
|
|
|
478
|
-
- ✅ Screen reader
|
|
479
|
-
- ✅ Touch target size
|
|
480
|
-
- ✅
|
|
481
|
-
- ✅ Disabled state
|
|
482
|
-
- ✅ Semantic
|
|
384
|
+
- ✅ Screen reader announces title and subtitle
|
|
385
|
+
- ✅ Touch target size maintained (min 44x44pt)
|
|
386
|
+
- ✅ Press feedback for screen readers
|
|
387
|
+
- ✅ Disabled state announced
|
|
388
|
+
- ✅ Semantic list item role
|
|
389
|
+
- ✅ Icon accessibility labels
|
|
483
390
|
|
|
484
391
|
## Performance Tips
|
|
485
392
|
|
|
486
|
-
1. **Memoization**: Memo
|
|
487
|
-
2. **
|
|
488
|
-
3. **
|
|
393
|
+
1. **Memoization**: Memo ListItem components for large lists
|
|
394
|
+
2. **Unique keys**: Always use unique IDs as keys (not index)
|
|
395
|
+
3. **Avoid inline functions**: Use useCallback for onPress handlers
|
|
396
|
+
4. **FlatList**: Use FlatList for long lists instead of map
|
|
489
397
|
|
|
490
398
|
## Related Components
|
|
491
399
|
|
|
492
400
|
- [`List`](../List/README.md) - List container component
|
|
493
401
|
- [`Avatar`](../avatar/README.md) - User avatar component
|
|
494
|
-
- [`AtomicIcon`](../../atoms/AtomicIcon/README.md) - Icon component
|
|
495
402
|
- [`Divider`](../Divider/README.md) - List divider component
|
|
403
|
+
- [`AtomicIcon`](../../atoms/AtomicIcon/README.md) - Icon component
|
|
496
404
|
|
|
497
405
|
## License
|
|
498
406
|
|