@umituz/react-native-design-system 2.6.85 → 2.6.86

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,533 @@
1
+ # SearchBar
2
+
3
+ SearchBar, React Native için modern ve özelleştirilebilir bir arama çubuğu bileşenidir. Material Design prensiplerine uygun olarak tasarlanmıştır.
4
+
5
+ ## Özellikler
6
+
7
+ - 🔍 **Arama İkonu**: Sol tarafta arama ikonu
8
+ - ❌ **Clear Button**: Sağ tarafta temizleme butonu
9
+ - ⏳ **Loading State**: Yükleme göstergesi
10
+ - 🎨 **Tema Bilinci**: Tam tema entegrasyonu
11
+ - ⌨️ **Klavye Desteği**: Return key olarak "search"
12
+ - ♿ **Erişilebilir**: Tam erişilebilirlik desteği
13
+
14
+ ## Kurulum
15
+
16
+ ```tsx
17
+ import { SearchBar } from 'react-native-design-system';
18
+ ```
19
+
20
+ ## Temel Kullanım
21
+
22
+ ```tsx
23
+ import React, { useState } from 'react';
24
+ import { View } from 'react-native';
25
+ import { SearchBar } from 'react-native-design-system';
26
+
27
+ export const BasicExample = () => {
28
+ const [searchQuery, setSearchQuery] = useState('');
29
+
30
+ return (
31
+ <View style={{ padding: 16 }}>
32
+ <SearchBar
33
+ value={searchQuery}
34
+ onChangeText={setSearchQuery}
35
+ placeholder="Ara..."
36
+ />
37
+ </View>
38
+ );
39
+ };
40
+ ```
41
+
42
+ ## Basic Search
43
+
44
+ ```tsx
45
+ const [query, setQuery] = useState('');
46
+
47
+ <SearchBar
48
+ value={query}
49
+ onChangeText={setQuery}
50
+ placeholder="Ürün ara..."
51
+ />
52
+ ```
53
+
54
+ ## With Submit Handler
55
+
56
+ ```tsx
57
+ const handleSearch = () => {
58
+ console.log('Searching for:', query);
59
+ // Arama yap
60
+ };
61
+
62
+ <SearchBar
63
+ value={query}
64
+ onChangeText={setQuery}
65
+ onSubmit={handleSearch}
66
+ placeholder="Ara..."
67
+ returnKeyType="search"
68
+ />
69
+ ```
70
+
71
+ ## Loading State
72
+
73
+ ```tsx
74
+ const [isSearching, setIsSearching] = useState(false);
75
+
76
+ const handleSearch = async () => {
77
+ setIsSearching(true);
78
+ await performSearch(query);
79
+ setIsSearching(false);
80
+ };
81
+
82
+ <SearchBar
83
+ value={query}
84
+ onChangeText={setQuery}
85
+ onSubmit={handleSearch}
86
+ loading={isSearching}
87
+ placeholder="Ara..."
88
+ />
89
+ ```
90
+
91
+ ## With Clear Handler
92
+
93
+ ```tsx
94
+ const handleClear = () => {
95
+ setSearchQuery('');
96
+ // Ek işlemler (örn: sonuçları sıfırla)
97
+ };
98
+
99
+ <SearchBar
100
+ value={searchQuery}
101
+ onChangeText={setSearchQuery}
102
+ onClear={handleClear}
103
+ placeholder="Ara..."
104
+ />
105
+ ```
106
+
107
+ ## Disabled State
108
+
109
+ ```tsx
110
+ <SearchBar
111
+ value={query}
112
+ onChangeText={setQuery}
113
+ disabled
114
+ placeholder="Arama devre dışı..."
115
+ />
116
+ ```
117
+
118
+ ## Auto Focus
119
+
120
+ ```tsx
121
+ <SearchBar
122
+ value={query}
123
+ onChangeText={setQuery}
124
+ autoFocus
125
+ placeholder="Ara..."
126
+ />
127
+ ```
128
+
129
+ ## Örnek Kullanımlar
130
+
131
+ ### Ürün Arama
132
+
133
+ ```tsx
134
+ import React, { useState, useEffect } from 'react';
135
+ import { View, FlatList, Pressable, Text } from 'react-native';
136
+ import { SearchBar } from 'react-native-design-system';
137
+
138
+ export const ProductSearch = () => {
139
+ const [query, setQuery] = useState('');
140
+ const [loading, setLoading] = useState(false);
141
+ const [results, setResults] = useState([]);
142
+
143
+ const handleSearch = async () => {
144
+ if (!query.trim()) return;
145
+
146
+ setLoading(true);
147
+ try {
148
+ const response = await fetchProducts(query);
149
+ setResults(response);
150
+ } catch (error) {
151
+ console.error('Search error:', error);
152
+ } finally {
153
+ setLoading(false);
154
+ }
155
+ };
156
+
157
+ const handleClear = () => {
158
+ setQuery('');
159
+ setResults([]);
160
+ };
161
+
162
+ return (
163
+ <View style={{ flex: 1, padding: 16 }}>
164
+ <SearchBar
165
+ value={query}
166
+ onChangeText={setQuery}
167
+ onSubmit={handleSearch}
168
+ onClear={handleClear}
169
+ loading={loading}
170
+ placeholder="Ürün ara..."
171
+ />
172
+
173
+ <FlatList
174
+ data={results}
175
+ keyExtractor={(item) => item.id}
176
+ renderItem={({ item }) => (
177
+ <Pressable style={{ padding: 16, borderBottomWidth: 1 }}>
178
+ <Text>{item.name}</Text>
179
+ </Pressable>
180
+ )}
181
+ />
182
+ </View>
183
+ );
184
+ };
185
+ ```
186
+
187
+ ### Kullanıcı Arama
188
+
189
+ ```tsx
190
+ export const UserSearch = () => {
191
+ const [searchQuery, setSearchQuery] = useState('');
192
+ const [users, setUsers] = useState([]);
193
+ const [loading, setLoading] = useState(false);
194
+
195
+ useEffect(() => {
196
+ if (searchQuery.length > 2) {
197
+ searchUsers(searchQuery);
198
+ } else {
199
+ setUsers([]);
200
+ }
201
+ }, [searchQuery]);
202
+
203
+ const searchUsers = async (query) => {
204
+ setLoading(true);
205
+ const results = await fetchUsers(query);
206
+ setUsers(results);
207
+ setLoading(false);
208
+ };
209
+
210
+ return (
211
+ <View style={{ padding: 16 }}>
212
+ <SearchBar
213
+ value={searchQuery}
214
+ onChangeText={setSearchQuery}
215
+ loading={loading}
216
+ placeholder="Kullanıcı ara..."
217
+ />
218
+
219
+ {users.map((user) => (
220
+ <View key={user.id} style={{ padding: 16 }}>
221
+ <Text>{user.name}</Text>
222
+ </View>
223
+ ))}
224
+ </View>
225
+ );
226
+ };
227
+ ```
228
+
229
+ ### Filtreleme ile Arama
230
+
231
+ ```tsx
232
+ export const FilterableSearch = () => {
233
+ const [query, setQuery] = useState('');
234
+ const [selectedFilter, setSelectedFilter] = useState('all');
235
+
236
+ const handleSearch = () => {
237
+ // Seçili filtreye göre arama
238
+ console.log(`Searching for "${query}" in ${selectedFilter}`);
239
+ };
240
+
241
+ return (
242
+ <View style={{ padding: 16 }}>
243
+ <SearchBar
244
+ value={query}
245
+ onChangeText={setQuery}
246
+ onSubmit={handleSearch}
247
+ placeholder={`${selectedFilter === 'all' ? 'Tümü' : selectedFilter} ara...`}
248
+ />
249
+
250
+ {/* Filtre seçimi */}
251
+ <View style={{ flexDirection: 'row', marginTop: 16, gap: 8 }}>
252
+ <Pressable onPress={() => setSelectedFilter('all')}>
253
+ <Text>Tümü</Text>
254
+ </Pressable>
255
+ <Pressable onPress={() => setSelectedFilter('users')}>
256
+ <Text>Kullanıcılar</Text>
257
+ </Pressable>
258
+ <Pressable onPress={() => setSelectedFilter('products')}>
259
+ <Text>Ürünler</Text>
260
+ </Pressable>
261
+ </View>
262
+ </View>
263
+ );
264
+ };
265
+ ```
266
+
267
+ ### Debounce ile Arama
268
+
269
+ ```tsx
270
+ import { useCallback, useEffect } from 'react';
271
+
272
+ export const DebouncedSearch = () => {
273
+ const [query, setQuery] = useState('');
274
+ const [debouncedQuery, setDebouncedQuery] = useState('');
275
+ const [loading, setLoading] = useState(false);
276
+
277
+ // Debounce
278
+ useEffect(() => {
279
+ const timer = setTimeout(() => {
280
+ setDebouncedQuery(query);
281
+ }, 500);
282
+
283
+ return () => clearTimeout(timer);
284
+ }, [query]);
285
+
286
+ // Arama
287
+ useEffect(() => {
288
+ if (debouncedQuery) {
289
+ performSearch(debouncedQuery);
290
+ }
291
+ }, [debouncedQuery]);
292
+
293
+ const performSearch = async (searchQuery) => {
294
+ setLoading(true);
295
+ await fetch(`/api/search?q=${searchQuery}`);
296
+ setLoading(false);
297
+ };
298
+
299
+ return (
300
+ <View style={{ padding: 16 }}>
301
+ <SearchBar
302
+ value={query}
303
+ onChangeText={setQuery}
304
+ loading={loading}
305
+ placeholder="Ara..."
306
+ />
307
+ </View>
308
+ );
309
+ };
310
+ ```
311
+
312
+ ## Props
313
+
314
+ ### SearchBarProps
315
+
316
+ | Prop | Tip | Varsayılan | Açıklama |
317
+ |------|-----|------------|----------|
318
+ | `value` | `string` | - **(Zorunlu)** | Arama sorgusu |
319
+ | `onChangeText` | `(text: string) => void` | - **(Zorunlu)** | Değişiklik olayı |
320
+ | `onSubmit` | `() => void` | - | Submit olayı |
321
+ | `onClear` | `() => void` | - | Temizleme olayı |
322
+ | `onFocus` | `() => void` | - | Focus olayı |
323
+ | `onBlur` | `() => void` | - | Blur olayı |
324
+ | `placeholder` | `string` | `'Search...'` | Placeholder metni |
325
+ | `autoFocus` | `boolean` | `false` | Otomatik odak |
326
+ | `loading` | `boolean` | `false` | Yükleme durumu |
327
+ | `disabled` | `boolean` | `false` | Devre dışı |
328
+ | `containerStyle` | `ViewStyle` | - | Container stil |
329
+ | `inputStyle` | `TextStyle` | - | Input stil |
330
+ | `testID` | `string` | - | Test ID'si |
331
+
332
+ ## Stil Özelleştirme
333
+
334
+ ```tsx
335
+ <SearchBar
336
+ value={query}
337
+ onChangeText={setQuery}
338
+ containerStyle={{
339
+ backgroundColor: '#f5f5f5',
340
+ borderWidth: 2,
341
+ borderColor: '#e0e0e0',
342
+ }}
343
+ inputStyle={{
344
+ fontSize: 16,
345
+ fontWeight: '500',
346
+ }}
347
+ />
348
+ ```
349
+
350
+ ## Best Practices
351
+
352
+ ### 1. Debounce Kullanımı
353
+
354
+ ```tsx
355
+ // API çağrılarını azaltmak için debounce kullanın
356
+ useEffect(() => {
357
+ const timer = setTimeout(() => {
358
+ if (query.length > 2) {
359
+ performSearch(query);
360
+ }
361
+ }, 500);
362
+
363
+ return () => clearTimeout(timer);
364
+ }, [query]);
365
+ ```
366
+
367
+ ### 2. Minimum Karakter
368
+
369
+ ```tsx
370
+ // En az 3 karakter sonra ara
371
+ useEffect(() => {
372
+ if (query.length > 2) {
373
+ performSearch(query);
374
+ } else {
375
+ setResults([]);
376
+ }
377
+ }, [query]);
378
+ ```
379
+
380
+ ### 3. Loading State
381
+
382
+ ```tsx
383
+ // Kullanıcıya geri bildirim verin
384
+ <SearchBar
385
+ value={query}
386
+ onChangeText={setQuery}
387
+ loading={isSearching}
388
+ onSubmit={handleSearch}
389
+ />
390
+ ```
391
+
392
+ ### 4. Clear Handler
393
+
394
+ ```tsx
395
+ // Temizleme ile sonuçları sıfırlayın
396
+ const handleClear = () => {
397
+ setQuery('');
398
+ setResults([]);
399
+ setFilters({});
400
+ };
401
+ ```
402
+
403
+ ## Erişilebilirlik
404
+
405
+ SearchBar, tam erişilebilirlik desteği sunar:
406
+
407
+ - ✅ Screen reader desteği
408
+ - ✅ Accessibility label
409
+ - ✅ Touch uygun boyut
410
+ - ✅ Keyboard navigation
411
+ - ✅ Test ID desteği
412
+
413
+ ## Performans İpuçları
414
+
415
+ 1. **Debouncing**: API çağrılarını azaltın
416
+ 2. **Minimum Length**: Gereksiz aramaları önleyin
417
+ 3. **Cancellation**: Async işlemleri iptal edin
418
+ 4. **Memoization**: Sonuçları memoize edin
419
+
420
+ ## İlgili Bileşenler
421
+
422
+ - [`AtomicInput`](../../atoms/input/README.md) - Input bileşeni
423
+ - [`BaseModal`](../BaseModal/README.md) - Modal arama sonuçları
424
+ - [`AtomicIcon`](../../atoms/AtomicIcon/README.md) - İkon bileşeni
425
+
426
+ ## Örnek Proje
427
+
428
+ ```tsx
429
+ import React, { useState, useEffect } from 'react';
430
+ import { View, FlatList, Pressable, Text, Image } from 'react-native';
431
+ import { SearchBar } from 'react-native-design-system';
432
+
433
+ export const AdvancedSearch = () => {
434
+ const [query, setQuery] = useState('');
435
+ const [loading, setLoading] = useState(false);
436
+ const [results, setResults] = useState([]);
437
+ const [history, setHistory] = useState([]);
438
+
439
+ useEffect(() => {
440
+ const timer = setTimeout(() => {
441
+ if (query.length > 2) {
442
+ performSearch(query);
443
+ }
444
+ }, 500);
445
+
446
+ return () => clearTimeout(timer);
447
+ }, [query]);
448
+
449
+ const performSearch = async (searchQuery) => {
450
+ setLoading(true);
451
+ try {
452
+ const response = await fetch(`/api/search?q=${searchQuery}`);
453
+ const data = await response.json();
454
+ setResults(data);
455
+
456
+ // Geçmişe ekle
457
+ setHistory(prev => [searchQuery, ...prev.slice(0, 9)]);
458
+ } catch (error) {
459
+ console.error('Search error:', error);
460
+ } finally {
461
+ setLoading(false);
462
+ }
463
+ };
464
+
465
+ const handleClear = () => {
466
+ setQuery('');
467
+ setResults([]);
468
+ };
469
+
470
+ return (
471
+ <View style={{ flex: 1, padding: 16 }}>
472
+ <SearchBar
473
+ value={query}
474
+ onChangeText={setQuery}
475
+ onClear={handleClear}
476
+ loading={loading}
477
+ placeholder="Ara..."
478
+ />
479
+
480
+ {/* Arama Geçmişi */}
481
+ {query.length === 0 && history.length > 0 && (
482
+ <View style={{ marginTop: 16 }}>
483
+ <Text style={{ marginBottom: 8, fontWeight: '600' }}>
484
+ Son Aramalar
485
+ </Text>
486
+ {history.map((item, index) => (
487
+ <Pressable
488
+ key={index}
489
+ onPress={() => setQuery(item)}
490
+ style={{ padding: 12 }}
491
+ >
492
+ <Text>{item}</Text>
493
+ </Pressable>
494
+ ))}
495
+ </View>
496
+ )}
497
+
498
+ {/* Arama Sonuçları */}
499
+ <FlatList
500
+ data={results}
501
+ keyExtractor={(item) => item.id}
502
+ renderItem={({ item }) => (
503
+ <Pressable
504
+ style={{
505
+ flexDirection: 'row',
506
+ padding: 16,
507
+ borderBottomWidth: 1,
508
+ borderBottomColor: '#e0e0e0',
509
+ }}
510
+ >
511
+ <Image
512
+ source={{ uri: item.image }}
513
+ style={{ width: 50, height: 50, borderRadius: 8 }}
514
+ />
515
+ <View style={{ marginLeft: 12, flex: 1 }}>
516
+ <Text style={{ fontSize: 16, fontWeight: '600' }}>
517
+ {item.title}
518
+ </Text>
519
+ <Text style={{ color: 'gray', marginTop: 4 }}>
520
+ {item.description}
521
+ </Text>
522
+ </View>
523
+ </Pressable>
524
+ )}
525
+ />
526
+ </View>
527
+ );
528
+ };
529
+ ```
530
+
531
+ ## Lisans
532
+
533
+ MIT