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

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.
@@ -0,0 +1,568 @@
1
+ # List
2
+
3
+ List, FlatList wrapper'ı olan responsive bir liste bileşenidir. Pull-to-refresh desteği, içerik padding'i ve theme-aware renkler sunar.
4
+
5
+ ## Özellikler
6
+
7
+ - 📜 **FlatList Wrapper**: React Native FlatList'in tüm özellikleri
8
+ - 🔄 **Pull-to-Refresh**: Yenileme desteği
9
+ - 📏 **Content Padding**: Responsive içerik padding'i
10
+ - 🎨 **Theme-Aware**: Design token uyumlu
11
+ - ⚡ **Performanslı**: Optimize edilmiş liste performansı
12
+ - ♿ **Erişilebilir**: Tam erişilebilirlik desteği
13
+
14
+ ## Kurulum
15
+
16
+ ```tsx
17
+ import { List } from 'react-native-design-system';
18
+ ```
19
+
20
+ ## Temel Kullanım
21
+
22
+ ```tsx
23
+ import React from 'react';
24
+ import { View } from 'react-native';
25
+ import { List } from 'react-native-design-system';
26
+
27
+ export const BasicExample = () => {
28
+ const data = [
29
+ { id: '1', title: 'Öğe 1' },
30
+ { id: '2', title: 'Öğe 2' },
31
+ { id: '3', title: 'Öğe 3' },
32
+ ];
33
+
34
+ return (
35
+ <List
36
+ data={data}
37
+ renderItem={({ item }) => (
38
+ <View style={{ padding: 16 }}>
39
+ <AtomicText>{item.title}</AtomicText>
40
+ </View>
41
+ )}
42
+ keyExtractor={(item) => item.id}
43
+ />
44
+ );
45
+ };
46
+ ```
47
+
48
+ ## Basit Liste
49
+
50
+ ```tsx
51
+ const users = [
52
+ { id: '1', name: 'Ahmet Yılmaz' },
53
+ { id: '2', name: 'Ayşe Demir' },
54
+ { id: '3', name: 'Mehmet Kaya' },
55
+ ];
56
+
57
+ <List
58
+ data={users}
59
+ renderItem={({ item }) => (
60
+ <ListItem title={item.name} />
61
+ )}
62
+ keyExtractor={(item) => item.id}
63
+ />
64
+ ```
65
+
66
+ ## Pull-to-Refresh
67
+
68
+ ```tsx
69
+ export const RefreshableList = () => {
70
+ const [data, setData] = useState([]);
71
+ const [refreshing, setRefreshing] = useState(false);
72
+
73
+ const onRefresh = async () => {
74
+ setRefreshing(true);
75
+ const newData = await fetchData();
76
+ setData(newData);
77
+ setRefreshing(false);
78
+ };
79
+
80
+ return (
81
+ <List
82
+ data={data}
83
+ renderItem={({ item }) => <ItemCard item={item} />}
84
+ keyExtractor={(item) => item.id}
85
+ onRefresh={onRefresh}
86
+ refreshing={refreshing}
87
+ />
88
+ );
89
+ };
90
+ ```
91
+
92
+ ## Content Padding
93
+
94
+ ```tsx
95
+ <List
96
+ data={items}
97
+ renderItem={({ item }) => <ItemCard item={item} />}
98
+ keyExtractor={(item) => item.id}
99
+ contentPadding
100
+ />
101
+ ```
102
+
103
+ ## Örnek Kullanımlar
104
+
105
+ ### Kullanıcı Listesi
106
+
107
+ ```tsx
108
+ export const UserList = () => {
109
+ const [users, setUsers] = useState([]);
110
+
111
+ useEffect(() => {
112
+ fetchUsers().then(setUsers);
113
+ }, []);
114
+
115
+ const renderUser = ({ item }) => (
116
+ <ListItem
117
+ title={item.name}
118
+ subtitle={item.email}
119
+ left={() => (
120
+ <Avatar
121
+ uri={item.avatar}
122
+ name={item.name}
123
+ size="md"
124
+ />
125
+ )}
126
+ onPress={() => navigation.navigate('UserProfile', { userId: item.id })}
127
+ />
128
+ );
129
+
130
+ return (
131
+ <List
132
+ data={users}
133
+ renderItem={renderUser}
134
+ keyExtractor={(item) => item.id}
135
+ contentPadding
136
+ />
137
+ );
138
+ };
139
+ ```
140
+
141
+ ### Sonsuz Kaydırma
142
+
143
+ ```tsx
144
+ export const InfiniteList = () => {
145
+ const [data, setData] = useState([]);
146
+ const [loading, setLoading] = useState(false);
147
+ const [page, setPage] = useState(1);
148
+
149
+ const loadMore = async () => {
150
+ if (loading) return;
151
+ setLoading(true);
152
+ const newData = await fetchItems(page);
153
+ setData([...data, ...newData]);
154
+ setPage(page + 1);
155
+ setLoading(false);
156
+ };
157
+
158
+ const renderFooter = () => {
159
+ if (!loading) return null;
160
+ return (
161
+ <View style={{ padding: 16 }}>
162
+ <AtomicSpinner size="md" />
163
+ </View>
164
+ );
165
+ };
166
+
167
+ return (
168
+ <List
169
+ data={data}
170
+ renderItem={({ item }) => <ItemCard item={item} />}
171
+ keyExtractor={(item) => item.id}
172
+ onEndReached={loadMore}
173
+ onEndReachedThreshold={0.5}
174
+ ListFooterComponent={renderFooter}
175
+ contentPadding
176
+ />
177
+ );
178
+ };
179
+ ```
180
+
181
+ ### Ürün Listesi
182
+
183
+ ```tsx
184
+ export const ProductList = () => {
185
+ const [products, setProducts] = useState([]);
186
+ const [refreshing, setRefreshing] = useState(false);
187
+
188
+ const onRefresh = async () => {
189
+ setRefreshing(true);
190
+ const data = await fetchProducts();
191
+ setProducts(data);
192
+ setRefreshing(false);
193
+ };
194
+
195
+ const renderProduct = ({ item }) => (
196
+ <MediaCard
197
+ uri={item.image}
198
+ title={item.name}
199
+ subtitle={`${item.price} TL`}
200
+ onPress={() => navigation.navigate('ProductDetail', { productId: item.id })}
201
+ />
202
+ );
203
+
204
+ return (
205
+ <List
206
+ data={products}
207
+ renderItem={renderProduct}
208
+ keyExtractor={(item) => item.id}
209
+ onRefresh={onRefresh}
210
+ refreshing={refreshing}
211
+ numColumns={2}
212
+ columnWrapperStyle={{ gap: 8 }}
213
+ contentPadding
214
+ />
215
+ );
216
+ };
217
+ ```
218
+
219
+ ### Haber Listesi
220
+
221
+ ```tsx
222
+ export const NewsList = () => {
223
+ const [articles, setArticles] = useState([]);
224
+
225
+ const renderArticle = ({ item }) => (
226
+ <AtomicCard
227
+ variant="outlined"
228
+ style={{ marginBottom: 16 }}
229
+ onPress={() => navigation.navigate('Article', { articleId: item.id })}
230
+ >
231
+ <View style={{ padding: 16 }}>
232
+ <AtomicText type="titleMedium" style={{ marginBottom: 8 }}>
233
+ {item.title}
234
+ </AtomicText>
235
+ <AtomicText type="bodyMedium" color="secondary" numberOfLines={2}>
236
+ {item.excerpt}
237
+ </AtomicText>
238
+ <View style={{ flexDirection: 'row', marginTop: 8 }}>
239
+ <AtomicText type="labelSmall" color="tertiary">
240
+ {item.category}
241
+ </AtomicText>
242
+ <AtomicText type="labelSmall" color="tertiary" style={{ marginLeft: 16 }}>
243
+ {formatDate(item.publishedAt)}
244
+ </AtomicText>
245
+ </View>
246
+ </View>
247
+ </AtomicCard>
248
+ );
249
+
250
+ return (
251
+ <List
252
+ data={articles}
253
+ renderItem={renderArticle}
254
+ keyExtractor={(item) => item.id}
255
+ contentPadding
256
+ />
257
+ );
258
+ };
259
+ ```
260
+
261
+ ### Sohbet Listesi
262
+
263
+ ```tsx
264
+ export const ChatList = () => {
265
+ const [conversations, setConversations] = useState([]);
266
+
267
+ const renderConversation = ({ item }) => (
268
+ <Pressable
269
+ style={{ flexDirection: 'row', alignItems: 'center', padding: 16 }}
270
+ onPress={() => navigation.navigate('Chat', { chatId: item.id })}
271
+ >
272
+ <Avatar
273
+ uri={item.avatar}
274
+ name={item.name}
275
+ showStatus
276
+ status={item.online ? 'online' : 'offline'}
277
+ size="md"
278
+ />
279
+
280
+ <View style={{ flex: 1, marginLeft: 12 }}>
281
+ <View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
282
+ <AtomicText type="bodyLarge" fontWeight="600">
283
+ {item.name}
284
+ </AtomicText>
285
+ <AtomicText type="bodySmall" color="tertiary">
286
+ {formatTime(item.lastMessageAt)}
287
+ </AtomicText>
288
+ </View>
289
+
290
+ <AtomicText
291
+ type="bodyMedium"
292
+ color="secondary"
293
+ numberOfLines={1}
294
+ style={{ marginTop: 4 }}
295
+ >
296
+ {item.lastMessage}
297
+ </AtomicText>
298
+ </View>
299
+ </Pressable>
300
+ );
301
+
302
+ return (
303
+ <List
304
+ data={conversations}
305
+ renderItem={renderConversation}
306
+ keyExtractor={(item) => item.id}
307
+ contentPadding
308
+ />
309
+ );
310
+ };
311
+ ```
312
+
313
+ ### Arama Sonuçları
314
+
315
+ ```tsx
316
+ export const SearchResults = ({ query }) => {
317
+ const [results, setResults] = useState([]);
318
+ const [loading, setLoading] = useState(false);
319
+
320
+ useEffect(() => {
321
+ const search = async () => {
322
+ setLoading(true);
323
+ const data = await searchItems(query);
324
+ setResults(data);
325
+ setLoading(false);
326
+ };
327
+
328
+ if (query.length > 2) {
329
+ search();
330
+ }
331
+ }, [query]);
332
+
333
+ const renderResult = ({ item }) => (
334
+ <ListItem
335
+ title={item.title}
336
+ subtitle={item.description}
337
+ left={() => <AtomicIcon name="search-outline" size="md" />}
338
+ onPress={() => navigation.navigate('Detail', { id: item.id })}
339
+ />
340
+ );
341
+
342
+ if (loading) {
343
+ return <AtomicSpinner fullContainer />;
344
+ }
345
+
346
+ if (results.length === 0) {
347
+ return (
348
+ <EmptyState
349
+ icon="search-outline"
350
+ title="Sonuç bulunamadı"
351
+ message={`"${query}" için sonuç bulunamadı`}
352
+ />
353
+ );
354
+ }
355
+
356
+ return (
357
+ <List
358
+ data={results}
359
+ renderItem={renderResult}
360
+ keyExtractor={(item) => item.id}
361
+ contentPadding
362
+ />
363
+ );
364
+ };
365
+ ```
366
+
367
+ ### Bildirim Listesi
368
+
369
+ ```tsx
370
+ export const NotificationList = () => {
371
+ const [notifications, setNotifications] = useState([]);
372
+
373
+ const renderNotification = ({ item }) => (
374
+ <View
375
+ style={{
376
+ flexDirection: 'row',
377
+ padding: 16,
378
+ backgroundColor: item.read ? 'transparent' : `${tokens.colors.primary}10`,
379
+ borderBottomWidth: 1,
380
+ borderBottomColor: tokens.colors.border,
381
+ }}
382
+ >
383
+ <View style={{ marginRight: 12 }}>
384
+ <AtomicIcon
385
+ name={item.type === 'success' ? 'checkmark-circle' : 'information-circle'}
386
+ size="lg"
387
+ color={item.type === 'success' ? 'success' : 'primary'}
388
+ />
389
+ </View>
390
+
391
+ <View style={{ flex: 1 }}>
392
+ <AtomicText type="bodyLarge" fontWeight="600">
393
+ {item.title}
394
+ </AtomicText>
395
+ <AtomicText type="bodyMedium" color="secondary" style={{ marginTop: 4 }}>
396
+ {item.message}
397
+ </AtomicText>
398
+ <AtomicText type="labelSmall" color="tertiary" style={{ marginTop: 8 }}>
399
+ {formatRelativeTime(item.createdAt)}
400
+ </AtomicText>
401
+ </View>
402
+ </View>
403
+ );
404
+
405
+ return (
406
+ <List
407
+ data={notifications}
408
+ renderItem={renderNotification}
409
+ keyExtractor={(item) => item.id}
410
+ contentPadding
411
+ />
412
+ );
413
+ };
414
+ ```
415
+
416
+ ### Görev Listesi
417
+
418
+ ```tsx
419
+ export const TaskList = () => {
420
+ const [tasks, setTasks] = useState([]);
421
+
422
+ const toggleTask = (taskId) => {
423
+ setTasks(tasks.map(task =>
424
+ task.id === taskId ? { ...task, completed: !task.completed } : task
425
+ ));
426
+ };
427
+
428
+ const renderTask = ({ item }) => (
429
+ <View
430
+ style={{
431
+ flexDirection: 'row',
432
+ alignItems: 'center',
433
+ padding: 16,
434
+ borderBottomWidth: 1,
435
+ borderBottomColor: tokens.colors.border,
436
+ }}
437
+ >
438
+ <Pressable onPress={() => toggleTask(item.id)}>
439
+ <AtomicIcon
440
+ name={item.completed ? 'checkmark-circle' : 'ellipse-outline'}
441
+ size="md"
442
+ color={item.completed ? 'success' : 'secondary'}
443
+ />
444
+ </Pressable>
445
+
446
+ <View style={{ flex: 1, marginLeft: 12 }}>
447
+ <AtomicText
448
+ type="bodyLarge"
449
+ style={{
450
+ textDecorationLine: item.completed ? 'line-through' : 'none',
451
+ opacity: item.completed ? 0.6 : 1,
452
+ }}
453
+ >
454
+ {item.title}
455
+ </AtomicText>
456
+
457
+ {item.dueDate && (
458
+ <AtomicText type="labelSmall" color="tertiary" style={{ marginTop: 4 }}>
459
+ {formatDate(item.dueDate)}
460
+ </AtomicText>
461
+ )}
462
+ </View>
463
+
464
+ {item.priority === 'high' && (
465
+ <AtomicIcon name="alert-circle" size="sm" color="error" />
466
+ )}
467
+ </View>
468
+ );
469
+
470
+ return (
471
+ <List
472
+ data={tasks}
473
+ renderItem={renderTask}
474
+ keyExtractor={(item) => item.id}
475
+ contentPadding
476
+ />
477
+ );
478
+ };
479
+ ```
480
+
481
+ ## Props
482
+
483
+ ### ListProps
484
+
485
+ | Prop | Tip | Varsayılan | Açıklama |
486
+ |------|-----|------------|----------|
487
+ | `data` | `T[]` | - **(Zorunlu)** | Liste verisi |
488
+ | `renderItem` | `ListRenderItem<T>` | - **(Zorunlu)** | Render fonksiyonu |
489
+ | `keyExtractor` | `(item, index) => string` | - **(Zorunlu)** | Key extractor |
490
+ | `onRefresh` | `() => void` | - | Yenileme callback'i |
491
+ | `refreshing` | `boolean` | `false` | Yeneleniyor durumunda |
492
+ | `contentPadding` | `boolean` | `false` | İçerik padding'i |
493
+
494
+ **Not:** List, FlatList'in tüm props'larını destekler.
495
+
496
+ ## Best Practices
497
+
498
+ ### 1. Key Extractor
499
+
500
+ ```tsx
501
+ // ✅ İyi - Unique ve stable
502
+ keyExtractor={(item) => item.id}
503
+
504
+ // ❌ Kötü - Index kullanımı
505
+ keyExtractor={(item, index) => index.toString()}
506
+ ```
507
+
508
+ ### 2. Performans
509
+
510
+ ```tsx
511
+ // ✅ Memo kullan
512
+ const renderItem = useCallback(({ item }) => (
513
+ <ItemCard item={item} />
514
+ ), []);
515
+
516
+ // ✅ Inline function değil
517
+ renderItem={renderItem}
518
+ ```
519
+
520
+ ### 3. Content Padding
521
+
522
+ ```tsx
523
+ // Liste için
524
+ <List contentPadding />
525
+
526
+ // Manuel padding
527
+ <View style={{ padding: 16 }}>
528
+ <List />
529
+ </View> // ❌ Gereksiz
530
+ ```
531
+
532
+ ### 4. Empty State
533
+
534
+ ```tsx
535
+ {data.length === 0 ? (
536
+ <EmptyState />
537
+ ) : (
538
+ <List data={data} />
539
+ )}
540
+ ```
541
+
542
+ ## Erişilebilirlik
543
+
544
+ List, tam erişilebilirlik desteği sunar:
545
+
546
+ - ✅ Screen reader desteği
547
+ - ✅ Semantic list anlamları
548
+ - ✅ Focus management
549
+ - ✅ Keyboard navigation
550
+
551
+ ## Performans İpuçları
552
+
553
+ 1. **keyExtractor**: Her item için unique key kullanın
554
+ 2. **Memoization**: renderItem fonksiyonunu memo edin
555
+ 3. **removeClippedSubviews**: Büyük listelerde kullanın
556
+ 4. **getItemLayout**: Sabit boyutlu item'larda kullanın
557
+ 5. **windowSize**: Viewport dışındaki item sayısını sınırlayın
558
+
559
+ ## İlgili Bileşenler
560
+
561
+ - [`FlatList`](https://reactnative.dev/docs/flatlist) - React Native FlatList
562
+ - [`ListItem`](../listitem/README.md) - Liste öğesi
563
+ - [`MediaCard`](../media-card/README.md) - Medya kartı
564
+ - [`Avatar`](../avatar/README.md) - Avatar bileşeni
565
+
566
+ ## Lisans
567
+
568
+ MIT