@umituz/react-native-design-system 2.6.85 → 2.6.87
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 -3
- package/src/atoms/AtomicAvatar.README.md +483 -0
- package/src/atoms/AtomicBadge.README.md +423 -0
- package/src/atoms/AtomicCard.README.md +337 -0
- package/src/atoms/AtomicDatePicker.README.md +399 -0
- package/src/atoms/AtomicFab.README.md +421 -0
- package/src/atoms/AtomicIcon.README.md +349 -0
- package/src/atoms/AtomicSpinner.README.md +433 -0
- package/src/atoms/AtomicText.README.md +470 -0
- package/src/atoms/AtomicTextArea.README.md +352 -0
- package/src/atoms/GlassView/GlassView.tsx +28 -17
- package/src/atoms/GlassView/README.md +521 -0
- package/src/atoms/button/README.md +363 -0
- package/src/atoms/chip/README.md +376 -0
- package/src/atoms/input/README.md +342 -0
- package/src/atoms/picker/README.md +412 -0
- package/src/molecules/BaseModal.README.md +435 -0
- package/src/molecules/FormField.README.md +486 -0
- package/src/molecules/GlowingCard/README.md +448 -0
- package/src/molecules/ListItem.README.md +402 -0
- package/src/molecules/SearchBar/README.md +533 -0
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
# FormField
|
|
2
|
+
|
|
3
|
+
FormField, form alanları için tam özellikli bir molekül bileşenidir. `AtomicInput` ile birlikte etiket, hata mesajı ve yardımcı metin sunar.
|
|
4
|
+
|
|
5
|
+
## Özellikler
|
|
6
|
+
|
|
7
|
+
- 🏷️ **Label Entegrasyonu**: Otomatik etiket oluşturma
|
|
8
|
+
- ❌ **Error Handling**: Hata mesajı gösterimi
|
|
9
|
+
- ℹ️ **Helper Text**: Yardımcı metin desteği
|
|
10
|
+
- ⭐ **Required Indicator**: Zorunlu alan işareti
|
|
11
|
+
- 🎨 **Tam Özelleştirilebilir**: Stil ve tema desteği
|
|
12
|
+
- ♿ **Erişilebilir**: Tam erişilebilirlik desteği
|
|
13
|
+
|
|
14
|
+
## Kurulum
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
import { FormField } 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 { FormField } from 'react-native-design-system';
|
|
26
|
+
|
|
27
|
+
export const BasicExample = () => {
|
|
28
|
+
const [email, setEmail] = useState('');
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<View style={{ padding: 16 }}>
|
|
32
|
+
<FormField
|
|
33
|
+
label="E-posta"
|
|
34
|
+
value={email}
|
|
35
|
+
onChangeText={setEmail}
|
|
36
|
+
placeholder="ornek@email.com"
|
|
37
|
+
keyboardType="email-address"
|
|
38
|
+
/>
|
|
39
|
+
</View>
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Zorunlu Alan
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
<FormField
|
|
48
|
+
label="Ad Soyad"
|
|
49
|
+
required
|
|
50
|
+
value={name}
|
|
51
|
+
onChangeText={setName}
|
|
52
|
+
placeholder="Adınız ve soyadınız"
|
|
53
|
+
/>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Hata Durumu
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
<FormField
|
|
60
|
+
label="E-posta"
|
|
61
|
+
value={email}
|
|
62
|
+
onChangeText={setEmail}
|
|
63
|
+
placeholder="ornek@email.com"
|
|
64
|
+
error="Geçerli bir e-posta adresi girin"
|
|
65
|
+
state="error"
|
|
66
|
+
/>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Helper Text
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
<FormField
|
|
73
|
+
label="Şifre"
|
|
74
|
+
value={password}
|
|
75
|
+
onChangeText={setPassword}
|
|
76
|
+
placeholder="Şifreniz"
|
|
77
|
+
secureTextEntry
|
|
78
|
+
helperText="En az 8 karakter olmalıdır"
|
|
79
|
+
/>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## İkonlu Form Field
|
|
83
|
+
|
|
84
|
+
```tsx
|
|
85
|
+
<FormField
|
|
86
|
+
label="Kullanıcı Adı"
|
|
87
|
+
value={username}
|
|
88
|
+
onChangeText={setUsername}
|
|
89
|
+
placeholder="Kullanıcı adınız"
|
|
90
|
+
leadingIcon="person-outline"
|
|
91
|
+
/>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Password Field
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
<FormField
|
|
98
|
+
label="Şifre"
|
|
99
|
+
value={password}
|
|
100
|
+
onChangeText={setPassword}
|
|
101
|
+
placeholder="Şifreniz"
|
|
102
|
+
secureTextEntry
|
|
103
|
+
showPasswordToggle
|
|
104
|
+
helperText="En az 8 karakter, 1 büyük harf ve 1 rakam"
|
|
105
|
+
required
|
|
106
|
+
/>
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Custom Required Indicator
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
<FormField
|
|
113
|
+
label="Telefon"
|
|
114
|
+
value={phone}
|
|
115
|
+
onChangeText={setPhone}
|
|
116
|
+
placeholder="+90 555 123 4567"
|
|
117
|
+
required
|
|
118
|
+
requiredIndicator=" *"
|
|
119
|
+
keyboardType="phone-pad"
|
|
120
|
+
/>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Örnek Kullanımlar
|
|
124
|
+
|
|
125
|
+
### Kayıt Formu
|
|
126
|
+
|
|
127
|
+
```tsx
|
|
128
|
+
import React, { useState } from 'react';
|
|
129
|
+
import { View, ScrollView, Button } from 'react-native';
|
|
130
|
+
import { FormField } from 'react-native-design-system';
|
|
131
|
+
|
|
132
|
+
export const RegisterForm = () => {
|
|
133
|
+
const [formData, setFormData] = useState({
|
|
134
|
+
name: '',
|
|
135
|
+
email: '',
|
|
136
|
+
password: '',
|
|
137
|
+
confirmPassword: '',
|
|
138
|
+
});
|
|
139
|
+
const [errors, setErrors] = useState({});
|
|
140
|
+
|
|
141
|
+
const handleInputChange = (field) => (value) => {
|
|
142
|
+
setFormData(prev => ({ ...prev, [field]: value }));
|
|
143
|
+
// Clear error when user types
|
|
144
|
+
if (errors[field]) {
|
|
145
|
+
setErrors(prev => ({ ...prev, [field]: null }));
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const validate = () => {
|
|
150
|
+
const newErrors = {};
|
|
151
|
+
|
|
152
|
+
if (!formData.name) {
|
|
153
|
+
newErrors.name = 'Ad soyad zorunludur';
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (!formData.email) {
|
|
157
|
+
newErrors.email = 'E-posta zorunludur';
|
|
158
|
+
} else if (!/\S+@\S+\.\S+/.test(formData.email)) {
|
|
159
|
+
newErrors.email = 'Geçerli bir e-posta adresi girin';
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (!formData.password) {
|
|
163
|
+
newErrors.password = 'Şifre zorunludur';
|
|
164
|
+
} else if (formData.password.length < 8) {
|
|
165
|
+
newErrors.password = 'Şifre en az 8 karakter olmalıdır';
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (formData.password !== formData.confirmPassword) {
|
|
169
|
+
newErrors.confirmPassword = 'Şifreler eşleşmiyor';
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
setErrors(newErrors);
|
|
173
|
+
return Object.keys(newErrors).length === 0;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const handleSubmit = () => {
|
|
177
|
+
if (validate()) {
|
|
178
|
+
console.log('Form submitted:', formData);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
return (
|
|
183
|
+
<ScrollView style={{ padding: 16 }}>
|
|
184
|
+
<FormField
|
|
185
|
+
label="Ad Soyad"
|
|
186
|
+
value={formData.name}
|
|
187
|
+
onChangeText={handleInputChange('name')}
|
|
188
|
+
placeholder="Adınız ve soyadınız"
|
|
189
|
+
error={errors.name}
|
|
190
|
+
required
|
|
191
|
+
/>
|
|
192
|
+
|
|
193
|
+
<FormField
|
|
194
|
+
label="E-posta"
|
|
195
|
+
value={formData.email}
|
|
196
|
+
onChangeText={handleInputChange('email')}
|
|
197
|
+
placeholder="ornek@email.com"
|
|
198
|
+
keyboardType="email-address"
|
|
199
|
+
autoCapitalize="none"
|
|
200
|
+
error={errors.email}
|
|
201
|
+
helperText="Size ulaşmak için kullanacağız"
|
|
202
|
+
required
|
|
203
|
+
/>
|
|
204
|
+
|
|
205
|
+
<FormField
|
|
206
|
+
label="Şifre"
|
|
207
|
+
value={formData.password}
|
|
208
|
+
onChangeText={handleInputChange('password')}
|
|
209
|
+
placeholder="En az 8 karakter"
|
|
210
|
+
secureTextEntry
|
|
211
|
+
showPasswordToggle
|
|
212
|
+
error={errors.password}
|
|
213
|
+
helperText="En az 8 karakter, 1 büyük harf ve 1 rakam"
|
|
214
|
+
required
|
|
215
|
+
/>
|
|
216
|
+
|
|
217
|
+
<FormField
|
|
218
|
+
label="Şifre Tekrar"
|
|
219
|
+
value={formData.confirmPassword}
|
|
220
|
+
onChangeText={handleInputChange('confirmPassword')}
|
|
221
|
+
placeholder="Şifrenizi tekrar girin"
|
|
222
|
+
secureTextEntry
|
|
223
|
+
showPasswordToggle
|
|
224
|
+
error={errors.confirmPassword}
|
|
225
|
+
required
|
|
226
|
+
/>
|
|
227
|
+
|
|
228
|
+
<Button title="Kayıt Ol" onPress={handleSubmit} />
|
|
229
|
+
</ScrollView>
|
|
230
|
+
);
|
|
231
|
+
};
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Giriş Formu
|
|
235
|
+
|
|
236
|
+
```tsx
|
|
237
|
+
export const LoginForm = () => {
|
|
238
|
+
const [email, setEmail] = useState('');
|
|
239
|
+
const [password, setPassword] = useState('');
|
|
240
|
+
const [error, setError] = useState('');
|
|
241
|
+
|
|
242
|
+
const handleLogin = () => {
|
|
243
|
+
if (!email || !password) {
|
|
244
|
+
setError('Lütfen tüm alanları doldurun');
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
// Login logic
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
return (
|
|
251
|
+
<View style={{ padding: 16 }}>
|
|
252
|
+
<FormField
|
|
253
|
+
label="E-posta"
|
|
254
|
+
value={email}
|
|
255
|
+
onChangeText={setEmail}
|
|
256
|
+
placeholder="ornek@email.com"
|
|
257
|
+
keyboardType="email-address"
|
|
258
|
+
autoCapitalize="none"
|
|
259
|
+
/>
|
|
260
|
+
|
|
261
|
+
<FormField
|
|
262
|
+
label="Şifre"
|
|
263
|
+
value={password}
|
|
264
|
+
onChangeText={setPassword}
|
|
265
|
+
placeholder="Şifreniz"
|
|
266
|
+
secureTextEntry
|
|
267
|
+
showPasswordToggle
|
|
268
|
+
/>
|
|
269
|
+
|
|
270
|
+
{error ? (
|
|
271
|
+
<AtomicText color="error" style={{ marginBottom: 16 }}>
|
|
272
|
+
{error}
|
|
273
|
+
</AtomicText>
|
|
274
|
+
) : null}
|
|
275
|
+
|
|
276
|
+
<Button title="Giriş Yap" onPress={handleLogin} />
|
|
277
|
+
</View>
|
|
278
|
+
);
|
|
279
|
+
};
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Profil Formu
|
|
283
|
+
|
|
284
|
+
```tsx
|
|
285
|
+
export const ProfileForm = () => {
|
|
286
|
+
const [profile, setProfile] = useState({
|
|
287
|
+
firstName: 'Ahmet',
|
|
288
|
+
lastName: 'Yılmaz',
|
|
289
|
+
email: 'ahmet@example.com',
|
|
290
|
+
phone: '+90 555 123 4567',
|
|
291
|
+
bio: '',
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
return (
|
|
295
|
+
<ScrollView style={{ padding: 16 }}>
|
|
296
|
+
<FormField
|
|
297
|
+
label="Ad"
|
|
298
|
+
value={profile.firstName}
|
|
299
|
+
onChangeText={(text) => setProfile({ ...profile, firstName: text })}
|
|
300
|
+
placeholder="Adınız"
|
|
301
|
+
required
|
|
302
|
+
/>
|
|
303
|
+
|
|
304
|
+
<FormField
|
|
305
|
+
label="Soyad"
|
|
306
|
+
value={profile.lastName}
|
|
307
|
+
onChangeText={(text) => setProfile({ ...profile, lastName: text })}
|
|
308
|
+
placeholder="Soyadınız"
|
|
309
|
+
required
|
|
310
|
+
/>
|
|
311
|
+
|
|
312
|
+
<FormField
|
|
313
|
+
label="E-posta"
|
|
314
|
+
value={profile.email}
|
|
315
|
+
onChangeText={(text) => setProfile({ ...profile, email: text })}
|
|
316
|
+
placeholder="ornek@email.com"
|
|
317
|
+
keyboardType="email-address"
|
|
318
|
+
autoCapitalize="none"
|
|
319
|
+
leadingIcon="mail-outline"
|
|
320
|
+
required
|
|
321
|
+
/>
|
|
322
|
+
|
|
323
|
+
<FormField
|
|
324
|
+
label="Telefon"
|
|
325
|
+
value={profile.phone}
|
|
326
|
+
onChangeText={(text) => setProfile({ ...profile, phone: text })}
|
|
327
|
+
placeholder="+90 555 123 4567"
|
|
328
|
+
keyboardType="phone-pad"
|
|
329
|
+
leadingIcon="call-outline"
|
|
330
|
+
/>
|
|
331
|
+
|
|
332
|
+
<FormField
|
|
333
|
+
label="Hakkımda"
|
|
334
|
+
value={profile.bio}
|
|
335
|
+
onChangeText={(text) => setProfile({ ...profile, bio: text })}
|
|
336
|
+
placeholder="Kendinizden bahsedin"
|
|
337
|
+
multiline
|
|
338
|
+
numberOfLines={4}
|
|
339
|
+
maxLength={200}
|
|
340
|
+
showCharacterCount
|
|
341
|
+
/>
|
|
342
|
+
</ScrollView>
|
|
343
|
+
);
|
|
344
|
+
};
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
## Props
|
|
348
|
+
|
|
349
|
+
### FormFieldProps
|
|
350
|
+
|
|
351
|
+
`FormField`, `AtomicInputProps`'ın tüm props'larını alır ve aşağıdakileri ekler:
|
|
352
|
+
|
|
353
|
+
| Prop | Tip | Varsayılan | Açıklama |
|
|
354
|
+
|------|-----|------------|----------|
|
|
355
|
+
| `label` | `string` | - | Alan etiketi |
|
|
356
|
+
| `error` | `string` | - | Hata mesajı |
|
|
357
|
+
| `helperText` | `string` | - | Yardımcı metin |
|
|
358
|
+
| `required` | `boolean` | `false` | Zorunlu alan |
|
|
359
|
+
| `requiredIndicator` | `string` | `' *'` | Zorunlu alan işareti |
|
|
360
|
+
| `containerStyle` | `ViewStyle` | - | Container stili |
|
|
361
|
+
| `style` | `ViewStyle` | - | Container stili (alias) |
|
|
362
|
+
|
|
363
|
+
## Stil Özelleştirme
|
|
364
|
+
|
|
365
|
+
```tsx
|
|
366
|
+
<FormField
|
|
367
|
+
label="Özel Alan"
|
|
368
|
+
value={value}
|
|
369
|
+
onChangeText={setValue}
|
|
370
|
+
containerStyle={{
|
|
371
|
+
marginBottom: 24,
|
|
372
|
+
backgroundColor: '#f9fafb',
|
|
373
|
+
padding: 16,
|
|
374
|
+
borderRadius: 8,
|
|
375
|
+
}}
|
|
376
|
+
/>
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
## Best Practices
|
|
380
|
+
|
|
381
|
+
### 1. Error Handling
|
|
382
|
+
|
|
383
|
+
```tsx
|
|
384
|
+
// Hataları state'de tutun
|
|
385
|
+
const [errors, setErrors] = useState({});
|
|
386
|
+
|
|
387
|
+
// Form submit'da validate edin
|
|
388
|
+
const validate = () => {
|
|
389
|
+
const newErrors = {};
|
|
390
|
+
if (!email) newErrors.email = 'Bu alan zorunludur';
|
|
391
|
+
setErrors(newErrors);
|
|
392
|
+
return Object.keys(newErrors).length === 0;
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
// Form field'da gösterin
|
|
396
|
+
<FormField
|
|
397
|
+
error={errors.email}
|
|
398
|
+
// ...
|
|
399
|
+
/>
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### 2. Helper Text Kullanımı
|
|
403
|
+
|
|
404
|
+
```tsx
|
|
405
|
+
// Kullanıcıya rehberlik edin
|
|
406
|
+
<FormField
|
|
407
|
+
label="Şifre"
|
|
408
|
+
helperText="En az 8 karakter, 1 büyük harf ve 1 rakam içermelidir"
|
|
409
|
+
// ...
|
|
410
|
+
/>
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
### 3. Required Fields
|
|
414
|
+
|
|
415
|
+
```tsx
|
|
416
|
+
// Zorunlu alanları işaretleyin
|
|
417
|
+
<FormField
|
|
418
|
+
label="E-posta"
|
|
419
|
+
required
|
|
420
|
+
// ...
|
|
421
|
+
/>
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
## Erişilebilirlik
|
|
425
|
+
|
|
426
|
+
FormField, tam erişilebilirlik desteği sunar:
|
|
427
|
+
|
|
428
|
+
- ✅ Label ilişkilendirmesi
|
|
429
|
+
- ✅ Error state anonsu
|
|
430
|
+
- ✅ Required field göstergesi
|
|
431
|
+
- ✅ Screen reader desteği
|
|
432
|
+
|
|
433
|
+
## Form Validasyon Örneği
|
|
434
|
+
|
|
435
|
+
```tsx
|
|
436
|
+
const useFormValidation = (schema) => {
|
|
437
|
+
const [errors, setErrors] = useState({});
|
|
438
|
+
|
|
439
|
+
const validate = (data) => {
|
|
440
|
+
const newErrors = {};
|
|
441
|
+
|
|
442
|
+
Object.keys(schema).forEach((key) => {
|
|
443
|
+
const rules = schema[key];
|
|
444
|
+
const value = data[key];
|
|
445
|
+
|
|
446
|
+
if (rules.required && !value) {
|
|
447
|
+
newErrors[key] = `${rules.label} zorunludur`;
|
|
448
|
+
} else if (rules.pattern && !rules.pattern.test(value)) {
|
|
449
|
+
newErrors[key] = rules.message || 'Geçersiz değer';
|
|
450
|
+
} else if (rules.minLength && value.length < rules.minLength) {
|
|
451
|
+
newErrors[key] = `${rules.label} en az ${rules.minLength} karakter olmalıdır`;
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
setErrors(newErrors);
|
|
456
|
+
return Object.keys(newErrors).length === 0;
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
return { errors, validate };
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
// Kullanım
|
|
463
|
+
const { errors, validate } = useFormValidation({
|
|
464
|
+
email: {
|
|
465
|
+
required: true,
|
|
466
|
+
label: 'E-posta',
|
|
467
|
+
pattern: /\S+@\S+\.\S+/,
|
|
468
|
+
message: 'Geçerli bir e-posta adresi girin',
|
|
469
|
+
},
|
|
470
|
+
password: {
|
|
471
|
+
required: true,
|
|
472
|
+
label: 'Şifre',
|
|
473
|
+
minLength: 8,
|
|
474
|
+
},
|
|
475
|
+
});
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
## İlgili Bileşenler
|
|
479
|
+
|
|
480
|
+
- [`AtomicInput`](../atoms/input/README.md) - Input bileşeni
|
|
481
|
+
- [`AtomicButton`](../atoms/button/README.md) - Form butonu
|
|
482
|
+
- [`BaseModal`](./BaseModal/README.md) - Modal form
|
|
483
|
+
|
|
484
|
+
## Lisans
|
|
485
|
+
|
|
486
|
+
MIT
|