@umituz/react-native-design-system 2.6.93 → 2.6.95
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/exports/theme.ts +0 -1
- 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/IconContainer.tsx +1 -1
- 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/splash/README.md +76 -92
- package/src/molecules/swipe-actions/README.md +376 -588
|
@@ -1,580 +1,347 @@
|
|
|
1
1
|
# EmojiPicker
|
|
2
2
|
|
|
3
|
-
EmojiPicker
|
|
3
|
+
EmojiPicker is a component for emoji selection that wraps `rn-emoji-keyboard` library. It provides category-based selection, search, and recently used emojis.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Import & Usage
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
- 🎨 **Özelleştirilebilir**: Konfigürasyon seçenekleri
|
|
13
|
-
- ✨ **Animasyonlar**: Smooth animasyonlar
|
|
7
|
+
```typescript
|
|
8
|
+
import { EmojiPicker } from 'react-native-design-system/src/molecules/emoji';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
**Location:** `src/molecules/emoji/EmojiPicker.tsx`
|
|
14
12
|
|
|
15
|
-
##
|
|
13
|
+
## Basic Usage
|
|
16
14
|
|
|
17
15
|
```tsx
|
|
18
|
-
|
|
16
|
+
<EmojiPicker
|
|
17
|
+
open={isOpen}
|
|
18
|
+
onClose={() => setIsOpen(false)}
|
|
19
|
+
onEmojiSelected={(emoji) => setEmoji(emoji.emoji)}
|
|
20
|
+
/>
|
|
19
21
|
```
|
|
20
22
|
|
|
21
|
-
##
|
|
23
|
+
## Strategy
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
<View>
|
|
39
|
-
<TouchableOpacity onPress={() => setIsOpen(true)}>
|
|
40
|
-
<Text>{emoji || 'Emoji Seç'}</Text>
|
|
41
|
-
</TouchableOpacity>
|
|
42
|
-
|
|
43
|
-
<EmojiPicker
|
|
44
|
-
open={isOpen}
|
|
45
|
-
onClose={() => setIsOpen(false)}
|
|
46
|
-
onEmojiSelected={handleEmojiSelect}
|
|
47
|
-
/>
|
|
48
|
-
</View>
|
|
49
|
-
);
|
|
50
|
-
};
|
|
51
|
-
```
|
|
25
|
+
**Purpose**: Provide a clean, user-friendly emoji selection interface for adding emojis to text content.
|
|
26
|
+
|
|
27
|
+
**When to Use**:
|
|
28
|
+
- Message composers and chat interfaces
|
|
29
|
+
- Comment input fields
|
|
30
|
+
- Post/reaction creators
|
|
31
|
+
- Profile or group name editors
|
|
32
|
+
- Emoji reactions and filters
|
|
33
|
+
|
|
34
|
+
**When NOT to Use**:
|
|
35
|
+
- For simple emoji display - use text directly
|
|
36
|
+
- For custom emoji systems - build custom solution
|
|
37
|
+
- For limited emoji sets - use simple picker
|
|
38
|
+
|
|
39
|
+
## Rules
|
|
52
40
|
|
|
53
|
-
|
|
41
|
+
### Required
|
|
42
|
+
|
|
43
|
+
1. **ALWAYS** control open state with parent component
|
|
44
|
+
2. **MUST** provide `onClose` callback
|
|
45
|
+
3. **MUST** provide `onEmojiSelected` handler
|
|
46
|
+
4. **ALWAYS** close picker after selection (usually)
|
|
47
|
+
5. **MUST** provide trigger button for opening
|
|
48
|
+
|
|
49
|
+
### State Management
|
|
50
|
+
|
|
51
|
+
1. **MUST** manage open state in parent component
|
|
52
|
+
2. **SHOULD** reset state after selection
|
|
53
|
+
3. **ALWAYS** handle close callback properly
|
|
54
|
+
4. **NEVER** leave picker open indefinitely
|
|
55
|
+
|
|
56
|
+
### User Experience
|
|
57
|
+
|
|
58
|
+
1. **ALWAYS** provide clear trigger button
|
|
59
|
+
2. **SHOULD** show selected emoji in UI
|
|
60
|
+
3. **MUST** close picker after selection (usually)
|
|
61
|
+
4. **NEVER** open picker automatically without user action
|
|
62
|
+
|
|
63
|
+
## Forbidden
|
|
64
|
+
|
|
65
|
+
❌ **NEVER** do these:
|
|
54
66
|
|
|
55
67
|
```tsx
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
68
|
+
// ❌ Missing required props
|
|
69
|
+
<EmojiPicker />
|
|
70
|
+
|
|
71
|
+
// ❌ Not controlled
|
|
72
|
+
<EmojiPicker open={true} /> {/* Always open */}
|
|
73
|
+
|
|
74
|
+
// ❌ No close handler
|
|
75
|
+
<EmojiPicker
|
|
76
|
+
open={isOpen}
|
|
77
|
+
onEmojiSelected={handleSelect}
|
|
78
|
+
// Missing onClose ❌
|
|
79
|
+
/>
|
|
80
|
+
|
|
81
|
+
// ❌ Not handling selection
|
|
82
|
+
<EmojiPicker
|
|
83
|
+
open={isOpen}
|
|
84
|
+
onClose={() => setIsOpen(false)}
|
|
85
|
+
// Missing onEmojiSelected ❌
|
|
86
|
+
/>
|
|
87
|
+
|
|
88
|
+
// ❌ Auto-opening
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
setOpen(true); // ❌ Don't auto-open
|
|
91
|
+
}, []);
|
|
92
|
+
|
|
93
|
+
// ❌ Not showing selected emoji
|
|
94
|
+
const [emoji, setEmoji] = useState('');
|
|
95
|
+
<EmojiPicker
|
|
96
|
+
onEmojiSelected={(emojiObject) => {
|
|
97
|
+
// Not updating state ❌
|
|
98
|
+
}}
|
|
99
|
+
/>
|
|
78
100
|
```
|
|
79
101
|
|
|
80
|
-
##
|
|
102
|
+
## Best Practices
|
|
103
|
+
|
|
104
|
+
### State Management
|
|
81
105
|
|
|
106
|
+
✅ **DO**:
|
|
82
107
|
```tsx
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const [emojiPickerOpen, setEmojiPickerOpen] = useState(false);
|
|
86
|
-
|
|
87
|
-
const handleEmojiSelect = (emojiObject) => {
|
|
88
|
-
setMessage(message + emojiObject.emoji);
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
return (
|
|
92
|
-
<View>
|
|
93
|
-
<View style={{ flexDirection: 'row', alignItems: 'center', padding: 16 }}>
|
|
94
|
-
<TouchableOpacity onPress={() => setEmojiPickerOpen(true)}>
|
|
95
|
-
<AtomicIcon name="happy-outline" size="lg" />
|
|
96
|
-
</TouchableOpacity>
|
|
97
|
-
|
|
98
|
-
<TextInput
|
|
99
|
-
style={{ flex: 1, marginLeft: 8 }}
|
|
100
|
-
value={message}
|
|
101
|
-
onChangeText={setMessage}
|
|
102
|
-
placeholder="Mesaj yazın..."
|
|
103
|
-
/>
|
|
104
|
-
|
|
105
|
-
<Button
|
|
106
|
-
title="Gönder"
|
|
107
|
-
onPress={() => sendMessage(message)}
|
|
108
|
-
/>
|
|
109
|
-
</View>
|
|
110
|
-
|
|
111
|
-
<EmojiPicker
|
|
112
|
-
open={emojiPickerOpen}
|
|
113
|
-
onClose={() => setEmojiPickerOpen(false)}
|
|
114
|
-
onEmojiSelected={handleEmojiSelect}
|
|
115
|
-
/>
|
|
116
|
-
</View>
|
|
117
|
-
);
|
|
118
|
-
};
|
|
119
|
-
```
|
|
108
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
109
|
+
const [selectedEmoji, setSelectedEmoji] = useState('');
|
|
120
110
|
|
|
121
|
-
|
|
111
|
+
<EmojiPicker
|
|
112
|
+
open={isOpen}
|
|
113
|
+
onClose={() => setIsOpen(false)}
|
|
114
|
+
onEmojiSelected={(emoji) => {
|
|
115
|
+
setSelectedEmoji(emoji.emoji);
|
|
116
|
+
setIsOpen(false); // Close after selection
|
|
117
|
+
}}
|
|
118
|
+
/>
|
|
119
|
+
```
|
|
122
120
|
|
|
121
|
+
❌ **DON'T**:
|
|
123
122
|
```tsx
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return (
|
|
133
|
-
<View>
|
|
134
|
-
<View style={{ padding: 16 }}>
|
|
135
|
-
<View style={{ flexDirection: 'row', alignItems: 'flex-start' }}>
|
|
136
|
-
<Avatar
|
|
137
|
-
uri={currentUser.avatar}
|
|
138
|
-
name={currentUser.name}
|
|
139
|
-
size="sm"
|
|
140
|
-
/>
|
|
141
|
-
|
|
142
|
-
<View style={{ flex: 1, marginLeft: 12 }}>
|
|
143
|
-
<TextInput
|
|
144
|
-
style={{
|
|
145
|
-
borderWidth: 1,
|
|
146
|
-
borderColor: '#e0e0e0',
|
|
147
|
-
borderRadius: 8,
|
|
148
|
-
padding: 12,
|
|
149
|
-
minHeight: 80,
|
|
150
|
-
}}
|
|
151
|
-
value={comment}
|
|
152
|
-
onChangeText={setComment}
|
|
153
|
-
placeholder="Yorumunuzu yazın..."
|
|
154
|
-
multiline
|
|
155
|
-
/>
|
|
156
|
-
|
|
157
|
-
<View style={{ flexDirection: 'row', justifyContent: 'space-between', marginTop: 8 }}>
|
|
158
|
-
<TouchableOpacity onPress={() => setShowEmojiPicker(true)}>
|
|
159
|
-
<AtomicIcon name="happy-outline" size="md" />
|
|
160
|
-
</TouchableOpacity>
|
|
161
|
-
|
|
162
|
-
<Button
|
|
163
|
-
title="Yorum Yap"
|
|
164
|
-
onPress={() => postComment(comment)}
|
|
165
|
-
/>
|
|
166
|
-
</View>
|
|
167
|
-
</View>
|
|
168
|
-
</View>
|
|
169
|
-
</View>
|
|
170
|
-
|
|
171
|
-
<EmojiPicker
|
|
172
|
-
open={showEmojiPicker}
|
|
173
|
-
onClose={() => setShowEmojiPicker(false)}
|
|
174
|
-
onEmojiSelected={insertEmoji}
|
|
175
|
-
/>
|
|
176
|
-
</View>
|
|
177
|
-
);
|
|
178
|
-
};
|
|
123
|
+
// Don't leave open
|
|
124
|
+
<EmojiPicker
|
|
125
|
+
open={isOpen}
|
|
126
|
+
onEmojiSelected={(emoji) => {
|
|
127
|
+
setSelectedEmoji(emoji.emoji);
|
|
128
|
+
// Not closing ❌
|
|
129
|
+
}}
|
|
130
|
+
/>
|
|
179
131
|
```
|
|
180
132
|
|
|
181
|
-
|
|
133
|
+
### Trigger Button
|
|
182
134
|
|
|
135
|
+
✅ **DO**:
|
|
183
136
|
```tsx
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
137
|
+
<TouchableOpacity onPress={() => setIsOpen(true)}>
|
|
138
|
+
<AtomicIcon name="happy-outline" size="lg" />
|
|
139
|
+
</TouchableOpacity>
|
|
140
|
+
```
|
|
187
141
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
<View>
|
|
195
|
-
<View style={{ flexDirection: 'row', alignItems: 'center', padding: 16 }}>
|
|
196
|
-
<Avatar
|
|
197
|
-
uri={user.avatar}
|
|
198
|
-
name={user.name}
|
|
199
|
-
size="lg"
|
|
200
|
-
/>
|
|
201
|
-
|
|
202
|
-
<View style={{ flex: 1, marginLeft: 12 }}>
|
|
203
|
-
<AtomicText type="titleMedium">{user.name}</AtomicText>
|
|
204
|
-
<AtomicText type="bodyMedium" color="secondary">
|
|
205
|
-
{user.bio}
|
|
206
|
-
</AtomicText>
|
|
207
|
-
</View>
|
|
208
|
-
|
|
209
|
-
<TouchableOpacity onPress={() => setEmojiPickerOpen(true)}>
|
|
210
|
-
<AtomicIcon name="heart-outline" size="lg" />
|
|
211
|
-
</TouchableOpacity>
|
|
212
|
-
</View>
|
|
213
|
-
|
|
214
|
-
{reactions.length > 0 && (
|
|
215
|
-
<View style={{ flexDirection: 'row', flexWrap: 'wrap', paddingHorizontal: 16, paddingBottom: 16 }}>
|
|
216
|
-
{reactions.map((emoji, index) => (
|
|
217
|
-
<View
|
|
218
|
-
key={index}
|
|
219
|
-
style={{
|
|
220
|
-
backgroundColor: '#f0f0f0',
|
|
221
|
-
paddingHorizontal: 12,
|
|
222
|
-
paddingVertical: 6,
|
|
223
|
-
borderRadius: 16,
|
|
224
|
-
marginRight: 8,
|
|
225
|
-
marginBottom: 8,
|
|
226
|
-
}}
|
|
227
|
-
>
|
|
228
|
-
<AtomicText style={{ fontSize: 18 }}>{emoji}</AtomicText>
|
|
229
|
-
</View>
|
|
230
|
-
))}
|
|
231
|
-
</View>
|
|
232
|
-
)}
|
|
233
|
-
|
|
234
|
-
<EmojiPicker
|
|
235
|
-
open={emojiPickerOpen}
|
|
236
|
-
onClose={() => setEmojiPickerOpen(false)}
|
|
237
|
-
onEmojiSelected={addReaction}
|
|
238
|
-
/>
|
|
239
|
-
</View>
|
|
240
|
-
);
|
|
241
|
-
};
|
|
142
|
+
❌ **DON'T**:
|
|
143
|
+
```tsx
|
|
144
|
+
// Don't use unclear trigger
|
|
145
|
+
<TouchableOpacity onPress={() => setIsOpen(true)}>
|
|
146
|
+
<Text>Click here</Text> {/* ❌ Unclear */}
|
|
147
|
+
</TouchableOpacity>
|
|
242
148
|
```
|
|
243
149
|
|
|
244
|
-
|
|
150
|
+
### Inserting Emoji
|
|
245
151
|
|
|
152
|
+
✅ **DO**:
|
|
246
153
|
```tsx
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
const [emojiPickerOpen, setEmojiPickerOpen] = useState(false);
|
|
250
|
-
|
|
251
|
-
const handleEmojiSelect = (emojiObject) => {
|
|
252
|
-
setContent(content + emojiObject.emoji);
|
|
253
|
-
};
|
|
254
|
-
|
|
255
|
-
const createPost = () => {
|
|
256
|
-
if (content.trim()) {
|
|
257
|
-
publishPost(content);
|
|
258
|
-
setContent('');
|
|
259
|
-
}
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
return (
|
|
263
|
-
<AtomicCard variant="outlined" style={{ margin: 16 }}>
|
|
264
|
-
<View style={{ padding: 16 }}>
|
|
265
|
-
<View style={{ flexDirection: 'row', alignItems: 'flex-start' }}>
|
|
266
|
-
<Avatar
|
|
267
|
-
uri={currentUser.avatar}
|
|
268
|
-
name={currentUser.name}
|
|
269
|
-
size="md"
|
|
270
|
-
/>
|
|
271
|
-
|
|
272
|
-
<View style={{ flex: 1, marginLeft: 12 }}>
|
|
273
|
-
<TextInput
|
|
274
|
-
style={{ minHeight: 100 }}
|
|
275
|
-
value={content}
|
|
276
|
-
onChangeText={setContent}
|
|
277
|
-
placeholder="Neler oluyor?"
|
|
278
|
-
multiline
|
|
279
|
-
/>
|
|
280
|
-
|
|
281
|
-
<View style={{ flexDirection: 'row', justifyContent: 'space-between', marginTop: 12 }}>
|
|
282
|
-
<View style={{ flexDirection: 'row', gap: 16 }}>
|
|
283
|
-
<TouchableOpacity onPress={() => setEmojiPickerOpen(true)}>
|
|
284
|
-
<AtomicIcon name="happy-outline" size="md" color="primary" />
|
|
285
|
-
</TouchableOpacity>
|
|
286
|
-
<TouchableOpacity>
|
|
287
|
-
<AtomicIcon name="image-outline" size="md" color="primary" />
|
|
288
|
-
</TouchableOpacity>
|
|
289
|
-
</View>
|
|
290
|
-
|
|
291
|
-
<Button
|
|
292
|
-
title="Paylaş"
|
|
293
|
-
onPress={createPost}
|
|
294
|
-
disabled={!content.trim()}
|
|
295
|
-
/>
|
|
296
|
-
</View>
|
|
297
|
-
</View>
|
|
298
|
-
</View>
|
|
299
|
-
</View>
|
|
300
|
-
|
|
301
|
-
<EmojiPicker
|
|
302
|
-
open={emojiPickerOpen}
|
|
303
|
-
onClose={() => setEmojiPickerOpen(false)}
|
|
304
|
-
onEmojiSelected={handleEmojiSelect}
|
|
305
|
-
/>
|
|
306
|
-
</AtomicCard>
|
|
307
|
-
);
|
|
154
|
+
const insertEmoji = (emojiObject) => {
|
|
155
|
+
setMessage(message + emojiObject.emoji);
|
|
308
156
|
};
|
|
309
157
|
```
|
|
310
158
|
|
|
311
|
-
|
|
312
|
-
|
|
159
|
+
❌ **DON'T**:
|
|
313
160
|
```tsx
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
return (
|
|
319
|
-
<View style={{ padding: 16 }}>
|
|
320
|
-
<AtomicText type="titleMedium" style={{ marginBottom: 8 }}>
|
|
321
|
-
Grup İsmi
|
|
322
|
-
</AtomicText>
|
|
323
|
-
|
|
324
|
-
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
|
|
325
|
-
<TouchableOpacity
|
|
326
|
-
style={{
|
|
327
|
-
width: 60,
|
|
328
|
-
height: 60,
|
|
329
|
-
borderRadius: 30,
|
|
330
|
-
backgroundColor: '#f0f0f0',
|
|
331
|
-
justifyContent: 'center',
|
|
332
|
-
alignItems: 'center',
|
|
333
|
-
}}
|
|
334
|
-
onPress={() => setEmojiPickerOpen(true)}
|
|
335
|
-
>
|
|
336
|
-
<AtomicText style={{ fontSize: 32 }}>
|
|
337
|
-
{groupName.slice(0, 1) || '📝'}
|
|
338
|
-
</AtomicText>
|
|
339
|
-
</TouchableOpacity>
|
|
340
|
-
|
|
341
|
-
<TextInput
|
|
342
|
-
style={{ flex: 1, marginLeft: 12 }}
|
|
343
|
-
value={groupName}
|
|
344
|
-
onChangeText={setGroupName}
|
|
345
|
-
placeholder="Grup adı..."
|
|
346
|
-
/>
|
|
347
|
-
</View>
|
|
348
|
-
|
|
349
|
-
<EmojiPicker
|
|
350
|
-
open={emojiPickerOpen}
|
|
351
|
-
onClose={() => setEmojiPickerOpen(false)}
|
|
352
|
-
onEmojiSelected={(emoji) => {
|
|
353
|
-
setGroupName(emoji.emoji + ' ' + groupName);
|
|
354
|
-
setEmojiPickerOpen(false);
|
|
355
|
-
}}
|
|
356
|
-
/>
|
|
357
|
-
</View>
|
|
358
|
-
);
|
|
161
|
+
// Don't replace entire message
|
|
162
|
+
const insertEmoji = (emojiObject) => {
|
|
163
|
+
setMessage(emojiObject.emoji); // ❌ Lost previous text
|
|
359
164
|
};
|
|
360
165
|
```
|
|
361
166
|
|
|
362
|
-
##
|
|
363
|
-
|
|
167
|
+
## AI Coding Guidelines
|
|
168
|
+
|
|
169
|
+
### For AI Agents
|
|
170
|
+
|
|
171
|
+
When generating EmojiPicker components, follow these rules:
|
|
172
|
+
|
|
173
|
+
1. **Always import from correct path**:
|
|
174
|
+
```typescript
|
|
175
|
+
import { EmojiPicker } from 'react-native-design-system/src/molecules/emoji';
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
2. **Always manage state properly**:
|
|
179
|
+
```tsx
|
|
180
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
181
|
+
const [selectedEmoji, setSelectedEmoji] = useState('');
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
3. **Always handle all callbacks**:
|
|
185
|
+
```tsx
|
|
186
|
+
<EmojiPicker
|
|
187
|
+
open={isOpen}
|
|
188
|
+
onClose={() => setIsOpen(false)}
|
|
189
|
+
onEmojiSelected={(emoji) => {
|
|
190
|
+
setSelectedEmoji(emoji.emoji);
|
|
191
|
+
setIsOpen(false);
|
|
192
|
+
}}
|
|
193
|
+
/>
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
4. **Always provide clear trigger**:
|
|
197
|
+
```tsx
|
|
198
|
+
<TouchableOpacity onPress={() => setIsOpen(true)}>
|
|
199
|
+
<AtomicIcon name="happy-outline" />
|
|
200
|
+
</TouchableOpacity>
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
5. **Never leave picker open**:
|
|
204
|
+
```tsx
|
|
205
|
+
// ❌ Bad
|
|
206
|
+
onEmojiSelected={(emoji) => {
|
|
207
|
+
setSelectedEmoji(emoji.emoji);
|
|
208
|
+
// Not closing
|
|
209
|
+
}}
|
|
210
|
+
|
|
211
|
+
// ✅ Good
|
|
212
|
+
onEmojiSelected={(emoji) => {
|
|
213
|
+
setSelectedEmoji(emoji.emoji);
|
|
214
|
+
setIsOpen(false); // Always close
|
|
215
|
+
}}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Common Patterns
|
|
219
|
+
|
|
220
|
+
#### Message Composer
|
|
364
221
|
```tsx
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
const [emojiPickerOpen, setEmojiPickerOpen] = useState(false);
|
|
368
|
-
|
|
369
|
-
const addReaction = (emojiObject) => {
|
|
370
|
-
const existing = reactions.find(r => r.emoji === emojiObject.emoji);
|
|
371
|
-
|
|
372
|
-
if (existing) {
|
|
373
|
-
// Remove reaction if already exists
|
|
374
|
-
setReactions(reactions.filter(r => r.emoji !== emojiObject.emoji));
|
|
375
|
-
} else {
|
|
376
|
-
// Add new reaction
|
|
377
|
-
setReactions([...reactions, {
|
|
378
|
-
emoji: emojiObject.emoji,
|
|
379
|
-
users: [currentUser.id],
|
|
380
|
-
count: 1,
|
|
381
|
-
}]);
|
|
382
|
-
}
|
|
222
|
+
const [message, setMessage] = useState('');
|
|
223
|
+
const [emojiPickerOpen, setEmojiPickerOpen] = useState(false);
|
|
383
224
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
borderColor: tokens.colors.primary,
|
|
404
|
-
}}
|
|
405
|
-
onPress={() => addReaction({ emoji: reaction.emoji })}
|
|
406
|
-
>
|
|
407
|
-
<AtomicText style={{ fontSize: 16, marginRight: 4 }}>
|
|
408
|
-
{reaction.emoji}
|
|
409
|
-
</AtomicText>
|
|
410
|
-
<AtomicText type="labelSmall">
|
|
411
|
-
{reaction.count}
|
|
412
|
-
</AtomicText>
|
|
413
|
-
</TouchableOpacity>
|
|
414
|
-
))}
|
|
415
|
-
|
|
416
|
-
<TouchableOpacity
|
|
417
|
-
style={{
|
|
418
|
-
flexDirection: 'row',
|
|
419
|
-
alignItems: 'center',
|
|
420
|
-
backgroundColor: '#f0f0f0',
|
|
421
|
-
paddingHorizontal: 8,
|
|
422
|
-
paddingVertical: 4,
|
|
423
|
-
borderRadius: 16,
|
|
424
|
-
}}
|
|
425
|
-
onPress={() => setEmojiPickerOpen(true)}
|
|
426
|
-
>
|
|
427
|
-
<AtomicIcon name="add-outline" size="sm" />
|
|
428
|
-
</TouchableOpacity>
|
|
429
|
-
</View>
|
|
430
|
-
|
|
431
|
-
<EmojiPicker
|
|
432
|
-
open={emojiPickerOpen}
|
|
433
|
-
onClose={() => setEmojiPickerOpen(false)}
|
|
434
|
-
onEmojiSelected={addReaction}
|
|
435
|
-
/>
|
|
436
|
-
</View>
|
|
437
|
-
);
|
|
438
|
-
};
|
|
225
|
+
<View>
|
|
226
|
+
<TextInput
|
|
227
|
+
value={message}
|
|
228
|
+
onChangeText={setMessage}
|
|
229
|
+
placeholder="Type a message..."
|
|
230
|
+
/>
|
|
231
|
+
|
|
232
|
+
<TouchableOpacity onPress={() => setEmojiPickerOpen(true)}>
|
|
233
|
+
<AtomicIcon name="happy-outline" />
|
|
234
|
+
</TouchableOpacity>
|
|
235
|
+
|
|
236
|
+
<EmojiPicker
|
|
237
|
+
open={emojiPickerOpen}
|
|
238
|
+
onClose={() => setEmojiPickerOpen(false)}
|
|
239
|
+
onEmojiSelected={(emoji) => {
|
|
240
|
+
setMessage(message + emoji.emoji);
|
|
241
|
+
}}
|
|
242
|
+
/>
|
|
243
|
+
</View>
|
|
439
244
|
```
|
|
440
245
|
|
|
441
|
-
|
|
246
|
+
#### Comment Input
|
|
247
|
+
```tsx
|
|
248
|
+
const [comment, setComment] = useState('');
|
|
249
|
+
const [showEmojiPicker, setShowEmojiPicker] = useState(false);
|
|
250
|
+
|
|
251
|
+
<View>
|
|
252
|
+
<TextInput
|
|
253
|
+
value={comment}
|
|
254
|
+
onChangeText={setComment}
|
|
255
|
+
placeholder="Add a comment..."
|
|
256
|
+
multiline
|
|
257
|
+
/>
|
|
258
|
+
|
|
259
|
+
<TouchableOpacity onPress={() => setShowEmojiPicker(true)}>
|
|
260
|
+
<AtomicIcon name="happy-outline" />
|
|
261
|
+
</TouchableOpacity>
|
|
262
|
+
|
|
263
|
+
<EmojiPicker
|
|
264
|
+
open={showEmojiPicker}
|
|
265
|
+
onClose={() => setShowEmojiPicker(false)}
|
|
266
|
+
onEmojiSelected={(emoji) => {
|
|
267
|
+
setComment(comment + emoji.emoji);
|
|
268
|
+
}}
|
|
269
|
+
/>
|
|
270
|
+
</View>
|
|
271
|
+
```
|
|
442
272
|
|
|
273
|
+
#### Emoji Selector
|
|
443
274
|
```tsx
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
const [emojiPickerOpen, setEmojiPickerOpen] = useState(false);
|
|
447
|
-
const [filteredContent, setFilteredContent] = useState([]);
|
|
275
|
+
const [selectedEmoji, setSelectedEmoji] = useState('');
|
|
276
|
+
const [emojiPickerOpen, setEmojiPickerOpen] = useState(false);
|
|
448
277
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
278
|
+
<Button
|
|
279
|
+
title={selectedEmoji || 'Select Emoji'}
|
|
280
|
+
onPress={() => setEmojiPickerOpen(true)}
|
|
281
|
+
/>
|
|
452
282
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
<View>
|
|
462
|
-
<View style={{ flexDirection: 'row', alignItems: 'center', padding: 16, gap: 8 }}>
|
|
463
|
-
<TouchableOpacity onPress={() => {
|
|
464
|
-
setSelectedEmoji('');
|
|
465
|
-
setFilteredContent(content);
|
|
466
|
-
}}>
|
|
467
|
-
<AtomicText type="labelLarge">Tümü</AtomicText>
|
|
468
|
-
</TouchableOpacity>
|
|
469
|
-
|
|
470
|
-
<TouchableOpacity
|
|
471
|
-
style={{
|
|
472
|
-
paddingHorizontal: 12,
|
|
473
|
-
paddingVertical: 6,
|
|
474
|
-
backgroundColor: selectedEmoji ? `${tokens.colors.primary}20` : '#f0f0f0',
|
|
475
|
-
borderRadius: 16,
|
|
476
|
-
}}
|
|
477
|
-
onPress={() => setEmojiPickerOpen(true)}
|
|
478
|
-
>
|
|
479
|
-
<AtomicText style={{ fontSize: 18 }}>
|
|
480
|
-
{selectedEmoji || '😀'}
|
|
481
|
-
</AtomicText>
|
|
482
|
-
</TouchableOpacity>
|
|
483
|
-
</View>
|
|
484
|
-
|
|
485
|
-
<EmojiPicker
|
|
486
|
-
open={emojiPickerOpen}
|
|
487
|
-
onClose={() => setEmojiPickerOpen(false)}
|
|
488
|
-
onEmojiSelected={handleEmojiSelect}
|
|
489
|
-
/>
|
|
490
|
-
</View>
|
|
491
|
-
);
|
|
492
|
-
};
|
|
283
|
+
<EmojiPicker
|
|
284
|
+
open={emojiPickerOpen}
|
|
285
|
+
onClose={() => setEmojiPickerOpen(false)}
|
|
286
|
+
onEmojiSelected={(emoji) => {
|
|
287
|
+
setSelectedEmoji(emoji.emoji);
|
|
288
|
+
setEmojiPickerOpen(false);
|
|
289
|
+
}}
|
|
290
|
+
/>
|
|
493
291
|
```
|
|
494
292
|
|
|
495
|
-
## Props
|
|
496
|
-
|
|
497
|
-
### EmojiPickerProps
|
|
293
|
+
## Props Reference
|
|
498
294
|
|
|
499
|
-
| Prop |
|
|
500
|
-
|
|
501
|
-
| `open` | `boolean` | -
|
|
502
|
-
| `onClose` | `() => void` | -
|
|
503
|
-
| `onEmojiSelected` | `(emoji) => void` | -
|
|
504
|
-
| `config` | `EmojiPickerConfig` | `{}` |
|
|
295
|
+
| Prop | Type | Required | Default | Description |
|
|
296
|
+
|------|------|----------|---------|-------------|
|
|
297
|
+
| `open` | `boolean` | Yes | - | Picker open state |
|
|
298
|
+
| `onClose` | `() => void` | Yes | - | Close callback |
|
|
299
|
+
| `onEmojiSelected` | `(emoji) => void` | Yes | - | Emoji selected callback |
|
|
300
|
+
| `config` | `EmojiPickerConfig` | No | `{}` | Picker configuration |
|
|
505
301
|
|
|
506
302
|
### EmojiPickerConfig
|
|
507
303
|
|
|
508
|
-
| Prop |
|
|
509
|
-
|
|
510
|
-
| `enableRecentlyUsed` | `boolean` | `true` |
|
|
511
|
-
| `enableSearch` | `boolean` | `true` |
|
|
512
|
-
| `enableCategoryTabs` | `boolean` | `true` |
|
|
513
|
-
| `categoryOrder` | `string[]` | - |
|
|
514
|
-
| `translation` | `object` | - |
|
|
304
|
+
| Prop | Type | Default | Description |
|
|
305
|
+
|------|------|---------|-------------|
|
|
306
|
+
| `enableRecentlyUsed` | `boolean` | `true` | Show recently used |
|
|
307
|
+
| `enableSearch` | `boolean` | `true` | Enable search |
|
|
308
|
+
| `enableCategoryTabs` | `boolean` | `true` | Show categories |
|
|
309
|
+
| `categoryOrder` | `string[]` | - | Category order |
|
|
310
|
+
| `translation` | `object` | - | Localization |
|
|
515
311
|
|
|
516
312
|
### EmojiObject
|
|
517
313
|
|
|
518
|
-
| Prop |
|
|
519
|
-
|
|
520
|
-
| `emoji` | `string` | Emoji
|
|
521
|
-
| `name` | `string` | Emoji
|
|
314
|
+
| Prop | Type | Description |
|
|
315
|
+
|------|------|-------------|
|
|
316
|
+
| `emoji` | `string` | Emoji character |
|
|
317
|
+
| `name` | `string` | Emoji name |
|
|
522
318
|
| `slug` | `string` | URL slug |
|
|
523
|
-
| `unicode_version` | `string` | Unicode
|
|
524
|
-
|
|
525
|
-
## Best Practices
|
|
526
|
-
|
|
527
|
-
### 1. Kullanıcı Deneyimi
|
|
528
|
-
|
|
529
|
-
```tsx
|
|
530
|
-
// ✅ İyi - Hemen kapanır
|
|
531
|
-
onEmojiSelected={(emoji) => {
|
|
532
|
-
onEmojiSelect(emoji);
|
|
533
|
-
onClose(); // Hemen kapat
|
|
534
|
-
}}
|
|
535
|
-
```
|
|
536
|
-
|
|
537
|
-
### 2. State Yönetimi
|
|
538
|
-
|
|
539
|
-
```tsx
|
|
540
|
-
// ✅ İyi - Ayrı state
|
|
541
|
-
const [emojiPickerOpen, setEmojiPickerOpen] = useState(false);
|
|
542
|
-
const [selectedEmoji, setSelectedEmoji] = useState('');
|
|
543
|
-
```
|
|
544
|
-
|
|
545
|
-
### 3. Trigger Butonu
|
|
546
|
-
|
|
547
|
-
```tsx
|
|
548
|
-
// ✅ İyi - İkonlu buton
|
|
549
|
-
<TouchableOpacity onPress={() => setEmojiPickerOpen(true)}>
|
|
550
|
-
<AtomicIcon name="happy-outline" size="lg" />
|
|
551
|
-
</TouchableOpacity>
|
|
552
|
-
```
|
|
553
|
-
|
|
554
|
-
## Erişilebilirlik
|
|
319
|
+
| `unicode_version` | `string` | Unicode version |
|
|
555
320
|
|
|
556
|
-
|
|
321
|
+
## Accessibility
|
|
557
322
|
|
|
558
|
-
- ✅ Screen reader
|
|
559
|
-
- ✅ Touch
|
|
560
|
-
- ✅ Semantic
|
|
323
|
+
- ✅ Screen reader support
|
|
324
|
+
- ✅ Touch target size: minimum 44x44pt
|
|
325
|
+
- ✅ Semantic emojis announced
|
|
326
|
+
- ✅ Keyboard navigation support
|
|
561
327
|
|
|
562
|
-
##
|
|
328
|
+
## Performance
|
|
563
329
|
|
|
564
|
-
1. **Lazy
|
|
565
|
-
2. **Cache**:
|
|
566
|
-
3. **Unmount**:
|
|
330
|
+
1. **Lazy loading**: Load picker on demand
|
|
331
|
+
2. **Cache**: Cache recently used emojis
|
|
332
|
+
3. **Unmount**: Unmount when closed
|
|
333
|
+
4. **Optimization**: Debounce search input
|
|
567
334
|
|
|
568
|
-
##
|
|
335
|
+
## Related Components
|
|
569
336
|
|
|
570
|
-
- [`AtomicIcon`](../../atoms/AtomicIcon/README.md) -
|
|
571
|
-
- [`FormField`](../FormField/README.md) - Form
|
|
572
|
-
- [`BaseModal`](../BaseModal/README.md) - Modal
|
|
337
|
+
- [`AtomicIcon`](../../atoms/AtomicIcon/README.md) - Icon component
|
|
338
|
+
- [`FormField`](../FormField/README.md) - Form field component
|
|
339
|
+
- [`BaseModal`](../BaseModal/README.md) - Modal component
|
|
573
340
|
|
|
574
|
-
##
|
|
341
|
+
## Dependencies
|
|
575
342
|
|
|
576
343
|
- `rn-emoji-keyboard` - Emoji picker UI library
|
|
577
344
|
|
|
578
|
-
##
|
|
345
|
+
## License
|
|
579
346
|
|
|
580
347
|
MIT
|