@schwitzerskills/emojipicker 1.0.0 → 1.0.3
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 +677 -0
- package/package.json +4 -4
package/README.md
ADDED
|
@@ -0,0 +1,677 @@
|
|
|
1
|
+
# EmojiPicker.js
|
|
2
|
+
|
|
3
|
+
> A flexible, event-driven emoji picker library for the web.
|
|
4
|
+
> Zero dependencies. Full developer control. Framework agnostic.
|
|
5
|
+
|
|
6
|
+
```js
|
|
7
|
+
const picker = new EmojiPicker({ container: '#btn' })
|
|
8
|
+
picker.on('emojiClick', (emoji) => console.log(emoji.char)) // 😂
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Table of Contents
|
|
14
|
+
|
|
15
|
+
- [Installation](#installation)
|
|
16
|
+
- [Module Formats](#module-formats)
|
|
17
|
+
- [Quick Start](#quick-start)
|
|
18
|
+
- [Configuration Options](#configuration-options)
|
|
19
|
+
- [Events](#events)
|
|
20
|
+
- [Methods](#methods)
|
|
21
|
+
- [Emoji Object](#emoji-object)
|
|
22
|
+
- [Modes](#modes)
|
|
23
|
+
- [Skin Tone Support](#skin-tone-support)
|
|
24
|
+
- [Theming & CSS Variables](#theming--css-variables)
|
|
25
|
+
- [Custom Emojis](#custom-emojis)
|
|
26
|
+
- [Helper: attachToInput()](#helper-attachtoinput)
|
|
27
|
+
- [Framework Integration](#framework-integration)
|
|
28
|
+
- [Recipes / Examples](#recipes--examples)
|
|
29
|
+
- [Accessibility](#accessibility)
|
|
30
|
+
- [Browser Support](#browser-support)
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm install emojipicker-js
|
|
38
|
+
# or
|
|
39
|
+
yarn add emojipicker-js
|
|
40
|
+
# or
|
|
41
|
+
pnpm add emojipicker-js
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Module Formats
|
|
47
|
+
|
|
48
|
+
The library ships as a **UMD build** and supports all environments out of the box.
|
|
49
|
+
|
|
50
|
+
### CommonJS (Node.js / bundlers)
|
|
51
|
+
|
|
52
|
+
```js
|
|
53
|
+
const EmojiPicker = require('emojipicker-js')
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### ES Module
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
import EmojiPicker from 'emojipicker-js'
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Browser global (CDN / script tag)
|
|
63
|
+
|
|
64
|
+
```html
|
|
65
|
+
<script src="emoji-picker.js"></script>
|
|
66
|
+
<script>
|
|
67
|
+
const picker = new EmojiPicker({ container: '#btn' })
|
|
68
|
+
</script>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### AMD (RequireJS)
|
|
72
|
+
|
|
73
|
+
```js
|
|
74
|
+
define(['emojipicker-js'], function(EmojiPicker) {
|
|
75
|
+
const picker = new EmojiPicker({ container: '#btn' })
|
|
76
|
+
})
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The `package.json` fields are set up accordingly:
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"main": "emoji-picker.js",
|
|
84
|
+
"module": "emoji-picker.esm.js",
|
|
85
|
+
"browser": "emoji-picker.js",
|
|
86
|
+
"exports": {
|
|
87
|
+
".": {
|
|
88
|
+
"import": "./emoji-picker.esm.js",
|
|
89
|
+
"require": "./emoji-picker.js"
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Quick Start
|
|
98
|
+
|
|
99
|
+
**Step 1 — Add a trigger button to your HTML:**
|
|
100
|
+
|
|
101
|
+
```html
|
|
102
|
+
<button id="emoji-btn">😊</button>
|
|
103
|
+
<input type="text" id="message" placeholder="Type a message...">
|
|
104
|
+
|
|
105
|
+
<script src="emoji-picker.js"></script>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Step 2 — Initialize and listen for events:**
|
|
109
|
+
|
|
110
|
+
```html
|
|
111
|
+
<script>
|
|
112
|
+
const picker = new EmojiPicker({
|
|
113
|
+
container: '#emoji-btn',
|
|
114
|
+
theme: 'auto'
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
picker.on('emojiClick', (emoji) => {
|
|
118
|
+
document.querySelector('#message').value += emoji.char
|
|
119
|
+
})
|
|
120
|
+
</script>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
That's it. Clicking `#emoji-btn` opens the picker. Clicking an emoji fires `emojiClick`.
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Configuration Options
|
|
128
|
+
|
|
129
|
+
All properties are optional.
|
|
130
|
+
|
|
131
|
+
```js
|
|
132
|
+
const picker = new EmojiPicker({
|
|
133
|
+
container: '#my-button', // Trigger element (CSS selector or DOM node)
|
|
134
|
+
theme: 'auto', // 'light' | 'dark' | 'auto'
|
|
135
|
+
mode: 'dropdown', // 'dropdown' | 'inline' | 'popup'
|
|
136
|
+
search: true, // Show search input
|
|
137
|
+
recentEmojis: true, // Track & show recently used emojis
|
|
138
|
+
maxRecent: 24, // Max number of recent emojis to store
|
|
139
|
+
skinTone: 'default', // Default skin tone (see Skin Tone Support)
|
|
140
|
+
customEmojis: [], // Array of custom emoji objects
|
|
141
|
+
perRow: 8, // Emojis per row in the grid
|
|
142
|
+
emojiSize: 28, // Emoji size in pixels
|
|
143
|
+
autoClose: true, // Close picker after selecting an emoji
|
|
144
|
+
})
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
| Option | Type | Default | Description |
|
|
148
|
+
|--------|------|---------|-------------|
|
|
149
|
+
| `container` | `string \| HTMLElement` | `null` | Element that triggers open/close on click |
|
|
150
|
+
| `theme` | `string` | `'auto'` | Color theme: `'light'`, `'dark'`, or `'auto'` |
|
|
151
|
+
| `mode` | `string` | `'dropdown'` | Display mode (see [Modes](#modes)) |
|
|
152
|
+
| `search` | `boolean` | `true` | Show/hide the search input |
|
|
153
|
+
| `recentEmojis` | `boolean` | `true` | Enable recent emojis tab (uses localStorage) |
|
|
154
|
+
| `maxRecent` | `number` | `24` | Max recent emojis to remember |
|
|
155
|
+
| `skinTone` | `string` | `'default'` | Default skin tone modifier |
|
|
156
|
+
| `customEmojis` | `array` | `[]` | Custom emoji definitions |
|
|
157
|
+
| `perRow` | `number` | `8` | Grid columns |
|
|
158
|
+
| `emojiSize` | `number` | `28` | Emoji size in px |
|
|
159
|
+
| `autoClose` | `boolean` | `true` | Auto-close picker on emoji select |
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Events
|
|
164
|
+
|
|
165
|
+
The library is fully event-driven. Attach any number of listeners to any event.
|
|
166
|
+
|
|
167
|
+
```js
|
|
168
|
+
picker.on(eventName, handler)
|
|
169
|
+
picker.off(eventName, handler) // Remove a specific listener
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### `emojiClick`
|
|
173
|
+
|
|
174
|
+
Fired when the user clicks/selects an emoji. This is the main event you'll use.
|
|
175
|
+
|
|
176
|
+
```js
|
|
177
|
+
picker.on('emojiClick', (emoji, mouseEvent) => {
|
|
178
|
+
console.log(emoji.char) // "😂"
|
|
179
|
+
console.log(emoji.name) // "face_with_tears_of_joy"
|
|
180
|
+
console.log(emoji.category) // "Smileys & Emotion"
|
|
181
|
+
console.log(emoji.unicode) // "1F602"
|
|
182
|
+
console.log(emoji.skinTone) // null | "light" | "medium" | ...
|
|
183
|
+
})
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### `emojiHover`
|
|
187
|
+
|
|
188
|
+
Fired when the user hovers over an emoji.
|
|
189
|
+
|
|
190
|
+
```js
|
|
191
|
+
picker.on('emojiHover', (emoji, mouseEvent) => {
|
|
192
|
+
myPreview.textContent = `${emoji.char} ${emoji.name}`
|
|
193
|
+
})
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### `pickerOpen`
|
|
197
|
+
|
|
198
|
+
```js
|
|
199
|
+
picker.on('pickerOpen', () => {
|
|
200
|
+
console.log('Picker opened')
|
|
201
|
+
})
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### `pickerClose`
|
|
205
|
+
|
|
206
|
+
```js
|
|
207
|
+
picker.on('pickerClose', () => {
|
|
208
|
+
console.log('Picker closed')
|
|
209
|
+
})
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### `categoryChange`
|
|
213
|
+
|
|
214
|
+
```js
|
|
215
|
+
picker.on('categoryChange', ({ category }) => {
|
|
216
|
+
console.log('Switched to:', category) // "Food & Drink"
|
|
217
|
+
})
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### `search`
|
|
221
|
+
|
|
222
|
+
```js
|
|
223
|
+
picker.on('search', ({ query }) => {
|
|
224
|
+
console.log('Searching for:', query)
|
|
225
|
+
})
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## Methods
|
|
231
|
+
|
|
232
|
+
All methods return `this` (chainable, except `destroy()`).
|
|
233
|
+
|
|
234
|
+
```js
|
|
235
|
+
picker.open() // Open the picker
|
|
236
|
+
picker.close() // Close the picker
|
|
237
|
+
picker.toggle() // Toggle open/close
|
|
238
|
+
picker.setTheme('dark') // Switch theme at runtime
|
|
239
|
+
picker.destroy() // Remove from DOM and clean up all listeners
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
**Chaining:**
|
|
243
|
+
|
|
244
|
+
```js
|
|
245
|
+
new EmojiPicker({ container: '#btn', theme: 'light' })
|
|
246
|
+
.on('emojiClick', (e) => addEmoji(e.char))
|
|
247
|
+
.on('pickerOpen', () => analytics.track('emoji_picker_opened'))
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## Emoji Object
|
|
253
|
+
|
|
254
|
+
Every emoji-related event provides this data structure:
|
|
255
|
+
|
|
256
|
+
```js
|
|
257
|
+
{
|
|
258
|
+
char: "👍🏽", // The emoji character (with skin tone applied)
|
|
259
|
+
name: "thumbs_up", // Snake_case identifier
|
|
260
|
+
category: "People & Body", // Category name
|
|
261
|
+
unicode: "1F44D", // Base Unicode code point (hex)
|
|
262
|
+
skinTone: "medium", // null if default, otherwise the tone name
|
|
263
|
+
isCustom: false // true for custom emojis
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## Modes
|
|
270
|
+
|
|
271
|
+
### `dropdown` *(default)*
|
|
272
|
+
|
|
273
|
+
Opens as a floating panel anchored to the trigger element. Closes on outside click or `Esc`.
|
|
274
|
+
|
|
275
|
+
```js
|
|
276
|
+
new EmojiPicker({ container: '#btn', mode: 'dropdown' })
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### `inline`
|
|
280
|
+
|
|
281
|
+
Always visible, embedded directly inside the container element. `autoClose` is ignored.
|
|
282
|
+
|
|
283
|
+
```js
|
|
284
|
+
new EmojiPicker({ container: '#my-div', mode: 'inline', autoClose: false })
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
```html
|
|
288
|
+
<div id="my-div"></div> <!-- Picker renders here -->
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### `popup`
|
|
292
|
+
|
|
293
|
+
Positions itself in the center of the viewport. Useful for modal-style pickers without a fixed trigger.
|
|
294
|
+
|
|
295
|
+
```js
|
|
296
|
+
const picker = new EmojiPicker({ mode: 'popup' })
|
|
297
|
+
document.getElementById('open-btn').addEventListener('click', () => picker.open())
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## Skin Tone Support
|
|
303
|
+
|
|
304
|
+
Users can select a skin tone in the picker footer. Set a default via options:
|
|
305
|
+
|
|
306
|
+
```js
|
|
307
|
+
new EmojiPicker({ skinTone: 'medium-dark' })
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
| Value | Example |
|
|
311
|
+
|-------|---------|
|
|
312
|
+
| `'default'` | 👍 |
|
|
313
|
+
| `'light'` | 👍🏻 |
|
|
314
|
+
| `'medium-light'` | 👍🏼 |
|
|
315
|
+
| `'medium'` | 👍🏽 |
|
|
316
|
+
| `'medium-dark'` | 👍🏾 |
|
|
317
|
+
| `'dark'` | 👍🏿 |
|
|
318
|
+
|
|
319
|
+
The selected tone is reflected in `emoji.char` and reported in `emoji.skinTone`.
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
## Theming & CSS Variables
|
|
324
|
+
|
|
325
|
+
Override CSS variables to match any design system:
|
|
326
|
+
|
|
327
|
+
```css
|
|
328
|
+
.ep-picker {
|
|
329
|
+
--ep-bg: #0f1117;
|
|
330
|
+
--ep-surface: #1a1d2e;
|
|
331
|
+
--ep-surface2: #21253a;
|
|
332
|
+
--ep-border: rgba(255,255,255,0.08);
|
|
333
|
+
--ep-text: #e4e7f3;
|
|
334
|
+
--ep-text-dim: #6b738f;
|
|
335
|
+
--ep-accent: #6c63ff;
|
|
336
|
+
--ep-hover: rgba(108,99,255,0.12);
|
|
337
|
+
--ep-size: 28px; /* Emoji size */
|
|
338
|
+
--ep-radius: 16px; /* Picker border radius */
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### Built-in themes
|
|
343
|
+
|
|
344
|
+
```js
|
|
345
|
+
new EmojiPicker({ theme: 'light' }) // Light
|
|
346
|
+
new EmojiPicker({ theme: 'dark' }) // Dark
|
|
347
|
+
new EmojiPicker({ theme: 'auto' }) // Follows OS preference
|
|
348
|
+
|
|
349
|
+
picker.setTheme('dark') // Switch at runtime
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### Custom accent color
|
|
353
|
+
|
|
354
|
+
```css
|
|
355
|
+
.ep-picker {
|
|
356
|
+
--ep-accent: #e91e8c;
|
|
357
|
+
--ep-hover: rgba(233, 30, 140, 0.1);
|
|
358
|
+
--ep-active-tab: rgba(233, 30, 140, 0.15);
|
|
359
|
+
}
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
## Custom Emojis
|
|
365
|
+
|
|
366
|
+
Add your own images, GIFs or SVGs alongside the standard set:
|
|
367
|
+
|
|
368
|
+
```js
|
|
369
|
+
new EmojiPicker({
|
|
370
|
+
customEmojis: [
|
|
371
|
+
{ name: 'party_parrot', url: '/assets/parrot.gif' },
|
|
372
|
+
{ name: 'company_logo', url: '/assets/logo.png' },
|
|
373
|
+
{ name: 'custom_star', url: 'https://example.com/star.svg' }
|
|
374
|
+
]
|
|
375
|
+
})
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
Custom emojis appear in their own **Custom** tab. The click event returns:
|
|
379
|
+
|
|
380
|
+
```js
|
|
381
|
+
{
|
|
382
|
+
char: null,
|
|
383
|
+
name: 'party_parrot',
|
|
384
|
+
category: 'custom',
|
|
385
|
+
isCustom: true
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
## Helper: attachToInput()
|
|
392
|
+
|
|
393
|
+
Wraps any `<input>` or `<textarea>` and inserts the selected emoji at the cursor position automatically.
|
|
394
|
+
|
|
395
|
+
```js
|
|
396
|
+
// Attach by selector
|
|
397
|
+
EmojiPicker.attachToInput('#message-input')
|
|
398
|
+
|
|
399
|
+
// With options
|
|
400
|
+
EmojiPicker.attachToInput('#chat-box', {
|
|
401
|
+
theme: 'dark',
|
|
402
|
+
skinTone: 'medium'
|
|
403
|
+
})
|
|
404
|
+
|
|
405
|
+
// Returns the picker instance for further event binding
|
|
406
|
+
const picker = EmojiPicker.attachToInput('#my-input')
|
|
407
|
+
picker.on('emojiClick', (emoji) => {
|
|
408
|
+
counter.textContent = myInput.value.length
|
|
409
|
+
})
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
---
|
|
413
|
+
|
|
414
|
+
## Framework Integration
|
|
415
|
+
|
|
416
|
+
### React
|
|
417
|
+
|
|
418
|
+
Works out of the box with a standard `import`. Initialize inside `useEffect` so the library only runs in the browser.
|
|
419
|
+
|
|
420
|
+
```jsx
|
|
421
|
+
import { useEffect, useRef } from 'react'
|
|
422
|
+
import EmojiPicker from 'emojipicker-js'
|
|
423
|
+
|
|
424
|
+
function EmojiButton({ onSelect }) {
|
|
425
|
+
const btnRef = useRef(null)
|
|
426
|
+
|
|
427
|
+
useEffect(() => {
|
|
428
|
+
const picker = new EmojiPicker({
|
|
429
|
+
container: btnRef.current,
|
|
430
|
+
theme: 'auto'
|
|
431
|
+
})
|
|
432
|
+
picker.on('emojiClick', onSelect)
|
|
433
|
+
|
|
434
|
+
return () => picker.destroy() // clean up on unmount
|
|
435
|
+
}, [onSelect])
|
|
436
|
+
|
|
437
|
+
return <button ref={btnRef}>😊</button>
|
|
438
|
+
}
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### Next.js
|
|
442
|
+
|
|
443
|
+
EmojiPicker uses `window` and `document` internally, so it must only run on the client. There are two ways to handle this:
|
|
444
|
+
|
|
445
|
+
**Option A — dynamic import (recommended):**
|
|
446
|
+
|
|
447
|
+
```jsx
|
|
448
|
+
// components/EmojiButton.jsx
|
|
449
|
+
import { useEffect, useRef } from 'react'
|
|
450
|
+
import EmojiPicker from 'emojipicker-js'
|
|
451
|
+
|
|
452
|
+
export default function EmojiButton({ onSelect }) {
|
|
453
|
+
const btnRef = useRef(null)
|
|
454
|
+
useEffect(() => {
|
|
455
|
+
const picker = new EmojiPicker({ container: btnRef.current })
|
|
456
|
+
picker.on('emojiClick', onSelect)
|
|
457
|
+
return () => picker.destroy()
|
|
458
|
+
}, [onSelect])
|
|
459
|
+
return <button ref={btnRef}>😊</button>
|
|
460
|
+
}
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
```jsx
|
|
464
|
+
// pages/index.jsx or any page
|
|
465
|
+
import dynamic from 'next/dynamic'
|
|
466
|
+
|
|
467
|
+
const EmojiButton = dynamic(() => import('../components/EmojiButton'), {
|
|
468
|
+
ssr: false // prevents server-side execution
|
|
469
|
+
})
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
**Option B — useEffect guard (also safe):**
|
|
473
|
+
|
|
474
|
+
```jsx
|
|
475
|
+
useEffect(() => {
|
|
476
|
+
// useEffect only runs in the browser, so this is always safe.
|
|
477
|
+
// Add typeof window check only if you import EmojiPicker outside of useEffect.
|
|
478
|
+
const picker = new EmojiPicker({ container: btnRef.current })
|
|
479
|
+
picker.on('emojiClick', onSelect)
|
|
480
|
+
return () => picker.destroy()
|
|
481
|
+
}, [onSelect])
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
### Vue 3
|
|
485
|
+
|
|
486
|
+
```vue
|
|
487
|
+
<template>
|
|
488
|
+
<button ref="btnRef">😊</button>
|
|
489
|
+
</template>
|
|
490
|
+
|
|
491
|
+
<script setup>
|
|
492
|
+
import { ref, onMounted, onUnmounted } from 'vue'
|
|
493
|
+
import EmojiPicker from 'emojipicker-js'
|
|
494
|
+
|
|
495
|
+
const emit = defineEmits(['select'])
|
|
496
|
+
const btnRef = ref(null)
|
|
497
|
+
let picker = null
|
|
498
|
+
|
|
499
|
+
onMounted(() => {
|
|
500
|
+
picker = new EmojiPicker({ container: btnRef.value, theme: 'auto' })
|
|
501
|
+
picker.on('emojiClick', (emoji) => emit('select', emoji))
|
|
502
|
+
})
|
|
503
|
+
|
|
504
|
+
onUnmounted(() => picker?.destroy())
|
|
505
|
+
</script>
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
### Svelte
|
|
509
|
+
|
|
510
|
+
```svelte
|
|
511
|
+
<script>
|
|
512
|
+
import { onMount, onDestroy, createEventDispatcher } from 'svelte'
|
|
513
|
+
import EmojiPicker from 'emojipicker-js'
|
|
514
|
+
|
|
515
|
+
const dispatch = createEventDispatcher()
|
|
516
|
+
let btnEl
|
|
517
|
+
let picker
|
|
518
|
+
|
|
519
|
+
onMount(() => {
|
|
520
|
+
picker = new EmojiPicker({ container: btnEl, theme: 'auto' })
|
|
521
|
+
picker.on('emojiClick', (emoji) => dispatch('select', emoji))
|
|
522
|
+
})
|
|
523
|
+
|
|
524
|
+
onDestroy(() => picker?.destroy())
|
|
525
|
+
</script>
|
|
526
|
+
|
|
527
|
+
<button bind:this={btnEl}>😊</button>
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
### Vanilla JS (no bundler)
|
|
531
|
+
|
|
532
|
+
```html
|
|
533
|
+
<script src="https://cdn.example.com/emoji-picker.js"></script>
|
|
534
|
+
<script>
|
|
535
|
+
const picker = new EmojiPicker({ container: '#btn', theme: 'auto' })
|
|
536
|
+
picker.on('emojiClick', (emoji) => {
|
|
537
|
+
document.querySelector('#input').value += emoji.char
|
|
538
|
+
})
|
|
539
|
+
</script>
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
---
|
|
543
|
+
|
|
544
|
+
## Recipes / Examples
|
|
545
|
+
|
|
546
|
+
### Insert emoji at cursor in a textarea
|
|
547
|
+
|
|
548
|
+
```js
|
|
549
|
+
const textarea = document.querySelector('#editor')
|
|
550
|
+
const picker = new EmojiPicker({ container: '#emoji-trigger' })
|
|
551
|
+
|
|
552
|
+
picker.on('emojiClick', (emoji) => {
|
|
553
|
+
const start = textarea.selectionStart
|
|
554
|
+
const end = textarea.selectionEnd
|
|
555
|
+
textarea.value =
|
|
556
|
+
textarea.value.substring(0, start) +
|
|
557
|
+
emoji.char +
|
|
558
|
+
textarea.value.substring(end)
|
|
559
|
+
textarea.setSelectionRange(
|
|
560
|
+
start + emoji.char.length,
|
|
561
|
+
start + emoji.char.length
|
|
562
|
+
)
|
|
563
|
+
textarea.focus()
|
|
564
|
+
})
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
### Copy to clipboard
|
|
568
|
+
|
|
569
|
+
```js
|
|
570
|
+
picker.on('emojiClick', (emoji) => {
|
|
571
|
+
navigator.clipboard.writeText(emoji.char).then(() => {
|
|
572
|
+
showToast(`Copied ${emoji.char}`)
|
|
573
|
+
})
|
|
574
|
+
})
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
### Send emoji reaction to a server
|
|
578
|
+
|
|
579
|
+
```js
|
|
580
|
+
picker.on('emojiClick', (emoji) => {
|
|
581
|
+
fetch('/api/reactions', {
|
|
582
|
+
method: 'POST',
|
|
583
|
+
headers: { 'Content-Type': 'application/json' },
|
|
584
|
+
body: JSON.stringify({
|
|
585
|
+
messageId: currentMessageId,
|
|
586
|
+
emoji: emoji.name,
|
|
587
|
+
char: emoji.char
|
|
588
|
+
})
|
|
589
|
+
})
|
|
590
|
+
})
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
### Multiple pickers on the same page
|
|
594
|
+
|
|
595
|
+
```js
|
|
596
|
+
// Minimal reaction picker
|
|
597
|
+
const reactionPicker = new EmojiPicker({
|
|
598
|
+
container: '#reaction-btn',
|
|
599
|
+
search: false,
|
|
600
|
+
recentEmojis: false,
|
|
601
|
+
autoClose: true
|
|
602
|
+
})
|
|
603
|
+
|
|
604
|
+
// Full editor picker
|
|
605
|
+
const editorPicker = new EmojiPicker({
|
|
606
|
+
container: '#editor-btn',
|
|
607
|
+
search: true,
|
|
608
|
+
skinTone: 'medium'
|
|
609
|
+
})
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
### Track analytics
|
|
613
|
+
|
|
614
|
+
```js
|
|
615
|
+
picker.on('pickerOpen', () => analytics.track('picker_opened'))
|
|
616
|
+
picker.on('emojiClick', (emoji) => analytics.track('emoji_used', { name: emoji.name }))
|
|
617
|
+
picker.on('search', ({ query }) => analytics.track('emoji_search', { query }))
|
|
618
|
+
picker.on('categoryChange', ({ category }) => analytics.track('category_view', { category }))
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
### Dynamically switch themes
|
|
622
|
+
|
|
623
|
+
```js
|
|
624
|
+
document.querySelector('#theme-toggle').addEventListener('click', () => {
|
|
625
|
+
const isDark = document.body.classList.toggle('dark')
|
|
626
|
+
picker.setTheme(isDark ? 'dark' : 'light')
|
|
627
|
+
})
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
---
|
|
631
|
+
|
|
632
|
+
## Accessibility
|
|
633
|
+
|
|
634
|
+
- All interactive elements have `aria-label` and `role` attributes
|
|
635
|
+
- The emoji grid uses `role="grid"` and `role="gridcell"`
|
|
636
|
+
- The picker uses `role="dialog"` with `aria-label`
|
|
637
|
+
- Category tabs use `role="tablist"`
|
|
638
|
+
- Preview area uses `aria-live="polite"` for screen reader announcements
|
|
639
|
+
- `Esc` closes the picker
|
|
640
|
+
- Focus moves to the search input on open
|
|
641
|
+
|
|
642
|
+
---
|
|
643
|
+
|
|
644
|
+
## Browser Support
|
|
645
|
+
|
|
646
|
+
| Browser | Version |
|
|
647
|
+
|---------|---------|
|
|
648
|
+
| Chrome | 80+ |
|
|
649
|
+
| Firefox | 78+ |
|
|
650
|
+
| Safari | 14+ |
|
|
651
|
+
| Edge | 80+ |
|
|
652
|
+
| iOS Safari | 14+ |
|
|
653
|
+
| Android Chrome | 80+ |
|
|
654
|
+
|
|
655
|
+
Requires `localStorage` for recent emojis — gracefully disabled if unavailable (e.g. private browsing).
|
|
656
|
+
|
|
657
|
+
---
|
|
658
|
+
|
|
659
|
+
## Categories
|
|
660
|
+
|
|
661
|
+
| Tab | Category | Example |
|
|
662
|
+
|-----|----------|---------|
|
|
663
|
+
| 🕐 | Recently Used | *dynamic* |
|
|
664
|
+
| 😊 | Smileys & Emotion | 😀 😂 🥰 😎 |
|
|
665
|
+
| 👋 | People & Body | 👋 💪 🙏 🤝 |
|
|
666
|
+
| 🐶 | Animals & Nature | 🐶 🦊 🌸 🌈 |
|
|
667
|
+
| 🍕 | Food & Drink | 🍕 🍜 🍺 🧋 |
|
|
668
|
+
| ⚽ | Activities | ⚽ 🎮 🎸 🏆 |
|
|
669
|
+
| ✈️ | Travel & Places | ✈️ 🚀 🏖️ 🏰 |
|
|
670
|
+
| 💡 | Objects | 💡 💻 📷 🔑 |
|
|
671
|
+
| ❤️ | Symbols | ❤️ ✅ ♻️ 💯 |
|
|
672
|
+
| 🏳️ | Flags | 🏳️🌈 🇺🇸 🇩🇪 🇯🇵 |
|
|
673
|
+
|
|
674
|
+
---
|
|
675
|
+
|
|
676
|
+
## License
|
|
677
|
+
Apache — free
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@schwitzerskills/emojipicker",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "A flexible, event-driven emoji picker library for the web.",
|
|
5
5
|
"main": "emoji-picker.js",
|
|
6
6
|
"scripts": {
|
|
@@ -66,15 +66,15 @@
|
|
|
66
66
|
"url": "https://github.com/pfurpass/EmojiPicker/issues"
|
|
67
67
|
},
|
|
68
68
|
"homepage": "https://github.com/pfurpass/EmojiPicker#readme",
|
|
69
|
-
"module": "emoji-picker.esm.js",
|
|
70
|
-
"browser": "emoji-picker.js",
|
|
69
|
+
"module": "emoji-picker.esm.js",
|
|
70
|
+
"browser": "emoji-picker.js",
|
|
71
71
|
"exports": {
|
|
72
72
|
".": {
|
|
73
73
|
"import": "./emoji-picker.esm.js",
|
|
74
74
|
"require": "./emoji-picker.js"
|
|
75
75
|
}
|
|
76
76
|
},
|
|
77
|
-
|
|
77
|
+
"files": [
|
|
78
78
|
"emoji-picker.js",
|
|
79
79
|
"emoji-picker.esm.js"
|
|
80
80
|
]
|