emoji-picker-react 4.20.0 → 4.20.2
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/README.md +55 -230
- package/dist/Stylesheet/stylesheet.d.ts +16 -4
- package/dist/dataUtils/emojiSelectors.d.ts +0 -2
- package/dist/emoji-picker-react.cjs.development.js +96 -114
- package/dist/emoji-picker-react.cjs.development.js.map +1 -1
- package/dist/emoji-picker-react.cjs.production.min.js +1 -1
- package/dist/emoji-picker-react.cjs.production.min.js.map +1 -1
- package/dist/emoji-picker-react.esm.js +97 -115
- package/dist/emoji-picker-react.esm.js.map +1 -1
- package/dist/hooks/useIsomorphicLayoutEffect.d.ts +2 -0
- package/llms.txt +425 -0
- package/package.json +11 -8
- package/src/components/body/EmojiList.tsx +15 -16
- package/src/components/body/EmojiVariationPicker.tsx +1 -1
- package/src/components/body/MeasureEmoji.tsx +4 -5
- package/src/components/context/PickerDataContext.tsx +9 -1
- package/src/components/emoji/Emoji.tsx +1 -1
- package/src/components/footer/Preview.tsx +1 -1
- package/src/dataUtils/emojiSelectors.ts +4 -31
- package/src/dataUtils/suggested.ts +7 -4
- package/src/hooks/useDebouncedState.ts +2 -2
- package/src/hooks/useIsomorphicLayoutEffect.ts +6 -0
- package/src/hooks/useMouseDownHandlers.ts +3 -3
- package/src/hooks/useOnFocus.ts +1 -1
package/llms.txt
ADDED
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
# emoji-picker-react — LLM context
|
|
2
|
+
|
|
3
|
+
> This file is auto-generated by `npm run docs:llms`. Do not edit by hand.
|
|
4
|
+
> Sources: README.md, PROPS.md, CUSTOMIZATION.md, INTERNATIONALIZATION.md, CSS_VARIABLES.md
|
|
5
|
+
|
|
6
|
+
<!-- source: README.md -->
|
|
7
|
+
|
|
8
|
+
# Emoji Picker React
|
|
9
|
+
|
|
10
|
+
The most popular fully customizable emoji picker for React.
|
|
11
|
+
|
|
12
|
+
[](https://www.npmjs.com/package/emoji-picker-react)
|
|
13
|
+
|
|
14
|
+
**[Live Demo](https://ealush.com/emoji-picker-react)** | **[Report a Bug](https://github.com/ealush/emoji-picker-react/issues)**
|
|
15
|
+
|
|
16
|
+

|
|
17
|
+
|
|
18
|
+

|
|
19
|
+
|
|
20
|
+
## Features
|
|
21
|
+
|
|
22
|
+
- Fully customizable through props and CSS variables
|
|
23
|
+
- Light, dark, and auto themes
|
|
24
|
+
- Reactions picker mode and custom click handlers
|
|
25
|
+
- Dozens of built-in languages
|
|
26
|
+
- Custom image-based emojis
|
|
27
|
+
- Apple, Google, Facebook, Twitter, and native emoji styles
|
|
28
|
+
- Responsive and mobile-friendly
|
|
29
|
+
- SSR-safe
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install emoji-picker-react
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
```jsx
|
|
40
|
+
import EmojiPicker from 'emoji-picker-react';
|
|
41
|
+
|
|
42
|
+
function App() {
|
|
43
|
+
return (
|
|
44
|
+
<EmojiPicker onEmojiClick={(emojiData) => console.log(emojiData.emoji)} />
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`onEmojiClick` receives an `EmojiClickData` object (unified code, names, image URL, active skin tone) and the underlying mouse event.
|
|
50
|
+
|
|
51
|
+
## Configuration
|
|
52
|
+
|
|
53
|
+
```jsx
|
|
54
|
+
<EmojiPicker
|
|
55
|
+
theme="dark"
|
|
56
|
+
emojiStyle="native"
|
|
57
|
+
width={320}
|
|
58
|
+
height={400}
|
|
59
|
+
previewConfig={{ showPreview: false }}
|
|
60
|
+
/>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
See [PROPS.md](PROPS.md) for the complete props reference.
|
|
64
|
+
|
|
65
|
+
## Styling
|
|
66
|
+
|
|
67
|
+
No stylesheet import needed. All styles are scoped via [Flairup](https://github.com/ealush/flairup) — generated class names are hashed, so the picker's CSS won't leak into or clash with your app's styles.
|
|
68
|
+
|
|
69
|
+
Restyle the picker by overriding [CSS variables](CSS_VARIABLES.md) on `.EmojiPickerReact`:
|
|
70
|
+
|
|
71
|
+
```css
|
|
72
|
+
.EmojiPickerReact {
|
|
73
|
+
--epr-emoji-size: 32px;
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Internationalization
|
|
78
|
+
|
|
79
|
+
Pass imported locale data via the `emojiData` prop:
|
|
80
|
+
|
|
81
|
+
```jsx
|
|
82
|
+
import EmojiPicker from 'emoji-picker-react';
|
|
83
|
+
import es from 'emoji-picker-react/dist/data/emojis-es'; // Spanish
|
|
84
|
+
|
|
85
|
+
function App() {
|
|
86
|
+
return <EmojiPicker emojiData={es} />;
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
See [INTERNATIONALIZATION.md](INTERNATIONALIZATION.md) for the supported languages.
|
|
91
|
+
|
|
92
|
+
## Customization
|
|
93
|
+
|
|
94
|
+
Custom emojis, custom category icons, preview configuration, and CSP nonces are covered in [CUSTOMIZATION.md](CUSTOMIZATION.md).
|
|
95
|
+
|
|
96
|
+
## Server-Side Rendering
|
|
97
|
+
|
|
98
|
+
The picker renders on the server, with styles inlined into the server HTML — no setup needed. Since the picker is usually opened on demand rather than shown immediately, lazy-loading it is still recommended to keep the initial bundle small:
|
|
99
|
+
|
|
100
|
+
```javascript
|
|
101
|
+
import dynamic from 'next/dynamic';
|
|
102
|
+
|
|
103
|
+
const Picker = dynamic(() => import('emoji-picker-react'));
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Troubleshooting
|
|
107
|
+
|
|
108
|
+
### `global is not defined` (Vite, versions before 4.20)
|
|
109
|
+
|
|
110
|
+
Since 4.20 the picker is SSR-safe and no longer references the Node-style
|
|
111
|
+
`global`. If you see `global is not defined`, upgrade to the latest version.
|
|
112
|
+
On older versions only, the workaround was adding this to your HTML:
|
|
113
|
+
|
|
114
|
+
```html
|
|
115
|
+
<script>
|
|
116
|
+
window.global = window;
|
|
117
|
+
</script>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## More from the maintainer
|
|
121
|
+
|
|
122
|
+
Building complex forms? Check out [**Vest**](https://vestjs.dev) — a validation framework for stateful, async, and dependent validation.
|
|
123
|
+
|
|
124
|
+
## Contributing
|
|
125
|
+
|
|
126
|
+
Contributions are welcome — see the [Contributing Guide](https://github.com/ealush/emoji-picker-react/blob/master/CONTRIBUTING.md).
|
|
127
|
+
|
|
128
|
+
Design inspiration by [Pavel Bolo](https://pavelbolo.com).
|
|
129
|
+
|
|
130
|
+
<!-- source: PROPS.md -->
|
|
131
|
+
|
|
132
|
+
# Props Reference
|
|
133
|
+
|
|
134
|
+
Complete list of all props accepted by `EmojiPicker`. All props are optional.
|
|
135
|
+
|
|
136
|
+
## General Configuration
|
|
137
|
+
|
|
138
|
+
| Prop | Type | Default | Description |
|
|
139
|
+
| ----------------- | ------------ | ------------------ | -------------------------------------------------------------------------------------------- |
|
|
140
|
+
| `open` | `boolean` | `true` | Controls the visibility of the picker. |
|
|
141
|
+
| `theme` | `Theme` | `Theme.LIGHT` | The visual theme. Options: `'light'`, `'dark'`, `'auto'`. |
|
|
142
|
+
| `emojiStyle` | `EmojiStyle` | `EmojiStyle.APPLE` | The emoji set to use. Options: `'apple'`, `'google'`, `'facebook'`, `'twitter'`, `'native'`. |
|
|
143
|
+
| `emojiVersion` | `string` | `null` | Limit emojis to a specific unicode version (e.g., `"14.0"`). |
|
|
144
|
+
| `lazyLoadEmojis` | `boolean` | `false` | If true, emoji images are loaded only when they scroll into view. |
|
|
145
|
+
| `autoFocusSearch` | `boolean` | `true` | Focuses the search input automatically when the picker mounts. |
|
|
146
|
+
| `emojiData` | `object` | `undefined` | Pass imported locale data here for internationalization. See [INTERNATIONALIZATION.md](INTERNATIONALIZATION.md). |
|
|
147
|
+
|
|
148
|
+
## Dimensions & Styling
|
|
149
|
+
|
|
150
|
+
| Prop | Type | Default | Description |
|
|
151
|
+
| ----------- | -------------------- | ------- | ----------------------------------------------------- |
|
|
152
|
+
| `width` | `string \| number` | `350` | Picker width. Numbers are treated as pixels. |
|
|
153
|
+
| `height` | `string \| number` | `450` | Picker height. Numbers are treated as pixels. |
|
|
154
|
+
| `style` | `CSSProperties` | `{}` | Inline styles applied to the root element. |
|
|
155
|
+
| `className` | `string` | `""` | CSS class applied to the root element. |
|
|
156
|
+
|
|
157
|
+
Visual styling beyond size is done via [CSS variables](CSS_VARIABLES.md).
|
|
158
|
+
|
|
159
|
+
## Events & Interaction
|
|
160
|
+
|
|
161
|
+
| Prop | Type | Description |
|
|
162
|
+
| ------------------ | -------------------------------------------------------- | -------------------------------------------------------------------- |
|
|
163
|
+
| `onEmojiClick` | `(emojiData: EmojiClickData, event: MouseEvent) => void` | Callback triggered when a user clicks an emoji. |
|
|
164
|
+
| `onReactionClick` | `(emojiData: EmojiClickData, event: MouseEvent) => void` | Callback triggered when a user clicks a reaction (in reaction mode). |
|
|
165
|
+
| `onSkinToneChange` | `(skinTone: SkinTones) => void` | Callback triggered when the user selects a new skin tone. |
|
|
166
|
+
|
|
167
|
+
## Search & Categories
|
|
168
|
+
|
|
169
|
+
| Prop | Type | Default | Description |
|
|
170
|
+
| ------------------------ | ------------------------ | ------------------------- | -------------------------------------------------------------------- |
|
|
171
|
+
| `searchDisabled` | `boolean` | `false` | If true, the search bar is completely removed. |
|
|
172
|
+
| `searchPlaceholder` | `string` | `"Search"` | Placeholder text for the search input. |
|
|
173
|
+
| `searchClearButtonLabel` | `string` | `"Clear"` | Aria label for the search clear button. |
|
|
174
|
+
| `categories` | `CategoryConfig[]` | _(All)_ | Array of category objects to customize order or visibility. |
|
|
175
|
+
| `suggestedEmojisMode` | `SuggestionMode` | `SuggestionMode.FREQUENT` | Logic for "Suggested" category. Options: `'recent'`, `'frequent'`. |
|
|
176
|
+
| `defaultSkinTone` | `SkinTones` | `SkinTones.NEUTRAL` | The initial skin tone. |
|
|
177
|
+
| `skinTonesDisabled` | `boolean` | `false` | If true, users cannot change the skin tone. |
|
|
178
|
+
| `skinTonePickerLocation` | `SkinTonePickerLocation` | `SEARCH` | Location of the skin tone trigger. Options: `'SEARCH'`, `'PREVIEW'`. |
|
|
179
|
+
|
|
180
|
+
## Customization & Advanced
|
|
181
|
+
|
|
182
|
+
| Prop | Type | Default | Description |
|
|
183
|
+
| --------------- | ------------------------------------------------ | ----------------------- | ------------------------------------------------------------------------ |
|
|
184
|
+
| `customEmojis` | `CustomEmoji[]` | `[]` | Array of custom image-based emojis to inject. See [CUSTOMIZATION.md](CUSTOMIZATION.md). |
|
|
185
|
+
| `hiddenEmojis` | `string[]` | `[]` | Array of unified IDs (e.g., `'1f921'`) to hide from the picker. |
|
|
186
|
+
| `previewConfig` | `PreviewConfig` | `{ showPreview: true }` | Configuration for the bottom preview bar. See [CUSTOMIZATION.md](CUSTOMIZATION.md). |
|
|
187
|
+
| `getEmojiUrl` | `(unified: string, style: EmojiStyle) => string` | - | Function to override the default CDN URL for emoji images. |
|
|
188
|
+
| `categoryIcons` | `CategoryIcons` | `{}` | Map `Categories` enum values to custom React nodes for navigation icons. See [CUSTOMIZATION.md](CUSTOMIZATION.md). |
|
|
189
|
+
| `nonce` | `string` | `undefined` | Content Security Policy (CSP) nonce for the inline style tag. See [CUSTOMIZATION.md](CUSTOMIZATION.md). |
|
|
190
|
+
|
|
191
|
+
## Reactions Picker Mode
|
|
192
|
+
|
|
193
|
+
| Prop | Type | Default | Description |
|
|
194
|
+
| ---------------------- | ---------- | --------------- | ------------------------------------------------------------------------ |
|
|
195
|
+
| `reactionsDefaultOpen` | `boolean` | `false` | If true, mounts in "Reactions" mode (single row) instead of full picker. |
|
|
196
|
+
| `reactions` | `string[]` | _(Default Set)_ | Array of unified IDs to display in the reactions bar. |
|
|
197
|
+
| `allowExpandReactions` | `boolean` | `true` | If true, shows a `+` button to switch from reactions to full picker. |
|
|
198
|
+
|
|
199
|
+
<!-- source: CUSTOMIZATION.md -->
|
|
200
|
+
|
|
201
|
+
# Customization
|
|
202
|
+
|
|
203
|
+
## Custom Emojis
|
|
204
|
+
|
|
205
|
+
Pass the `customEmojis` prop to inject image-based emojis. Each entry uses this structure:
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
{
|
|
209
|
+
id: string; // Unique ID
|
|
210
|
+
names: string[]; // Search keywords
|
|
211
|
+
imgUrl: string; // Image source
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
```jsx
|
|
216
|
+
<EmojiPicker
|
|
217
|
+
customEmojis={[
|
|
218
|
+
{
|
|
219
|
+
id: 'panda',
|
|
220
|
+
names: ['panda', 'bear'],
|
|
221
|
+
imgUrl: 'https://example.com/panda.png',
|
|
222
|
+
},
|
|
223
|
+
]}
|
|
224
|
+
/>
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## Preview Bar
|
|
228
|
+
|
|
229
|
+
Control the footer preview area with `previewConfig`:
|
|
230
|
+
|
|
231
|
+
```ts
|
|
232
|
+
{
|
|
233
|
+
defaultEmoji: string; // Default: "1f60a"
|
|
234
|
+
defaultCaption: string; // Default: "What's your mood?"
|
|
235
|
+
showPreview: boolean; // Default: true
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Custom Category Icons
|
|
240
|
+
|
|
241
|
+
Customize the navigation icons using one of two methods.
|
|
242
|
+
|
|
243
|
+
**Method 1: The `categoryIcons` prop**
|
|
244
|
+
|
|
245
|
+
Map `Categories` enum values to React nodes:
|
|
246
|
+
|
|
247
|
+
```tsx
|
|
248
|
+
import EmojiPicker, { Categories } from 'emoji-picker-react';
|
|
249
|
+
|
|
250
|
+
<EmojiPicker
|
|
251
|
+
categoryIcons={{
|
|
252
|
+
[Categories.SUGGESTED]: <img src="recent.png" alt="Recent" />,
|
|
253
|
+
[Categories.SMILEYS_PEOPLE]: <MyCustomFaceIcon />,
|
|
254
|
+
}}
|
|
255
|
+
/>;
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
**Method 2: The `categories` configuration array**
|
|
259
|
+
|
|
260
|
+
Define the icon directly within the category configuration object:
|
|
261
|
+
|
|
262
|
+
```tsx
|
|
263
|
+
import EmojiPicker, { Categories } from 'emoji-picker-react';
|
|
264
|
+
|
|
265
|
+
<EmojiPicker
|
|
266
|
+
categories={[
|
|
267
|
+
{
|
|
268
|
+
category: Categories.SUGGESTED,
|
|
269
|
+
name: 'Recently Used',
|
|
270
|
+
icon: <img src="recent.png" alt="Recent" />,
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
category: Categories.SMILEYS_PEOPLE,
|
|
274
|
+
name: 'Smileys & People',
|
|
275
|
+
icon: <MyCustomFaceIcon />,
|
|
276
|
+
},
|
|
277
|
+
]}
|
|
278
|
+
/>;
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
Note: if both methods are used for the same category, the icon from the `categories` configuration takes precedence over the `categoryIcons` prop.
|
|
282
|
+
|
|
283
|
+
## Content Security Policy (CSP)
|
|
284
|
+
|
|
285
|
+
If your site has a CSP that blocks inline styles, pass a `nonce` to the `EmojiPicker` component. It is applied to the inline `<style>` tag:
|
|
286
|
+
|
|
287
|
+
```jsx
|
|
288
|
+
<EmojiPicker nonce="your-nonce-value" />
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
<!-- source: INTERNATIONALIZATION.md -->
|
|
292
|
+
|
|
293
|
+
# Internationalization (i18n)
|
|
294
|
+
|
|
295
|
+
Import the dictionary you need and pass it to the `emojiData` prop:
|
|
296
|
+
|
|
297
|
+
```javascript
|
|
298
|
+
import EmojiPicker from 'emoji-picker-react';
|
|
299
|
+
import es from 'emoji-picker-react/dist/data/emojis-es'; // Spanish
|
|
300
|
+
|
|
301
|
+
function App() {
|
|
302
|
+
return <EmojiPicker emojiData={es} />;
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
## Supported Languages
|
|
307
|
+
|
|
308
|
+
- `emojis-bn` (Bengali)
|
|
309
|
+
- `emojis-da` (Danish)
|
|
310
|
+
- `emojis-de` (German)
|
|
311
|
+
- `emojis-en-gb` (English, GB)
|
|
312
|
+
- `emojis-en` (English, US)
|
|
313
|
+
- `emojis-es-mx` (Spanish, Mexico)
|
|
314
|
+
- `emojis-es` (Spanish)
|
|
315
|
+
- `emojis-et` (Estonian)
|
|
316
|
+
- `emojis-fi` (Finnish)
|
|
317
|
+
- `emojis-fr` (French)
|
|
318
|
+
- `emojis-hi` (Hindi)
|
|
319
|
+
- `emojis-hu` (Hungarian)
|
|
320
|
+
- `emojis-it` (Italian)
|
|
321
|
+
- `emojis-ja` (Japanese)
|
|
322
|
+
- `emojis-ko` (Korean)
|
|
323
|
+
- `emojis-lt` (Lithuanian)
|
|
324
|
+
- `emojis-ms` (Malay)
|
|
325
|
+
- `emojis-nb` (Norwegian Bokmal)
|
|
326
|
+
- `emojis-nl` (Dutch)
|
|
327
|
+
- `emojis-pl` (Polish)
|
|
328
|
+
- `emojis-pt` (Portuguese)
|
|
329
|
+
- `emojis-ru` (Russian)
|
|
330
|
+
- `emojis-sv` (Swedish)
|
|
331
|
+
- `emojis-th` (Thai)
|
|
332
|
+
- `emojis-uk` (Ukrainian)
|
|
333
|
+
- `emojis-vi` (Vietnamese)
|
|
334
|
+
- `emojis-zh-hant` (Traditional Chinese)
|
|
335
|
+
- `emojis-zh` (Simplified Chinese)
|
|
336
|
+
|
|
337
|
+
<!-- source: CSS_VARIABLES.md -->
|
|
338
|
+
|
|
339
|
+
# 🎨 CSS Variables
|
|
340
|
+
|
|
341
|
+
You can customize specific parts of the picker by overriding these CSS variables. Target `.EmojiPickerReact` or `aside.EmojiPickerReact` to apply them.
|
|
342
|
+
|
|
343
|
+
```css
|
|
344
|
+
.EmojiPickerReact {
|
|
345
|
+
--epr-emoji-size: 32px;
|
|
346
|
+
}
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
or
|
|
350
|
+
|
|
351
|
+
```css
|
|
352
|
+
aside.EmojiPickerReact {
|
|
353
|
+
--epr-emoji-size: 32px;
|
|
354
|
+
}
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
## 🎛️ General Variables
|
|
358
|
+
|
|
359
|
+
| Variable | Description | Default |
|
|
360
|
+
| :--------------------------- | :------------------------------------------------- | :-------- |
|
|
361
|
+
| `--epr-emoji-size` | Size of the emojis. | `30px` |
|
|
362
|
+
| `--epr-emoji-padding` | Padding around each emoji. | `5px` |
|
|
363
|
+
| `--epr-emoji-gap` | **Deprecated**. Use `--epr-emoji-padding` instead. | - |
|
|
364
|
+
| `--epr-bg-color` | Background color of the picker. | `#fff` |
|
|
365
|
+
| `--epr-text-color` | Main text color. | `#858585` |
|
|
366
|
+
| `--epr-picker-border-color` | Border color of the picker container. | `#e7e7e7` |
|
|
367
|
+
| `--epr-picker-border-radius` | Border radius of the picker. | `8px` |
|
|
368
|
+
| `--epr-horizontal-padding` | Horizontal padding for various elements. | `10px` |
|
|
369
|
+
| `--epr-highlight-color` | Color for active states and highlights. | `#007aeb` |
|
|
370
|
+
| `--epr-hover-bg-color` | Background color on hover. | `#e5f0fa` |
|
|
371
|
+
| `--epr-focus-bg-color` | Background color on focus. | `#e0f0ff` |
|
|
372
|
+
|
|
373
|
+
## 🔎 Search Input
|
|
374
|
+
|
|
375
|
+
| Variable | Description | Default |
|
|
376
|
+
| :------------------------------------- | :-------------------------------------- | :--------------------------------- |
|
|
377
|
+
| `--epr-search-input-bg-color` | Background color of the search input. | `#f6f6f6` |
|
|
378
|
+
| `--epr-search-input-bg-color-active` | Background color when search is active. | `var(--epr-search-input-bg-color)` |
|
|
379
|
+
| `--epr-search-input-text-color` | Text color in the search input. | `var(--epr-text-color)` |
|
|
380
|
+
| `--epr-search-input-placeholder-color` | Placeholder text color. | `var(--epr-text-color)` |
|
|
381
|
+
| `--epr-search-border-color` | Border color of the search input. | `var(--epr-search-input-bg-color)` |
|
|
382
|
+
| `--epr-search-border-color-active` | Border color when search is active. | `var(--epr-highlight-color)` |
|
|
383
|
+
| `--epr-search-input-border-radius` | Border radius of the search input. | `8px` |
|
|
384
|
+
| `--epr-search-input-height` | Height of the search input. | `40px` |
|
|
385
|
+
| `--epr-search-icon-color` | Color of the search icon. | - |
|
|
386
|
+
|
|
387
|
+
## 🏷️ Category Navigation
|
|
388
|
+
|
|
389
|
+
| Variable | Description | Default |
|
|
390
|
+
| :-------------------------------------- | :----------------------------------- | :-------- |
|
|
391
|
+
| `--epr-category-navigation-button-size` | Size of category navigation buttons. | `30px` |
|
|
392
|
+
| `--epr-category-icon-active-color` | Color of the active category icon. | `#6aa8de` |
|
|
393
|
+
|
|
394
|
+
## 📃 Category Labels
|
|
395
|
+
|
|
396
|
+
| Variable | Description | Default |
|
|
397
|
+
| :-------------------------------- | :--------------------------------------------- | :---------------------- |
|
|
398
|
+
| `--epr-category-label-bg-color` | Background color of the sticky category label. | `#ffffffe6` |
|
|
399
|
+
| `--epr-category-label-text-color` | Text color of the category label. | `var(--epr-text-color)` |
|
|
400
|
+
| `--epr-category-label-height` | Height of the category label. | `40px` |
|
|
401
|
+
|
|
402
|
+
## 👁️ Preview Area
|
|
403
|
+
|
|
404
|
+
| Variable | Description | Default |
|
|
405
|
+
| :------------------------- | :------------------------------------- | :---------------------- |
|
|
406
|
+
| `--epr-preview-height` | Height of the preview area. | `70px` |
|
|
407
|
+
| `--epr-preview-text-size` | Font size of text in the preview area. | `14px` |
|
|
408
|
+
| `--epr-preview-text-color` | Text color in the preview area. | `var(--epr-text-color)` |
|
|
409
|
+
|
|
410
|
+
## 🖐️ Skin Tone Picker
|
|
411
|
+
|
|
412
|
+
| Variable | Description | Default |
|
|
413
|
+
| :---------------------------------- | :--------------------------------------------- | :---------- |
|
|
414
|
+
| `--epr-skin-tone-picker-menu-color` | Background color of the skin tone picker menu. | `#ffffff95` |
|
|
415
|
+
| `--epr-skin-tone-size` | Size of skin tone circles. | `20px` |
|
|
416
|
+
|
|
417
|
+
## 🌑 Dark Mode
|
|
418
|
+
|
|
419
|
+
| Variable | Description | Default |
|
|
420
|
+
| :--------------------------------- | :------------------------------------ | :--------------------------- |
|
|
421
|
+
| `--epr-dark-bg-color` | Background color in dark mode. | `#222222` |
|
|
422
|
+
| `--epr-dark-picker-border-color` | Border color in dark mode. | `#151617` |
|
|
423
|
+
| `--epr-dark-text-color` | Text color in dark mode. | `var(--epr-highlight-color)` |
|
|
424
|
+
| `--epr-dark-search-input-bg-color` | Search input background in dark mode. | `#333333` |
|
|
425
|
+
| `--epr-dark-hover-bg-color` | Hover background color in dark mode. | `#363636f6` |
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "4.20.
|
|
2
|
+
"version": "4.20.2",
|
|
3
3
|
"license": "MIT",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"typings": "dist/index.d.ts",
|
|
6
6
|
"homepage": "https://ealush.com/emoji-picker-react",
|
|
7
7
|
"files": [
|
|
8
8
|
"dist",
|
|
9
|
-
"src"
|
|
9
|
+
"src",
|
|
10
|
+
"llms.txt"
|
|
10
11
|
],
|
|
11
12
|
"description": "Emoji Picker component for React Applications on the web",
|
|
12
13
|
"keywords": [
|
|
@@ -27,7 +28,7 @@
|
|
|
27
28
|
},
|
|
28
29
|
"scripts": {
|
|
29
30
|
"start": "tsdx watch",
|
|
30
|
-
"build": "npm run build:data:src && npm run type-check && tsdx build --transpileOnly && npm run build:data:dist",
|
|
31
|
+
"build": "npm run build:data:src && npm run type-check && tsdx build --transpileOnly && npm run build:data:dist && npm run docs:llms",
|
|
31
32
|
"type-check": "tsc --noEmit --incremental --pretty --project tsconfig.json",
|
|
32
33
|
"test": "vitest run",
|
|
33
34
|
"test:watch": "vitest",
|
|
@@ -40,10 +41,12 @@
|
|
|
40
41
|
"build:data:dist": "tsc --project tsconfig.data.json && node ./scripts/copyData.js",
|
|
41
42
|
"build:data": "npm run build:data:src && npm run build:data:dist",
|
|
42
43
|
"storybook": "npm run build:data && storybook dev -p 6006",
|
|
43
|
-
"build-storybook": "storybook build"
|
|
44
|
+
"build-storybook": "storybook build",
|
|
45
|
+
"docs:llms": "node ./scripts/generateLlmsTxt.js",
|
|
46
|
+
"check:react-floor": "node ./scripts/checkReactApiFloor.js"
|
|
44
47
|
},
|
|
45
48
|
"peerDependencies": {
|
|
46
|
-
"react": ">=16"
|
|
49
|
+
"react": ">=16.8"
|
|
47
50
|
},
|
|
48
51
|
"husky": {
|
|
49
52
|
"hooks": {
|
|
@@ -61,11 +64,11 @@
|
|
|
61
64
|
"size-limit": [
|
|
62
65
|
{
|
|
63
66
|
"path": "dist/emoji-picker-react.cjs.production.min.js",
|
|
64
|
-
"limit": "
|
|
67
|
+
"limit": "95 KB"
|
|
65
68
|
},
|
|
66
69
|
{
|
|
67
70
|
"path": "dist/emoji-picker-react.esm.js",
|
|
68
|
-
"limit": "
|
|
71
|
+
"limit": "95 KB"
|
|
69
72
|
}
|
|
70
73
|
],
|
|
71
74
|
"devDependencies": {
|
|
@@ -116,6 +119,6 @@
|
|
|
116
119
|
"vitest": "^2.0.5"
|
|
117
120
|
},
|
|
118
121
|
"dependencies": {
|
|
119
|
-
"flairup": "1.1.
|
|
122
|
+
"flairup": "1.1.2"
|
|
120
123
|
}
|
|
121
124
|
}
|
|
@@ -40,22 +40,21 @@ export function EmojiList({ scrollTop }: { scrollTop: number }) {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
return (
|
|
43
|
-
<
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
</React.Suspense>
|
|
43
|
+
<RenderCategory
|
|
44
|
+
key={category}
|
|
45
|
+
categoryEmojis={getEmojisByCategory(category)}
|
|
46
|
+
categoryConfig={categoryConfig}
|
|
47
|
+
topOffset={currentOffset}
|
|
48
|
+
onHeightReady={(height) => {
|
|
49
|
+
if (categoryHeights[category] !== height) {
|
|
50
|
+
setCategoryHeights((prev) => ({
|
|
51
|
+
...prev,
|
|
52
|
+
[category]: height,
|
|
53
|
+
}));
|
|
54
|
+
}
|
|
55
|
+
}}
|
|
56
|
+
scrollTop={scrollTop}
|
|
57
|
+
/>
|
|
59
58
|
);
|
|
60
59
|
})}
|
|
61
60
|
</ul>
|
|
@@ -7,14 +7,13 @@ import {
|
|
|
7
7
|
useGetEmojiUrlConfig,
|
|
8
8
|
useLazyLoadEmojisConfig,
|
|
9
9
|
} from '../../config/useConfig';
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
emojiUnified,
|
|
13
|
-
} from '../../dataUtils/emojiSelectors';
|
|
10
|
+
import { emojiUnified } from '../../dataUtils/emojiUtils';
|
|
11
|
+
import { useIsomorphicLayoutEffect } from '../../hooks/useIsomorphicLayoutEffect';
|
|
14
12
|
import {
|
|
15
13
|
useActiveSkinToneState,
|
|
16
14
|
useEmojiSizeState,
|
|
17
15
|
} from '../context/PickerContext';
|
|
16
|
+
import { useGetEmojisByCategory } from '../context/PickerDataContext';
|
|
18
17
|
import { ClickableEmoji } from '../emoji/Emoji';
|
|
19
18
|
|
|
20
19
|
export function MeasureEmoji() {
|
|
@@ -27,7 +26,7 @@ export function MeasureEmoji() {
|
|
|
27
26
|
const [emojiSize, setEmojiSize] = useEmojiSizeState();
|
|
28
27
|
const ref = React.useRef<HTMLDivElement>(null);
|
|
29
28
|
|
|
30
|
-
|
|
29
|
+
useIsomorphicLayoutEffect(() => {
|
|
31
30
|
if (ref.current) {
|
|
32
31
|
setEmojiSize(ref.current.clientHeight);
|
|
33
32
|
}
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
unifiedWithoutSkinTone,
|
|
16
16
|
} from '../../dataUtils/emojiUtils';
|
|
17
17
|
import { getSuggested } from '../../dataUtils/suggested';
|
|
18
|
+
import { useIsMounted } from '../../hooks/useIsMounted';
|
|
18
19
|
import { Categories, EmojiData, SkinTones } from '../../types/exposedTypes';
|
|
19
20
|
|
|
20
21
|
import { usePickerConfig } from './PickerConfigContext';
|
|
@@ -126,8 +127,15 @@ export function useGetEmojisByCategory() {
|
|
|
126
127
|
const { emojiData, emojiByUnified } = usePickerDataContext();
|
|
127
128
|
const suggestedEmojisModeConfig = useSuggestedEmojisModeConfig();
|
|
128
129
|
const [suggestedUpdated] = useUpdateSuggested();
|
|
130
|
+
// Suggestions come from localStorage, which doesn't exist during SSR.
|
|
131
|
+
// Read them only after mount so the first client render matches the server.
|
|
132
|
+
const isMounted = useIsMounted();
|
|
129
133
|
|
|
130
134
|
const suggested = React.useMemo(() => {
|
|
135
|
+
if (!isMounted) {
|
|
136
|
+
return [] as DataEmojis;
|
|
137
|
+
}
|
|
138
|
+
|
|
131
139
|
const suggested = getSuggested(suggestedEmojisModeConfig) ?? [];
|
|
132
140
|
|
|
133
141
|
return suggested
|
|
@@ -141,7 +149,7 @@ export function useGetEmojisByCategory() {
|
|
|
141
149
|
})
|
|
142
150
|
.filter(Boolean) as DataEmojis;
|
|
143
151
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
144
|
-
}, [suggestedUpdated, suggestedEmojisModeConfig, emojiByUnified]);
|
|
152
|
+
}, [isMounted, suggestedUpdated, suggestedEmojisModeConfig, emojiByUnified]);
|
|
145
153
|
|
|
146
154
|
return function getEmojisByCategory(category: Categories): DataEmojis {
|
|
147
155
|
if (category === Categories.SUGGESTED) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
|
|
3
3
|
import { DataEmoji } from '../../dataUtils/DataTypes';
|
|
4
|
-
import { emojiHasVariations, emojiNames } from '../../dataUtils/
|
|
4
|
+
import { emojiHasVariations, emojiNames } from '../../dataUtils/emojiUtils';
|
|
5
5
|
|
|
6
6
|
import { BaseEmojiProps } from './BaseEmojiProps';
|
|
7
7
|
import { ClickableEmojiButton } from './ClickableEmojiButton';
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
useGetEmojiUrlConfig,
|
|
12
12
|
usePreviewConfig,
|
|
13
13
|
} from '../../config/useConfig';
|
|
14
|
-
import { emojiName, emojiUnified } from '../../dataUtils/
|
|
14
|
+
import { emojiName, emojiUnified } from '../../dataUtils/emojiUtils';
|
|
15
15
|
import { useEmojiPreviewEvents } from '../../hooks/useEmojiPreviewEvents';
|
|
16
16
|
import { useIsSkinToneInPreview } from '../../hooks/useShouldShowSkinTonePicker';
|
|
17
17
|
import Flex from '../Layout/Flex';
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import
|
|
1
|
+
// Pure data helpers plus the legacy global registry. This module intentionally
|
|
2
|
+
// imports no React components, hooks, or context providers so it can never
|
|
3
|
+
// form an import cycle with them. Context-aware lookups live in
|
|
4
|
+
// components/context/PickerDataContext instead.
|
|
4
5
|
import { Categories } from '../config/categoryConfig';
|
|
5
6
|
import { cdnUrl } from '../config/cdnUrls';
|
|
6
7
|
import { CustomEmoji } from '../config/customEmojiConfig';
|
|
7
|
-
import { useSuggestedEmojisModeConfig } from '../config/useConfig';
|
|
8
8
|
import emojis from '../data/emojis';
|
|
9
9
|
import skinToneVariations, {
|
|
10
10
|
skinTonesMapped,
|
|
@@ -13,7 +13,6 @@ import { EmojiStyle, SkinTones } from '../types/exposedTypes';
|
|
|
13
13
|
|
|
14
14
|
import { DataEmoji, DataEmojis, EmojiProperties, WithName } from './DataTypes';
|
|
15
15
|
import { indexEmoji } from './alphaNumericEmojiIndex';
|
|
16
|
-
import { getSuggested } from './suggested';
|
|
17
16
|
|
|
18
17
|
export function emojiNames(emoji: WithName): string[] {
|
|
19
18
|
return emoji[EmojiProperties.name] ?? [];
|
|
@@ -53,32 +52,6 @@ export function emojiUnified(emoji: DataEmoji, skinTone?: string): string {
|
|
|
53
52
|
return emojiVariationUnified(emoji, skinTone) ?? unified;
|
|
54
53
|
}
|
|
55
54
|
|
|
56
|
-
export function useGetEmojisByCategory(): (Category: Categories) => DataEmojis {
|
|
57
|
-
const suggestedEmojisModeConfig = useSuggestedEmojisModeConfig();
|
|
58
|
-
const [suggestedUpdated] = useUpdateSuggested();
|
|
59
|
-
|
|
60
|
-
const suggested = React.useMemo(
|
|
61
|
-
() => {
|
|
62
|
-
const suggested = getSuggested(suggestedEmojisModeConfig) ?? [];
|
|
63
|
-
|
|
64
|
-
return suggested
|
|
65
|
-
.map((s) => emojiByUnified(s.unified))
|
|
66
|
-
.filter(Boolean) as DataEmojis;
|
|
67
|
-
},
|
|
68
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
69
|
-
[suggestedUpdated, suggestedEmojisModeConfig],
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
return function getEmojisByCategory(category: Categories): DataEmojis {
|
|
73
|
-
if (category === Categories.SUGGESTED) {
|
|
74
|
-
return suggested;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// @ts-ignore
|
|
78
|
-
return emojis.emojis[category] ?? [];
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
|
|
82
55
|
// WARNING: DO NOT USE DIRECTLY
|
|
83
56
|
export function emojiUrlByUnified(
|
|
84
57
|
unified: string,
|