@ohos-ports/emoji-picker-element 1.29.1-beta.0

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.
Files changed (60) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +1025 -0
  3. package/custom-elements.json +257 -0
  4. package/database.d.ts +108 -0
  5. package/database.js +1004 -0
  6. package/database.js.map +1 -0
  7. package/i18n/ar.d.ts +3 -0
  8. package/i18n/ar.js +34 -0
  9. package/i18n/de.d.ts +3 -0
  10. package/i18n/de.js +34 -0
  11. package/i18n/en.d.ts +3 -0
  12. package/i18n/en.js +34 -0
  13. package/i18n/es.d.ts +3 -0
  14. package/i18n/es.js +34 -0
  15. package/i18n/fa.d.ts +3 -0
  16. package/i18n/fa.js +34 -0
  17. package/i18n/fr.d.ts +3 -0
  18. package/i18n/fr.js +34 -0
  19. package/i18n/hi.d.ts +3 -0
  20. package/i18n/hi.js +34 -0
  21. package/i18n/hu.d.ts +3 -0
  22. package/i18n/hu.js +34 -0
  23. package/i18n/id.d.ts +3 -0
  24. package/i18n/id.js +34 -0
  25. package/i18n/it.d.ts +3 -0
  26. package/i18n/it.js +34 -0
  27. package/i18n/ja.d.ts +3 -0
  28. package/i18n/ja.js +34 -0
  29. package/i18n/ms_MY.d.ts +3 -0
  30. package/i18n/ms_MY.js +34 -0
  31. package/i18n/nl.d.ts +3 -0
  32. package/i18n/nl.js +34 -0
  33. package/i18n/pl.d.ts +3 -0
  34. package/i18n/pl.js +34 -0
  35. package/i18n/pt_BR.d.ts +3 -0
  36. package/i18n/pt_BR.js +34 -0
  37. package/i18n/pt_PT.d.ts +3 -0
  38. package/i18n/pt_PT.js +34 -0
  39. package/i18n/ru_RU.d.ts +3 -0
  40. package/i18n/ru_RU.js +34 -0
  41. package/i18n/tr.d.ts +3 -0
  42. package/i18n/tr.js +34 -0
  43. package/i18n/vi.d.ts +3 -0
  44. package/i18n/vi.js +34 -0
  45. package/i18n/zh_CN.d.ts +3 -0
  46. package/i18n/zh_CN.js +27 -0
  47. package/index.d.ts +7 -0
  48. package/index.js +6 -0
  49. package/package.json +196 -0
  50. package/picker.d.ts +30 -0
  51. package/picker.js +1836 -0
  52. package/picker.js.map +1 -0
  53. package/polyfill.js +62 -0
  54. package/shared.d.ts +99 -0
  55. package/svelte.js +4 -0
  56. package/trimEmojiData.cjs +54 -0
  57. package/trimEmojiData.cjs.map +1 -0
  58. package/trimEmojiData.d.ts +8 -0
  59. package/trimEmojiData.js +52 -0
  60. package/trimEmojiData.js.map +1 -0
package/README.md ADDED
@@ -0,0 +1,1025 @@
1
+ emoji-picker-element
2
+ ====
3
+
4
+ ![Screenshot of emoji-picker-element in light and dark modes](https://nolanwlawson.files.wordpress.com/2020/06/out.png)
5
+
6
+ ```html
7
+ <emoji-picker></emoji-picker>
8
+ ```
9
+
10
+ A lightweight emoji picker, distributed as a web component.
11
+
12
+ **Features:**
13
+
14
+ - Supports [Emoji v17.0](https://emojipedia.org/emoji-17.0) (depending on OS) and custom emoji
15
+ - Uses IndexedDB, so it consumes [far less memory](https://nolanlawson.com/2020/06/28/introducing-emoji-picker-element-a-memory-efficient-emoji-picker-for-the-web/) than other emoji pickers
16
+ - [Small bundle size](https://bundlephobia.com/result?p=emoji-picker-element) (~12.5kB min+gz)
17
+ - Renders native emoji by default, with support for custom fonts
18
+ - [Accessible by default](https://nolanlawson.com/2020/07/01/building-an-accessible-emoji-picker/)
19
+ - Framework and bundler not required, just add a `<script>` tag and use it
20
+
21
+ **Table of contents:**
22
+
23
+ <!-- toc start -->
24
+
25
+ - [emoji-picker-element](#emoji-picker-element-)
26
+ * [Usage](#usage)
27
+ + [Examples](#examples)
28
+ + [Emoji support](#emoji-support)
29
+ - [Custom emoji font](#custom-emoji-font)
30
+ - [Polyfilling flag emoji on Windows](#polyfilling-flag-emoji-on-windows)
31
+ * [Styling](#styling)
32
+ + [Size](#size)
33
+ + [Dark mode](#dark-mode)
34
+ + [CSS variables](#css-variables)
35
+ + [Focus outline](#focus-outline)
36
+ + [Small screen sizes](#small-screen-sizes)
37
+ + [Custom styling](#custom-styling)
38
+ * [JavaScript API](#javascript-api)
39
+ + [Picker](#picker)
40
+ - [Events](#events)
41
+ * [`emoji-click`](#emoji-click)
42
+ * [`emoji-click-sync`](#emoji-click-sync)
43
+ * [`skin-tone-change`](#skin-tone-change)
44
+ - [Internationalization](#internationalization)
45
+ * [Built-in translations](#built-in-translations)
46
+ - [Custom category order](#custom-category-order)
47
+ + [Database](#database)
48
+ - [Constructors](#constructors)
49
+ * [constructor](#constructor)
50
+ - [Accessors](#accessors)
51
+ * [customEmoji](#customemoji)
52
+ - [Methods](#methods)
53
+ * [close](#close)
54
+ * [delete](#delete)
55
+ * [getEmojiByGroup](#getemojibygroup)
56
+ * [getEmojiBySearchQuery](#getemojibysearchquery)
57
+ * [getEmojiByShortcode](#getemojibyshortcode)
58
+ * [getEmojiByUnicodeOrName](#getemojibyunicodeorname)
59
+ * [getPreferredSkinTone](#getpreferredskintone)
60
+ * [getTopFavoriteEmoji](#gettopfavoriteemoji)
61
+ * [incrementFavoriteEmojiCount](#incrementfavoriteemojicount)
62
+ * [ready](#ready)
63
+ * [setPreferredSkinTone](#setpreferredskintone)
64
+ + [Custom emoji](#custom-emoji)
65
+ + [Tree-shaking](#tree-shaking)
66
+ + [Within a meta-framework (Next.js, SvelteKit, etc.)](#within-a-meta-framework-nextjs-sveltekit-etc)
67
+ + [Within a Svelte project](#within-a-svelte-project)
68
+ * [Data and offline](#data-and-offline)
69
+ + [Data source and JSON format](#data-source-and-json-format)
70
+ + [Shortcodes](#shortcodes)
71
+ + [Cache performance](#cache-performance)
72
+ + [emojibase-data compatibility (deprecated)](#emojibase-data-compatibility-deprecated)
73
+ + [Trimming the emoji data (deprecated)](#trimming-the-emoji-data-deprecated)
74
+ + [Offline-first](#offline-first)
75
+ + [Environments without IndexedDB](#environments-without-indexeddb)
76
+ * [Design decisions](#design-decisions)
77
+ + [IndexedDB](#indexeddb)
78
+ + [Native emoji](#native-emoji)
79
+ + [JSON loading](#json-loading)
80
+ + [Browser support](#browser-support)
81
+ * [Contributing](#contributing)
82
+
83
+ <!-- toc end -->
84
+
85
+ ## Usage
86
+
87
+ Via npm:
88
+
89
+ npm install emoji-picker-element
90
+
91
+ ```js
92
+ import 'emoji-picker-element';
93
+ ```
94
+
95
+ Or as a `<script>` tag:
96
+
97
+ ```html
98
+ <script type="module" src="https://cdn.jsdelivr.net/npm/emoji-picker-element@^1/index.js"></script>
99
+ ```
100
+
101
+ Then use the HTML:
102
+
103
+ ```html
104
+ <emoji-picker></emoji-picker>
105
+ ```
106
+
107
+ And listen for `emoji-click` events:
108
+
109
+ ```js
110
+ document.querySelector('emoji-picker')
111
+ .addEventListener('emoji-click', event => console.log(event.detail));
112
+ ```
113
+
114
+ This will log:
115
+
116
+ ```json
117
+ {
118
+ "emoji": {
119
+ "annotation": "grinning face",
120
+ "group": 0,
121
+ "order": 1,
122
+ "shortcodes": [ "grinning_face", "grinning" ],
123
+ "tags": [ "face", "grin" ],
124
+ "unicode": "😀",
125
+ "version": 1,
126
+ "emoticon": ":D"
127
+ },
128
+ "skinTone": 0,
129
+ "unicode": "😀"
130
+ }
131
+ ```
132
+
133
+ ### Examples
134
+
135
+ - [Demo](https://nolanlawson.github.io/emoji-picker-element) ([source](https://github.com/nolanlawson/emoji-picker-element/blob/master/docs/index.html))
136
+ - [Button with tooltip/popover](https://nolanlawson.github.io/emoji-picker-element/demos/tooltip/index.html) ([source](https://github.com/nolanlawson/emoji-picker-element/blob/master/docs/demos/tooltip/index.html))
137
+ - [Inserting emoji into a text input](https://nolanlawson.github.io/emoji-picker-element/demos/input/index.html) ([source](https://github.com/nolanlawson/emoji-picker-element/blob/master/docs/demos/input/index.html))
138
+ - [In a React app](https://nolanlawson.github.io/emoji-picker-element/demos/react/index.html) ([source](https://github.com/nolanlawson/emoji-picker-element/blob/master/docs/demos/react/index.html))
139
+ - [Custom emoji font](https://nolanlawson.github.io/emoji-picker-element/demos/custom-font/index.html) ([source](https://github.com/nolanlawson/emoji-picker-element/blob/master/docs/demos/custom-font/index.html))
140
+ - [Fallback for missing flag emoji on Windows](https://nolanlawson.github.io/emoji-picker-element/demos/flags/index.html) ([source](https://github.com/nolanlawson/emoji-picker-element/blob/master/docs/demos/flags/index.html))
141
+
142
+ ### Emoji support
143
+
144
+ [Emoji support varies](https://nolanlawson.com/2022/04/08/the-struggle-of-using-native-emoji-on-the-web/) across browsers and operating systems. By default, `emoji-picker-element` will hide unsupported emoji from the picker.
145
+
146
+ To work around this, you can use [a custom emoji font](#custom-emoji-font) or [polyfill flag emoji on Windows](#polyfilling-flag-emoji-on-windows).
147
+
148
+ #### Custom emoji font
149
+
150
+ To use a custom emoji font, first set the `--emoji-font-family` CSS property:
151
+
152
+ ```css
153
+ emoji-picker {
154
+ --emoji-font-family: MyCustomFont;
155
+ }
156
+ ```
157
+
158
+ Then, specify the maximum emoji version supported by the font. (See [Emojipedia](https://emojipedia.org/emoji-versions/) for a list of versions.)
159
+
160
+ In HTML:
161
+
162
+ ```html
163
+ <emoji-picker emoji-version="15.0"></emoji-picker>
164
+ ```
165
+
166
+ Or JavaScript:
167
+
168
+ ```js
169
+ const picker = new Picker({
170
+ emojiVersion: 15.0
171
+ });
172
+ ```
173
+
174
+ If the `emoji-version`/`emojiVersion` option is set, then `emoji-picker-element` will not attempt to detect unsupported emoji or hide them.
175
+
176
+ Also note that support for color fonts [varies across browsers and OSes](https://caniuse.com/colr), and some browsers may have <a href="https://github.com/nolanlawson/emoji-picker-element/pull/308#issuecomment-1367491149">bugs</a> or not render the font at all. Be careful to test your supported browsers when using this approach.
177
+
178
+ #### Polyfilling flag emoji on Windows
179
+
180
+ As of this writing, [Windows does not support country flag emoji](https://answers.microsoft.com/en-us/windows/forum/all/where-are-the-flag-emoji-in-windows-10/93daa6e8-880a-48b1-9891-ab5bfbfbce98). This is only a problem in Chromium-based browsers, because Firefox ships with its own emoji font.
181
+
182
+ To work around this, you can use [country-flag-emoji-polyfill](https://www.npmjs.com/package/country-flag-emoji-polyfill):
183
+
184
+ ```js
185
+ import { polyfillCountryFlagEmojis } from 'country-flag-emoji-polyfill';
186
+
187
+ // emoji-picker-element will use "Twemoji Mozilla" and fall back to other fonts for non-flag emoji
188
+ polyfillCountryFlagEmojis('Twemoji Mozilla');
189
+ ```
190
+
191
+ Note that you do not need to do this if you are using [a custom emoji font](#custom-emoji-font).
192
+
193
+ ## Styling
194
+
195
+ `emoji-picker-element` uses [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM), so its inner styling cannot be (easily) changed with arbitrary CSS. Refer to the API below for style customization.
196
+
197
+ ### Size
198
+
199
+ `emoji-picker-element` has a default size, but you can change it to whatever you want:
200
+
201
+ ```css
202
+ emoji-picker {
203
+ width: 400px;
204
+ height: 300px;
205
+ }
206
+ ```
207
+
208
+ For instance, to make it expand to fit whatever container you give it:
209
+
210
+ ```css
211
+ emoji-picker {
212
+ width: 100%;
213
+ height: 100%;
214
+ }
215
+ ```
216
+
217
+ ### Dark mode
218
+
219
+ By default, `emoji-picker-element` will automatically switch to dark mode based on
220
+ [`prefers-color-scheme`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme).
221
+ Or you can add the class `dark` or `light` to force dark/light mode:
222
+
223
+ ```html
224
+ <emoji-picker class="dark"></emoji-picker>
225
+ <emoji-picker class="light"></emoji-picker>
226
+ ```
227
+
228
+ ### CSS variables
229
+
230
+ Most colors and sizes can be styled with CSS variables. For example:
231
+
232
+ ```css
233
+ emoji-picker {
234
+ --num-columns: 6;
235
+ --emoji-size: 3rem;
236
+ --background: gray;
237
+ }
238
+ ```
239
+
240
+ Here is a full list of options:
241
+
242
+ <!-- CSS variable options start -->
243
+
244
+ | Variable | Default | Default (dark) | Description |
245
+ | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | -------------- | ---------------------------------------------------------------------------------------------------- |
246
+ | `--background` | `#fff` | `#222` | Background of the entire `<emoji-picker>` |
247
+ | `--border-color` | `#e0e0e0` | `#444` | |
248
+ | `--border-radius` | `0` | | Border radius of the entire picker |
249
+ | `--border-size` | `1px` | | Width of border used in most of the picker |
250
+ | `--button-active-background` | `#e6e6e6` | `#555555` | Background of an active button |
251
+ | `--button-hover-background` | `#d9d9d9` | `#484848` | Background of a hovered button |
252
+ | `--category-emoji-padding` | `var(--emoji-padding)` | | Vertical/horizontal padding on category emoji, if you want it to be different from `--emoji-padding` |
253
+ | `--category-emoji-size` | `var(--emoji-size)` | | Width/height of category emoji, if you want it to be different from `--emoji-size` |
254
+ | `--category-font-color` | `#111` | `#efefef` | Font color of custom emoji category headings |
255
+ | `--category-font-size` | `1rem` | | Font size of custom emoji category headings |
256
+ | `--emoji-font-family` | `"Twemoji Mozilla","Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji","EmojiOne Color","Android Emoji",sans-serif` | | Font family for a custom emoji font (as opposed to native emoji) |
257
+ | `--emoji-padding` | `0.5rem` | | Vertical and horizontal padding on emoji |
258
+ | `--emoji-size` | `1.375rem` | | Width and height of all emoji |
259
+ | `--indicator-color` | `#385ac1` | `#5373ec` | Color of the nav indicator |
260
+ | `--indicator-height` | `3px` | | Height of the nav indicator |
261
+ | `--input-border-color` | `#999` | `#ccc` | |
262
+ | `--input-border-radius` | `0.5rem` | | |
263
+ | `--input-border-size` | `1px` | | |
264
+ | `--input-font-color` | `#111` | `#efefef` | |
265
+ | `--input-font-size` | `1rem` | | |
266
+ | `--input-line-height` | `1.5` | | |
267
+ | `--input-padding` | `0.25rem` | | |
268
+ | `--input-placeholder-color` | `#999` | `#ccc` | |
269
+ | `--num-columns` | `8` | | How many columns to display in the emoji grid |
270
+ | `--outline-color` | `#999` | `#fff` | Focus outline color |
271
+ | `--outline-size` | `2px` | | Focus outline width |
272
+ | `--skintone-border-radius` | `1rem` | | Border radius of the skintone dropdown |
273
+
274
+ <!-- CSS variable options end -->
275
+
276
+ ### Focus outline
277
+
278
+ For accessibility reasons, `emoji-picker-element` displays a prominent focus ring for keyboard users. This uses [`:focus-visible`](https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible) under the hood. To properly support [browsers that do not support `:focus-visible`](https://caniuse.com/css-focus-visible), you can use the [focus-visible](https://github.com/WICG/focus-visible) polyfill, e.g.:
279
+
280
+ ```js
281
+ import 'focus-visible';
282
+
283
+ const picker = new Picker();
284
+ applyFocusVisiblePolyfill(picker.shadowRoot);
285
+ ```
286
+
287
+ `emoji-picker-element` already ships with the proper CSS for both the `:focus-visible` standard and the polyfill.
288
+
289
+ ### Small screen sizes
290
+
291
+ For small screen sizes, you should probably add some CSS like the following:
292
+
293
+ ```css
294
+ @media screen and (max-width: 320px) {
295
+ emoji-picker {
296
+ --num-columns: 6;
297
+ --category-emoji-size: 1.125rem;
298
+ }
299
+ }
300
+ ```
301
+
302
+ `emoji-picker-element` does not ship with any CSS to explicitly handle small screen sizes. The right CSS depends on which screen sizes your app supports, and the size of the picker within your app. Perhaps in the future [container queries](https://caniuse.com/css-container-queries) can solve this problem.
303
+
304
+ ### Custom styling
305
+
306
+ If you absolutely must go beyond the styling API above, you can do something like this:
307
+
308
+ ```js
309
+ const style = document.createElement('style');
310
+ style.textContent = `/* custom shadow dom styles here */`
311
+ picker.shadowRoot.appendChild(style);
312
+ ```
313
+
314
+ ## JavaScript API
315
+
316
+ ### Picker
317
+
318
+ Basic usage:
319
+
320
+ ```js
321
+ import { Picker } from 'emoji-picker-element';
322
+ const picker = new Picker();
323
+ document.body.appendChild(picker);
324
+ ```
325
+
326
+ The `new Picker(options)` constructor supports several options:
327
+
328
+ | Name | Type | Default | Description |
329
+ |-------------------------|---------------|------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------|
330
+ | `customCategorySorting` | function | - | Function to sort custom category strings (sorted alphabetically by default) |
331
+ | `customEmoji` | CustomEmoji[] | - | Array of custom emoji |
332
+ | `dataSource` | string | "https://cdn.jsdelivr.net/npm/emoji-picker-element-data@^1/en/emojibase/data.json" | URL to fetch the emoji data from (`data-source` when used as an attribute) |
333
+ | `emojiVersion` | number | - | Maximum supported emoji version as a number (e.g. `14.0` or `13.1`). Setting this disables the default emoji support detection. |
334
+ | `i18n` | I18n | - | i18n object (see below for details) |
335
+ | `locale` | string | "en" | Locale string |
336
+ | `skinToneEmoji` | string | "🖐️" | The emoji to use for the skin tone picker (`skin-tone-emoji` when used as an attribute) |
337
+
338
+
339
+
340
+ For instance:
341
+
342
+ ```js
343
+ const picker = new Picker({
344
+ locale: 'fr',
345
+ dataSource: '/fr-emoji.json'
346
+ })
347
+ ```
348
+
349
+ These values can also be set at runtime:
350
+
351
+ ```js
352
+ const picker = new Picker();
353
+ picker.dataSource = '/my-emoji.json';
354
+ ```
355
+
356
+ Some values can also be set as declarative attributes:
357
+
358
+ ```html
359
+ <emoji-picker
360
+ locale="fr"
361
+ data-source="/fr-emoji.json"
362
+ skin-tone-emoji="✌"
363
+ ></emoji-picker>
364
+ ```
365
+
366
+ Note that complex properties like `i18n` or `customEmoji` are not supported as attributes, because the DOM only
367
+ supports string attributes, not complex objects.
368
+
369
+ #### Events
370
+
371
+ ##### `emoji-click`
372
+
373
+ The `emoji-click` event is fired when an emoji is selected by the user. Example format:
374
+
375
+ ```javascript
376
+ {
377
+ emoji: {
378
+ annotation: 'thumbs up',
379
+ group: 1,
380
+ order: 280,
381
+ shortcodes: ['thumbsup', '+1', 'yes'],
382
+ tags: ['+1', 'hand', 'thumb', 'up'],
383
+ unicode: '👍️',
384
+ version: 0.6,
385
+ skins: [
386
+ { tone: 1, unicode: '👍🏻', version: 1 },
387
+ { tone: 2, unicode: '👍🏼', version: 1 },
388
+ { tone: 3, unicode: '👍🏽', version: 1 },
389
+ { tone: 4, unicode: '👍🏾', version: 1 },
390
+ { tone: 5, unicode: '👍🏿', version: 1 }
391
+ ]
392
+ },
393
+ skinTone: 4,
394
+ unicode: '👍🏾'
395
+ }
396
+ ```
397
+
398
+ And usage:
399
+
400
+ ```js
401
+ picker.addEventListener('emoji-click', event => {
402
+ console.log(event.detail); // will log something like the above
403
+ });
404
+ ```
405
+
406
+ Note that `unicode` will represent whatever the emoji should look like
407
+ with the given `skinTone`. If the `skinTone` is 0, or if the emoji has
408
+ no skin tones, then no skin tone is applied to `unicode`.
409
+
410
+ ##### `emoji-click-sync`
411
+
412
+ > [!NOTE]
413
+ > Most likely, you should only use this event if you need to copy an emoji to the clipboard,
414
+ > due to [a Safari bug](https://github.com/nolanlawson/emoji-picker-element/issues/281#issuecomment-3256832247).
415
+
416
+ The `emoji-click-sync` event is exactly the same as `emoji-click`, except that the event is fired
417
+ synchronously relative to the original `click` event, and the `event.detail` is a `Promise` that must be `await`ed:
418
+
419
+ ```js
420
+ picker.addEventListener('emoji-click-sync', async event => {
421
+ console.log(await event.detail); // same as above
422
+ });
423
+ ```
424
+
425
+ This is useful to work around [a Safari bug](https://github.com/nolanlawson/emoji-picker-element/issues/281#issuecomment-3256832247)
426
+ when using the [Clipboard API](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard), which causes
427
+ the error `NotAllowedError: The request is not allowed by the user agent or the platform in the current context, possibly because the user denied permission.`
428
+ This error occurs due to Safari not recognizing that the `emoji-click` event is user-initiated due to the presence of
429
+ `await`s for IndexedDB data.
430
+
431
+ Example of correct usage to copy an emoji to the clipboard:
432
+
433
+ ```js
434
+ picker.addEventListener('emoji-click-sync', async event => {
435
+ try {
436
+ await navigator.clipboard.write([new ClipboardItem({
437
+ 'text/plain': e.detail.then(({ unicode }) => unicode),
438
+ })]);
439
+ console.log('Copied to clipboard!');
440
+ } catch (err) {
441
+ console.log('Failed to copy to clipboard', err);
442
+ }
443
+ });
444
+ ```
445
+
446
+ If you don't need to work around the Safari bug, then you can just use the `emoji-click` event instead.
447
+
448
+ ##### `skin-tone-change`
449
+
450
+ This event is fired whenever the user selects a new skin tone. Example format:
451
+
452
+ ```js
453
+ {
454
+ skinTone: 5
455
+ }
456
+ ```
457
+
458
+ And usage:
459
+
460
+ ```js
461
+ picker.addEventListener('skin-tone-change', event => {
462
+ console.log(event.detail); // will log something like the above
463
+ })
464
+ ```
465
+
466
+ Note that skin tones are an integer from 0 (default) to 1 (light) through 5 (dark).
467
+
468
+ #### Internationalization
469
+
470
+ The `i18n` parameter specifies translations for the picker interface. Here is the default English `i18n` object:
471
+
472
+ <!-- i18n options start -->
473
+
474
+ ```json
475
+ {
476
+ "categories": {
477
+ "custom": "Custom",
478
+ "smileys-emotion": "Smileys and emoticons",
479
+ "people-body": "People and body",
480
+ "animals-nature": "Animals and nature",
481
+ "food-drink": "Food and drink",
482
+ "travel-places": "Travel and places",
483
+ "activities": "Activities",
484
+ "objects": "Objects",
485
+ "symbols": "Symbols",
486
+ "flags": "Flags"
487
+ },
488
+ "categoriesLabel": "Categories",
489
+ "emojiUnsupportedMessage": "Your browser does not support color emoji.",
490
+ "favoritesLabel": "Favorites",
491
+ "loadingMessage": "Loading…",
492
+ "networkErrorMessage": "Could not load emoji.",
493
+ "regionLabel": "Emoji picker",
494
+ "searchDescription": "When search results are available, press up or down to select and enter to choose.",
495
+ "searchLabel": "Search",
496
+ "searchResultsLabel": "Search results",
497
+ "skinToneDescription": "When expanded, press up or down to select and enter to choose.",
498
+ "skinToneLabel": "Choose a skin tone (currently {skinTone})",
499
+ "skinTones": [
500
+ "Default",
501
+ "Light",
502
+ "Medium-Light",
503
+ "Medium",
504
+ "Medium-Dark",
505
+ "Dark"
506
+ ],
507
+ "skinTonesLabel": "Skin tones"
508
+ }
509
+ ```
510
+
511
+ <!-- i18n options end -->
512
+
513
+ Note that some of these strings are only visible to users of screen readers. They are still important for accessibility!
514
+
515
+ ##### Built-in translations
516
+
517
+ Community-provided translations for some languages [are available](https://github.com/nolanlawson/emoji-picker-element/tree/master/src/picker/i18n). You can use them like so:
518
+
519
+ ```js
520
+ import fr from 'emoji-picker-element/i18n/fr';
521
+ import de from 'emoji-picker-element/i18n/de';
522
+
523
+ // French
524
+ picker.i18n = fr;
525
+
526
+ // German
527
+ picker.i18n = de;
528
+ ```
529
+
530
+ Note that translations for the interface (`i18n`) are not the same as translations for the emoji data (`dataSource` and `locale`). To support both, you should do something like:
531
+
532
+ ```js
533
+ import fr from 'emoji-picker-element/i18n/fr';
534
+
535
+ const picker = new Picker({
536
+ i18n: fr,
537
+ locale: 'fr',
538
+ dataSource: 'https://cdn.jsdelivr.net/npm/emoji-picker-element-data@^1/fr/emojibase/data.json',
539
+ });
540
+ ```
541
+
542
+ If a built-in translation for your target language is not available, you can also write your own translation and pass it in as `i18n`. Please feel free to contribute your translation [here](https://github.com/nolanlawson/emoji-picker-element/tree/master/src/picker/i18n).
543
+
544
+ #### Custom category order
545
+
546
+ By default, custom categories are sorted alphabetically. To change this, pass in your own `customCategorySorting`:
547
+
548
+ ```js
549
+ picker.customCategorySorting = (category1, category2) => { /* your sorting code */ };
550
+ ```
551
+
552
+ This function should accept two strings and return a number.
553
+
554
+ Custom emoji with no category will pass in `undefined`. By default, these are shown first, with the label `"Custom"`
555
+ (determined by `i18n.categories.custom`).
556
+
557
+ ### Database
558
+
559
+ You can work with the database API separately, which allows you to query emoji the same
560
+ way that the picker does:
561
+
562
+ ```js
563
+ import { Database } from 'emoji-picker-element';
564
+
565
+ const database = new Database();
566
+ await database.getEmojiBySearchPrefix('elephant'); // [{unicode: "🐘", ...}]
567
+ ```
568
+
569
+ Note that under the hood, IndexedDB data is partitioned based on the `locale`. So if you create two `Database`s with two different `locale`s, it will store twice as much data.
570
+
571
+ Also note that, unlike the picker, the database does not filter emoji based on whether they are supported by the current browser/OS or not. To detect emoji support, you can use a library like [is-emoji-supported](https://github.com/koala-interactive/is-emoji-supported).
572
+
573
+ Full API:
574
+
575
+ #### Constructors
576
+
577
+ ##### constructor
578
+
579
+ \+ **new Database**(`__namedParameters`: object): *Database*
580
+
581
+ Create a new Database.
582
+
583
+ Note that multiple Databases pointing to the same locale will share the
584
+ same underlying IndexedDB connection and database.
585
+
586
+ **Parameters:**
587
+
588
+ ▪`Default value` **__namedParameters**: *object*= {}
589
+
590
+ Name | Type | Default | Description |
591
+ ------ | ------ | ------ | ------ |
592
+ `customEmoji` | CustomEmoji[] | [] | Array of custom emoji |
593
+ `dataSource` | string | "https://cdn.jsdelivr.net/npm/emoji-picker-element-data@^1/en/emojibase/data.json" | URL to fetch the emoji data from |
594
+ `locale` | string | "en" | Locale string |
595
+
596
+ **Returns:** *Database*
597
+
598
+ #### Accessors
599
+
600
+ ##### customEmoji
601
+
602
+ • **get customEmoji**(): *CustomEmoji[]*
603
+
604
+ Return the custom emoji associated with this Database, or the empty array if none.
605
+
606
+ **Returns:** *CustomEmoji[]*
607
+
608
+ • **set customEmoji**(`customEmoji`: CustomEmoji[]): *void*
609
+
610
+ Set the custom emoji for this database. Throws an error if custom emoji are not in the correct format.
611
+
612
+ **Parameters:**
613
+
614
+ Name | Type | Description |
615
+ ------ | ------ | ------ |
616
+ `customEmoji` | CustomEmoji[] | |
617
+
618
+ **Returns:** *void*
619
+
620
+ #### Methods
621
+
622
+ ##### close
623
+
624
+ ▸ **close**(): *Promise‹void›*
625
+
626
+ Closes the underlying IndexedDB connection. The Database is not usable after that (or any other Databases
627
+ with the same locale).
628
+
629
+ Note that as soon as any other non-close/delete method is called, the database will automatically reopen.
630
+
631
+ **Returns:** *Promise‹void›*
632
+
633
+ ___
634
+
635
+ ##### delete
636
+
637
+ ▸ **delete**(): *Promise‹void›*
638
+
639
+ Deletes the underlying IndexedDB database. The Database is not usable after that (or any other Databases
640
+ with the same locale).
641
+
642
+ Note that as soon as any other non-close/delete method is called, the database will be recreated.
643
+
644
+ **Returns:** *Promise‹void›*
645
+
646
+ ___
647
+
648
+ ##### getEmojiByGroup
649
+
650
+ ▸ **getEmojiByGroup**(`group`: number): *Promise‹NativeEmoji[]›*
651
+
652
+ Returns all emoji belonging to a group, ordered by `order`. Only returns native emoji;
653
+ custom emoji don't belong to a group.
654
+
655
+ Non-numbers throw an error.
656
+
657
+ **Parameters:**
658
+
659
+ Name | Type | Description |
660
+ ------ | ------ | ------ |
661
+ `group` | number | the group number |
662
+
663
+ **Returns:** *Promise‹NativeEmoji[]›*
664
+
665
+ ___
666
+
667
+ ##### getEmojiBySearchQuery
668
+
669
+ ▸ **getEmojiBySearchQuery**(`query`: string): *Promise‹Emoji[]›*
670
+
671
+ Returns all emoji matching the given search query, ordered by `order`.
672
+
673
+ Empty/null strings throw an error.
674
+
675
+ **Parameters:**
676
+
677
+ Name | Type | Description |
678
+ ------ | ------ | ------ |
679
+ `query` | string | search query string |
680
+
681
+ **Returns:** *Promise‹Emoji[]›*
682
+
683
+ ___
684
+
685
+ ##### getEmojiByShortcode
686
+
687
+ ▸ **getEmojiByShortcode**(`shortcode`: string): *Promise‹Emoji | null›*
688
+
689
+ Return a single emoji matching the shortcode, or null if not found.
690
+
691
+ The colons around the shortcode should not be included when querying, e.g.
692
+ use "slight_smile", not ":slight_smile:". Uppercase versus lowercase
693
+ does not matter. Empty/null strings throw an error.
694
+
695
+ **Parameters:**
696
+
697
+ Name | Type | Description |
698
+ ------ | ------ | ------ |
699
+ `shortcode` | string | |
700
+
701
+ **Returns:** *Promise‹Emoji | null›*
702
+
703
+ ___
704
+
705
+ ##### getEmojiByUnicodeOrName
706
+
707
+ ▸ **getEmojiByUnicodeOrName**(`unicodeOrName`: string): *Promise‹Emoji | null›*
708
+
709
+ Return a single native emoji matching the unicode string, or
710
+ a custom emoji matching the name, or null if not found.
711
+
712
+ In the case of native emoji, the unicode string can be either the
713
+ main unicode string, or the unicode of one of the skin tone variants.
714
+
715
+ Empty/null strings throw an error.
716
+
717
+ **Parameters:**
718
+
719
+ Name | Type | Description |
720
+ ------ | ------ | ------ |
721
+ `unicodeOrName` | string | unicode (native emoji) or name (custom emoji) |
722
+
723
+ **Returns:** *Promise‹Emoji | null›*
724
+
725
+ ___
726
+
727
+ ##### getPreferredSkinTone
728
+
729
+ ▸ **getPreferredSkinTone**(): *Promise‹SkinTone›*
730
+
731
+ Get the user's preferred skin tone. Returns 0 if not found.
732
+
733
+ **Returns:** *Promise‹SkinTone›*
734
+
735
+ ___
736
+
737
+ ##### getTopFavoriteEmoji
738
+
739
+ ▸ **getTopFavoriteEmoji**(`limit`: number): *Promise‹Emoji[]›*
740
+
741
+ Get the top favorite emoji in descending order. If there are no favorite emoji yet, returns an empty array.
742
+
743
+ **Parameters:**
744
+
745
+ Name | Type | Description |
746
+ ------ | ------ | ------ |
747
+ `limit` | number | maximum number of results to return |
748
+
749
+ **Returns:** *Promise‹Emoji[]›*
750
+
751
+ ___
752
+
753
+ ##### incrementFavoriteEmojiCount
754
+
755
+ ▸ **incrementFavoriteEmojiCount**(`unicodeOrName`: string): *Promise‹void›*
756
+
757
+ Increment the favorite count for an emoji by one. The unicode string must be non-empty. It should
758
+ correspond to the base (non-skin-tone) unicode string from the emoji object, or in the case of
759
+ custom emoji, it should be the name.
760
+
761
+ **Parameters:**
762
+
763
+ Name | Type | Description |
764
+ ------ | ------ | ------ |
765
+ `unicodeOrName` | string | unicode of a native emoji, or name of a custom emoji |
766
+
767
+ **Returns:** *Promise‹void›*
768
+
769
+ ___
770
+
771
+ ##### ready
772
+
773
+ ▸ **ready**(): *Promise‹void›*
774
+
775
+ Resolves when the Database is ready, or throws an error if
776
+ the Database could not initialize.
777
+
778
+ Note that you don't need to do this before calling other APIs – they will
779
+ all wait for this promise to resolve before doing anything.
780
+
781
+ **Returns:** *Promise‹void›*
782
+
783
+ ___
784
+
785
+ ##### setPreferredSkinTone
786
+
787
+ ▸ **setPreferredSkinTone**(`skinTone`: SkinTone): *Promise‹void›*
788
+
789
+ Set the user's preferred skin tone. Non-numbers throw an error.
790
+
791
+ **Parameters:**
792
+
793
+ Name | Type | Description |
794
+ ------ | ------ | ------ |
795
+ `skinTone` | SkinTone | preferred skin tone |
796
+
797
+ **Returns:** *Promise‹void›*
798
+
799
+ ### Custom emoji
800
+
801
+ Both the Picker and the Database support custom emoji. Unlike regular emoji, custom emoji
802
+ are kept in-memory. (It's assumed that they're small, and they might frequently change, so
803
+ there's not much point in storing them in IndexedDB.)
804
+
805
+ Custom emoji should follow the format:
806
+
807
+ ```js
808
+ [
809
+ {
810
+ name: 'Garfield',
811
+ shortcodes: ['garfield'],
812
+ url: 'http://example.com/garfield.png',
813
+ category: 'Cats'
814
+ },
815
+ {
816
+ name: 'Heathcliff',
817
+ shortcodes: ['heathcliff'],
818
+ url: 'http://example.com/heathcliff.png',
819
+ category: 'Cats'
820
+ },
821
+ {
822
+ name: 'Scooby-Doo',
823
+ shortcodes: ['scooby'],
824
+ url: 'http://example.com/scooby.png',
825
+ category: 'Dogs'
826
+ }
827
+ ]
828
+ ```
829
+
830
+ Note that names are assumed to be unique (case-insensitive), and it's assumed that the `shortcodes` have at least one entry.
831
+
832
+ The `category` is optional. If you don't provide it, then the custom emoji will appear in a
833
+ single category called "Custom".
834
+
835
+ To pass custom emoji into the `Picker`:
836
+
837
+ ```js
838
+ const picker = new Picker({
839
+ customEmoji: [ /* ... */ ]
840
+ });
841
+ ```
842
+
843
+ Or the `Database`:
844
+
845
+ ```js
846
+ const database = new Database({
847
+ customEmoji: [ /* ... */ ]
848
+ });
849
+ ```
850
+
851
+ Custom emoji can also be set at runtime:
852
+
853
+ ```js
854
+ picker.customEmoji = [ /* ... */ ];
855
+ database.customEmoji = [ /* ... */ ];
856
+ ```
857
+
858
+ ### Tree-shaking
859
+
860
+ If you want to import the `Database` without the `Picker`, or you want to code-split them separately, then do:
861
+
862
+ ```js
863
+ import Picker from 'emoji-picker-element/picker';
864
+ import Database from 'emoji-picker-element/database';
865
+ ```
866
+
867
+ The reason for this is that `Picker` automatically registers itself as a custom element, following [web component best practices](https://justinfagnani.com/2019/11/01/how-to-publish-web-components-to-npm/). But this adds side effects, so bundlers like Webpack and Rollup do not tree-shake as well, unless the modules are imported from completely separate files.
868
+
869
+ ### Within a meta-framework (Next.js, SvelteKit, etc.)
870
+
871
+ Some meta-frameworks will attempt to server-side render (SSR) any dependencies you `import`. However, `emoji-picker-element` only supports client-side rendering – it does not work on the server side. If you attempt to import it on the server side, you will see an error like `requestAnimationFrame is not defined`.
872
+
873
+ To load `emoji-picker-element` only on the client side, use your meta-framework's technique for client-side-only imports. For example, you can use [dynamic `import()`s](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import) with [`next/dynamic` in Next.js](https://stackoverflow.com/a/61881528/680742) or [`onMount()` in SvelteKit](https://www.banjocode.com/post/svelte/client-side-library).
874
+
875
+ `emoji-picker-element` is not designed for SSR. In most apps, an emoji picker should be lazy-loaded upon user interaction (for example, when the user clicks a button).
876
+
877
+ ### Within a Svelte project
878
+
879
+ > [!WARNING]
880
+ > `emoji-picker-element` is no longer based on Svelte, so importing from `emoji-picker-element/svelte` is now deprecated.
881
+
882
+ Previously, `emoji-picker-element` was based on Svelte v3/v4, and you could do:
883
+
884
+ ```js
885
+ import Picker from 'emoji-picker-element/svelte';
886
+ ```
887
+
888
+ The goal was to slightly reduce the bundle size by sharing common `svelte` imports.
889
+
890
+ This is still supported for backwards compatibility, but it is deprecated and just re-exports the Picker. Instead, do:
891
+
892
+ ```js
893
+ import Picker from 'emoji-picker-element/picker';
894
+ ```
895
+
896
+ ## Data and offline
897
+
898
+ ### Data source and JSON format
899
+
900
+ If you'd like to host the emoji data (`dataSource`) yourself, you can do:
901
+
902
+ npm install emoji-picker-element-data@^1
903
+
904
+ Then host `node_modules/emoji-picker-element-data/en/emojibase/data.json` (or other JSON files) on your web server.
905
+
906
+ ```js
907
+ const picker = new Picker({
908
+ dataSource: '/path/to/my/webserver/data.json'
909
+ });
910
+ ```
911
+
912
+ See [`emoji-picker-element-data`](https://www.npmjs.com/package/emoji-picker-element-data) for details.
913
+
914
+ ### Shortcodes
915
+
916
+ There is no standard for shortcodes, so unlike other emoji data, there is some disagreement as to what a "shortcode" actually is.
917
+
918
+ `emoji-picker-element-data` is based on `emojibase-data`, which offers several shortcode packs per language. For instance,
919
+ you may choose shortcodes from GitHub, Slack, Discord, or Emojibase (the default). You
920
+ can browse the available data files [on jsdelivr](https://www.jsdelivr.com/package/npm/emoji-picker-element-data) and see
921
+ more details on shortcodes [in the Emojibase docs](https://emojibase.dev/docs/shortcodes).
922
+
923
+ ### Cache performance
924
+
925
+ For optimal cache performance, it's recommended that your server expose an `ETag` header. If so, `emoji-picker-element` can avoid re-downloading the entire JSON file over and over again. Instead, it will do a `HEAD` request and just check the `ETag`.
926
+
927
+ If the server hosting the JSON file is not the same as the one containing the emoji picker, then the cross-origin server will also need to expose `Access-Control-Allow-Origin: *` and `Access-Control-Expose-Headers: ETag` (or `Access-Control-Expose-Headers: *` ). `jsdelivr` already does this, which is partly why it is the default.
928
+
929
+ If `emoji-picker-element` cannot use the `ETag` for any reason, it will fall back to the less performant option, doing a full `GET` request on every page load.
930
+
931
+ ### emojibase-data compatibility (deprecated)
932
+
933
+ _**Deprecated:** in v1.3.0, `emoji-picker-element` switched from [`emojibase-data`](https://github.com/milesj/emojibase) to
934
+ [`emoji-picker-element-data`](https://npmjs.com/package/emoji-picker-element-data) as its default data source. You can still use `emojibase-data`, but only v5 is supported, not v6. Support may be removed in a later release._
935
+
936
+ When using `emojibase-data`, you must use the _full_ [`emojibase-data`](https://github.com/milesj/emojibase) JSON file, not the "compact" one (i.e. `data.json`, not `compact.json`).
937
+
938
+ ### Trimming the emoji data (deprecated)
939
+
940
+ _**Deprecated:** in v1.3.0, `emoji-picker-element` switched from [`emojibase-data`](https://github.com/milesj/emojibase) to
941
+ [`emoji-picker-element-data`](https://npmjs.com/package/emoji-picker-element-data) as its default data source. With the new `emoji-picker-element-data`, there is no need to trim the emoji down to size. This function is deprecated and may be removed eventually._
942
+
943
+ If you are hosting the `emojibase-data` JSON file yourself and would like it to be as small as possible, then you can use the utility `trimEmojiData` function:
944
+
945
+ ```js
946
+ import trimEmojiData from 'emoji-picker-element/trimEmojiData.js';
947
+ import emojiBaseData from 'emojibase-data/en/data.json';
948
+
949
+ const trimmedData = trimEmojiData(emojiBaseData);
950
+ ```
951
+
952
+ Or if your version of Node doesn't support ES modules:
953
+
954
+ ```js
955
+ const trimEmojiData = require('emoji-picker-element/trimEmojiData.cjs');
956
+ ```
957
+
958
+ ### Offline-first
959
+
960
+ `emoji-picker-element` uses a "stale while revalidate" strategy to update emoji data. In other words, it will use any existing data it finds in IndexedDB, and lazily update via the `dataSource` in case that data has changed. This means it will work [offline-first](http://offlinefirst.org/) the second time it runs.
961
+
962
+ If you would like to manage the database yourself (e.g. to ensure that it's correctly populated before displaying the `Picker`), then create a new `Database` instance and wait for its `ready()` promise to resolve:
963
+
964
+ ```js
965
+ const database = new Database();
966
+ try {
967
+ await database.ready();
968
+ } catch (err) {
969
+ // Deal with any errors (e.g. offline)
970
+ }
971
+ ```
972
+
973
+ If `emoji-picker-element` fails to fetch the JSON data the first time it loads, then it will display an error message.
974
+
975
+ ### Environments without IndexedDB
976
+
977
+ `emoji-picker-element` has a hard requirement on [IndexedDB](https://developer.mozilla.org/en-US/docs/Glossary/IndexedDB), and will not work without it.
978
+
979
+ For browsers that don't support IndexedDB, such as [Firefox in private browsing mode](https://bugzilla.mozilla.org/show_bug.cgi?id=1639542), you can polyfill it using [fake-indexeddb](https://github.com/dumbmatter/fakeIndexedDB). Here is [a working example](https://bl.ocks.org/nolanlawson/651e6fbe4356ff098f505e6cc5fb8cd8) and [more details](https://github.com/nolanlawson/emoji-picker-element/issues/9).
980
+
981
+ For Node.js environments such as [Jest](https://jestjs.io/) or [JSDom](https://github.com/jsdom/jsdom), you can also use fake-indexeddb. A [working example](https://github.com/nolanlawson/emoji-picker-element/blob/39c50c3ce4c4c4d2cd8a15f337a722ad86c739e9/config/jest.setup.js#L28-L29) can be found in the tests for this very project.
982
+
983
+ ## Design decisions
984
+
985
+ Some of the reasoning behind why `emoji-picker-element` is built the way it is.
986
+
987
+ ### IndexedDB
988
+
989
+ The [`emojibase-data`](https://github.com/milesj/emojibase) English JSON file is [854kB](https://unpkg.com/browse/emojibase-data@5.0.1/en/), and the "compact" version is still 543kB. That's a lot of data to keep in memory just for an emoji picker. And it's not as if that number is ever going down; the Unicode Consortium keeps adding more emoji every year.
990
+
991
+ Using IndexedDB has a few advantages:
992
+
993
+ 1. We don't need to keep the full emoji data in memory at all times.
994
+ 2. After the first load, there is no need to download, parse, and index the JSON file again, because it's already available in IndexedDB.
995
+ 3. If you want, you can even [load the IndexedDB data in a web worker](https://github.com/nolanlawson/emoji-picker-element/blob/ff86a42/test/adhoc/worker.js), keeping the main thread free from non-UI data processing.
996
+
997
+ ### Native emoji
998
+
999
+ To avoid downloading a large sprite sheet or font file – which may look out-of-place on different platforms, or may have [IP issues](https://blog.emojipedia.org/apples-emoji-crackdown/) – `emoji-picker-element` only renders native emoji by default. This means it is limited to the emoji font actually installed on the user's device.
1000
+
1001
+ To avoid rendering ugly unsupported or half-supported emoji, `emoji-picker-element` will automatically detect emoji support and only render the supported characters. (So no empty boxes or awkward double emoji.) If no color emoji are supported by the browser/OS, then an error message is displayed (e.g. older browsers, some odd Linux configurations).
1002
+
1003
+ That said, `emoji-picker-element` does support [custom emoji fonts](#custom-emoji-font) if you really want.
1004
+
1005
+ ### JSON loading
1006
+
1007
+ Browsers deal with JSON more efficiently when it's loaded via `fetch()` rather than embedded in JavaScript. It's
1008
+ [faster for the browser to parse JSON than JavaScript](https://joreteg.com/blog/improving-redux-state-transfer-performance),
1009
+ becuase the data is being parsed in the more tightly-constrained JSON format than the generic JavaScript format.
1010
+
1011
+ Plus, embedding the JSON directly would mean re-parsing the entire object on second load, which is something we want to avoid since the data is already in IndexedDB.
1012
+
1013
+ ### Browser support
1014
+
1015
+ `emoji-picker-element` only supports the latest versions of Chrome, Firefox, and Safari, as well as equivalent browsers (Edge, Opera, etc.). If you need support for older browsers, you will need polyfills for the following things (non-exhaustive list):
1016
+
1017
+ - Custom elements
1018
+ - Shadow DOM
1019
+ - ES2019+
1020
+
1021
+ That said, older browsers may not have a color emoji font installed at all, so `emoji-picker-element` will not work in those cases.
1022
+
1023
+ ## Contributing
1024
+
1025
+ See [CONTRIBUTING.md](https://github.com/nolanlawson/emoji-picker-element/blob/master/CONTRIBUTING.md).