An emoji dictionary in Svelte
Tags: emoji, svelte, javascript, tutorial
Published: 2019-05-24 20:37:10

As an avid user of the [🅱️ emoji](https://knowyourmeme.com/memes/b-button-emoji-%F0%9F%85%B1), I get (too much) enjoyment out of the few alphabetical characters in the emoji alphabet.

But we can do more than just substitute a `b` with a 🅱️; I wanted to know many words can be written _entirely_ with emoji. Let's find out!

<img src="https://i.giphy.com/media/l2Je66zG6mAAZxgqI/giphy.gif" />

First I found an [(English) dictionary](https://github.com/dwyl/english-words) and wrote a quick & dirty [Rust script](https://github.com/brycedorn/emoji-dict/blob/master/src/main.rs) to generate the words. Just a list of words isn't fun though, it needs interactivity! I chose [Svelte](https://svelte.dev/) for this to get some hands-on with its dev experience (it's pretty good!) and performance. 

To start, I made a basic webpack config with [svelte-loader](https://github.com/sveltejs/svelte-loader) and three files:
<figcaption>Note: if you want to skip to the end, the source is <a href="https://github.com/brycedorn/emoji-dict/tree/master/web">here.</a></figcaption>
 - `index.html`, with a `<body>` where the Svelte app will be mounted to (just like `ReactDOM`)
 - `main.js`, where the app is mounted & passed props
 - `App.svelte`, for the component & filtering logic

---
<img src="https://thepracticaldev.s3.amazonaws.com/i/atloi9557ivpk64uvphk.png" width=200/>

# JavaScript

In `main.js`, the words are imported and prepared for the component:
```js
import words from 'output.txt';

// Associate letters & sequences with their 
// emoji equivalents
const emojiHash = {
	"id": "🆔",
	"a": "🅰️",
        ...
	"soon": "🔜"
};

// Replace the letters/sequences in a string as 
// their respective emoji
const convertToEmoji = (word) => {
	let emojified = String(word);
	regexKeys.forEach((key, i) => {
        emojified = emojified.replace(key, emojiHash[sortedKeys[i]];
    }));
	return emojified;
};

// Render letters/sequences as emoji by running 
// the above function until there are no letters
// remaining
function emojify(word) {
	let emojified = String(word);
	do {
		emojified = convertToEmoji(emojified);
	} while (emojified.split('').some(e => /^[a-zA-Z]+$/.test(e)));
	return emojified;
};

```

Then the component is mounted to the DOM:
```js
const app = new App({
	target: document.body,
	props: {
		emoji: Object.values(emojiHash),
		sort: 'default',
		words: words.split('\n').map(emojify)
	}
});
```
---
<img src="https://thepracticaldev.s3.amazonaws.com/i/ji7zisis4c0f4ce2cer1.png" width=200/>

# Svelte

Great! Now we have formatted data coming into the component, let's do something with it.

`*.svelte` files are HTML files with some syntactic sugar. The basic structure is as follows:
```html
<script>
  // Functions, variables
  export let words;

  function clicky(e) {
    console.log(e.target.innerText);
  }
</script>

<!-- Any styles associated with the component -->
<style>
  .container { 
    background: palevioletred; 
  }
</style>

<!-- The rendered markup -->
<div class="container">
  <ul>
    {#each words as word}
      <li>
        <p on:click={clicky}>
          {word}
        </p>
      </li>
    {/each}
  </ul>
</div>
```

:tada: ta-da! :tada: A list of words rendered with Svelte! Note that since `words` is being passed in as a prop, the `export` keyword is needed.

For the sake of brevity I'll just go through adding filtering (sorting is in the [repo](https://github.com/brycedorn/emoji-dict/blob/master/web/App.svelte#L39) if you want to take a look).

![](https://thepracticaldev.s3.amazonaws.com/i/ypaz2g1ztab0cya0dor0.png)

Somewhere in the component, let's render a list of checkboxes for each emoji:

```html
Filter:
{#each emoji as moji}
  <label>
    <input on:change={handleFilterClick} type="checkbox" checked={!filteredEmoji.includes(moji)} value={moji}>
    <span>{moji}</span>
  </label>
{/each}
```

Since we're rendering the list via the `words` variable, we'll need to update it to reflect the filter.

```html
<script>
  export let words;

  // Keep an immutable version of words in memory
  export let wordsImm = Array.from(words);

  function handleFilterClick(e) {
    const { checked, value } = e.target;

    // Keep immutable version of original prop & make a copy 
    // to apply filters to
    let wordsMut = Array.from(wordsImm);

    // Either add or remove the current emoji from the filters
    if (checked) {
      filteredEmoji.splice(filteredEmoji.indexOf(value), 1);
    } else {
      filteredEmoji.push(value);
    }

    // If there are filters, apply them to list of words
    if (filteredEmoji.length > 0) {
      filteredEmoji.forEach(emoji => {
        wordsMut = wordsMut.filter(word => !word.includes(emoji));
      });
    }

    // Set variable to new list
    words = wordsMut;
  }
</script>
```

When `words` is updated to the filtered (mutated) version after selecting a filter, it will trigger an update and the DOM will render the new list. 

<figcaption>Side-note: this could be refactored to have the filtering in the `{each}`, (preventing the need to update `words`) but I wanted to render the number of words in a different part of the component.</figcaption>

# Final thoughts

Svelte is nice & fast! I plan to use it again, ideally for something more resource intensive/visually demanding to really push it to its limits (beyond where React would have issues).

I also want to see how it is to work on a larger project using [Sapper](https://github.com/sveltejs/sapper), once the framework is more mature.


Go play with it here! [https://bryce.io/emoji-dict](https://bryce.io/emoji-dict)

View [source on Github](https://github.com/brycedorn/emoji-dict).