Giter VIP home page Giter VIP logo

emoji-picker-element's Introduction

emoji-picker-element

Screenshot of emoji-picker-element in light and dark modes

<emoji-picker></emoji-picker>

A lightweight emoji picker, distributed as a web component.

Features:

Table of contents:

Usage

Via npm:

npm install emoji-picker-element
import 'emoji-picker-element';

Or as a <script> tag:

<script type="module" src="https://cdn.jsdelivr.net/npm/emoji-picker-element@^1/index.js"></script>

Then use the HTML:

<emoji-picker></emoji-picker>

And listen for emoji-click events:

document.querySelector('emoji-picker')
  .addEventListener('emoji-click', event => console.log(event.detail));

This will log:

{
  "emoji": {
    "annotation": "grinning face",
    "group": 0,
    "order": 1,
    "shortcodes": [ "grinning_face", "grinning" ],
    "tags": [ "face", "grin" ],
    "unicode": "πŸ˜€",
    "version": 1,
    "emoticon": ":D"
  },
  "skinTone": 0,
  "unicode": "πŸ˜€"
}

Examples

Emoji support

Emoji support varies across browsers and operating systems. By default, emoji-picker-element will hide unsupported emoji from the picker.

To work around this, you can use a custom emoji font or polyfill flag emoji on Windows.

Custom emoji font

To use a custom emoji font, first set the --emoji-font-family CSS property:

emoji-picker {
  --emoji-font-family: MyCustomFont;
}

Then, specify the maximum emoji version supported by the font (see Emojipedia for a list of versions).

In HTML:

<emoji-picker emoji-version="15.0"></emoji-picker>

Or JavaScript:

const picker = new Picker({
  emojiVersion: 15.0
});

If the emoji-version/emojiVersion option is set, then emoji-picker-element will not attempt to detect unsupported emoji or hide them.

Also note that support for color fonts varies across browsers and OSes, and some browsers may have bugs or not render the font at all. Be careful to test your supported browsers when using this approach.

Polyfilling flag emoji on Windows

As of this writing, Windows does not support country flag emoji. This is only a problem in Chromium-based browsers, because Firefox ships with its own emoji font.

To work around this, you can use country-flag-emoji-polyfill:

import { polyfillCountryFlagEmojis } from 'country-flag-emoji-polyfill';

// emoji-picker-element will use "Twemoji Mozilla" and fall back to other fonts for non-flag emoji
polyfillCountryFlagEmojis('Twemoji Mozilla');

Note that you do not need to do this if you are using a custom emoji font.

Styling

emoji-picker-element uses Shadow DOM, so its inner styling cannot be (easily) changed with arbitrary CSS. Refer to the API below for style customization.

Size

emoji-picker-element has a default size, but you can change it to whatever you want:

emoji-picker {
  width: 400px;
  height: 300px;
}

For instance, to make it expand to fit whatever container you give it:

emoji-picker {
  width: 100%;
  height: 100%;
}

Dark mode

By default, emoji-picker-element will automatically switch to dark mode based on prefers-color-scheme. Or you can add the class dark or light to force dark/light mode:

<emoji-picker class="dark"></emoji-picker>
<emoji-picker class="light"></emoji-picker>

CSS variables

Most colors and sizes can be styled with CSS variables. For example:

emoji-picker {
  --num-columns: 6;
  --emoji-size: 3rem;
  --background: gray;
}

Here is a full list of options:

Variable Default Default (dark) Description
--background #fff #222 Background of the entire <emoji-picker>
--border-color #e0e0e0 #444
--border-size 1px Width of border used in most of the picker
--button-active-background #e6e6e6 #555555 Background of an active button
--button-hover-background #d9d9d9 #484848 Background of a hovered button
--category-emoji-padding var(--emoji-padding) Vertical/horizontal padding on category emoji, if you want it to be different from --emoji-padding
--category-emoji-size var(--emoji-size) Width/height of category emoji, if you want it to be different from --emoji-size
--category-font-color #111 #efefef Font color of custom emoji category headings
--category-font-size 1rem Font size of custom emoji category headings
--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)
--emoji-padding 0.5rem Vertical and horizontal padding on emoji
--emoji-size 1.375rem Width and height of all emoji
--indicator-color #385ac1 #5373ec Color of the nav indicator
--indicator-height 3px Height of the nav indicator
--input-border-color #999 #ccc
--input-border-radius 0.5rem
--input-border-size 1px
--input-font-color #111 #efefef
--input-font-size 1rem
--input-line-height 1.5
--input-padding 0.25rem
--input-placeholder-color #999 #ccc
--num-columns 8 How many columns to display in the emoji grid
--outline-color #999 #fff Focus outline color
--outline-size 2px Focus outline width
--skintone-border-radius 1rem Border radius of the skintone dropdown

Focus outline

For accessibility reasons, emoji-picker-element displays a prominent focus ring for keyboard users. This uses :focus-visible under the hood. To properly support browsers that do not support :focus-visible, you can use the focus-visible polyfill, e.g.:

import 'focus-visible';

const picker = new Picker();
applyFocusVisiblePolyfill(picker.shadowRoot);

emoji-picker-element already ships with the proper CSS for both the :focus-visible standard and the polyfill.

Small screen sizes

For small screen sizes, you should probably add some CSS like the following:

@media screen and (max-width: 320px) {
  emoji-picker {
    --num-columns: 6;
    --category-emoji-size: 1.125rem;
  }
}

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 can solve this problem.

Custom styling

If you absolutely must go beyond the styling API above, you can do something like this:

const style = document.createElement('style');
style.textContent = `/* custom shadow dom styles here */`
picker.shadowRoot.appendChild(style);

JavaScript API

Picker

Basic usage:

import { Picker } from 'emoji-picker-element';
const picker = new Picker();
document.body.appendChild(picker);

The new Picker(options) constructor supports several options:

Name Type Default Description
customCategorySorting function - Function to sort custom category strings (sorted alphabetically by default)
customEmoji CustomEmoji[] - Array of custom emoji
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)
emojiVersion number - Maximum supported emoji version as a number (e.g. 14.0 or 13.1). Setting this disables the default emoji support detection.
i18n I18n - i18n object (see below for details)
locale string "en" Locale string
skinToneEmoji string "πŸ–οΈ" The emoji to use for the skin tone picker (skin-tone-emoji when used as an attribute)

For instance:

const picker = new Picker({
  locale: 'fr',
  dataSource: '/fr-emoji.json'
})

These values can also be set at runtime:

const picker = new Picker();
picker.dataSource = '/my-emoji.json';

Some values can also be set as declarative attributes:

<emoji-picker
  locale="fr"
  data-source="/fr-emoji.json"
  skin-tone-emoji="✌"
></emoji-picker>

Note that complex properties like i18n or customEmoji are not supported as attributes, because the DOM only supports string attributes, not complex objects.

Events

emoji-click

The emoji-click event is fired when an emoji is selected by the user. Example format:

{
  emoji: {
    annotation: 'thumbs up',
    group: 1,
    order: 280,
    shortcodes: ['thumbsup', '+1', 'yes'],
    tags: ['+1', 'hand', 'thumb', 'up'],
    unicode: 'πŸ‘οΈ',
    version: 0.6,
    skins: [
      { tone: 1, unicode: 'πŸ‘πŸ»', version: 1 },
      { tone: 2, unicode: 'πŸ‘πŸΌ', version: 1 },
      { tone: 3, unicode: 'πŸ‘πŸ½', version: 1 },
      { tone: 4, unicode: 'πŸ‘πŸΎ', version: 1 },
      { tone: 5, unicode: 'πŸ‘πŸΏ', version: 1 }
    ]
  },
  skinTone: 4,
  unicode: 'πŸ‘πŸΎ'
}

And usage:

picker.addEventListener('emoji-click', event => {
  console.log(event.detail); // will log something like the above
});

Note that unicode will represent whatever the emoji should look like with the given skinTone. If the skinTone is 0, or if the emoji has no skin tones, then no skin tone is applied to unicode.

skin-tone-change

This event is fired whenever the user selects a new skin tone. Example format:

{
  skinTone: 5
}

And usage:

picker.addEventListener('skin-tone-change', event => {
  console.log(event.detail); // will log something like the above
})

Note that skin tones are an integer from 0 (default) to 1 (light) through 5 (dark).

Internationalization

The i18n parameter specifies translations for the picker interface. Here is the default English i18n object:

{
  "categories": {
    "custom": "Custom",
    "smileys-emotion": "Smileys and emoticons",
    "people-body": "People and body",
    "animals-nature": "Animals and nature",
    "food-drink": "Food and drink",
    "travel-places": "Travel and places",
    "activities": "Activities",
    "objects": "Objects",
    "symbols": "Symbols",
    "flags": "Flags"
  },
  "categoriesLabel": "Categories",
  "emojiUnsupportedMessage": "Your browser does not support color emoji.",
  "favoritesLabel": "Favorites",
  "loadingMessage": "Loading…",
  "networkErrorMessage": "Could not load emoji.",
  "regionLabel": "Emoji picker",
  "searchDescription": "When search results are available, press up or down to select and enter to choose.",
  "searchLabel": "Search",
  "searchResultsLabel": "Search results",
  "skinToneDescription": "When expanded, press up or down to select and enter to choose.",
  "skinToneLabel": "Choose a skin tone (currently {skinTone})",
  "skinTones": [
    "Default",
    "Light",
    "Medium-Light",
    "Medium",
    "Medium-Dark",
    "Dark"
  ],
  "skinTonesLabel": "Skin tones"
}

Note that some of these strings are only visible to users of screen readers. They are still important for accessibility!

Built-in translations

Community-provided translations for some languages are available. You can use them like so:

import fr from 'emoji-picker-element/i18n/fr';
import de from 'emoji-picker-element/i18n/de';

// French
picker.i18n = fr;

// German
picker.i18n = de;

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:

import fr from 'emoji-picker-element/i18n/fr';

const picker = new Picker({ 
  i18n: fr,
  locale: 'fr',
  dataSource: 'https://cdn.jsdelivr.net/npm/emoji-picker-element-data@^1/fr/emojibase/data.json',
});

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.

Custom category order

By default, custom categories are sorted alphabetically. To change this, pass in your own customCategorySorting:

picker.customCategorySorting = (category1, category2) => { /* your sorting code */ };

This function should accept two strings and return a number.

Custom emoji with no category will pass in undefined. By default, these are shown first, with the label "Custom" (determined by i18n.categories.custom).

Database

You can work with the database API separately, which allows you to query emoji the same way that the picker does:

import { Database } from 'emoji-picker-element';

const database = new Database();
await database.getEmojiBySearchPrefix('elephant'); // [{unicode: "🐘", ...}]

Note that under the hood, IndexedDB data is partitioned based on the locale. So if you create two Databases with two different locales, it will store twice as much data.

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.

Full API:

Constructors

constructor

+ new Database(__namedParameters: object): Database

Create a new Database.

Note that multiple Databases pointing to the same locale will share the same underlying IndexedDB connection and database.

Parameters:

β–ͺDefault value __namedParameters: object= {}

Name Type Default Description
customEmoji CustomEmoji[] [] Array of custom emoji
dataSource string "https://cdn.jsdelivr.net/npm/emoji-picker-element-data@^1/en/emojibase/data.json" URL to fetch the emoji data from
locale string "en" Locale string

Returns: Database

Accessors

customEmoji

β€’ get customEmoji(): CustomEmoji[]

Return the custom emoji associated with this Database, or the empty array if none.

Returns: CustomEmoji[]

β€’ set customEmoji(customEmoji: CustomEmoji[]): void

Set the custom emoji for this database. Throws an error if custom emoji are not in the correct format.

Parameters:

Name Type Description
customEmoji CustomEmoji[]

Returns: void

Methods

close

β–Έ close(): Promiseβ€Ήvoidβ€Ί

Closes the underlying IndexedDB connection. The Database is not usable after that (or any other Databases with the same locale).

Note that as soon as any other non-close/delete method is called, the database will automatically reopen.

Returns: Promiseβ€Ήvoidβ€Ί


delete

β–Έ delete(): Promiseβ€Ήvoidβ€Ί

Deletes the underlying IndexedDB database. The Database is not usable after that (or any other Databases with the same locale).

Note that as soon as any other non-close/delete method is called, the database will be recreated.

Returns: Promiseβ€Ήvoidβ€Ί


getEmojiByGroup

β–Έ getEmojiByGroup(group: number): Promiseβ€ΉNativeEmoji[]β€Ί

Returns all emoji belonging to a group, ordered by order. Only returns native emoji; custom emoji don't belong to a group.

Non-numbers throw an error.

Parameters:

Name Type Description
group number the group number

Returns: Promiseβ€ΉNativeEmoji[]β€Ί


getEmojiBySearchQuery

β–Έ getEmojiBySearchQuery(query: string): Promiseβ€ΉEmoji[]β€Ί

Returns all emoji matching the given search query, ordered by order.

Empty/null strings throw an error.

Parameters:

Name Type Description
query string search query string

Returns: Promiseβ€ΉEmoji[]β€Ί


getEmojiByShortcode

β–Έ getEmojiByShortcode(shortcode: string): Promiseβ€ΉEmoji | nullβ€Ί

Return a single emoji matching the shortcode, or null if not found.

The colons around the shortcode should not be included when querying, e.g. use "slight_smile", not ":slight_smile:". Uppercase versus lowercase does not matter. Empty/null strings throw an error.

Parameters:

Name Type Description
shortcode string

Returns: Promiseβ€ΉEmoji | nullβ€Ί


getEmojiByUnicodeOrName

β–Έ getEmojiByUnicodeOrName(unicodeOrName: string): Promiseβ€ΉEmoji | nullβ€Ί

Return a single native emoji matching the unicode string, or a custom emoji matching the name, or null if not found.

In the case of native emoji, the unicode string can be either the main unicode string, or the unicode of one of the skin tone variants.

Empty/null strings throw an error.

Parameters:

Name Type Description
unicodeOrName string unicode (native emoji) or name (custom emoji)

Returns: Promiseβ€ΉEmoji | nullβ€Ί


getPreferredSkinTone

β–Έ getPreferredSkinTone(): Promiseβ€ΉSkinToneβ€Ί

Get the user's preferred skin tone. Returns 0 if not found.

Returns: Promiseβ€ΉSkinToneβ€Ί


getTopFavoriteEmoji

β–Έ getTopFavoriteEmoji(limit: number): Promiseβ€ΉEmoji[]β€Ί

Get the top favorite emoji in descending order. If there are no favorite emoji yet, returns an empty array.

Parameters:

Name Type Description
limit number maximum number of results to return

Returns: Promiseβ€ΉEmoji[]β€Ί


incrementFavoriteEmojiCount

β–Έ incrementFavoriteEmojiCount(unicodeOrName: string): Promiseβ€Ήvoidβ€Ί

Increment the favorite count for an emoji by one. The unicode string must be non-empty. It should correspond to the base (non-skin-tone) unicode string from the emoji object, or in the case of custom emoji, it should be the name.

Parameters:

Name Type Description
unicodeOrName string unicode of a native emoji, or name of a custom emoji

Returns: Promiseβ€Ήvoidβ€Ί


ready

β–Έ ready(): Promiseβ€Ήvoidβ€Ί

Resolves when the Database is ready, or throws an error if the Database could not initialize.

Note that you don't need to do this before calling other APIs – they will all wait for this promise to resolve before doing anything.

Returns: Promiseβ€Ήvoidβ€Ί


setPreferredSkinTone

β–Έ setPreferredSkinTone(skinTone: SkinTone): Promiseβ€Ήvoidβ€Ί

Set the user's preferred skin tone. Non-numbers throw an error.

Parameters:

Name Type Description
skinTone SkinTone preferred skin tone

Returns: Promiseβ€Ήvoidβ€Ί

Custom emoji

Both the Picker and the Database support custom emoji. Unlike regular emoji, custom emoji are kept in-memory. (It's assumed that they're small, and they might frequently change, so there's not much point in storing them in IndexedDB.)

Custom emoji should follow the format:

[
  {
    name: 'Garfield',
    shortcodes: ['garfield'],
    url: 'http://example.com/garfield.png',
    category: 'Cats'
  },
  {
    name: 'Heathcliff',
    shortcodes: ['heathcliff'],
    url: 'http://example.com/heathcliff.png',
    category: 'Cats'
  },
  {
    name: 'Scooby-Doo',
    shortcodes: ['scooby'],
    url: 'http://example.com/scooby.png',
    category: 'Dogs'
  }  
]

Note that names are assumed to be unique (case-insensitive), and it's assumed that the shortcodes have at least one entry.

The category is optional. If you don't provide it, then the custom emoji will appear in a single category called "Custom".

To pass custom emoji into the Picker:

const picker = new Picker({
  customEmoji: [ /* ... */ ]
});

Or the Database:

const database = new Database({
  customEmoji: [ /* ... */ ]
});

Custom emoji can also be set at runtime:

picker.customEmoji = [ /* ... */ ];
database.customEmoji = [ /* ... */ ];

Tree-shaking

If you want to import the Database without the Picker, or you want to code-split them separately, then do:

import Picker from 'emoji-picker-element/picker';
import Database from 'emoji-picker-element/database';

The reason for this is that Picker automatically registers itself as a custom element, following web component best practices. 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.

Within a meta-framework (Next.js, SvelteKit, etc.)

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.

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 with next/dynamic in Next.js or onMount() in SvelteKit.

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).

Within a Svelte project

Warning

emoji-picker-element is no longer based on Svelte, so importing from emoji-picker-element/svelte is now deprecated.

Previously, emoji-picker-element was based on Svelte v3/v4, and you could do:

import Picker from 'emoji-picker-element/svelte';

The goal was to slightly reduce the bundle size by sharing common svelte imports.

This is still supported for backwards compatibility, but it is deprecated and just re-exports the Picker. Instead, do:

import Picker from 'emoji-picker-element/picker';

Data and offline

Data source and JSON format

If you'd like to host the emoji data (dataSource) yourself, you can do:

npm install emoji-picker-element-data@^1

Then host node_modules/emoji-picker-element-data/en/emojibase/data.json (or other JSON files) on your web server.

const picker = new Picker({
  dataSource: '/path/to/my/webserver/data.json'
});

See emoji-picker-element-data for details.

Shortcodes

There is no standard for shortcodes, so unlike other emoji data, there is some disagreement as to what a "shortcode" actually is.

emoji-picker-element-data is based on emojibase-data, which offers several shortcode packs per language. For instance, you may choose shortcodes from GitHub, Slack, Discord, or Emojibase (the default). You can browse the available data files on jsdelivr and see more details on shortcodes in the Emojibase docs.

Cache performance

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.

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-Allow-Headers: ETag (or Access-Control-Allow-Headers: * ). jsdelivr already does this, which is partly why it is the default.

Note that Safari does not currently support Access-Control-Allow-Headers: *, but it does support Access-Control-Allow-Headers: ETag.

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.

emojibase-data compatibility (deprecated)

Deprecated: in v1.3.0, emoji-picker-element switched from emojibase-data to 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.

When using emojibase-data, you must use the full emojibase-data JSON file, not the "compact" one (i.e. data.json, not compact.json).

Trimming the emoji data (deprecated)

Deprecated: in v1.3.0, emoji-picker-element switched from emojibase-data to 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.

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:

import trimEmojiData from 'emoji-picker-element/trimEmojiData.js';
import emojiBaseData from 'emojibase-data/en/data.json';

const trimmedData = trimEmojiData(emojiBaseData);

Or if your version of Node doesn't support ES modules:

const trimEmojiData = require('emoji-picker-element/trimEmojiData.cjs');

Offline-first

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 the second time it runs.

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:

const database = new Database();
try {
  await database.ready();
} catch (err) {
  // Deal with any errors (e.g. offline)
}

If emoji-picker-element fails to fetch the JSON data the first time it loads, then it will display an error message.

Environments without IndexedDB

emoji-picker-element has a hard requirement on IndexedDB, and will not work without it.

For browsers that don't support IndexedDB, such as Firefox in private browsing mode, you can polyfill it using fake-indexeddb. Here is a working example and more details.

For Node.js environments such as Jest or JSDom, you can also use fake-indexeddb. A working example can be found in the tests for this very project.

Design decisions

Some of the reasoning behind why emoji-picker-element is built the way it is.

IndexedDB

The emojibase-data English JSON file is 854kB, 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.

Using IndexedDB has a few advantages:

  1. We don't need to keep the full emoji data in memory at all times.
  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.
  3. If you want, you can even load the IndexedDB data in a web worker, keeping the main thread free from non-UI data processing.

Native emoji

To avoid downloading a large sprite sheet or font file – which may look out-of-place on different platforms, or may have IP issues – 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.

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).

That said, emoji-picker-element does support custom emoji fonts if you really want.

JSON loading

Browsers deal with JSON more efficiently when it's loaded via fetch() rather than embedded in JavaScript. It's faster for the browser to parse JSON than JavaScript, becuase the data is being parsed in the more tightly-constrained JSON format than the generic JavaScript format.

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.

Browser support

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):

  • Custom elements
  • Shadow DOM
  • ES2019+

That said, older browsers may not have a color emoji font installed at all, so emoji-picker-element will not work in those cases.

Contributing

See CONTRIBUTING.md.

emoji-picker-element's People

Contributors

arbarwings avatar dependabot[bot] avatar dimdengd avatar ericlm avatar eteeselink avatar guopengsong avatar kroniapp avatar mavrin avatar michalorman avatar mrdrogdrog avatar nolanlawson avatar popperz0r avatar szepeviktor avatar toschlog avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

emoji-picker-element's Issues

Cache problem.

First of all, thank you for EmojiPickerElement!

Next, I'd like to share a little problem with you, I have two pages, one page with an messaging system that uses emojiPickerElement with all emojis.
And I have a page with posts, on which you can react to posts with only about ten emojis.
What I do is I initialize the database and then EmojiPickerElement by giving them a JSON in dataSource with only the emojis I want and with the 'click' event I record the user's reaction on the post.

But here's the problem, if the user has been on the messaging page before going to the posts page, because of the cache, all the emojis are displayed and not only the ones from the dataSource (you have to reload the page so that the dataSource is taken into account instead of the cache). What can I do?

Custom emoji with one single category don't visually show the category

This is definitely an edge case, but if the user has set customCategory on their custom emoji, even if all custom emoji have a single category, then I think we should visually show it. Otherwise it's not clear to sighted users that the category may be something other than just the default ("Custom").

Sapper - ReferenceError: requestAnimationFrame is not defined

In a svelte component within a Sapper project, I am trying to add the picker:

<script>
  import { onMount } from "svelte";

  // https://github.com/nolanlawson/emoji-picker-element#within-a-svelte-project
  import Picker from "emoji-picker-element/svelte";

  let sidebar;

  onMount(() => {

    // Picker:
    const picker = new Picker({
      locale: "en",
    });
    sidebar.appendChild(picker);
  });
</script>

<div bind:this={sidebar}></div>

error:

const rAF = requestAnimationFrame;
            ^
ReferenceError: requestAnimationFrame is not defined

Any ideas or advice on how to get this running in sapper? Thanks!

NVDA reads native emoji shortcode twice

For native (not custom) emoji, NVDA+Firefox reads the shortcode name twice when pressing up/down through search results. VoiceOver+Safari does not have this issue.

Not sure why; maybe it's the title? I don't plan on getting rid of the title, though.

emoji-picker-element v2

Breaking changes we may want to make in v2:

Search tokens are missing some common tags ("danish", "praying")

I guess it is hard to handle all these, but with GBoard I can search for Danish or Denmark and it will give me the Danish flag - here only Denmark works

Also 'pray' shows praying hands but not 'praying'. Also high fives on GBoard will give you praying hands :)

Fall back to basic, non-color emoji v1

In browsers that don't support color emoji (e.g. KaiOS, Chrome on Linux without Noto color emoji package installed), we currently show an error message: "your browser does not support color emoji."

Another possibility is to just show the v1 emoji, even if there's no color. It may be better than nothing.

It's worth investigating how many browser/OS combos actually have this problem, and what non-color emoji they would support in that case. For now, I'm only aware of KaiOS and Linux Chrome.

getEmojiByShortcode('smiling_face_with_3_hearts') returns null

So I have this svelte app inside which I do

import EmojiDatabase from 'emoji-picker-element/database'
…
const edb = new EmojiDatabase()
...
async function click(shortcode) {
    emoji = await edb.getEmojiByShortcode(shortcode)
}

And this fails for shortcode = 'smiling_face_with_3_hearts'. Maybe it has something to do with the number inside the short code?

Issue on Edge: Expected identifier, string or number

Hey there!
Thanks for the awesome package, we really enjoy using it and we have implemented it as a tooltip in our vue project. However, we’ve noticed issues in our reporting tool piling up on Edge (EdgeHTML: 18.17763):

Expected identifier, string or number in picker.js  2011  13  currentSkinTone(...)

Backtrace:

fireEvent(β€œemoji-click”, {
--
2010 | emoji,
2011 | skinTone: currentSkinTone, <---
2012 | ...skinTonedUnicode && { unicode: skinTonedUnicode },
2013 | ...emojiSummary.name && { name: emojiSummary.name }

Any ideas how this can be resolved? Perhaps, it is a really old version of the browser, if so, is there a way to detect and maybe hide the picker? Some more context about our use case:

  • We use the element inside a vue component alongside popperjs and we listen for the emoji-click which then emits another event that we use outside of the component.

Thanks for your time!

Update Typedoc

Just making a note here so I don't forget.

I've been trying to update typedoc, but the latest version has plenty of breaking changes, including one that I'm not sure how to work around. For the Picker and Database constructor options, it just renders __namedParameters which is ugly and unhelpful. In the old version is actually rendered each of the named parameters in a table.

I can rename it using this plugin but that only partially solves the problem.

I don't see any way around this from looking through the typedoc open issues and stackoverflow questions, so I'm just sticking with the old version for now.

Abandoned attempt: https://github.com/nolanlawson/emoji-picker-element/tree/try-to-update-typdoc

Firefox for Android throws canvas error for cross-origin iframes

It seems that Firefox for Android blocks using canvas in a cross-origin iframe for measuring text. I noticed it because the emoji picker can't load in the CodePen posted in this blog post: https://css-tricks.com/awesome-standalone-web-components/

Not sure there's anything I can do about this. But maybe if canvas fails, we should just show all the emoji, even if they're broken? Probably better to show some οΏ½s than to not show the emoji picker at all.

Escape key doesn't dismiss listbox

  1. Tab to the skintone dropdown
  2. Enter
  3. Press Esc

It should close the listbox and select the dropdown button, but instead it enters a weird state where the listbox is still open but you can tab around to other stuff.

Firefox/Safari on Mac: can't change skin tone

The skin tone selector does not work on my Firefox 77 / macOS. For some reason, there's no activeElement on the focusout event when I click on a skin tone button.

async function onSkinToneOptionsBlur () {
// On blur outside of the skintone options, collapse the skintone picker.
// Except if focus is just moving to another skintone option, e.g. pressing up/down to change focus
// I would use relatedTarget here, but iOS Safari seems to have a bug where it does not figure out
// the relatedTarget correctly, so I delay with rAF instead
await new Promise(resolve => requestAnimationFrame(resolve))
const { activeElement } = rootElement.getRootNode()
if (!activeElement || !activeElement.classList.contains('skintone-option')) {
skinTonePickerExpanded = false
}
}

Support events without hyphens (?)

It's come to my attention that the event names skin-tone-change and emoji-click may be problematic in some cases. It looks like some frameworks may not be able to use anything but lowercase, non-hyphenated event names, because that's what's supported in the DOM. E.g.:

<emoji-picker onemojiclick></emoji-picker>

but not

<emoji-picker onemoji-click></emoji-picker>

The second one looks like of bizarre anyway, so we should probably just support the non-hyphenated version, and maybe remove the hyphenated version in a major update later.

Note this would only apply to declarative usage not imperative usage. element.addEventListener() should work in all cases.

Reduce bundle size

Right now my goal is to keep the minified size under 40kB. Intuitively the size right now just feels way too big, but most of it is in the Svelte code so I don't have much direct impact over it. Still, if I can buy us some more kilobytes, it makes me less nervous about implementing new features.

Support custom emoji font

I've been sort of dogmatic about only supporting native emoji, but it is extremely frustrating how slowly OSes and browsers are rolling out Emoji v13.

Also I noticed an interesting way someone is using the project: they're using the Twemoji font and overriding the built-in font list: hedgedoc/react-client#664

This could be made an official API. For instance, you could set picker.customFont = 'myFont', and it would just set that as the font and ignore all the logic to try to figure out whether Emoji v13 is supported or not. (I think we could safely assume that if you're using a custom font, then it's probably because it supports all the emoji, so there's no need to try to sniff out unsupported emoji.)

Swipe left/right between tab panels

Feature: being able to swipe left or right (e.g. on a touchscreen) to change tabpanels in the picker.

This would be neat to implement, although I think it would be very technically challenging. I would like to do something like react-swipeable-views, where it mimics the momentum of native implements. Either that, or we could use something more like a horizontally-scrolling carousel.

The main challenge is that I implemented the picker in such a way that the list of emoji is basically the same DOM node, and we just swap the list of emoji in and out. This is great for performance, because Svelte only has to do a keyed list update, e.g. when changing tab panels or searching in the search box. But for swipeable views to work properly, we would probably at least have to have a "preview" to the left and right of the current results.

Alternatively, we could just allow swiping left and right to show blank space on either side, and then once your finger lets go, we actually make the call to IndexedDB and show the emoji. This would be less nice-looking but probably a lot easier to implement.

Internationalization

I've already started to receive some contributions for translations for emoji-picker-element. I wasn't planning on including these in the emoji-picker-element repo, but I admit it's not clear where else to put them.

On the one hand, it's a bit wasteful for everyone using emoji-picker-element to write their own translations (assuming they internationalize). But on the other hand, managing a lot of translations in one repo could be a heavy maintenance burden.

Maybe I should rather look into some kind of service for managing translation files. In any case, opening this issue so I can noodle on it.

Search only supported emojis

First off, this library is absolutely amazing, I've yet to find an OSS UI component that I feel is as well done (inside and out) as this one. Hats off!

When using the Database to search emojis, I noticed that the results include emojis that the current os/browser does not support. This makes sense, I guess, as the Database is a pure JS thing that doesn't depend on the browser at all, but it does make it hard to use your excellent native-emoji-filtering code in scenarios where you don't want to show the entire picker.

I've been thinking a bit about what approaches there might be that keep with the spirit of your design. One might be totally extracting the emoji support detection and filtering into a third exported class (besides Picker and Database). But that feels a bit over-engineered somehow - especially since it'd need to do all the init and browser setup code that the Picker also does.

A simpler option might be to maybe have the Picker class expose some of the same methods that Database does, eg getEmojisBySearchQuery and getEmojisByGroup. Then, any user who wants the browser-curated version of the emoji database can just instantiate a Picker and call it from there. But it feels a bit quirky too, with the Picker instance being a DOM node and and these functions influencing the DOM node's behavior whatsoever. What do you think?

I'd happily provide a PR, as I guess most solutions aren't hard per se. But many solutions would mess up your code in ways you'd likely prefer not to accept, and suppose there's more fun to be had than arguing with contributors about why their code is too shitty to make it in. So I figured I'd ask you for your thoughts first.

More efficient ZWJ support detection

Right now the algorithm for detecting emoji support is:

  1. Go through a representative emoji from each version, check if it's rendered in color
  2. Filter out any emoji versions greater than the version detected by the above
  3. When rendering any emoji, if it has a ZWJ in it, check that is rendered width is within a certain threshold relative to the width of a baseline emoji

The problem is with emoji versions like 13.1, which only have ZWJ additions. So there's no good color emoji test available. This means that if a device supports 13 but not 13.1, we will do a width check on every single emoji from 13.1 rather than testing one and skipping the others.

This is just a perf improvement, but it could be a big one (maybe). My concerns:

  1. Extra complexity in the code for an as-yet unknown perf benefit
  2. Potential blocking by UAs when reading the width of emoji via canvas (I'm already nervous about using canvas at all given fingerprint-blocking, although so far I haven't heard any complaints)

Add support for icon-fonts for custom emojis

Currently, custom emojis are limited to single image files. It would be great though if one can use custom non-image "emojis". This is especially useful if you want to support icons from icon-fonts such as FontAwesome or material-icons.

My idea how to realize this looks as following:

  • Custom emojis get a new optional property html
  • If the html-property is not set, the emoji will be rendered as an image with the defined source (url)
  • If the html-property is set, the url-property will be ignored and instead the html code will be rendered instead of the image

This allows setting e.g. <i class="fa fa-home"></i> as html.

If such an approach sounds reasonable, I'd like to help implement it.


The background for this issue: I'm one of the devs of @codimd. We're currently re-building our app and want to include your emoji-picker as it performs quite well. As we allow users to insert ForkAwesome icons, we wanted to integrate that into the picker without the need for single image files.

Set picker as Button with Overlay

Hello there, first of all, amazing work here.

How I can set the Picker as a Button with floating overlay such as datepicker?

Here my code

<a id="emojiPicker" class="btn btn-primary" type="button">Emoji</a>
        <script type="module">
            let picker = new emoji_picker.Picker();
            let style = document.createElement('style');
            style.textContent = `
            .picker {
    z-index: 1;
    position: relative;
    width: auto;
}
            `
            picker.shadowRoot.appendChild(style);
            let emojiPicker = $('#emojiPicker');
            emojiPicker.click(function() {
                $('#emojiPicker').after(picker);
            });

            const pre = $('#pre')
            const onEvent = e => {
                console.log(e)
                pre.html(`Event: ${JSON.stringify(e.type)}\n\nData:\n\n${JSON.stringify(e.detail, null, 2)}`);
            }
            picker.addEventListener('emoji-click', onEvent);
        </script>

I'm not that good in JS and JQuery that i can exactly describe the wanted behavior.
On Datepicker Plugin, if you click into an input field for example it will show a nice "overlay" popup
https://jqueryui.com/datepicker/

Is it posible to do that for this project?

Update to new Svelte mount/unmount behavior

Looks like Svelte has changed how mounting/unmounting works for custom elements, so I can remove some of my hacks: sveltejs/svelte#4522

This won't affect users of the normal version of emoji-picker-element, but it could affect users of emoji-picker-element/svelte when Svelte releases this update. (It might be a breaking change, not sure.)

Picker cannot be re-inserted into the DOM

Steps to repro (jsfiddle):

import { Picker } from 'https://cdn.jsdelivr.net/npm/[email protected]/index.min.js'
const picker = new Picker()
document.body.appendChild(picker)
document.body.removeChild(picker)
document.body.appendChild(picker)

It shows a blank element instead of the picker.

I'm not sure how important this use-case is (you can always just call new Picker() instead and insert that), but maybe this should be supported?

Error if it is not used in body element

Error if it is not used in body element.

import 'emoji-picker-element';
<emoji-picker></emoji-picker>

picker.js:2403 Uncaught TypeError: Cannot set property 'scrollTop' of null
at eval (picker.js:2403)

rAF(() => { // reset scroll top to 0 when emojis change $$invalidate(6, tabpanelElement.scrollTop = 0, tabpanelElement); });

Easier way to get to favorites for keyboard/screenreader users

For keyboard and screenreader users, it would be nice to have a faster way to get to the favorites bar. They're supposed to be easily-accessible, which they are for mouse and touch users, but keyboard/screenreader users may have to tab through a lot of content to get to the favorites.

Something like skip links but for the favorites bar ("Skip to favorites"?) could work, although I need to do some research first to see if this is a generic pattern that would make sense for an emoji picker component.

Use latest versions of packages from jsdelivr

Apparently jsdelivr treats package@1 differently from package@^1 – the second is more likely to be fresher. So we should fix this in the default data source and in the documentation.

Multiple skintone picker

Some emoji pickers are now able to select multiple skintones for those emoji that support it (e.g. 🀝 and πŸ§‘β€πŸ€β€πŸ§‘).

I'm a bit worried that this feature would require complicating the interface quite a bit, and increase the bundle size. Also it's not clear to me what the support is across all platforms. E.g. apparently Microsoft supports 52,000 family combinations.

Still, it's a necessary feature to be able to pick all the emoji.

Allow for spritesheets in custom emoji

Right now custom emoji assume that there is one image file for each custom emoji, but presumably some people are using spritesheets. Even in the age of HTTP/2, there is some argument for doing so, since a single PNG compressed better than many small ones. It seems reasonable to support.

Allow for tags in custom emoji

Having the ability to add extra tags to use when searching for custom emoji would be neat.

One use-case I can think of is that Mastodon marks some emoji as "not shown in the picker," because they are intended only for autocomplete searches. E.g. the same PNG may have two shortcodes: :foo: and :foobar:, but we don't want to show duplicates in the picker. In this case, a user of emoji-picker-element could take the non-shown emoji and add its shortcode to the tags of the other emoji.

Tooltip with additional information when hovering emoji

Using title is the easiest option, but it has a few drawbacks:

  • doesn't work for keyboard users
  • can't display rich data, only text

I think there's a case to be made that we should add some kind of custom tooltip. It could show things like a larger version of the emoji, the shortcodes, the annotation, etc. Using a pattern like this, we could even make it accessible to keyboard and screen reader users.

The main downside is increasing the bundle size and adding complexity. Also either way, it's not going to be accessible to mobile touchscreen users (although this could be said of title as well).

Skin tone dropdown should collapse on blur

When the skin tone dropdown is expanded and the user focuses somewhere else, it should automatically collapse.

This was already implemented in v1.0.0. However it didn't work properly cross-browser and cross-OS (see #14 and #15), so it needs to be carefully implemented if we want to bring this behavior back.

Uncaught TypeErrors

Hi,

I've installed the repo using npm install emoji-picker-element and imported it via import 'emoji-picker-element';. But when I try to use <emoji-picker></emoji-picker> in my component, these console errors shows up:

Uncaught TypeError: each_value_2 is undefined
create_each_block_1 picker.js:1045
...
Uncaught TypeError: tabpanelElement is undefined
update picker.js:2402

Everything is working fine if I import it as a <script> tag instead of using npm

<script type="module" src="https://cdn.jsdelivr.net/npm/emoji-picker-element@^1/index.js"></script>

I'm on FireFox 89.0.2, haven't tested with other browsers.

How to use in angular

Hello Team,
I am trying to use it in angular application when I add in html its asking add as a angular component.
But I don't see any module exposed in library

Reflected attributes

It feels like this should work:

 <emoji-picker
  dataSource="https://example.com/data.json"
  locale="en"
></emoji-picker>

...but currently it doesn't. Maybe it should?

Open question is how we would handle i18n or customEmoji, or the case sensitivity of dataSource, but it seems worth investigating.

Custom emoji category order

By default, custom emoji categories are sorted alphabetically. I wish to pin/put certain categories over others.
I don't see a current way to do this, is this something that could be added? Maybe let me provide a custom sort-function?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.