@schwitzerskills/emojipicker 1.0.5 → 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 +394 -296
- 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,34 +24,70 @@ 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
|
|
|
36
|
-
### npm
|
|
40
|
+
### npm
|
|
37
41
|
|
|
38
42
|
```bash
|
|
39
43
|
npm install @schwitzerskills/emojipicker
|
|
40
|
-
# or
|
|
41
|
-
yarn add @schwitzerskills/emojipicker
|
|
42
|
-
# or
|
|
43
|
-
pnpm add @schwitzerskills/emojipicker
|
|
44
44
|
```
|
|
45
45
|
|
|
46
46
|
### CDN (jsDelivr)
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
```html
|
|
49
|
+
<script src="https://cdn.jsdelivr.net/npm/@schwitzerskills/emojipicker/emoji-picker.js"></script>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Setup — Two Files
|
|
55
|
+
|
|
56
|
+
> **Important:** The library needs two files to work.
|
|
57
|
+
|
|
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) |
|
|
62
|
+
|
|
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.
|
|
64
|
+
|
|
65
|
+
**If you use npm / a bundler**, copy `emoji-data.json` to your public/static folder and pass the URL manually:
|
|
66
|
+
|
|
67
|
+
```js
|
|
68
|
+
new EmojiPicker({
|
|
69
|
+
container: '#btn',
|
|
70
|
+
dataUrl: '/static/emoji-data.json' // or your CDN URL
|
|
71
|
+
})
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**If you use CDN**, both files are already on jsDelivr — no config needed:
|
|
49
75
|
|
|
50
76
|
```html
|
|
51
77
|
<script src="https://cdn.jsdelivr.net/npm/@schwitzerskills/emojipicker/emoji-picker.js"></script>
|
|
52
78
|
```
|
|
53
79
|
|
|
54
|
-
|
|
80
|
+
**How the caching works:**
|
|
81
|
+
|
|
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
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Quick Start
|
|
89
|
+
|
|
90
|
+
### CDN / Vanilla JS
|
|
55
91
|
|
|
56
92
|
```html
|
|
57
93
|
<!DOCTYPE html>
|
|
@@ -78,87 +114,30 @@ Full working example:
|
|
|
78
114
|
</html>
|
|
79
115
|
```
|
|
80
116
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
## Module Formats
|
|
84
|
-
|
|
85
|
-
The library ships as a **UMD build** and supports all environments out of the box.
|
|
86
|
-
|
|
87
|
-
### CommonJS (Node.js / bundlers)
|
|
88
|
-
|
|
89
|
-
```js
|
|
90
|
-
const EmojiPicker = require('@schwitzerskills/emojipicker')
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
### ES Module
|
|
117
|
+
### npm / Bundler (Vite, Webpack, etc.)
|
|
94
118
|
|
|
95
119
|
```js
|
|
96
120
|
import EmojiPicker from '@schwitzerskills/emojipicker'
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
### Browser global (CDN / script tag)
|
|
100
121
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const picker = new EmojiPicker({ container: '#btn' })
|
|
105
|
-
</script>
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
### AMD (RequireJS)
|
|
109
|
-
|
|
110
|
-
```js
|
|
111
|
-
define(['@schwitzerskills/emojipicker'], function(EmojiPicker) {
|
|
112
|
-
const picker = new EmojiPicker({ container: '#btn' })
|
|
122
|
+
const picker = new EmojiPicker({
|
|
123
|
+
container: '#emoji-btn',
|
|
124
|
+
dataUrl: '/public/emoji-data.json' // adjust to your setup
|
|
113
125
|
})
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
The `package.json` fields are set up accordingly:
|
|
117
126
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
"module": "emoji-picker.esm.js",
|
|
122
|
-
"browser": "emoji-picker.js",
|
|
123
|
-
"exports": {
|
|
124
|
-
".": {
|
|
125
|
-
"import": "./emoji-picker.esm.js",
|
|
126
|
-
"require": "./emoji-picker.js"
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
---
|
|
133
|
-
|
|
134
|
-
## Quick Start
|
|
135
|
-
|
|
136
|
-
**Step 1 — Add a trigger button to your HTML:**
|
|
137
|
-
|
|
138
|
-
```html
|
|
139
|
-
<button id="emoji-btn">😊</button>
|
|
140
|
-
<input type="text" id="message" placeholder="Type a message...">
|
|
141
|
-
|
|
142
|
-
<script src="emoji-picker.js"></script>
|
|
127
|
+
picker.on('emojiClick', (emoji) => {
|
|
128
|
+
document.querySelector('#message').value += emoji.char
|
|
129
|
+
})
|
|
143
130
|
```
|
|
144
131
|
|
|
145
|
-
|
|
132
|
+
### One-liner with `attachToInput()`
|
|
146
133
|
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
const picker = new EmojiPicker({
|
|
150
|
-
container: '#emoji-btn',
|
|
151
|
-
theme: 'auto'
|
|
152
|
-
})
|
|
134
|
+
```js
|
|
135
|
+
import EmojiPicker from '@schwitzerskills/emojipicker'
|
|
153
136
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
})
|
|
157
|
-
</script>
|
|
137
|
+
// Automatically adds a 😊 button and inserts emoji at cursor
|
|
138
|
+
EmojiPicker.attachToInput('#message')
|
|
158
139
|
```
|
|
159
140
|
|
|
160
|
-
That's it. Clicking `#emoji-btn` opens the picker. Clicking an emoji fires `emojiClick`.
|
|
161
|
-
|
|
162
141
|
---
|
|
163
142
|
|
|
164
143
|
## Configuration Options
|
|
@@ -167,48 +146,50 @@ All properties are optional.
|
|
|
167
146
|
|
|
168
147
|
```js
|
|
169
148
|
const picker = new EmojiPicker({
|
|
170
|
-
container: '#my-button',
|
|
171
|
-
theme: 'auto',
|
|
172
|
-
mode: 'dropdown',
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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,
|
|
181
162
|
})
|
|
182
163
|
```
|
|
183
164
|
|
|
184
165
|
| Option | Type | Default | Description |
|
|
185
166
|
|--------|------|---------|-------------|
|
|
186
|
-
| `container` | `string \| HTMLElement` | `null` |
|
|
187
|
-
| `theme` | `string` | `'auto'` |
|
|
188
|
-
| `mode` | `string` | `'dropdown'` |
|
|
189
|
-
| `
|
|
190
|
-
| `
|
|
191
|
-
| `
|
|
192
|
-
| `
|
|
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 |
|
|
193
175
|
| `customEmojis` | `array` | `[]` | Custom emoji definitions |
|
|
194
176
|
| `perRow` | `number` | `8` | Grid columns |
|
|
195
|
-
| `emojiSize` | `number` | `28` | Emoji size in px |
|
|
196
|
-
| `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` |
|
|
197
180
|
|
|
198
181
|
---
|
|
199
182
|
|
|
200
183
|
## Events
|
|
201
184
|
|
|
202
|
-
The library is fully event-driven. Attach any number of listeners to any event.
|
|
203
|
-
|
|
204
185
|
```js
|
|
205
|
-
picker.on(eventName, handler)
|
|
206
|
-
picker.off(eventName, handler) //
|
|
186
|
+
picker.on(eventName, handler) // add listener
|
|
187
|
+
picker.off(eventName, handler) // remove listener
|
|
207
188
|
```
|
|
208
189
|
|
|
209
190
|
### `emojiClick`
|
|
210
191
|
|
|
211
|
-
Fired when the user
|
|
192
|
+
Fired when the user selects an emoji. The main event you'll use.
|
|
212
193
|
|
|
213
194
|
```js
|
|
214
195
|
picker.on('emojiClick', (emoji, mouseEvent) => {
|
|
@@ -216,34 +197,25 @@ picker.on('emojiClick', (emoji, mouseEvent) => {
|
|
|
216
197
|
console.log(emoji.name) // "face_with_tears_of_joy"
|
|
217
198
|
console.log(emoji.category) // "Smileys & Emotion"
|
|
218
199
|
console.log(emoji.unicode) // "1F602"
|
|
219
|
-
console.log(emoji.skinTone) // null | "
|
|
200
|
+
console.log(emoji.skinTone) // null | "medium" | ...
|
|
220
201
|
})
|
|
221
202
|
```
|
|
222
203
|
|
|
223
204
|
### `emojiHover`
|
|
224
205
|
|
|
225
|
-
Fired when
|
|
206
|
+
Fired when hovering over an emoji.
|
|
226
207
|
|
|
227
208
|
```js
|
|
228
209
|
picker.on('emojiHover', (emoji, mouseEvent) => {
|
|
229
|
-
myPreview.textContent =
|
|
210
|
+
myPreview.textContent = emoji.char + ' ' + emoji.name
|
|
230
211
|
})
|
|
231
212
|
```
|
|
232
213
|
|
|
233
|
-
### `pickerOpen`
|
|
214
|
+
### `pickerOpen` / `pickerClose`
|
|
234
215
|
|
|
235
216
|
```js
|
|
236
|
-
picker.on('pickerOpen',
|
|
237
|
-
|
|
238
|
-
})
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
### `pickerClose`
|
|
242
|
-
|
|
243
|
-
```js
|
|
244
|
-
picker.on('pickerClose', () => {
|
|
245
|
-
console.log('Picker closed')
|
|
246
|
-
})
|
|
217
|
+
picker.on('pickerOpen', () => console.log('opened'))
|
|
218
|
+
picker.on('pickerClose', () => console.log('closed'))
|
|
247
219
|
```
|
|
248
220
|
|
|
249
221
|
### `categoryChange`
|
|
@@ -258,7 +230,7 @@ picker.on('categoryChange', ({ category }) => {
|
|
|
258
230
|
|
|
259
231
|
```js
|
|
260
232
|
picker.on('search', ({ query }) => {
|
|
261
|
-
console.log('
|
|
233
|
+
console.log('User typed:', query)
|
|
262
234
|
})
|
|
263
235
|
```
|
|
264
236
|
|
|
@@ -266,38 +238,65 @@ picker.on('search', ({ query }) => {
|
|
|
266
238
|
|
|
267
239
|
## Methods
|
|
268
240
|
|
|
269
|
-
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:**
|
|
270
267
|
|
|
271
268
|
```js
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
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' })
|
|
277
275
|
```
|
|
278
276
|
|
|
279
277
|
**Chaining:**
|
|
280
278
|
|
|
281
279
|
```js
|
|
282
|
-
new EmojiPicker({ container: '#btn'
|
|
283
|
-
.on('emojiClick',
|
|
284
|
-
.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'))
|
|
285
284
|
```
|
|
286
285
|
|
|
287
286
|
---
|
|
288
287
|
|
|
289
288
|
## Emoji Object
|
|
290
289
|
|
|
291
|
-
Every emoji-related event provides this
|
|
290
|
+
Every emoji-related event provides this structure:
|
|
292
291
|
|
|
293
292
|
```js
|
|
294
293
|
{
|
|
295
|
-
char: "👍🏽", //
|
|
296
|
-
name: "thumbs_up", //
|
|
297
|
-
category: "People & Body", //
|
|
298
|
-
unicode: "1F44D", //
|
|
299
|
-
skinTone: "medium", // null if default
|
|
300
|
-
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
|
|
301
300
|
}
|
|
302
301
|
```
|
|
303
302
|
|
|
@@ -307,7 +306,7 @@ Every emoji-related event provides this data structure:
|
|
|
307
306
|
|
|
308
307
|
### `dropdown` *(default)*
|
|
309
308
|
|
|
310
|
-
|
|
309
|
+
Floating panel anchored to the trigger element. Closes on outside click or `Esc`.
|
|
311
310
|
|
|
312
311
|
```js
|
|
313
312
|
new EmojiPicker({ container: '#btn', mode: 'dropdown' })
|
|
@@ -315,7 +314,7 @@ new EmojiPicker({ container: '#btn', mode: 'dropdown' })
|
|
|
315
314
|
|
|
316
315
|
### `inline`
|
|
317
316
|
|
|
318
|
-
Always visible, embedded
|
|
317
|
+
Always visible, embedded inside the container. `autoClose` is ignored.
|
|
319
318
|
|
|
320
319
|
```js
|
|
321
320
|
new EmojiPicker({ container: '#my-div', mode: 'inline', autoClose: false })
|
|
@@ -327,18 +326,18 @@ new EmojiPicker({ container: '#my-div', mode: 'inline', autoClose: false })
|
|
|
327
326
|
|
|
328
327
|
### `popup`
|
|
329
328
|
|
|
330
|
-
|
|
329
|
+
Centers in the viewport — ideal for modals or custom trigger logic.
|
|
331
330
|
|
|
332
331
|
```js
|
|
333
332
|
const picker = new EmojiPicker({ mode: 'popup' })
|
|
334
|
-
document.getElementById('
|
|
333
|
+
document.getElementById('btn').addEventListener('click', () => picker.open())
|
|
335
334
|
```
|
|
336
335
|
|
|
337
336
|
---
|
|
338
337
|
|
|
339
338
|
## Skin Tone Support
|
|
340
339
|
|
|
341
|
-
Users can
|
|
340
|
+
Users can pick a skin tone in the footer. Set a default in options:
|
|
342
341
|
|
|
343
342
|
```js
|
|
344
343
|
new EmojiPicker({ skinTone: 'medium-dark' })
|
|
@@ -353,46 +352,41 @@ new EmojiPicker({ skinTone: 'medium-dark' })
|
|
|
353
352
|
| `'medium-dark'` | 👍🏾 |
|
|
354
353
|
| `'dark'` | 👍🏿 |
|
|
355
354
|
|
|
356
|
-
The selected tone is reflected in `emoji.char` and reported in `emoji.skinTone`.
|
|
357
|
-
|
|
358
355
|
---
|
|
359
356
|
|
|
360
357
|
## Theming & CSS Variables
|
|
361
358
|
|
|
362
|
-
Override CSS variables to match any design system:
|
|
363
|
-
|
|
364
359
|
```css
|
|
365
360
|
.ep-picker {
|
|
366
|
-
--ep-bg: #
|
|
367
|
-
--ep-surface: #
|
|
368
|
-
--ep-
|
|
369
|
-
--ep-
|
|
370
|
-
--ep-text:
|
|
371
|
-
--ep-
|
|
372
|
-
--ep-
|
|
373
|
-
--ep-
|
|
374
|
-
--ep-
|
|
375
|
-
--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 */
|
|
376
370
|
}
|
|
377
371
|
```
|
|
378
372
|
|
|
379
373
|
### Built-in themes
|
|
380
374
|
|
|
381
375
|
```js
|
|
382
|
-
new EmojiPicker({ theme: 'light' })
|
|
383
|
-
new EmojiPicker({ theme: 'dark' })
|
|
384
|
-
new EmojiPicker({ theme: 'auto' })
|
|
376
|
+
new EmojiPicker({ theme: 'light' }) // light
|
|
377
|
+
new EmojiPicker({ theme: 'dark' }) // dark
|
|
378
|
+
new EmojiPicker({ theme: 'auto' }) // follows OS
|
|
385
379
|
|
|
386
|
-
picker.setTheme('dark')
|
|
380
|
+
picker.setTheme('dark') // switch at runtime
|
|
387
381
|
```
|
|
388
382
|
|
|
389
|
-
### Custom
|
|
383
|
+
### Custom brand color
|
|
390
384
|
|
|
391
385
|
```css
|
|
392
386
|
.ep-picker {
|
|
393
387
|
--ep-accent: #e91e8c;
|
|
394
|
-
--ep-hover: rgba(233,
|
|
395
|
-
--ep-active-tab: rgba(233,
|
|
388
|
+
--ep-hover: rgba(233,30,140,0.10);
|
|
389
|
+
--ep-active-tab: rgba(233,30,140,0.18);
|
|
396
390
|
}
|
|
397
391
|
```
|
|
398
392
|
|
|
@@ -400,50 +394,139 @@ picker.setTheme('dark') // Switch at runtime
|
|
|
400
394
|
|
|
401
395
|
## Custom Emojis
|
|
402
396
|
|
|
403
|
-
Add your own
|
|
397
|
+
Add your own GIFs, PNGs or SVGs alongside the standard set:
|
|
404
398
|
|
|
405
399
|
```js
|
|
406
400
|
new EmojiPicker({
|
|
407
401
|
customEmojis: [
|
|
408
|
-
{ name: 'party_parrot', url: '/assets/parrot.gif'
|
|
409
|
-
{ name: 'company_logo', url: '/assets/logo.png'
|
|
410
|
-
{ 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' }
|
|
411
405
|
]
|
|
412
406
|
})
|
|
413
407
|
```
|
|
414
408
|
|
|
415
|
-
|
|
409
|
+
They appear in a dedicated **Custom** tab. Click event returns:
|
|
416
410
|
|
|
417
411
|
```js
|
|
418
|
-
{
|
|
419
|
-
char: null,
|
|
420
|
-
name: 'party_parrot',
|
|
421
|
-
category: 'custom',
|
|
422
|
-
isCustom: true
|
|
423
|
-
}
|
|
412
|
+
{ char: null, name: 'party_parrot', category: 'custom', isCustom: true }
|
|
424
413
|
```
|
|
425
414
|
|
|
426
415
|
---
|
|
427
416
|
|
|
428
417
|
## Helper: attachToInput()
|
|
429
418
|
|
|
430
|
-
Wraps any `<input>` or `<textarea>` and
|
|
419
|
+
Wraps any `<input>` or `<textarea>` and handles cursor-position insertion automatically.
|
|
431
420
|
|
|
432
421
|
```js
|
|
433
|
-
//
|
|
434
|
-
EmojiPicker.attachToInput('#message
|
|
422
|
+
// Basic
|
|
423
|
+
EmojiPicker.attachToInput('#message')
|
|
435
424
|
|
|
436
425
|
// With options
|
|
437
426
|
EmojiPicker.attachToInput('#chat-box', {
|
|
438
|
-
theme:
|
|
439
|
-
skinTone: 'medium'
|
|
427
|
+
theme: 'dark',
|
|
428
|
+
skinTone: 'medium',
|
|
429
|
+
dataUrl: '/static/emoji-data.json'
|
|
440
430
|
})
|
|
441
431
|
|
|
442
|
-
// Returns the picker instance
|
|
443
|
-
const picker = EmojiPicker.attachToInput('#
|
|
444
|
-
picker.on('emojiClick', (
|
|
445
|
-
|
|
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)
|
|
446
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)
|
|
527
|
+
})
|
|
528
|
+
|
|
529
|
+
const favs: FavoriteEmoji[] = await picker.getTopFavorites(10)
|
|
447
530
|
```
|
|
448
531
|
|
|
449
532
|
---
|
|
@@ -452,44 +535,48 @@ picker.on('emojiClick', (emoji) => {
|
|
|
452
535
|
|
|
453
536
|
### React
|
|
454
537
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
```jsx
|
|
538
|
+
```tsx
|
|
458
539
|
import { useEffect, useRef } from 'react'
|
|
459
|
-
import EmojiPicker from '@schwitzerskills/emojipicker'
|
|
540
|
+
import EmojiPicker, { EmojiObject } from '@schwitzerskills/emojipicker'
|
|
460
541
|
|
|
461
|
-
|
|
462
|
-
|
|
542
|
+
interface Props {
|
|
543
|
+
onSelect: (emoji: EmojiObject) => void
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
export function EmojiButton({ onSelect }: Props) {
|
|
547
|
+
const btnRef = useRef<HTMLButtonElement>(null)
|
|
463
548
|
|
|
464
549
|
useEffect(() => {
|
|
550
|
+
if (!btnRef.current) return
|
|
465
551
|
const picker = new EmojiPicker({
|
|
466
552
|
container: btnRef.current,
|
|
467
|
-
theme:
|
|
553
|
+
theme: 'auto',
|
|
554
|
+
dataUrl: '/static/emoji-data.json'
|
|
468
555
|
})
|
|
469
556
|
picker.on('emojiClick', onSelect)
|
|
470
|
-
|
|
471
|
-
return () => picker.destroy() // clean up on unmount
|
|
557
|
+
return () => picker.destroy()
|
|
472
558
|
}, [onSelect])
|
|
473
559
|
|
|
474
|
-
return <button ref={btnRef}>😊</button>
|
|
560
|
+
return <button ref={btnRef} type="button">😊</button>
|
|
475
561
|
}
|
|
476
562
|
```
|
|
477
563
|
|
|
478
564
|
### Next.js
|
|
479
565
|
|
|
480
|
-
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.
|
|
481
567
|
|
|
482
568
|
**Option A — dynamic import (recommended):**
|
|
483
569
|
|
|
484
|
-
```
|
|
485
|
-
// components/EmojiButton.
|
|
570
|
+
```tsx
|
|
571
|
+
// components/EmojiButton.tsx ← client-only wrapper
|
|
572
|
+
'use client'
|
|
486
573
|
import { useEffect, useRef } from 'react'
|
|
487
574
|
import EmojiPicker from '@schwitzerskills/emojipicker'
|
|
488
575
|
|
|
489
576
|
export default function EmojiButton({ onSelect }) {
|
|
490
577
|
const btnRef = useRef(null)
|
|
491
578
|
useEffect(() => {
|
|
492
|
-
const picker = new EmojiPicker({ container: btnRef.current })
|
|
579
|
+
const picker = new EmojiPicker({ container: btnRef.current, dataUrl: '/emoji-data.json' })
|
|
493
580
|
picker.on('emojiClick', onSelect)
|
|
494
581
|
return () => picker.destroy()
|
|
495
582
|
}, [onSelect])
|
|
@@ -497,25 +584,18 @@ export default function EmojiButton({ onSelect }) {
|
|
|
497
584
|
}
|
|
498
585
|
```
|
|
499
586
|
|
|
500
|
-
```
|
|
501
|
-
// pages/index.
|
|
587
|
+
```tsx
|
|
588
|
+
// app/page.tsx or pages/index.tsx
|
|
502
589
|
import dynamic from 'next/dynamic'
|
|
503
590
|
|
|
504
|
-
const EmojiButton = dynamic(() => import('../components/EmojiButton'), {
|
|
505
|
-
ssr: false // prevents server-side execution
|
|
506
|
-
})
|
|
591
|
+
const EmojiButton = dynamic(() => import('../components/EmojiButton'), { ssr: false })
|
|
507
592
|
```
|
|
508
593
|
|
|
509
|
-
**Option B —
|
|
594
|
+
**Option B — App Router `'use client'` directive:**
|
|
510
595
|
|
|
511
|
-
```
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
// Add typeof window check only if you import EmojiPicker outside of useEffect.
|
|
515
|
-
const picker = new EmojiPicker({ container: btnRef.current })
|
|
516
|
-
picker.on('emojiClick', onSelect)
|
|
517
|
-
return () => picker.destroy()
|
|
518
|
-
}, [onSelect])
|
|
596
|
+
```tsx
|
|
597
|
+
'use client'
|
|
598
|
+
// useEffect only runs in the browser — safe without ssr:false
|
|
519
599
|
```
|
|
520
600
|
|
|
521
601
|
### Vue 3
|
|
@@ -525,16 +605,16 @@ useEffect(() => {
|
|
|
525
605
|
<button ref="btnRef">😊</button>
|
|
526
606
|
</template>
|
|
527
607
|
|
|
528
|
-
<script setup>
|
|
608
|
+
<script setup lang="ts">
|
|
529
609
|
import { ref, onMounted, onUnmounted } from 'vue'
|
|
530
|
-
import EmojiPicker from '@schwitzerskills/emojipicker'
|
|
610
|
+
import EmojiPicker, { EmojiObject } from '@schwitzerskills/emojipicker'
|
|
531
611
|
|
|
532
|
-
const emit
|
|
533
|
-
const btnRef = ref(null)
|
|
534
|
-
let picker = null
|
|
612
|
+
const emit = defineEmits<{ select: [emoji: EmojiObject] }>()
|
|
613
|
+
const btnRef = ref<HTMLButtonElement | null>(null)
|
|
614
|
+
let picker: EmojiPicker | null = null
|
|
535
615
|
|
|
536
616
|
onMounted(() => {
|
|
537
|
-
picker = new EmojiPicker({ container: btnRef.value
|
|
617
|
+
picker = new EmojiPicker({ container: btnRef.value!, dataUrl: '/emoji-data.json' })
|
|
538
618
|
picker.on('emojiClick', (emoji) => emit('select', emoji))
|
|
539
619
|
})
|
|
540
620
|
|
|
@@ -545,16 +625,16 @@ onUnmounted(() => picker?.destroy())
|
|
|
545
625
|
### Svelte
|
|
546
626
|
|
|
547
627
|
```svelte
|
|
548
|
-
<script>
|
|
628
|
+
<script lang="ts">
|
|
549
629
|
import { onMount, onDestroy, createEventDispatcher } from 'svelte'
|
|
550
630
|
import EmojiPicker from '@schwitzerskills/emojipicker'
|
|
551
631
|
|
|
552
632
|
const dispatch = createEventDispatcher()
|
|
553
|
-
let btnEl
|
|
554
|
-
let picker
|
|
633
|
+
let btnEl: HTMLButtonElement
|
|
634
|
+
let picker: EmojiPicker
|
|
555
635
|
|
|
556
636
|
onMount(() => {
|
|
557
|
-
picker = new EmojiPicker({ container: btnEl,
|
|
637
|
+
picker = new EmojiPicker({ container: btnEl, dataUrl: '/emoji-data.json' })
|
|
558
638
|
picker.on('emojiClick', (emoji) => dispatch('select', emoji))
|
|
559
639
|
})
|
|
560
640
|
|
|
@@ -564,39 +644,22 @@ onUnmounted(() => picker?.destroy())
|
|
|
564
644
|
<button bind:this={btnEl}>😊</button>
|
|
565
645
|
```
|
|
566
646
|
|
|
567
|
-
### Vanilla JS (no bundler)
|
|
568
|
-
|
|
569
|
-
```html
|
|
570
|
-
<script src="https://cdn.example.com/emoji-picker.js"></script>
|
|
571
|
-
<script>
|
|
572
|
-
const picker = new EmojiPicker({ container: '#btn', theme: 'auto' })
|
|
573
|
-
picker.on('emojiClick', (emoji) => {
|
|
574
|
-
document.querySelector('#input').value += emoji.char
|
|
575
|
-
})
|
|
576
|
-
</script>
|
|
577
|
-
```
|
|
578
|
-
|
|
579
647
|
---
|
|
580
648
|
|
|
581
649
|
## Recipes / Examples
|
|
582
650
|
|
|
583
|
-
### Insert
|
|
651
|
+
### Insert at cursor in a textarea
|
|
584
652
|
|
|
585
653
|
```js
|
|
586
654
|
const textarea = document.querySelector('#editor')
|
|
587
|
-
const picker
|
|
655
|
+
const picker = new EmojiPicker({ container: '#btn' })
|
|
588
656
|
|
|
589
657
|
picker.on('emojiClick', (emoji) => {
|
|
590
|
-
const
|
|
591
|
-
const
|
|
658
|
+
const s = textarea.selectionStart
|
|
659
|
+
const e = textarea.selectionEnd
|
|
592
660
|
textarea.value =
|
|
593
|
-
textarea.value.
|
|
594
|
-
|
|
595
|
-
textarea.value.substring(end)
|
|
596
|
-
textarea.setSelectionRange(
|
|
597
|
-
start + emoji.char.length,
|
|
598
|
-
start + emoji.char.length
|
|
599
|
-
)
|
|
661
|
+
textarea.value.slice(0, s) + emoji.char + textarea.value.slice(e)
|
|
662
|
+
textarea.setSelectionRange(s + emoji.char.length, s + emoji.char.length)
|
|
600
663
|
textarea.focus()
|
|
601
664
|
})
|
|
602
665
|
```
|
|
@@ -605,76 +668,66 @@ picker.on('emojiClick', (emoji) => {
|
|
|
605
668
|
|
|
606
669
|
```js
|
|
607
670
|
picker.on('emojiClick', (emoji) => {
|
|
608
|
-
navigator.clipboard.writeText(emoji.char).then(() =>
|
|
609
|
-
showToast(`Copied ${emoji.char}`)
|
|
610
|
-
})
|
|
671
|
+
navigator.clipboard.writeText(emoji.char).then(() => showToast('Copied!'))
|
|
611
672
|
})
|
|
612
673
|
```
|
|
613
674
|
|
|
614
|
-
### Send
|
|
675
|
+
### Send to a server
|
|
615
676
|
|
|
616
677
|
```js
|
|
617
678
|
picker.on('emojiClick', (emoji) => {
|
|
618
679
|
fetch('/api/reactions', {
|
|
619
|
-
method:
|
|
680
|
+
method: 'POST',
|
|
620
681
|
headers: { 'Content-Type': 'application/json' },
|
|
621
|
-
body:
|
|
622
|
-
messageId: currentMessageId,
|
|
623
|
-
emoji: emoji.name,
|
|
624
|
-
char: emoji.char
|
|
625
|
-
})
|
|
682
|
+
body: JSON.stringify({ messageId, emoji: emoji.name, char: emoji.char })
|
|
626
683
|
})
|
|
627
684
|
})
|
|
628
685
|
```
|
|
629
686
|
|
|
630
|
-
###
|
|
687
|
+
### Preload data on app startup
|
|
631
688
|
|
|
632
689
|
```js
|
|
633
|
-
//
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
search: false,
|
|
637
|
-
recentEmojis: false,
|
|
638
|
-
autoClose: true
|
|
639
|
-
})
|
|
640
|
-
|
|
641
|
-
// Full editor picker
|
|
642
|
-
const editorPicker = new EmojiPicker({
|
|
643
|
-
container: '#editor-btn',
|
|
644
|
-
search: true,
|
|
645
|
-
skinTone: 'medium'
|
|
646
|
-
})
|
|
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' })
|
|
647
693
|
```
|
|
648
694
|
|
|
649
|
-
###
|
|
695
|
+
### Quick-access bar from favorites
|
|
650
696
|
|
|
651
697
|
```js
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
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)
|
|
656
708
|
```
|
|
657
709
|
|
|
658
|
-
###
|
|
710
|
+
### Analytics
|
|
659
711
|
|
|
660
712
|
```js
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
})
|
|
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 }))
|
|
665
717
|
```
|
|
666
718
|
|
|
667
719
|
---
|
|
668
720
|
|
|
669
721
|
## Accessibility
|
|
670
722
|
|
|
671
|
-
-
|
|
672
|
-
-
|
|
673
|
-
-
|
|
674
|
-
-
|
|
675
|
-
-
|
|
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
|
|
676
728
|
- `Esc` closes the picker
|
|
677
729
|
- Focus moves to the search input on open
|
|
730
|
+
- `type="button"` on all buttons — safe inside `<form>` elements
|
|
678
731
|
|
|
679
732
|
---
|
|
680
733
|
|
|
@@ -689,24 +742,69 @@ document.querySelector('#theme-toggle').addEventListener('click', () => {
|
|
|
689
742
|
| iOS Safari | 14+ |
|
|
690
743
|
| Android Chrome | 80+ |
|
|
691
744
|
|
|
692
|
-
Requires
|
|
745
|
+
Requires **IndexedDB** for caching — available in all modern browsers, gracefully degraded if blocked (e.g. Firefox private mode with `resistFingerprinting`).
|
|
693
746
|
|
|
694
747
|
---
|
|
695
748
|
|
|
696
|
-
##
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
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.
|
|
780
|
+
|
|
781
|
+
---
|
|
782
|
+
|
|
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
|
+
```
|
|
710
808
|
|
|
711
809
|
---
|
|
712
810
|
|