Skip to content

Deveo/ember-emojione

Repository files navigation

ember-emojione

CircleCI build status Ember Observer Score npm package version license MIT ember-versions 2.16+ node-versions 8+ ember-cli 3.12.0

ember-emojione is your emoji solution for Ember, based on the EmojiOne project.

EmojiOne version 2 is used, which is free to use for everyone (CC BY-SA 4.0), you're only required to give appropriate credit to EmojiOne authors somewhere in your app/website. Unfortunately, EmojiOne 3 is a paid product for commercial use.

Demo: https://deveo.github.io/ember-emojione/

Table of contents

Support

Bug reports and feature requests are very welcome. In case you have something to suggest or report, please file an issue to the issue queue. But first make sure there's no similar issue. ;)

If you're having trouble using this addon in your project, please file a properly structured question at StackOverflow. It is important that you use ember.js and ember-emojione tags for your question to be seen.

Requirements

  • Ember 2.16+
  • Node 8+

Quick installation

If you don't want to go into the nuances of installation and configuration, use these short installation instructions. Then you can skip to the Usage section.

Quick installation will use these defaults:

  • Emoji are rendered as PNG sprites.
  • Sprite size is 64×64 px.
  • The sprite sheet is included into your app's distro (it's not available on JSDelivr).
  • This addon's components are available (picker, wrapper, typing assistance).

Run these console commands in your app:

ember install ember-emojione
bower install -S emojione-js=https://raw.githubusercontent.com/Ranks/emojione/v2.2.7/lib/js/emojione.js
bower install -S emojione-css=https://raw.githubusercontent.com/Ranks/emojione/v2.2.7/assets/sprites/emojione.sprites.css
bower install -S emojione-png=https://raw.githubusercontent.com/Ranks/emojione/v2.2.7/assets/sprites/emojione.sprites.png
bower install -S emojione-defs=https://raw.githubusercontent.com/Ranks/emojione/v2.2.7/emoji.json

If your app uses ember-cli-sass, the addon will not import component CSS automatically, so that you have more control. Import manually what you need:

@import "node_modules/ember-emojione/app/styles/helpers/mixin";
@import "node_modules/ember-emojione/app/styles/components/emoji-assist";
@import "node_modules/ember-emojione/app/styles/components/emoji-picker";

Now you should be good to go. If your development server has been running, don't forget to restart it.

Detailed Installation

The emojione Bower package, which this addon relies on, is over 90 MiB large. As EmojiOne assets can be served from the "free and super-fast" JSDelivr CDN, many developers won't need any EmojiOne images locally.

This addon can be configured to include as few or as much assets and dependencies as you need.

1. Installing the addon itself

With npm:

ember install ember-emojione

With Yarn:

yarn add -D ember-emojione

2. Installing the EmojiOne library

If you choose to use individual images (i. e. no sprite sheet) AND you want to bundle individual emoji images into your app distro (i. e. no separate CDN), you'll need to install the full emojione package (option 2).

In any other case, it is recommended that you install only the files you need (option 1).

Option 1: including only the necessary assets (recommended)

The bare minimum you need to display emoji are just two files: JS and CSS. To use this addon's components, you'll also need the JSON file with emoji definitions.

Luckily, Bower allows downloading individual files as individual dependencies:

First, include the EmojiOne JS library (note that the package name is emojione-js and not simply emojione):

bower install -S emojione-js=https://raw.githubusercontent.com/Ranks/emojione/v2.2.7/lib/js/emojione.js

Then include the CSS. Here you have two options.

Normal CSS, works with individual PNGs, individual SVGs and SVG sprite sheet:

bower install -S emojione-css=https://raw.githubusercontent.com/Ranks/emojione/v2.2.7/assets/css/emojione.css

Or, if you want to use PNG sprite sheet, add this CSS instead:

bower install -S emojione-css=https://raw.githubusercontent.com/Ranks/emojione/v2.2.7/assets/sprites/emojione.sprites.css

If you're gonna use the components, you'll also need emoji definitions. Definitions add 438 KiB to your distro size (54 KiB gzipped).

bower install -S emojione-defs=https://raw.githubusercontent.com/Ranks/emojione/v2.2.7/emoji.json

If you chose to use a sprite sheet, you must install it locally, because EmojiOne sprite sheets aren't available on JSDelivr (as of January 2017). Skip this step if you're gonna serve the sprite sheet from your own CDN, separate from you app distro.

PNG sprite sheet:

bower install -S emojione-png=https://raw.githubusercontent.com/Ranks/emojione/v2.2.7/assets/sprites/emojione.sprites.png

SVG sprite sheet:

bower install -S emojione-svg=https://raw.githubusercontent.com/Ranks/emojione/v2.2.7/assets/sprites/emojione.sprites.svg

Option 2: including the whole emojione package

Including the whole package, which is over 90 MiB large, is only reasonable if you want to use individual images rather than sprite sheets AND you want to serve them locally rather than via the free CDN.

Simply install it via:

bower install -S emojione

Note: your app's distro size will not grow by 90 MiB. In the next installation step, you'll decide which assets to include into the distro.

But every clean bower install will be substantially slower. This may include your CI builds.

3. Asset configuration in ember-cli-build.js to tell the addon about the choices you made above

Values shown below are the defaults. If you're happy with them, you don't need to edit ember-cli-build.js at all.

  var app = new EmberApp(defaults, {
    'ember-emojione': {
      // Did you install individual files or the full package?
      separatePackages: true,

      // Do you want EmojiOne CSS to be included in your app?
      shouldImportCss: true,

      // Are you going to use components that insert emoji?
      shouldImportDefs: true,

      // Whether to use a sprite sheet or individual images
      spriteSheet: true,

      // Enable one of these options if you want to include
      // EmojiOne assets into your app's distro.
      shouldIncludePngSprite: true,
      shouldIncludeSvgSprite: false,
      shouldIncludePngImages: false,
      shouldIncludeSvgImages: false,

      // If you chose individual images in the previous section,
      // you can customize their size and color here.
      // 'png' and 'png_bw' assets come in 64×64 px size.
      pngImagesKind: 'png', // png, png_128x128, png_512x512, png_bw
      svgImagesKind: 'svg', // svg, svg_bw

      // You can also customize package names to import.
      // You don't need to edit this if you carefully followed
      // previous installation steps
      packageNameMain:        'emojione',
      packageNameJs:          'emojione-js',
      packageNameCss:         'emojione-css',
      packageNameDefs:        'emojione-defs',
      packageNamePngSprite:   'emojione-png',
      packageNameSvgSprite:   'emojione-svg',

      // Whether to keep or remove `ember-emojione` components from build
      shouldIncludeComponents: true,
    },
  });

Note: if you set shouldIncludeComponents to false, you will not be able to define the following modules in your app:

  • app/components/emoji-picker
  • app/components/emoji-picker/*
  • app/components/emoji-picker-wrapper
  • app/components/emoji-picker-toggler
  • app/components/emoji-typing-assistance
  • app/helpers/eeo-*

Including component styles

Skip this section if you're not using this addon's components.

If your app uses ember-cli-sass, add some of these to your Sass:

@import "node_modules/ember-emojione/app/styles/helpers/mixin";
@import "node_modules/ember-emojione/app/styles/components/emoji-assist";
@import "node_modules/ember-emojione/app/styles/components/emoji-picker";

If your app doesn't use ember-cli-sass, precompiled styles will be included automatically.

Runtime Configuration in config/environment.js

Configuration is optional. If you're happy with defaults shown below, you don't need to edit config/environment.js.

ember-emojione relies on emojione.js defaults.

To configure ember-emojione and override emojione options, add these options to your app's config/environment.js. Default values are shown:

"ember-emojione": {

  // Used to skip certain portions of the input string.
  // Useful for Markdown code blocks. Apply after Markdown transformation.
  // Set to `false` to disable.
  regexToSkip: /<code[\s\S]*?>[\s\S]*?<\/code>/gm,

  // EmojiOne library options
  emojione: {
    imageType:     'png', // or svg
    imageTitleTag: true,  // set to false to remove title attribute from img tag
    unicodeAlt:    true,  // use the unicode char as the alt attribute (makes copy and pasting the resulting text better)
    ascii:         false, // change to true to convert ascii smileys

    // The following options are inferred from `ember-cli-build.js`.
    // You only need to override these options if you want to serve
    // assets from a custom CDN separate from you app's distro.
    imagePathPNG:        'https://cdn.jsdelivr.net/emojione/assets/png/',
    imagePathSVG:        'https://cdn.jsdelivr.net/emojione/assets/svg/',
    imagePathSVGSprites: './../assets/sprites/emojione.sprites.svg',
  }
}

Things to note:

  • The sprites option can not be configured from here. To enable sprites, override the spriteSheet option in ember-cli-build.js. It is also possible to override the sprites when invoking the inject-emoji helper.

  • The path to PNG sprite sheet is configured via CSS. If you're serving it from a custom CDN, add this style into your app:

    .emojione {
      background-image: url('path/to/your/emojione.sprites.png');
    }
  • If you chose to include local SVG assets, default SVG paths will use root-relative urls, e. g. /ember-emojione/svg/1f631.svg. If you serve your app from a subdirectory, please override one of the SVG URL options to include the subdirectory. You don't need this if you serve SVG assets from a CDN (default).

Usage

inject-emoji helper

This helper is used to convert a string with emoji codes into a string of HTML with emoji images.

You must manually mark the input string as HTML-safe:

Ember.String.htmlSafe("Hi! :sunglasses:")

By doing so you acknowledge responsibility that the input string never contains malicious code. Neglecting this responsibility will make your app/website prone to XSS attacks.

Use triple curlies to inject HTML into your template:

<div>
  {{{inject-emoji inputStr}}}
</div>

Result:

<div>
  Hi! <img class="emojione" alt="😎" title=":sunglasses:" src="https://cdn.jsdelivr.net/emojione/assets/png/1f60e.png?v=2.2.7"/>
</div>

Overriding options

You can override ember-emojione and emojione.js options for a single invocation of inject-emoji:

<div>
  {{{inject-emoji inputStr (hash
    regexToSkip = false
    emojione = (hash
      imageType = 'svg'
      sprites   = true
    )
  )}}}
</div>

Customizing emoji size via CSS

If you use individual PNGs, individual SVGs or SVG sprites, customizing emoji size is quite easy: you simply apply width and height to the .emojione selector.

But for PNG sprites (default), that won't work.

The easiest solution for PNG sprites is to use the zoom CSS property, but it doesn't work in Firefox.

For a cross-browser solution, use the code below. It is a Sass mixin; if you're not using Sass, you can compile it on SassMeister. The trick is to scale the emoji via CSS transform, then apply negative margins to remove extra whitespace.

@mixin emojione-size ($target-size, $original-size: 64px) {
  transform: scale(#{$target-size / $original-size});
  margin: ($target-size - $original-size) / 2;
}

.emojione {
  @include emojione-size(20px);
}

⚠️ Warning: this trick may produce blurry sprites in certain situations. It also causes text selection to look taller than it should:

tall text selection problem

If this is unacceptable for you, use custom-sized PNG spritesheets. Official EmojiOne spritesheets have an unfortunate 1px padding between sprites, making it impossible to resize it with background-size.

To resolve this problem, whe have prepared cusrom PNG spritesheets tailored to various sizes: emojione-png-sprites.

Here's a StackOverflow post explaining in detail how to use ember-emojione with emojione-png-sprites.

Using from JS

You can inject emoji programmatically via the injectEmoji convenience function:

import {injectEmoji} from 'ember-emojione/helpers/inject-emoji';

injectEmoji(inputString, options);

It returns an html-safe string if the input was html-safe. Otherwise, it returns a regular string.

import {htmlSafe, isHTMLSafe} from '@ember/string';
import {injectEmoji} from 'ember-emojione/helpers/inject-emoji';

const options = {regexToSkip: false, emojiOne: {ascii: true}};



const inputUnsafeString  = ':D';
const resultUnsafeString = injectEmoji(inputUnsafeString, options);

isHTMLSafe(resultUnsafeString) // => false
typeof resultUnsafeString;     // => "string"



const inputSafeString  = htmlSafe(inputUnsafeString);
const resultSafeString = injectEmoji(inputSafeString, options);

isHTMLSafe(resultSafeString);       // => true
typeof resultSafeString;            // => "object"
typeof resultSafeString.toString(); // => "string"

Skipping code blocks

inject-emoji will ignore emoji located within portions of the input string that match given regular expression.

The regex can be configured via the regexToSkip option.

By default, the regex matches <code>...</code> elements.

To disable skipping, set regexToSkip to false.

emoji-picker-wrapper component

emoji-picker-wrapper abstracts the logic of:

  • showing/hiding the emoji-picker and emoji-typing-assistance popups;
  • positioning the emoji-typing-assistance popup next to the caret;
  • inserting emoji into the text field, taking into consideration caret position;
  • navigating the emoji-typing-assistance popup with the keyboard while staying focused on the text field...

...while making no assumptions about page layout and looks, giving you full control.

emoji-picker-wrapper usage example

The price of giving you full control of layout and looks is pretty elaborate usage.

Here's the full example. Below it's broken down into steps.

{
  wikiPageText: 'This is a sample wiki page',
}
{{#emoji-picker-wrapper
  text                = wikiPageText
  inputSelector       = ".my-input"
  emojiInsertedAction = (action (mut wikiPageText))
  as |h|
}}

  <span style="position: relative; display: inline-block;">
    {{textarea class="my-input" value=wikiPageText}}
    {{component h.emojiAssist}}
  </span>

  {{component h.emojiPickerToggler}}

  {{component h.emojiPicker}}

{{/emoji-picker-wrapper}}

emoji-picker-wrapper usage example breakdown

To understand the example, let's assemble it step by step.

  1. We start with a text field, either an input or textarea. This text field accepts a value with a text from the wikiPageText property:

    {
      wikiPageText: 'This is a sample wiki page',
    }
    {{textarea class="my-input" value=wikiPageText}}
  2. In your HTML structure, decide where you're gonna put the emoji picker popup and the button that toggles the emoji picker.

    The locations depend on your HTML layout and page design. In this example, we're gonna keep it simple:

    {{textarea class="my-input" value=wikiPageText}}
    
    <!-- emoji picker popup is gonna be located here -->
    
    <!-- the button-that-toggles-the-popup is gonna be located here -->
  3. Wrap the text field with an HTML element that has position: relative and display of either block or inline-block:

    <span style="position: relative; display: inline-block;">
      {{textarea class="my-input" value=wikiPageText}}
    </span>
    
    <!-- emoji picker popup is gonna be located here -->
    
    <!-- the button-that-toggles-the-popup is gonna be located here -->

    In this example the HTML element uses inline styles for simplicity, but that's not a requirement.

  4. The emoji typing assistance popup should be located next to the textarea, within that element with position: relative:

    <span style="position: relative; display: inline-block;">
      {{textarea class="my-input" value=wikiPageText}}
      <!-- emoji typing assistance popup is gonna be located here -->
    </span>
    
    <!-- emoji picker popup is gonna be located here -->
    
    <!-- the button-that-toggles-the-popup is gonna be located here -->
  5. Wrap the three items with the emoji-picker-wrapper component:

    {{#emoji-picker-wrapper}}
      <span style="position: relative; display: inline-block;">
        {{textarea class="my-input" value=wikiPageText}}
        <!-- emoji typing assistance popup is gonna be located here -->
      </span>
    
      <!-- emoji picker popup is gonna be located here -->
    
      <!-- the button-that-toggles-the-popup is gonna be located here -->
    {{/emoji-picker-wrapper}}

    It should serve as a common parent for everything that we set up earlier.

  6. Pass the following arguments into the wrapper component:

    • text: the same bound property as you pass into the text field;
    • inputSelector: a CSS selector that uniquely identifies the text field within the wrapper;
    {{#emoji-picker-wrapper
      text                = wikiPageText
      inputSelector       = ".my-input"
    }}
      <span style="position: relative; display: inline-block;">
        {{textarea class="my-input" value=wikiPageText}}
        <!-- emoji typing assistance popup is gonna be located here -->
      </span>
    
      <!-- emoji picker popup is gonna be located here -->
    
      <!-- the button-that-toggles-the-popup is gonna be located here -->
    {{/emoji-picker-wrapper}}
  7. Implement the emojiInsertedAction action and pass it into the wrapper component.

    The action receives the text updated with emoji inserted. It must update the value that you pass into both the text field and the wrapper.

    The action could look like this:

    actions: {
      emojiInserted(text) {
        this.setProperties({text});
      }
    }

    But the simplest way of implementing the action is the mut helper, which lets you avoid adding the above action code to your controller/component.

    {{#emoji-picker-wrapper
      text                = wikiPageText
      inputSelector       = ".my-input"
      emojiInsertedAction = (action (mut wikiPageText))
    }}
      <span style="position: relative; display: inline-block;">
        {{textarea class="my-input" value=wikiPageText}}
        <!-- emoji typing assistance popup is gonna be located here -->
      </span>
    
      <!-- emoji picker popup is gonna be located here -->
    
      <!-- the button-that-toggles-the-popup is gonna be located here -->
    {{/emoji-picker-wrapper}}
  8. The wrapper component will create instances of the three child components: emojiPicker, emojiPickerToggler and emojiAssist.

    These instances come preconfigured to work together, saving you a ton of boilerplate code.

    Receive them from the wrapper component and insert into dedicated places:

    {{#emoji-picker-wrapper
      text                = wikiPageText
      inputSelector       = ".my-input"
      emojiInsertedAction = (action (mut wikiPageText))
      as |h|
    }}
      <span style="position: relative; display: inline-block;">
        {{textarea class="my-input" value=wikiPageText}}
        {{component h.emojiAssist}}
      </span>
    
      {{component h.emojiPicker}}
    
      {{component h.emojiPickerToggler}}
    {{/emoji-picker-wrapper}}

    You can customize them if you want. Read each component's documentation below to see how you can customize them.

That's it!

Here's a list of options that are preconfigured for the components. Make sure not to override them:

  • emoji-picker

    • selectAction
    • closeAction
    • isVisible
  • emoji-picker-toggler

    • toggleAction
    • isEmojiPickerVisible
  • emoji-typing-assistance

    • filterInput
    • $input
    • keyPressNotifier
    • selectAction

emoji-picker-wrapper options

Option Type Default value Description
inputSelector String required Unique selector of a text field where emoji will be inserted.
text String required Content of the text field. Emoji code will be inserted into this text.
emojiInsertedAction Action required Action to execute when emoji is inserted. Will be called with new text already containing an emoji code.
shouldSetFocusToInput Boolean true Whether to focus on the input field after emoji insertion or closing the picker with Esc.
isEmojiPickerVisible Boolean false Lets you control picker visibility manually. Don't override this if you're using emojiPickerToggler which controls it for you.
emojiTypingRegex RegExp `/(?:^ \s)(:[\w_+-]+)$/`

emoji-picker-wrapper yielded values

emoji-picker-wrapper yields a hash that contains the following properties:

Option Type Description
emojiPicker Component Preconfigured emoji-picker component.
emojiPickerToggler Component Preconfigured emoji-picker-toggler component.
emojiAssist Component Preconfigured emoji-typing assistance component.
isEmojiPickerVisible Boolean Whether emoji picker is visible.

emoji-picker component

This component is used to select emoji from a list. Designed as a popup.

Render it like this:

{{#if isPickerVisible}}
  {{emoji-picker
    selectAction = (action 'selectEmoji')
  }}
{{/if}}

emoji-picker options

The following options are preconfigured by the wrapper component. Override them only if you're using emoji-picker manually, without the wrapper:

Option Type Default value Description
selectAction Action required Action to execute when an emoji is clicked
closeAction Action undefined Action to execute on click outside of the component
isVisible Boolean false Controls component visibility with display: none (from Ember.Component)

These are the options that you can configure regardless of whether you're using the wrapper.

Option Type Default value Description
toneSelectAction Action undefined Action to execute when skin tone is changed
shouldCloseOnSelect Boolean false Whether to execute the close action when an emoji is selected
disableAutoFocus Boolean false Prevents from focusing on component when first rendered
textNoEmojiFound String "No emoji found" Override for i18n
textSearch String "Search" Override for i18n
textClearSearch String "Clear search" Override for i18n

emoji-picker-toggler component

This component isn't supposed to be used separately. It's yielded by the emoji-picker-wrapper component and is used to control emoji picker visibility.

This component has no styles, you'll have to style it yourself.

Can be used in inline and block form.

emoji-picker-toggler options

Options preconfigured by the wrapper are not listed.

Option Type Default value Description
label String or undefined undefined If block is not provided, will be used for button content.
labelWhenOpen String or undefined undefined If block is not provided, will be used for button content when the picker is visible. If not provided, label will be used instead.
any other attribute String or undefined undefined Any other attributes will be bound to HTML attributes of the component.

emoji-typing-assistance component

This component isn't supposed to be used separately. It's yielded by the emoji-picker-wrapper component and is used to control emoji picker visibility.

It shows suggestions when user types in an emoji code, letting them insert the emoji quicker.

emoji-typing-assistance options

Options preconfigured by the wrapper are not listed.

Option Type Default value Description
minLength Integer 3 Minimum length of the emoji code fragment for the component to appear, including the leading colon.

Using the emojione JS library directly

The emojione library makes itself available as a global.

To help you stay true to the Ember way, this addon lets you import the library as a ES module:

import emojione from 'emojione';

Using emojione-png-sprites tailored spritesheets

The hack we

I18n

The addon itself does not integrate with any i18n solution.

Components accept i18n strings as arguments. You can subclass components to change defaults.

In order to translate emoji descriptions (visibile on some emoji on hover), you'll have to override the emojiDefs property on the emoji service.

Upgrading from older versions

Some major version bumps introduce breaking changes. Read below what you need to change in your app in order to upgrade ember-emojione.

1.x → 2.x

emoji-picker-wrapper used to yield three components separately:

{{#emoji-picker-wrapper
  as |emojiAssist emojiPickerToggler emojiPicker|
}}
  {{component emojiAssist}}
  {{component emojiPickerToggler}}
  {{component emojiPicker}}
{{/emoji-picker-wrapper}}

In 2.0.0+, it yields a hash that contains the components:

{{#emoji-picker-wrapper
  as |h|
}}
  {{component h.emojiAssist}}
  {{component h.emojiPickerToggler}}
  {{component h.emojiPicker}}
{{/emoji-picker-wrapper}}

2.x → 3.x

No changes required.

Development

Installation

  • npm i -g yarn
  • git clone <repository-url> this repository
  • cd ember-emojione
  • yarn install ⚠️
  • bower install

Running

  • npm run lint:hbs
  • npm run lint:js
  • npm run lint:js -- --fix

Running tests

  • ember test – Runs the test suite on the current Ember version
  • ember test --server – Runs the test suite in "watch mode"
  • ember try:each – Runs the test suite against multiple Ember versions

Do not use npm or ember install, use yarn

This project uses Yarn to lock dependencies. Install yarn with npm i -g yarn.

To install this addon's npm dependencies locally, do:

yarn install

To install an Ember addon into this addon, do:

yarn add -D <package-name>
ember g <package-name>

An error message "no such blueprint" is expected in case the addon does not want to do boilerplate customizations.

For more information on using ember-cli, visit https://ember-cli.com/.

Branch names

Main branches are named as gen-1, gen-2, etc. Default branch on GitHub is where active development happens.

This naming scheme is due to the fact that this project uses SemVer. As a result, major version number will rise very quickly, without any correlation with actual major changes in the app.

The number in the branch name, "generation", is supposed to be incremented in these cases:

  • A huge improvement or change happens in the addon.
  • There's a change in the addon's API or architecture which introduces a necessity to maintain more than one branch at a time.
  • The codebase is started from scratch.

Pull requests are welcome from feature branches. Make sure to discus proposed changes with addon maintainers to avoid wasted effort.

Updating the table of contents

Maintaining the TOC by hand is extremely tedious. Use this tiny webapp to generate the TOC automatically. Enable the first two checkboxes there.

Demo deployment

This command will deploy the app to https://deveo.github.io/ember-emojione/ :

ember deploy prod

Credits

Proudly built in @Deveo by @lolmaus, @vvainio and contributors.

https://deveo.com

This addon includes fragments of code borrowed from:

License

MIT.