@umituz/react-native-design-system 2.6.84 → 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,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
@@ -33,16 +33,23 @@ export const GlowingCard: React.FC<GlowingCardProps> = ({
33
33
  shadowOpacity: 0.6 * intensity,
34
34
  shadowRadius: 10 * intensity,
35
35
  elevation: 8 * intensity, // Android elevation
36
- backgroundColor: tokens.colors.surface, // Ensure bg is solid or it looks weird
36
+ // We allow the style prop to override backgroundColor, default to surface if not provided
37
+ backgroundColor: tokens.colors.surface,
37
38
  borderRadius: tokens.borders.radius.md,
38
39
  borderColor: resolvedColor,
39
40
  borderWidth: 1,
40
41
  };
41
42
 
43
+ // Extract background color from style if present to override default
44
+ const styleObj = StyleSheet.flatten(style) || {};
45
+ const finalBackgroundColor = styleObj.backgroundColor || shadowStyle.backgroundColor;
46
+
42
47
  const containerStyle = [
43
48
  styles.container,
44
49
  shadowStyle,
45
50
  style,
51
+ // Ensure background color is consistent
52
+ { backgroundColor: finalBackgroundColor }
46
53
  ];
47
54
 
48
55
  if (onPress) {