@schwitzerskills/emojipicker 1.0.4 → 1.0.6
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 +414 -278
- package/emoji-data.json +1 -0
- package/emoji-picker.d.ts +196 -0
- package/emoji-picker.js +805 -1627
- package/package.json +7 -5
- package/emoji-picker.esm.js +0 -2
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ picker.on('emojiClick', (emoji) => console.log(emoji.char)) // 😂
|
|
|
13
13
|
## Table of Contents
|
|
14
14
|
|
|
15
15
|
- [Installation](#installation)
|
|
16
|
-
- [
|
|
16
|
+
- [Setup — Two Files](#setup--two-files)
|
|
17
17
|
- [Quick Start](#quick-start)
|
|
18
18
|
- [Configuration Options](#configuration-options)
|
|
19
19
|
- [Events](#events)
|
|
@@ -24,103 +24,119 @@ picker.on('emojiClick', (emoji) => console.log(emoji.char)) // 😂
|
|
|
24
24
|
- [Theming & CSS Variables](#theming--css-variables)
|
|
25
25
|
- [Custom Emojis](#custom-emojis)
|
|
26
26
|
- [Helper: attachToInput()](#helper-attachtoinput)
|
|
27
|
+
- [Favorites & getTopFavorites()](#favorites--gettopfavorites)
|
|
28
|
+
- [i18n / Localization](#i18n--localization)
|
|
29
|
+
- [TypeScript](#typescript)
|
|
27
30
|
- [Framework Integration](#framework-integration)
|
|
28
31
|
- [Recipes / Examples](#recipes--examples)
|
|
29
32
|
- [Accessibility](#accessibility)
|
|
30
33
|
- [Browser Support](#browser-support)
|
|
34
|
+
- [How It Works Internally](#how-it-works-internally)
|
|
31
35
|
|
|
32
36
|
---
|
|
33
37
|
|
|
34
38
|
## Installation
|
|
35
39
|
|
|
40
|
+
### npm
|
|
41
|
+
|
|
36
42
|
```bash
|
|
37
43
|
npm install @schwitzerskills/emojipicker
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### CDN (jsDelivr)
|
|
47
|
+
|
|
48
|
+
```html
|
|
49
|
+
<script src="https://cdn.jsdelivr.net/npm/@schwitzerskills/emojipicker/emoji-picker.js"></script>
|
|
42
50
|
```
|
|
43
51
|
|
|
44
52
|
---
|
|
45
53
|
|
|
46
|
-
##
|
|
54
|
+
## Setup — Two Files
|
|
47
55
|
|
|
48
|
-
|
|
56
|
+
> **Important:** The library needs two files to work.
|
|
49
57
|
|
|
50
|
-
|
|
58
|
+
| File | Purpose |
|
|
59
|
+
|------|---------|
|
|
60
|
+
| `emoji-picker.js` | The picker core (~5 KB) |
|
|
61
|
+
| `emoji-data.json` | All emoji data (~850 KB, loaded once, cached forever) |
|
|
51
62
|
|
|
52
|
-
|
|
53
|
-
const EmojiPicker = require('emojipicker-js')
|
|
54
|
-
```
|
|
63
|
+
Both files must be accessible at the same URL path. The library auto-detects `emoji-data.json` relative to its own `<script src>` tag.
|
|
55
64
|
|
|
56
|
-
|
|
65
|
+
**If you use npm / a bundler**, copy `emoji-data.json` to your public/static folder and pass the URL manually:
|
|
57
66
|
|
|
58
67
|
```js
|
|
59
|
-
|
|
68
|
+
new EmojiPicker({
|
|
69
|
+
container: '#btn',
|
|
70
|
+
dataUrl: '/static/emoji-data.json' // or your CDN URL
|
|
71
|
+
})
|
|
60
72
|
```
|
|
61
73
|
|
|
62
|
-
|
|
74
|
+
**If you use CDN**, both files are already on jsDelivr — no config needed:
|
|
63
75
|
|
|
64
76
|
```html
|
|
65
|
-
<script src="emoji-picker.js"></script>
|
|
66
|
-
<script>
|
|
67
|
-
const picker = new EmojiPicker({ container: '#btn' })
|
|
68
|
-
</script>
|
|
77
|
+
<script src="https://cdn.jsdelivr.net/npm/@schwitzerskills/emojipicker/emoji-picker.js"></script>
|
|
69
78
|
```
|
|
70
79
|
|
|
71
|
-
|
|
80
|
+
**How the caching works:**
|
|
72
81
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
```
|
|
82
|
+
1. First visit → `emoji-data.json` is fetched once (~850 KB)
|
|
83
|
+
2. Data is stored in **IndexedDB** on the user's device
|
|
84
|
+
3. Every visit after that → loaded from IndexedDB in milliseconds, zero network request
|
|
94
85
|
|
|
95
86
|
---
|
|
96
87
|
|
|
97
88
|
## Quick Start
|
|
98
89
|
|
|
99
|
-
|
|
90
|
+
### CDN / Vanilla JS
|
|
100
91
|
|
|
101
92
|
```html
|
|
102
|
-
|
|
103
|
-
<
|
|
93
|
+
<!DOCTYPE html>
|
|
94
|
+
<html lang="en">
|
|
95
|
+
<head>
|
|
96
|
+
<meta charset="UTF-8">
|
|
97
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
98
|
+
<title>My App</title>
|
|
99
|
+
</head>
|
|
100
|
+
<body>
|
|
101
|
+
|
|
102
|
+
<input type="text" id="message" placeholder="Type a message...">
|
|
103
|
+
<button id="emoji-btn">😊</button>
|
|
104
|
+
|
|
105
|
+
<script src="https://cdn.jsdelivr.net/npm/@schwitzerskills/emojipicker/emoji-picker.js"></script>
|
|
106
|
+
<script>
|
|
107
|
+
const picker = new EmojiPicker({ container: '#emoji-btn' })
|
|
108
|
+
picker.on('emojiClick', (emoji) => {
|
|
109
|
+
document.querySelector('#message').value += emoji.char
|
|
110
|
+
})
|
|
111
|
+
</script>
|
|
104
112
|
|
|
105
|
-
|
|
113
|
+
</body>
|
|
114
|
+
</html>
|
|
106
115
|
```
|
|
107
116
|
|
|
108
|
-
|
|
117
|
+
### npm / Bundler (Vite, Webpack, etc.)
|
|
109
118
|
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
const picker = new EmojiPicker({
|
|
113
|
-
container: '#emoji-btn',
|
|
114
|
-
theme: 'auto'
|
|
115
|
-
})
|
|
119
|
+
```js
|
|
120
|
+
import EmojiPicker from '@schwitzerskills/emojipicker'
|
|
116
121
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
122
|
+
const picker = new EmojiPicker({
|
|
123
|
+
container: '#emoji-btn',
|
|
124
|
+
dataUrl: '/public/emoji-data.json' // adjust to your setup
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
picker.on('emojiClick', (emoji) => {
|
|
128
|
+
document.querySelector('#message').value += emoji.char
|
|
129
|
+
})
|
|
121
130
|
```
|
|
122
131
|
|
|
123
|
-
|
|
132
|
+
### One-liner with `attachToInput()`
|
|
133
|
+
|
|
134
|
+
```js
|
|
135
|
+
import EmojiPicker from '@schwitzerskills/emojipicker'
|
|
136
|
+
|
|
137
|
+
// Automatically adds a 😊 button and inserts emoji at cursor
|
|
138
|
+
EmojiPicker.attachToInput('#message')
|
|
139
|
+
```
|
|
124
140
|
|
|
125
141
|
---
|
|
126
142
|
|
|
@@ -130,48 +146,50 @@ All properties are optional.
|
|
|
130
146
|
|
|
131
147
|
```js
|
|
132
148
|
const picker = new EmojiPicker({
|
|
133
|
-
container: '#my-button',
|
|
134
|
-
theme: 'auto',
|
|
135
|
-
mode: 'dropdown',
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
149
|
+
container: '#my-button',
|
|
150
|
+
theme: 'auto',
|
|
151
|
+
mode: 'dropdown',
|
|
152
|
+
locale: 'en',
|
|
153
|
+
search: true,
|
|
154
|
+
recentEmojis: true,
|
|
155
|
+
maxRecent: 24,
|
|
156
|
+
skinTone: 'default',
|
|
157
|
+
customEmojis: [],
|
|
158
|
+
perRow: 8,
|
|
159
|
+
emojiSize: 28,
|
|
160
|
+
autoClose: true,
|
|
161
|
+
dataUrl: null,
|
|
144
162
|
})
|
|
145
163
|
```
|
|
146
164
|
|
|
147
165
|
| Option | Type | Default | Description |
|
|
148
166
|
|--------|------|---------|-------------|
|
|
149
|
-
| `container` | `string \| HTMLElement` | `null` |
|
|
150
|
-
| `theme` | `string` | `'auto'` |
|
|
151
|
-
| `mode` | `string` | `'dropdown'` |
|
|
152
|
-
| `
|
|
153
|
-
| `
|
|
154
|
-
| `
|
|
155
|
-
| `
|
|
167
|
+
| `container` | `string \| HTMLElement` | `null` | Trigger element — toggles picker on click |
|
|
168
|
+
| `theme` | `string` | `'auto'` | `'light'`, `'dark'`, or `'auto'` |
|
|
169
|
+
| `mode` | `string` | `'dropdown'` | See [Modes](#modes) |
|
|
170
|
+
| `locale` | `string` | `'en'` | UI language — see [i18n](#i18n--localization) |
|
|
171
|
+
| `search` | `boolean` | `true` | Show/hide search input |
|
|
172
|
+
| `recentEmojis` | `boolean` | `true` | Track recents in IndexedDB |
|
|
173
|
+
| `maxRecent` | `number` | `24` | Max recent emojis to store |
|
|
174
|
+
| `skinTone` | `string` | `'default'` | Default skin tone |
|
|
156
175
|
| `customEmojis` | `array` | `[]` | Custom emoji definitions |
|
|
157
176
|
| `perRow` | `number` | `8` | Grid columns |
|
|
158
|
-
| `emojiSize` | `number` | `28` | Emoji size in px |
|
|
159
|
-
| `autoClose` | `boolean` | `true` |
|
|
177
|
+
| `emojiSize` | `number` | `28` | Emoji size in px (`--ep-size`) |
|
|
178
|
+
| `autoClose` | `boolean` | `true` | Close after selecting |
|
|
179
|
+
| `dataUrl` | `string` | `null` | Custom URL to `emoji-data.json` |
|
|
160
180
|
|
|
161
181
|
---
|
|
162
182
|
|
|
163
183
|
## Events
|
|
164
184
|
|
|
165
|
-
The library is fully event-driven. Attach any number of listeners to any event.
|
|
166
|
-
|
|
167
185
|
```js
|
|
168
|
-
picker.on(eventName, handler)
|
|
169
|
-
picker.off(eventName, handler) //
|
|
186
|
+
picker.on(eventName, handler) // add listener
|
|
187
|
+
picker.off(eventName, handler) // remove listener
|
|
170
188
|
```
|
|
171
189
|
|
|
172
190
|
### `emojiClick`
|
|
173
191
|
|
|
174
|
-
Fired when the user
|
|
192
|
+
Fired when the user selects an emoji. The main event you'll use.
|
|
175
193
|
|
|
176
194
|
```js
|
|
177
195
|
picker.on('emojiClick', (emoji, mouseEvent) => {
|
|
@@ -179,34 +197,25 @@ picker.on('emojiClick', (emoji, mouseEvent) => {
|
|
|
179
197
|
console.log(emoji.name) // "face_with_tears_of_joy"
|
|
180
198
|
console.log(emoji.category) // "Smileys & Emotion"
|
|
181
199
|
console.log(emoji.unicode) // "1F602"
|
|
182
|
-
console.log(emoji.skinTone) // null | "
|
|
200
|
+
console.log(emoji.skinTone) // null | "medium" | ...
|
|
183
201
|
})
|
|
184
202
|
```
|
|
185
203
|
|
|
186
204
|
### `emojiHover`
|
|
187
205
|
|
|
188
|
-
Fired when
|
|
206
|
+
Fired when hovering over an emoji.
|
|
189
207
|
|
|
190
208
|
```js
|
|
191
209
|
picker.on('emojiHover', (emoji, mouseEvent) => {
|
|
192
|
-
myPreview.textContent =
|
|
193
|
-
})
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
### `pickerOpen`
|
|
197
|
-
|
|
198
|
-
```js
|
|
199
|
-
picker.on('pickerOpen', () => {
|
|
200
|
-
console.log('Picker opened')
|
|
210
|
+
myPreview.textContent = emoji.char + ' ' + emoji.name
|
|
201
211
|
})
|
|
202
212
|
```
|
|
203
213
|
|
|
204
|
-
### `pickerClose`
|
|
214
|
+
### `pickerOpen` / `pickerClose`
|
|
205
215
|
|
|
206
216
|
```js
|
|
207
|
-
picker.on('
|
|
208
|
-
|
|
209
|
-
})
|
|
217
|
+
picker.on('pickerOpen', () => console.log('opened'))
|
|
218
|
+
picker.on('pickerClose', () => console.log('closed'))
|
|
210
219
|
```
|
|
211
220
|
|
|
212
221
|
### `categoryChange`
|
|
@@ -221,7 +230,7 @@ picker.on('categoryChange', ({ category }) => {
|
|
|
221
230
|
|
|
222
231
|
```js
|
|
223
232
|
picker.on('search', ({ query }) => {
|
|
224
|
-
console.log('
|
|
233
|
+
console.log('User typed:', query)
|
|
225
234
|
})
|
|
226
235
|
```
|
|
227
236
|
|
|
@@ -229,38 +238,65 @@ picker.on('search', ({ query }) => {
|
|
|
229
238
|
|
|
230
239
|
## Methods
|
|
231
240
|
|
|
232
|
-
All methods return `this` (chainable, except `destroy()`
|
|
241
|
+
All methods return `this` (chainable), except `destroy()` and the async methods.
|
|
242
|
+
|
|
243
|
+
```js
|
|
244
|
+
picker.open() // open
|
|
245
|
+
picker.close() // close
|
|
246
|
+
picker.toggle() // toggle
|
|
247
|
+
picker.setTheme('dark') // switch theme
|
|
248
|
+
picker.setLocale('de') // switch language
|
|
249
|
+
picker.destroy() // remove from DOM, clean up listeners
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
**Async methods:**
|
|
253
|
+
|
|
254
|
+
```js
|
|
255
|
+
// Returns top N most-clicked emojis
|
|
256
|
+
const favs = await picker.getTopFavorites(8)
|
|
257
|
+
// → [{ name, char, count }, ...]
|
|
258
|
+
|
|
259
|
+
// Clear recent history
|
|
260
|
+
await picker.clearRecent()
|
|
261
|
+
|
|
262
|
+
// Clear favorite click counts
|
|
263
|
+
await picker.clearFavorites()
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
**Static methods:**
|
|
233
267
|
|
|
234
268
|
```js
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
269
|
+
// Attach to any input (see section below)
|
|
270
|
+
EmojiPicker.attachToInput('#message', opts)
|
|
271
|
+
|
|
272
|
+
// Pre-warm: fetch + cache data without showing any UI
|
|
273
|
+
// Call this on app startup so first open is instant
|
|
274
|
+
await EmojiPicker.preload({ dataUrl: '/static/emoji-data.json' })
|
|
240
275
|
```
|
|
241
276
|
|
|
242
277
|
**Chaining:**
|
|
243
278
|
|
|
244
279
|
```js
|
|
245
|
-
new EmojiPicker({ container: '#btn'
|
|
246
|
-
.on('emojiClick',
|
|
247
|
-
.on('pickerOpen',
|
|
280
|
+
new EmojiPicker({ container: '#btn' })
|
|
281
|
+
.on('emojiClick', (e) => insertEmoji(e.char))
|
|
282
|
+
.on('pickerOpen', () => analytics.track('picker_opened'))
|
|
283
|
+
.on('pickerClose', () => analytics.track('picker_closed'))
|
|
248
284
|
```
|
|
249
285
|
|
|
250
286
|
---
|
|
251
287
|
|
|
252
288
|
## Emoji Object
|
|
253
289
|
|
|
254
|
-
Every emoji-related event provides this
|
|
290
|
+
Every emoji-related event provides this structure:
|
|
255
291
|
|
|
256
292
|
```js
|
|
257
293
|
{
|
|
258
|
-
char: "👍🏽", //
|
|
259
|
-
name: "thumbs_up", //
|
|
260
|
-
category: "People & Body", //
|
|
261
|
-
unicode: "1F44D", //
|
|
262
|
-
skinTone: "medium", // null if default
|
|
263
|
-
isCustom: false // true for custom emojis
|
|
294
|
+
char: "👍🏽", // emoji character, skin tone applied
|
|
295
|
+
name: "thumbs_up", // snake_case identifier
|
|
296
|
+
category: "People & Body", // category name
|
|
297
|
+
unicode: "1F44D", // base code point (hex)
|
|
298
|
+
skinTone: "medium", // null if default
|
|
299
|
+
isCustom: false // true for custom image emojis
|
|
264
300
|
}
|
|
265
301
|
```
|
|
266
302
|
|
|
@@ -270,7 +306,7 @@ Every emoji-related event provides this data structure:
|
|
|
270
306
|
|
|
271
307
|
### `dropdown` *(default)*
|
|
272
308
|
|
|
273
|
-
|
|
309
|
+
Floating panel anchored to the trigger element. Closes on outside click or `Esc`.
|
|
274
310
|
|
|
275
311
|
```js
|
|
276
312
|
new EmojiPicker({ container: '#btn', mode: 'dropdown' })
|
|
@@ -278,7 +314,7 @@ new EmojiPicker({ container: '#btn', mode: 'dropdown' })
|
|
|
278
314
|
|
|
279
315
|
### `inline`
|
|
280
316
|
|
|
281
|
-
Always visible, embedded
|
|
317
|
+
Always visible, embedded inside the container. `autoClose` is ignored.
|
|
282
318
|
|
|
283
319
|
```js
|
|
284
320
|
new EmojiPicker({ container: '#my-div', mode: 'inline', autoClose: false })
|
|
@@ -290,18 +326,18 @@ new EmojiPicker({ container: '#my-div', mode: 'inline', autoClose: false })
|
|
|
290
326
|
|
|
291
327
|
### `popup`
|
|
292
328
|
|
|
293
|
-
|
|
329
|
+
Centers in the viewport — ideal for modals or custom trigger logic.
|
|
294
330
|
|
|
295
331
|
```js
|
|
296
332
|
const picker = new EmojiPicker({ mode: 'popup' })
|
|
297
|
-
document.getElementById('
|
|
333
|
+
document.getElementById('btn').addEventListener('click', () => picker.open())
|
|
298
334
|
```
|
|
299
335
|
|
|
300
336
|
---
|
|
301
337
|
|
|
302
338
|
## Skin Tone Support
|
|
303
339
|
|
|
304
|
-
Users can
|
|
340
|
+
Users can pick a skin tone in the footer. Set a default in options:
|
|
305
341
|
|
|
306
342
|
```js
|
|
307
343
|
new EmojiPicker({ skinTone: 'medium-dark' })
|
|
@@ -316,46 +352,41 @@ new EmojiPicker({ skinTone: 'medium-dark' })
|
|
|
316
352
|
| `'medium-dark'` | 👍🏾 |
|
|
317
353
|
| `'dark'` | 👍🏿 |
|
|
318
354
|
|
|
319
|
-
The selected tone is reflected in `emoji.char` and reported in `emoji.skinTone`.
|
|
320
|
-
|
|
321
355
|
---
|
|
322
356
|
|
|
323
357
|
## Theming & CSS Variables
|
|
324
358
|
|
|
325
|
-
Override CSS variables to match any design system:
|
|
326
|
-
|
|
327
359
|
```css
|
|
328
360
|
.ep-picker {
|
|
329
|
-
--ep-bg: #
|
|
330
|
-
--ep-surface: #
|
|
331
|
-
--ep-
|
|
332
|
-
--ep-
|
|
333
|
-
--ep-text:
|
|
334
|
-
--ep-
|
|
335
|
-
--ep-
|
|
336
|
-
--ep-
|
|
337
|
-
--ep-
|
|
338
|
-
--ep-radius: 16px; /* Picker border radius */
|
|
361
|
+
--ep-bg: #16192a; /* picker background */
|
|
362
|
+
--ep-surface: #1e2236; /* surface / hover bg */
|
|
363
|
+
--ep-border: rgba(255,255,255,0.07);
|
|
364
|
+
--ep-text: #e2e6f5;
|
|
365
|
+
--ep-text-dim: #636b86;
|
|
366
|
+
--ep-accent: #6c63ff; /* active tab, focus rings */
|
|
367
|
+
--ep-hover: rgba(108,99,255,0.13);
|
|
368
|
+
--ep-size: 28px; /* emoji size */
|
|
369
|
+
--ep-radius: 18px; /* picker border-radius */
|
|
339
370
|
}
|
|
340
371
|
```
|
|
341
372
|
|
|
342
373
|
### Built-in themes
|
|
343
374
|
|
|
344
375
|
```js
|
|
345
|
-
new EmojiPicker({ theme: 'light' })
|
|
346
|
-
new EmojiPicker({ theme: 'dark' })
|
|
347
|
-
new EmojiPicker({ theme: 'auto' })
|
|
376
|
+
new EmojiPicker({ theme: 'light' }) // light
|
|
377
|
+
new EmojiPicker({ theme: 'dark' }) // dark
|
|
378
|
+
new EmojiPicker({ theme: 'auto' }) // follows OS
|
|
348
379
|
|
|
349
|
-
picker.setTheme('dark')
|
|
380
|
+
picker.setTheme('dark') // switch at runtime
|
|
350
381
|
```
|
|
351
382
|
|
|
352
|
-
### Custom
|
|
383
|
+
### Custom brand color
|
|
353
384
|
|
|
354
385
|
```css
|
|
355
386
|
.ep-picker {
|
|
356
387
|
--ep-accent: #e91e8c;
|
|
357
|
-
--ep-hover: rgba(233,
|
|
358
|
-
--ep-active-tab: rgba(233,
|
|
388
|
+
--ep-hover: rgba(233,30,140,0.10);
|
|
389
|
+
--ep-active-tab: rgba(233,30,140,0.18);
|
|
359
390
|
}
|
|
360
391
|
```
|
|
361
392
|
|
|
@@ -363,50 +394,139 @@ picker.setTheme('dark') // Switch at runtime
|
|
|
363
394
|
|
|
364
395
|
## Custom Emojis
|
|
365
396
|
|
|
366
|
-
Add your own
|
|
397
|
+
Add your own GIFs, PNGs or SVGs alongside the standard set:
|
|
367
398
|
|
|
368
399
|
```js
|
|
369
400
|
new EmojiPicker({
|
|
370
401
|
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' }
|
|
402
|
+
{ name: 'party_parrot', url: '/assets/parrot.gif' },
|
|
403
|
+
{ name: 'company_logo', url: '/assets/logo.png' },
|
|
404
|
+
{ name: 'custom_star', url: 'https://cdn.example.com/star.svg' }
|
|
374
405
|
]
|
|
375
406
|
})
|
|
376
407
|
```
|
|
377
408
|
|
|
378
|
-
|
|
409
|
+
They appear in a dedicated **Custom** tab. Click event returns:
|
|
379
410
|
|
|
380
411
|
```js
|
|
381
|
-
{
|
|
382
|
-
char: null,
|
|
383
|
-
name: 'party_parrot',
|
|
384
|
-
category: 'custom',
|
|
385
|
-
isCustom: true
|
|
386
|
-
}
|
|
412
|
+
{ char: null, name: 'party_parrot', category: 'custom', isCustom: true }
|
|
387
413
|
```
|
|
388
414
|
|
|
389
415
|
---
|
|
390
416
|
|
|
391
417
|
## Helper: attachToInput()
|
|
392
418
|
|
|
393
|
-
Wraps any `<input>` or `<textarea>` and
|
|
419
|
+
Wraps any `<input>` or `<textarea>` and handles cursor-position insertion automatically.
|
|
394
420
|
|
|
395
421
|
```js
|
|
396
|
-
//
|
|
397
|
-
EmojiPicker.attachToInput('#message
|
|
422
|
+
// Basic
|
|
423
|
+
EmojiPicker.attachToInput('#message')
|
|
398
424
|
|
|
399
425
|
// With options
|
|
400
426
|
EmojiPicker.attachToInput('#chat-box', {
|
|
401
|
-
theme:
|
|
402
|
-
skinTone: 'medium'
|
|
427
|
+
theme: 'dark',
|
|
428
|
+
skinTone: 'medium',
|
|
429
|
+
dataUrl: '/static/emoji-data.json'
|
|
403
430
|
})
|
|
404
431
|
|
|
405
|
-
// Returns the picker instance
|
|
406
|
-
const picker = EmojiPicker.attachToInput('#
|
|
407
|
-
picker.on('emojiClick', (
|
|
408
|
-
|
|
432
|
+
// Returns the picker instance
|
|
433
|
+
const picker = EmojiPicker.attachToInput('#editor')
|
|
434
|
+
picker.on('emojiClick', () => updateCharCount())
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
---
|
|
438
|
+
|
|
439
|
+
## Favorites & getTopFavorites()
|
|
440
|
+
|
|
441
|
+
Every emoji click is counted and stored in IndexedDB. Use this to build "most used" sections, reaction quick-bars, or analytics.
|
|
442
|
+
|
|
443
|
+
```js
|
|
444
|
+
// Get top 8 most-clicked emojis
|
|
445
|
+
const favs = await picker.getTopFavorites(8)
|
|
446
|
+
// → [{ name: 'thumbs_up', char: '👍', count: 42 }, ...]
|
|
447
|
+
|
|
448
|
+
// Render a quick-access bar
|
|
449
|
+
favs.forEach(({ char }) => {
|
|
450
|
+
const btn = document.createElement('button')
|
|
451
|
+
btn.textContent = char
|
|
452
|
+
quickBar.appendChild(btn)
|
|
453
|
+
})
|
|
454
|
+
|
|
455
|
+
// Reset counts
|
|
456
|
+
await picker.clearFavorites()
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
---
|
|
460
|
+
|
|
461
|
+
## i18n / Localization
|
|
462
|
+
|
|
463
|
+
### Built-in languages
|
|
464
|
+
|
|
465
|
+
| Code | Language |
|
|
466
|
+
|------|----------|
|
|
467
|
+
| `en` | English *(default)* |
|
|
468
|
+
| `de` | German |
|
|
469
|
+
| `fr` | French |
|
|
470
|
+
| `es` | Spanish |
|
|
471
|
+
| `pt` | Portuguese |
|
|
472
|
+
| `ja` | Japanese |
|
|
473
|
+
|
|
474
|
+
```js
|
|
475
|
+
// Set at construction
|
|
476
|
+
new EmojiPicker({ locale: 'de' })
|
|
477
|
+
|
|
478
|
+
// Switch at runtime (re-renders if open)
|
|
479
|
+
picker.setLocale('fr')
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
### Add a custom language
|
|
483
|
+
|
|
484
|
+
```js
|
|
485
|
+
EmojiPicker.LOCALES['nl'] = {
|
|
486
|
+
search: 'Zoek emoji…',
|
|
487
|
+
noResults: 'Geen resultaten voor',
|
|
488
|
+
noRecent: 'Nog geen recente emojis',
|
|
489
|
+
recent: 'Recent gebruikt',
|
|
490
|
+
custom: 'Aangepast',
|
|
491
|
+
loading: 'Laden…',
|
|
492
|
+
categories: {
|
|
493
|
+
recent: 'Recent', 'Smileys & Emotion': 'Smileys', 'People & Body': 'Mensen',
|
|
494
|
+
'Animals & Nature': 'Natuur', 'Food & Drink': 'Eten', Activities: 'Activiteiten',
|
|
495
|
+
'Travel & Places': 'Reizen', Objects: 'Objecten', Symbols: 'Symbolen',
|
|
496
|
+
Flags: 'Vlaggen', custom: 'Aangepast'
|
|
497
|
+
},
|
|
498
|
+
skinTones: {
|
|
499
|
+
default: 'Standaard', light: 'Licht', 'medium-light': 'Medium licht',
|
|
500
|
+
medium: 'Medium', 'medium-dark': 'Medium donker', dark: 'Donker'
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
new EmojiPicker({ locale: 'nl' })
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
---
|
|
508
|
+
|
|
509
|
+
## TypeScript
|
|
510
|
+
|
|
511
|
+
The package ships with a `.d.ts` file. No `@types/` package needed.
|
|
512
|
+
|
|
513
|
+
```ts
|
|
514
|
+
import EmojiPicker, { EmojiObject, EmojiPickerOptions, FavoriteEmoji } from '@schwitzerskills/emojipicker'
|
|
515
|
+
|
|
516
|
+
const options: EmojiPickerOptions = {
|
|
517
|
+
container: '#btn',
|
|
518
|
+
theme: 'auto',
|
|
519
|
+
locale: 'de',
|
|
520
|
+
dataUrl: '/static/emoji-data.json'
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
const picker = new EmojiPicker(options)
|
|
524
|
+
|
|
525
|
+
picker.on('emojiClick', (emoji: EmojiObject) => {
|
|
526
|
+
console.log(emoji.char, emoji.name)
|
|
409
527
|
})
|
|
528
|
+
|
|
529
|
+
const favs: FavoriteEmoji[] = await picker.getTopFavorites(10)
|
|
410
530
|
```
|
|
411
531
|
|
|
412
532
|
---
|
|
@@ -415,44 +535,48 @@ picker.on('emojiClick', (emoji) => {
|
|
|
415
535
|
|
|
416
536
|
### React
|
|
417
537
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
```jsx
|
|
538
|
+
```tsx
|
|
421
539
|
import { useEffect, useRef } from 'react'
|
|
422
|
-
import EmojiPicker from 'emojipicker
|
|
540
|
+
import EmojiPicker, { EmojiObject } from '@schwitzerskills/emojipicker'
|
|
423
541
|
|
|
424
|
-
|
|
425
|
-
|
|
542
|
+
interface Props {
|
|
543
|
+
onSelect: (emoji: EmojiObject) => void
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
export function EmojiButton({ onSelect }: Props) {
|
|
547
|
+
const btnRef = useRef<HTMLButtonElement>(null)
|
|
426
548
|
|
|
427
549
|
useEffect(() => {
|
|
550
|
+
if (!btnRef.current) return
|
|
428
551
|
const picker = new EmojiPicker({
|
|
429
552
|
container: btnRef.current,
|
|
430
|
-
theme:
|
|
553
|
+
theme: 'auto',
|
|
554
|
+
dataUrl: '/static/emoji-data.json'
|
|
431
555
|
})
|
|
432
556
|
picker.on('emojiClick', onSelect)
|
|
433
|
-
|
|
434
|
-
return () => picker.destroy() // clean up on unmount
|
|
557
|
+
return () => picker.destroy()
|
|
435
558
|
}, [onSelect])
|
|
436
559
|
|
|
437
|
-
return <button ref={btnRef}>😊</button>
|
|
560
|
+
return <button ref={btnRef} type="button">😊</button>
|
|
438
561
|
}
|
|
439
562
|
```
|
|
440
563
|
|
|
441
564
|
### Next.js
|
|
442
565
|
|
|
443
|
-
EmojiPicker uses `window` and `document` internally, so it must only run on the client.
|
|
566
|
+
`EmojiPicker` uses `window` and `document` internally, so it must only run on the client.
|
|
444
567
|
|
|
445
568
|
**Option A — dynamic import (recommended):**
|
|
446
569
|
|
|
447
|
-
```
|
|
448
|
-
// components/EmojiButton.
|
|
570
|
+
```tsx
|
|
571
|
+
// components/EmojiButton.tsx ← client-only wrapper
|
|
572
|
+
'use client'
|
|
449
573
|
import { useEffect, useRef } from 'react'
|
|
450
|
-
import EmojiPicker from 'emojipicker
|
|
574
|
+
import EmojiPicker from '@schwitzerskills/emojipicker'
|
|
451
575
|
|
|
452
576
|
export default function EmojiButton({ onSelect }) {
|
|
453
577
|
const btnRef = useRef(null)
|
|
454
578
|
useEffect(() => {
|
|
455
|
-
const picker = new EmojiPicker({ container: btnRef.current })
|
|
579
|
+
const picker = new EmojiPicker({ container: btnRef.current, dataUrl: '/emoji-data.json' })
|
|
456
580
|
picker.on('emojiClick', onSelect)
|
|
457
581
|
return () => picker.destroy()
|
|
458
582
|
}, [onSelect])
|
|
@@ -460,25 +584,18 @@ export default function EmojiButton({ onSelect }) {
|
|
|
460
584
|
}
|
|
461
585
|
```
|
|
462
586
|
|
|
463
|
-
```
|
|
464
|
-
// pages/index.
|
|
587
|
+
```tsx
|
|
588
|
+
// app/page.tsx or pages/index.tsx
|
|
465
589
|
import dynamic from 'next/dynamic'
|
|
466
590
|
|
|
467
|
-
const EmojiButton = dynamic(() => import('../components/EmojiButton'), {
|
|
468
|
-
ssr: false // prevents server-side execution
|
|
469
|
-
})
|
|
591
|
+
const EmojiButton = dynamic(() => import('../components/EmojiButton'), { ssr: false })
|
|
470
592
|
```
|
|
471
593
|
|
|
472
|
-
**Option B —
|
|
594
|
+
**Option B — App Router `'use client'` directive:**
|
|
473
595
|
|
|
474
|
-
```
|
|
475
|
-
|
|
476
|
-
|
|
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])
|
|
596
|
+
```tsx
|
|
597
|
+
'use client'
|
|
598
|
+
// useEffect only runs in the browser — safe without ssr:false
|
|
482
599
|
```
|
|
483
600
|
|
|
484
601
|
### Vue 3
|
|
@@ -488,16 +605,16 @@ useEffect(() => {
|
|
|
488
605
|
<button ref="btnRef">😊</button>
|
|
489
606
|
</template>
|
|
490
607
|
|
|
491
|
-
<script setup>
|
|
608
|
+
<script setup lang="ts">
|
|
492
609
|
import { ref, onMounted, onUnmounted } from 'vue'
|
|
493
|
-
import EmojiPicker from 'emojipicker
|
|
610
|
+
import EmojiPicker, { EmojiObject } from '@schwitzerskills/emojipicker'
|
|
494
611
|
|
|
495
|
-
const emit
|
|
496
|
-
const btnRef = ref(null)
|
|
497
|
-
let picker = null
|
|
612
|
+
const emit = defineEmits<{ select: [emoji: EmojiObject] }>()
|
|
613
|
+
const btnRef = ref<HTMLButtonElement | null>(null)
|
|
614
|
+
let picker: EmojiPicker | null = null
|
|
498
615
|
|
|
499
616
|
onMounted(() => {
|
|
500
|
-
picker = new EmojiPicker({ container: btnRef.value
|
|
617
|
+
picker = new EmojiPicker({ container: btnRef.value!, dataUrl: '/emoji-data.json' })
|
|
501
618
|
picker.on('emojiClick', (emoji) => emit('select', emoji))
|
|
502
619
|
})
|
|
503
620
|
|
|
@@ -508,16 +625,16 @@ onUnmounted(() => picker?.destroy())
|
|
|
508
625
|
### Svelte
|
|
509
626
|
|
|
510
627
|
```svelte
|
|
511
|
-
<script>
|
|
628
|
+
<script lang="ts">
|
|
512
629
|
import { onMount, onDestroy, createEventDispatcher } from 'svelte'
|
|
513
|
-
import EmojiPicker from 'emojipicker
|
|
630
|
+
import EmojiPicker from '@schwitzerskills/emojipicker'
|
|
514
631
|
|
|
515
632
|
const dispatch = createEventDispatcher()
|
|
516
|
-
let btnEl
|
|
517
|
-
let picker
|
|
633
|
+
let btnEl: HTMLButtonElement
|
|
634
|
+
let picker: EmojiPicker
|
|
518
635
|
|
|
519
636
|
onMount(() => {
|
|
520
|
-
picker = new EmojiPicker({ container: btnEl,
|
|
637
|
+
picker = new EmojiPicker({ container: btnEl, dataUrl: '/emoji-data.json' })
|
|
521
638
|
picker.on('emojiClick', (emoji) => dispatch('select', emoji))
|
|
522
639
|
})
|
|
523
640
|
|
|
@@ -527,39 +644,22 @@ onUnmounted(() => picker?.destroy())
|
|
|
527
644
|
<button bind:this={btnEl}>😊</button>
|
|
528
645
|
```
|
|
529
646
|
|
|
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
647
|
---
|
|
543
648
|
|
|
544
649
|
## Recipes / Examples
|
|
545
650
|
|
|
546
|
-
### Insert
|
|
651
|
+
### Insert at cursor in a textarea
|
|
547
652
|
|
|
548
653
|
```js
|
|
549
654
|
const textarea = document.querySelector('#editor')
|
|
550
|
-
const picker
|
|
655
|
+
const picker = new EmojiPicker({ container: '#btn' })
|
|
551
656
|
|
|
552
657
|
picker.on('emojiClick', (emoji) => {
|
|
553
|
-
const
|
|
554
|
-
const
|
|
658
|
+
const s = textarea.selectionStart
|
|
659
|
+
const e = textarea.selectionEnd
|
|
555
660
|
textarea.value =
|
|
556
|
-
textarea.value.
|
|
557
|
-
|
|
558
|
-
textarea.value.substring(end)
|
|
559
|
-
textarea.setSelectionRange(
|
|
560
|
-
start + emoji.char.length,
|
|
561
|
-
start + emoji.char.length
|
|
562
|
-
)
|
|
661
|
+
textarea.value.slice(0, s) + emoji.char + textarea.value.slice(e)
|
|
662
|
+
textarea.setSelectionRange(s + emoji.char.length, s + emoji.char.length)
|
|
563
663
|
textarea.focus()
|
|
564
664
|
})
|
|
565
665
|
```
|
|
@@ -568,76 +668,66 @@ picker.on('emojiClick', (emoji) => {
|
|
|
568
668
|
|
|
569
669
|
```js
|
|
570
670
|
picker.on('emojiClick', (emoji) => {
|
|
571
|
-
navigator.clipboard.writeText(emoji.char).then(() =>
|
|
572
|
-
showToast(`Copied ${emoji.char}`)
|
|
573
|
-
})
|
|
671
|
+
navigator.clipboard.writeText(emoji.char).then(() => showToast('Copied!'))
|
|
574
672
|
})
|
|
575
673
|
```
|
|
576
674
|
|
|
577
|
-
### Send
|
|
675
|
+
### Send to a server
|
|
578
676
|
|
|
579
677
|
```js
|
|
580
678
|
picker.on('emojiClick', (emoji) => {
|
|
581
679
|
fetch('/api/reactions', {
|
|
582
|
-
method:
|
|
680
|
+
method: 'POST',
|
|
583
681
|
headers: { 'Content-Type': 'application/json' },
|
|
584
|
-
body:
|
|
585
|
-
messageId: currentMessageId,
|
|
586
|
-
emoji: emoji.name,
|
|
587
|
-
char: emoji.char
|
|
588
|
-
})
|
|
682
|
+
body: JSON.stringify({ messageId, emoji: emoji.name, char: emoji.char })
|
|
589
683
|
})
|
|
590
684
|
})
|
|
591
685
|
```
|
|
592
686
|
|
|
593
|
-
###
|
|
687
|
+
### Preload data on app startup
|
|
594
688
|
|
|
595
689
|
```js
|
|
596
|
-
//
|
|
597
|
-
|
|
598
|
-
|
|
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
|
-
})
|
|
690
|
+
// Call once at app init — data is fetched and cached in IndexedDB.
|
|
691
|
+
// Every picker opened after this is instant.
|
|
692
|
+
EmojiPicker.preload({ dataUrl: '/static/emoji-data.json' })
|
|
610
693
|
```
|
|
611
694
|
|
|
612
|
-
###
|
|
695
|
+
### Quick-access bar from favorites
|
|
613
696
|
|
|
614
697
|
```js
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
698
|
+
const bar = document.getElementById('quick-bar')
|
|
699
|
+
|
|
700
|
+
async function renderFavBar() {
|
|
701
|
+
const favs = await picker.getTopFavorites(6)
|
|
702
|
+
bar.innerHTML = favs.map(({ char, name }) =>
|
|
703
|
+
`<button title="${name}" onclick="insertEmoji('${char}')">${char}</button>`
|
|
704
|
+
).join('')
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
picker.on('pickerClose', renderFavBar)
|
|
619
708
|
```
|
|
620
709
|
|
|
621
|
-
###
|
|
710
|
+
### Analytics
|
|
622
711
|
|
|
623
712
|
```js
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
})
|
|
713
|
+
picker.on('pickerOpen', () => analytics.track('picker_opened'))
|
|
714
|
+
picker.on('emojiClick', ({ name }) => analytics.track('emoji_used', { name }))
|
|
715
|
+
picker.on('search', ({ query }) => analytics.track('emoji_search', { query }))
|
|
716
|
+
picker.on('categoryChange', ({ category }) => analytics.track('category_view', { category }))
|
|
628
717
|
```
|
|
629
718
|
|
|
630
719
|
---
|
|
631
720
|
|
|
632
721
|
## Accessibility
|
|
633
722
|
|
|
634
|
-
-
|
|
635
|
-
-
|
|
636
|
-
-
|
|
637
|
-
-
|
|
638
|
-
-
|
|
723
|
+
- `role="dialog"` + `aria-modal="true"` on the picker
|
|
724
|
+
- `role="tablist"` on category tabs, `aria-selected` on active tab
|
|
725
|
+
- `role="grid"` + `role="gridcell"` on emoji grid
|
|
726
|
+
- `aria-label` on every emoji button
|
|
727
|
+
- `aria-live="polite"` on category label and preview
|
|
639
728
|
- `Esc` closes the picker
|
|
640
729
|
- Focus moves to the search input on open
|
|
730
|
+
- `type="button"` on all buttons — safe inside `<form>` elements
|
|
641
731
|
|
|
642
732
|
---
|
|
643
733
|
|
|
@@ -652,26 +742,72 @@ document.querySelector('#theme-toggle').addEventListener('click', () => {
|
|
|
652
742
|
| iOS Safari | 14+ |
|
|
653
743
|
| Android Chrome | 80+ |
|
|
654
744
|
|
|
655
|
-
Requires
|
|
745
|
+
Requires **IndexedDB** for caching — available in all modern browsers, gracefully degraded if blocked (e.g. Firefox private mode with `resistFingerprinting`).
|
|
746
|
+
|
|
747
|
+
---
|
|
748
|
+
|
|
749
|
+
## How It Works Internally
|
|
750
|
+
|
|
751
|
+
```
|
|
752
|
+
First visit:
|
|
753
|
+
emoji-picker.js (~5 KB) loads instantly
|
|
754
|
+
↓
|
|
755
|
+
picker.open() is called
|
|
756
|
+
↓
|
|
757
|
+
emoji-data.json is fetched (~850 KB, one time only)
|
|
758
|
+
↓
|
|
759
|
+
data is stored in IndexedDB
|
|
760
|
+
|
|
761
|
+
Every visit after:
|
|
762
|
+
emoji-picker.js loads
|
|
763
|
+
↓
|
|
764
|
+
picker.open() → data loads from IndexedDB in <5ms
|
|
765
|
+
↓
|
|
766
|
+
zero network request for emoji data
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
**IndexedDB stores:**
|
|
770
|
+
|
|
771
|
+
| Store | Contents | Key |
|
|
772
|
+
|-------|----------|-----|
|
|
773
|
+
| `cache` | Full emoji data JSON | `'emojidata'` |
|
|
774
|
+
| `recent` | Last used emojis + timestamps | `name` |
|
|
775
|
+
| `favorites` | Click counts per emoji | `name` |
|
|
776
|
+
|
|
777
|
+
**Emoji support detection:**
|
|
778
|
+
|
|
779
|
+
The library uses a canvas-based test to detect which Unicode Emoji version the OS supports, then hides emojis that would render as broken boxes. Tests run once per session and are cached in memory.
|
|
656
780
|
|
|
657
781
|
---
|
|
658
782
|
|
|
659
|
-
##
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
783
|
+
## package.json
|
|
784
|
+
|
|
785
|
+
No separate ESM build is needed. The UMD bundle handles `require()`, `import`, and `<script>` tags.
|
|
786
|
+
|
|
787
|
+
```json
|
|
788
|
+
{
|
|
789
|
+
"name": "@schwitzerskills/emojipicker",
|
|
790
|
+
"version": "2.0.0",
|
|
791
|
+
"main": "emoji-picker.js",
|
|
792
|
+
"browser": "emoji-picker.js",
|
|
793
|
+
"types": "emoji-picker.d.ts",
|
|
794
|
+
"exports": {
|
|
795
|
+
".": {
|
|
796
|
+
"require": "./emoji-picker.js",
|
|
797
|
+
"import": "./emoji-picker.js",
|
|
798
|
+
"types": "./emoji-picker.d.ts"
|
|
799
|
+
}
|
|
800
|
+
},
|
|
801
|
+
"files": [
|
|
802
|
+
"emoji-picker.js",
|
|
803
|
+
"emoji-picker.d.ts",
|
|
804
|
+
"emoji-data.json"
|
|
805
|
+
]
|
|
806
|
+
}
|
|
807
|
+
```
|
|
673
808
|
|
|
674
809
|
---
|
|
675
810
|
|
|
676
811
|
## License
|
|
677
|
-
|
|
812
|
+
|
|
813
|
+
Apache
|