@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,413 +1,376 @@
|
|
|
1
1
|
# BottomSheet
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A modal-like component that opens from the bottom of the screen, ideal for filtering, selection, or on-screen actions.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Import & Usage
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
- ♿ **Erişilebilir**: Tam erişilebilirlik desteği
|
|
7
|
+
```typescript
|
|
8
|
+
import { BottomSheet } from 'react-native-design-system/src/molecules/bottom-sheet';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
**Location:** `src/molecules/bottom-sheet/BottomSheet.tsx`
|
|
13
12
|
|
|
14
|
-
##
|
|
13
|
+
## Basic Usage
|
|
15
14
|
|
|
16
15
|
```tsx
|
|
17
|
-
|
|
16
|
+
const bottomSheetRef = useRef<BottomSheetRef>(null);
|
|
17
|
+
|
|
18
|
+
<BottomSheet ref={bottomSheetRef} preset="medium">
|
|
19
|
+
<View style={{ padding: 24 }}>
|
|
20
|
+
<YourContent />
|
|
21
|
+
</View>
|
|
22
|
+
</BottomSheet>
|
|
18
23
|
```
|
|
19
24
|
|
|
20
|
-
##
|
|
25
|
+
## Strategy
|
|
21
26
|
|
|
22
|
-
|
|
23
|
-
import React, { useRef } from 'react';
|
|
24
|
-
import { View, Button } from 'react-native';
|
|
25
|
-
import { BottomSheet } from 'react-native-design-system';
|
|
27
|
+
**Purpose**: Provide a contextual, gesture-friendly interface for mobile-first interactions from the bottom of the screen.
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
**When to Use**:
|
|
30
|
+
- Filtering and sorting options
|
|
31
|
+
- Selection from lists (share, actions, options)
|
|
32
|
+
- Form inputs and data entry
|
|
33
|
+
- Contextual actions (edit, delete, share)
|
|
34
|
+
- Multi-step wizards
|
|
35
|
+
- Settings panels
|
|
29
36
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
37
|
+
**When NOT to Use**:
|
|
38
|
+
- For critical confirmations (use BaseModal instead)
|
|
39
|
+
- For simple alerts (use Alert/toast instead)
|
|
40
|
+
- For dropdown menus (use Dropdown/Popover instead)
|
|
41
|
+
- For side panels (use Drawer instead)
|
|
42
|
+
- For desktop-first interfaces (use Modal instead)
|
|
33
43
|
|
|
34
|
-
|
|
35
|
-
<View style={{ flex: 1 }}>
|
|
36
|
-
<Button title="Bottom Sheet Aç" onPress={openSheet} />
|
|
44
|
+
## Rules
|
|
37
45
|
|
|
38
|
-
|
|
39
|
-
ref={bottomSheetRef}
|
|
40
|
-
preset="medium"
|
|
41
|
-
>
|
|
42
|
-
<View style={{ padding: 24 }}>
|
|
43
|
-
<AtomicText>Bottom Sheet İçeriği</AtomicText>
|
|
44
|
-
</View>
|
|
45
|
-
</BottomSheet>
|
|
46
|
-
</View>
|
|
47
|
-
);
|
|
48
|
-
};
|
|
49
|
-
```
|
|
46
|
+
### Required
|
|
50
47
|
|
|
51
|
-
|
|
48
|
+
1. **MUST** provide a close mechanism (ref with close() or onClose callback)
|
|
49
|
+
2. **ALWAYS** use appropriate preset for content length
|
|
50
|
+
3. **MUST** handle back button (Android) and escape key (desktop)
|
|
51
|
+
4. **NEVER** nest bottom sheets inside bottom sheets
|
|
52
|
+
5. **SHOULD** have clear title at top
|
|
53
|
+
6. **MUST** respect safe area insets
|
|
52
54
|
|
|
53
|
-
|
|
54
|
-
<View style={{ gap: 16 }}>
|
|
55
|
-
{/* Small - %35 */}
|
|
56
|
-
<BottomSheet ref={ref1} preset="small">
|
|
57
|
-
<SmallContent />
|
|
58
|
-
</BottomSheet>
|
|
55
|
+
### Preset Selection
|
|
59
56
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
57
|
+
1. **Small (35%)**: Selections, short lists, simple forms
|
|
58
|
+
2. **Medium (60%)**: Forms with multiple fields, medium lists
|
|
59
|
+
3. **Large (85%)**: Long forms, complex filters, long lists
|
|
60
|
+
4. **Full (100%)**: Full-screen content, complex wizards
|
|
64
61
|
|
|
65
|
-
|
|
66
|
-
<BottomSheet ref={ref3} preset="large">
|
|
67
|
-
<LargeContent />
|
|
68
|
-
</BottomSheet>
|
|
62
|
+
### Dismiss Behavior
|
|
69
63
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
</BottomSheet>
|
|
74
|
-
</View>
|
|
75
|
-
```
|
|
64
|
+
1. **Default**: Swipe down enabled
|
|
65
|
+
2. **Critical actions**: Consider disabling swipe, require explicit action
|
|
66
|
+
3. **Always provide close button** (don't rely only on swipe)
|
|
76
67
|
|
|
77
|
-
|
|
68
|
+
### Content Guidelines
|
|
78
69
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
snapPoints={['25%', '50%', '75%']}
|
|
83
|
-
initialIndex={1}
|
|
84
|
-
>
|
|
85
|
-
<Content />
|
|
86
|
-
</BottomSheet>
|
|
87
|
-
```
|
|
70
|
+
1. **Scrollable content**: Use ScrollView for long content
|
|
71
|
+
2. **Actions at bottom**: Place primary actions at bottom
|
|
72
|
+
3. **Handle at top**: Optional visual drag handle
|
|
88
73
|
|
|
89
|
-
##
|
|
74
|
+
## Forbidden
|
|
75
|
+
|
|
76
|
+
❌ **NEVER** do these:
|
|
90
77
|
|
|
91
78
|
```tsx
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
backgroundColor="#f8f9fa"
|
|
95
|
-
>
|
|
79
|
+
// ❌ No close mechanism
|
|
80
|
+
<BottomSheet ref={ref}>
|
|
96
81
|
<Content />
|
|
97
82
|
</BottomSheet>
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
## OnChange Callback
|
|
101
|
-
|
|
102
|
-
```tsx
|
|
103
|
-
const [sheetIndex, setSheetIndex] = useState(-1);
|
|
104
83
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
onChange={(index) => setSheetIndex(index)}
|
|
108
|
-
>
|
|
84
|
+
// ❌ Nested bottom sheets
|
|
85
|
+
<BottomSheet ref={ref1}>
|
|
109
86
|
<Content />
|
|
87
|
+
<BottomSheet ref={ref2}>
|
|
88
|
+
<NestedContent />
|
|
89
|
+
</BottomSheet>
|
|
110
90
|
</BottomSheet>
|
|
111
|
-
```
|
|
112
91
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
<
|
|
117
|
-
ref={bottomSheetRef}
|
|
118
|
-
onClose={() => console.log('Sheet closed')}
|
|
119
|
-
>
|
|
120
|
-
<Content />
|
|
92
|
+
// ❌ Wrong preset choice
|
|
93
|
+
<BottomSheet ref={ref} preset="full">
|
|
94
|
+
{/* ❌ Use small for simple selection */}
|
|
95
|
+
<Text>Select option</Text>
|
|
121
96
|
</BottomSheet>
|
|
122
|
-
```
|
|
123
97
|
|
|
124
|
-
|
|
98
|
+
// ❌ No title (accessibility issue)
|
|
99
|
+
<BottomSheet ref={ref} onClose={onClose}>
|
|
100
|
+
<Content /> {/* ❌ Missing title */}
|
|
101
|
+
</BottomSheet>
|
|
125
102
|
|
|
126
|
-
|
|
103
|
+
// ❌ Hardcoded height without scroll
|
|
104
|
+
<BottomSheet ref={ref} preset="small">
|
|
105
|
+
<View style={{ height: 1000 }}>
|
|
106
|
+
{/* ❌ Content too long, won't scroll */}
|
|
107
|
+
<LongContent />
|
|
108
|
+
</View>
|
|
109
|
+
</BottomSheet>
|
|
127
110
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
<
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
</AtomicText>
|
|
136
|
-
|
|
137
|
-
{options.map((option) => (
|
|
138
|
-
<Pressable
|
|
139
|
-
key={option.id}
|
|
140
|
-
style={{ padding: 16 }}
|
|
141
|
-
onPress={() => {
|
|
142
|
-
onSelect(option);
|
|
143
|
-
ref.current?.close();
|
|
144
|
-
}}
|
|
145
|
-
>
|
|
146
|
-
<AtomicText type="bodyLarge">{option.label}</AtomicText>
|
|
147
|
-
</Pressable>
|
|
148
|
-
))}
|
|
149
|
-
</View>
|
|
150
|
-
</BottomSheet>
|
|
151
|
-
);
|
|
152
|
-
};
|
|
111
|
+
// ❌ Actions at top (bad UX)
|
|
112
|
+
<BottomSheet ref={ref}>
|
|
113
|
+
<View style={{ padding: 16 }}>
|
|
114
|
+
<Button title="Save" /> {/* ❌ Should be at bottom */}
|
|
115
|
+
</View>
|
|
116
|
+
<Content />
|
|
117
|
+
</BottomSheet>
|
|
153
118
|
```
|
|
154
119
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
```tsx
|
|
158
|
-
export const ShareBottomSheet = ({ ref, item, onShare }) => {
|
|
159
|
-
const shareOptions = [
|
|
160
|
-
{ id: 'copy', icon: 'copy-outline', label: 'Linki Kopyala' },
|
|
161
|
-
{ id: 'whatsapp', icon: 'logo-whatsapp', label: 'WhatsApp' },
|
|
162
|
-
{ id: 'twitter', icon: 'logo-twitter', label: 'Twitter' },
|
|
163
|
-
{ id: 'facebook', icon: 'logo-facebook', label: 'Facebook' },
|
|
164
|
-
];
|
|
165
|
-
|
|
166
|
-
return (
|
|
167
|
-
<BottomSheet ref={ref} preset="small">
|
|
168
|
-
<View style={{ padding: 24 }}>
|
|
169
|
-
<AtomicText type="titleLarge" style={{ marginBottom: 16 }}>
|
|
170
|
-
Paylaş
|
|
171
|
-
</AtomicText>
|
|
172
|
-
|
|
173
|
-
{shareOptions.map((option) => (
|
|
174
|
-
<Pressable
|
|
175
|
-
key={option.id}
|
|
176
|
-
style={{ flexDirection: 'row', alignItems: 'center', padding: 16 }}
|
|
177
|
-
onPress={() => onShare(option.id)}
|
|
178
|
-
>
|
|
179
|
-
<AtomicIcon name={option.icon} size="lg" style={{ marginRight: 16 }} />
|
|
180
|
-
<AtomicText type="bodyLarge">{option.label}</AtomicText>
|
|
181
|
-
</Pressable>
|
|
182
|
-
))}
|
|
183
|
-
</View>
|
|
184
|
-
</BottomSheet>
|
|
185
|
-
);
|
|
186
|
-
};
|
|
187
|
-
```
|
|
120
|
+
## Best Practices
|
|
188
121
|
|
|
189
|
-
###
|
|
122
|
+
### Selection Sheet
|
|
190
123
|
|
|
124
|
+
✅ **DO**:
|
|
191
125
|
```tsx
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
<
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
<View style={{ flexDirection: 'row', gap: 16, marginTop: 24 }}>
|
|
212
|
-
<Button
|
|
213
|
-
title="Temizle"
|
|
214
|
-
mode="outlined"
|
|
215
|
-
style={{ flex: 1 }}
|
|
216
|
-
onPress={() => setLocalFilters({})}
|
|
217
|
-
/>
|
|
218
|
-
<Button
|
|
219
|
-
title="Uygula"
|
|
220
|
-
mode="contained"
|
|
221
|
-
style={{ flex: 1 }}
|
|
222
|
-
onPress={() => onApply(localFilters)}
|
|
223
|
-
/>
|
|
224
|
-
</View>
|
|
225
|
-
</View>
|
|
226
|
-
</BottomSheet>
|
|
227
|
-
);
|
|
228
|
-
};
|
|
126
|
+
<BottomSheet ref={ref} preset="small">
|
|
127
|
+
<View style={{ padding: 24 }}>
|
|
128
|
+
<AtomicText type="titleLarge" style={{ marginBottom: 16 }}>
|
|
129
|
+
Select Option
|
|
130
|
+
</AtomicText>
|
|
131
|
+
{options.map((option) => (
|
|
132
|
+
<Pressable
|
|
133
|
+
key={option.id}
|
|
134
|
+
style={{ padding: 16 }}
|
|
135
|
+
onPress={() => {
|
|
136
|
+
onSelect(option);
|
|
137
|
+
ref.current?.close();
|
|
138
|
+
}}
|
|
139
|
+
>
|
|
140
|
+
<AtomicText type="bodyLarge">{option.label}</AtomicText>
|
|
141
|
+
</Pressable>
|
|
142
|
+
))}
|
|
143
|
+
</View>
|
|
144
|
+
</BottomSheet>
|
|
229
145
|
```
|
|
230
146
|
|
|
231
|
-
|
|
232
|
-
|
|
147
|
+
❌ **DON'T**:
|
|
233
148
|
```tsx
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
<SettingItem icon="notifications-outline" label="Bildirimler" />
|
|
243
|
-
<SettingItem icon="moon-outline" label="Karanlık Mod" />
|
|
244
|
-
<SettingItem icon="globe-outline" label="Dil" />
|
|
245
|
-
<SettingItem icon="information-circle-outline" label="Hakkında" />
|
|
246
|
-
</View>
|
|
247
|
-
</BottomSheet>
|
|
248
|
-
);
|
|
249
|
-
};
|
|
149
|
+
// ❌ No close after selection
|
|
150
|
+
<BottomSheet ref={ref} preset="small">
|
|
151
|
+
{options.map((option) => (
|
|
152
|
+
<Pressable onPress={() => onSelect(option)}>
|
|
153
|
+
<AtomicText>{option.label}</AtomicText>
|
|
154
|
+
</Pressable>
|
|
155
|
+
))}
|
|
156
|
+
</BottomSheet>
|
|
250
157
|
```
|
|
251
158
|
|
|
252
|
-
###
|
|
159
|
+
### Filter Sheet
|
|
253
160
|
|
|
161
|
+
✅ **DO**:
|
|
254
162
|
```tsx
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
{
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
return (
|
|
264
|
-
<BottomSheet ref={ref} preset="small">
|
|
265
|
-
<View style={{ padding: 8 }}>
|
|
266
|
-
{actions.map((action) => (
|
|
267
|
-
<Pressable
|
|
268
|
-
key={action.id}
|
|
269
|
-
style={{
|
|
270
|
-
flexDirection: 'row',
|
|
271
|
-
alignItems: 'center',
|
|
272
|
-
padding: 16,
|
|
273
|
-
borderBottomWidth: 1,
|
|
274
|
-
borderBottomColor: '#e0e0e0',
|
|
275
|
-
}}
|
|
276
|
-
onPress={() => onAction(action.id)}
|
|
277
|
-
>
|
|
278
|
-
<AtomicIcon
|
|
279
|
-
name={action.icon}
|
|
280
|
-
size="md"
|
|
281
|
-
color={action.color}
|
|
282
|
-
style={{ marginRight: 16 }}
|
|
283
|
-
/>
|
|
284
|
-
<AtomicText
|
|
285
|
-
type="bodyLarge"
|
|
286
|
-
color={action.color}
|
|
287
|
-
>
|
|
288
|
-
{action.label}
|
|
289
|
-
</AtomicText>
|
|
290
|
-
</Pressable>
|
|
291
|
-
))}
|
|
292
|
-
</View>
|
|
293
|
-
</BottomSheet>
|
|
294
|
-
);
|
|
295
|
-
};
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
## Ref Methods
|
|
163
|
+
<BottomSheet ref={ref} preset="large">
|
|
164
|
+
<View style={{ padding: 24 }}>
|
|
165
|
+
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
|
|
166
|
+
<AtomicText type="titleLarge">Filter</AtomicText>
|
|
167
|
+
<Pressable onPress={() => ref.current?.close()}>
|
|
168
|
+
<AtomicIcon name="close" />
|
|
169
|
+
</Pressable>
|
|
170
|
+
</View>
|
|
299
171
|
|
|
300
|
-
|
|
172
|
+
<ScrollView style={{ marginTop: 16 }}>
|
|
173
|
+
<FilterOptions filters={filters} onChange={setFilters} />
|
|
174
|
+
</ScrollView>
|
|
301
175
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
close(): void; // Kapat
|
|
309
|
-
}
|
|
176
|
+
<View style={{ flexDirection: 'row', gap: 16, marginTop: 24 }}>
|
|
177
|
+
<Button title="Clear" variant="ghost" style={{ flex: 1 }} onPress={handleClear} />
|
|
178
|
+
<Button title="Apply" style={{ flex: 1 }} onPress={handleApply} />
|
|
179
|
+
</View>
|
|
180
|
+
</View>
|
|
181
|
+
</BottomSheet>
|
|
310
182
|
```
|
|
311
183
|
|
|
312
|
-
###
|
|
313
|
-
|
|
184
|
+
### Preset Choice
|
|
185
|
+
|
|
186
|
+
✅ **DO**:
|
|
187
|
+
- Small: Simple selections (3-5 items)
|
|
188
|
+
- Medium: Forms with 3-5 fields
|
|
189
|
+
- Large: Long forms, complex filters
|
|
190
|
+
- Full: Multi-step wizards, complex content
|
|
191
|
+
|
|
192
|
+
❌ **DON'T**:
|
|
193
|
+
- Use full preset for simple selection
|
|
194
|
+
- Use small preset for long forms
|
|
195
|
+
- Use same preset for all use cases
|
|
196
|
+
|
|
197
|
+
## AI Coding Guidelines
|
|
198
|
+
|
|
199
|
+
### For AI Agents
|
|
200
|
+
|
|
201
|
+
When generating BottomSheet components, follow these rules:
|
|
202
|
+
|
|
203
|
+
1. **Always import from correct path**:
|
|
204
|
+
```typescript
|
|
205
|
+
import { BottomSheet } from 'react-native-design-system/src/molecules/bottom-sheet';
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
2. **Always provide ref and close mechanism**:
|
|
209
|
+
```tsx
|
|
210
|
+
const ref = useRef<BottomSheetRef>(null);
|
|
211
|
+
|
|
212
|
+
<BottomSheet ref={ref} onClose={handleClose}>
|
|
213
|
+
{content}
|
|
214
|
+
</BottomSheet>
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
3. **Always choose appropriate preset**:
|
|
218
|
+
```tsx
|
|
219
|
+
// ✅ Good - preset by content type
|
|
220
|
+
const getPreset = (type: 'selection' | 'form' | 'filter') => {
|
|
221
|
+
switch (type) {
|
|
222
|
+
case 'selection': return 'small';
|
|
223
|
+
case 'form': return 'medium';
|
|
224
|
+
case 'filter': return 'large';
|
|
225
|
+
default: return 'medium';
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
4. **Always handle close actions**:
|
|
231
|
+
```tsx
|
|
232
|
+
// ✅ Good - close after action
|
|
233
|
+
const handleSelect = (option) => {
|
|
234
|
+
onSelect(option);
|
|
235
|
+
ref.current?.close();
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
// ❌ Bad - no close
|
|
239
|
+
const handleSelect = (option) => {
|
|
240
|
+
onSelect(option);
|
|
241
|
+
// Sheet stays open
|
|
242
|
+
};
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
5. **Always use ScrollView for long content**:
|
|
246
|
+
```tsx
|
|
247
|
+
// ✅ Good - scrollable content
|
|
248
|
+
<BottomSheet ref={ref} preset="medium">
|
|
249
|
+
<View style={{ padding: 24 }}>
|
|
250
|
+
<AtomicText type="titleLarge">Title</AtomicText>
|
|
251
|
+
<ScrollView style={{ marginTop: 16 }}>
|
|
252
|
+
<LongFormContent />
|
|
253
|
+
</ScrollView>
|
|
254
|
+
<View style={{ marginTop: 24 }}>
|
|
255
|
+
<Button title="Save" onPress={handleSave} />
|
|
256
|
+
</View>
|
|
257
|
+
</View>
|
|
258
|
+
</BottomSheet>
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Common Patterns
|
|
262
|
+
|
|
263
|
+
#### Selection Bottom Sheet
|
|
314
264
|
```tsx
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
| `preset` | `BottomSheetPreset` | `'medium'` | Yükseklik preset'i |
|
|
335
|
-
| `snapPoints` | `(number \| string)[]` | - | Custom snap points |
|
|
336
|
-
| `initialIndex` | `number` | - | Başlangıç index'i |
|
|
337
|
-
| `backgroundColor` | `string` | - | Arka plan rengi |
|
|
338
|
-
| `onChange` | `(index: number) => void` | - | Değişiklik callback'i |
|
|
339
|
-
| `onClose` | `() => void` | - | Kapatma callback'i |
|
|
340
|
-
|
|
341
|
-
### BottomSheetPreset
|
|
342
|
-
|
|
343
|
-
```typescript
|
|
344
|
-
type BottomSheetPreset =
|
|
345
|
-
| 'small' // %35
|
|
346
|
-
| 'medium' // %60 (varsayılan)
|
|
347
|
-
| 'large' // %85
|
|
348
|
-
| 'full'; // %100
|
|
265
|
+
<BottomSheet ref={ref} preset="small">
|
|
266
|
+
<View style={{ padding: 24 }}>
|
|
267
|
+
<AtomicText type="titleLarge">Select Option</AtomicText>
|
|
268
|
+
<ScrollView style={{ marginTop: 16 }}>
|
|
269
|
+
{options.map((option) => (
|
|
270
|
+
<Pressable
|
|
271
|
+
key={option.id}
|
|
272
|
+
style={{ padding: 16 }}
|
|
273
|
+
onPress={() => {
|
|
274
|
+
onSelect(option);
|
|
275
|
+
ref.current?.close();
|
|
276
|
+
}}
|
|
277
|
+
>
|
|
278
|
+
<AtomicText>{option.label}</AtomicText>
|
|
279
|
+
</Pressable>
|
|
280
|
+
))}
|
|
281
|
+
</ScrollView>
|
|
282
|
+
</View>
|
|
283
|
+
</BottomSheet>
|
|
349
284
|
```
|
|
350
285
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
### 1. Preset Seçimi
|
|
354
|
-
|
|
286
|
+
#### Filter Bottom Sheet
|
|
355
287
|
```tsx
|
|
356
|
-
|
|
357
|
-
<
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
<
|
|
288
|
+
<BottomSheet ref={ref} preset="large">
|
|
289
|
+
<View style={{ padding: 24 }}>
|
|
290
|
+
<View style={{ flexDirection: 'row', justifyContent: 'space-between', marginBottom: 16 }}>
|
|
291
|
+
<AtomicText type="titleLarge">Filter</AtomicText>
|
|
292
|
+
<Pressable onPress={() => ref.current?.close()}>
|
|
293
|
+
<AtomicIcon name="close" size="md" />
|
|
294
|
+
</Pressable>
|
|
295
|
+
</View>
|
|
361
296
|
|
|
362
|
-
|
|
363
|
-
<
|
|
297
|
+
<ScrollView>
|
|
298
|
+
<FilterOptions filters={filters} onChange={setFilters} />
|
|
299
|
+
</ScrollView>
|
|
364
300
|
|
|
365
|
-
|
|
366
|
-
<
|
|
301
|
+
<View style={{ flexDirection: 'row', gap: 12, marginTop: 24 }}>
|
|
302
|
+
<Button title="Clear" variant="ghost" style={{ flex: 1 }} onPress={handleClear} />
|
|
303
|
+
<Button title="Apply" style={{ flex: 1 }} onPress={handleApply} />
|
|
304
|
+
</View>
|
|
305
|
+
</View>
|
|
306
|
+
</BottomSheet>
|
|
367
307
|
```
|
|
368
308
|
|
|
369
|
-
|
|
370
|
-
|
|
309
|
+
#### Share Bottom Sheet
|
|
371
310
|
```tsx
|
|
372
|
-
|
|
373
|
-
<
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
311
|
+
<BottomSheet ref={ref} preset="small">
|
|
312
|
+
<View style={{ padding: 24 }}>
|
|
313
|
+
<AtomicText type="titleLarge" style={{ marginBottom: 16 }}>
|
|
314
|
+
Share
|
|
315
|
+
</AtomicText>
|
|
316
|
+
{shareOptions.map((option) => (
|
|
317
|
+
<Pressable
|
|
318
|
+
key={option.id}
|
|
319
|
+
style={{ flexDirection: 'row', alignItems: 'center', padding: 16 }}
|
|
320
|
+
onPress={() => {
|
|
321
|
+
onShare(option.id);
|
|
322
|
+
ref.current?.close();
|
|
323
|
+
}}
|
|
324
|
+
>
|
|
325
|
+
<AtomicIcon name={option.icon} size="lg" style={{ marginRight: 16 }} />
|
|
326
|
+
<AtomicText type="bodyLarge">{option.label}</AtomicText>
|
|
327
|
+
</Pressable>
|
|
328
|
+
))}
|
|
329
|
+
</View>
|
|
330
|
+
</BottomSheet>
|
|
377
331
|
```
|
|
378
332
|
|
|
379
|
-
|
|
333
|
+
## Props Reference
|
|
380
334
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
ref
|
|
335
|
+
| Prop | Type | Required | Default | Description |
|
|
336
|
+
|------|------|----------|---------|-------------|
|
|
337
|
+
| `ref` | `BottomSheetRef` | Yes | - | Sheet ref for control |
|
|
338
|
+
| `children` | `ReactNode` | Yes | - | Sheet content |
|
|
339
|
+
| `preset` | `'small' \| 'medium' \| 'large' \| 'full'` | No | `'medium'` | Height preset |
|
|
340
|
+
| `snapPoints` | `(number \| string)[]` | No | - | Custom snap points |
|
|
341
|
+
| `initialIndex` | `number` | No | - | Initial snap index |
|
|
342
|
+
| `backgroundColor` | `string` | No | - | Background color |
|
|
343
|
+
| `onChange` | `(index: number) => void` | No | - | Index change callback |
|
|
344
|
+
| `onClose` | `() => void` | No | - | Close callback |
|
|
384
345
|
|
|
385
|
-
|
|
386
|
-
<BottomSheet onClose={handleClose} />
|
|
387
|
-
```
|
|
346
|
+
### Ref Methods
|
|
388
347
|
|
|
389
|
-
|
|
348
|
+
- `expand()`: Open sheet
|
|
349
|
+
- `close()`: Close sheet
|
|
350
|
+
- `snapToIndex(index)`: Go to snap point
|
|
390
351
|
|
|
391
|
-
|
|
352
|
+
## Accessibility
|
|
392
353
|
|
|
393
|
-
- ✅ Screen reader
|
|
394
|
-
- ✅ Touch
|
|
395
|
-
- ✅ Escape key (
|
|
396
|
-
- ✅ Focus trap
|
|
397
|
-
- ✅
|
|
354
|
+
- ✅ Screen reader announces sheet title and content
|
|
355
|
+
- ✅ Touch target size maintained (min 44x44pt)
|
|
356
|
+
- ✅ Escape key handling (desktop)
|
|
357
|
+
- ✅ Focus trap within sheet
|
|
358
|
+
- ✅ Swipe gesture for dismiss
|
|
359
|
+
- ✅ Back button handling (mobile)
|
|
398
360
|
|
|
399
|
-
##
|
|
361
|
+
## Performance Tips
|
|
400
362
|
|
|
401
|
-
1. **Lazy
|
|
402
|
-
2. **
|
|
403
|
-
3. **
|
|
363
|
+
1. **Lazy loading**: Load content only when sheet opens
|
|
364
|
+
2. **Memoization**: Memo sheet content to prevent re-renders
|
|
365
|
+
3. **Unmount**: Consider unmounting when closed for heavy content
|
|
366
|
+
4. **ScrollView**: Always use ScrollView for long lists
|
|
404
367
|
|
|
405
|
-
##
|
|
368
|
+
## Related Components
|
|
406
369
|
|
|
407
|
-
- [`BaseModal`](../BaseModal/README.md) - Modal
|
|
408
|
-
- [`
|
|
409
|
-
- [`
|
|
370
|
+
- [`BaseModal`](../BaseModal/README.md) - Modal component for critical actions
|
|
371
|
+
- [`FormField`](../FormField/README.md) - Form field component
|
|
372
|
+
- [`Button`](../../atoms/button/README.md) - Button component
|
|
410
373
|
|
|
411
|
-
##
|
|
374
|
+
## License
|
|
412
375
|
|
|
413
376
|
MIT
|