nolanlawson / emoji-picker-element

A lightweight emoji picker for the modern web
https://nolanlawson.github.io/emoji-picker-element/
Apache License 2.0
1.43k stars 82 forks source link

Install procedure with a SvelteKit app #371

Closed BigBoulard closed 10 months ago

BigBoulard commented 10 months ago

Hi guys,

What's the best way to install your project with a SvelteKit app?

Best.

nolanlawson commented 10 months ago

I'm not familiar with SvelteKit, but emoji-picker-element is a bog-standard custom element. So you should be able to just import it the way you would import it in any other framework.

The main difference is that emoji-picker-element uses Svelte under the hood, so you're free to do import 'emoji-picker-element/svelte.js' to save some bytes. But it's a very minor performance optimization.

The other consideration is that this project doesn't support SSR, and you probably wouldn't want to SSR it anyway since it makes more sense to lazy-load it from a user interaction, client-side.

I hope that helps! Let me know if anything is unclear from the documentation. :bow:

BigBoulard commented 9 months ago

Hi @nolanlawson thank you for your response. I just realized that my question was misleading, sorry. In fact, I'm using sveltekit but I don't need SSR, so my question is not really specific to sveltekit but svelte.

I just tried to install using the npm module cause I don't want to load the library at the application startup using the script tag (and all dependencies are actually managed as npm modules) but it failed I don't know why (I'm not a frontend dev):

pnpm run dev emoji-picker-element

In component.svelte ...

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

<emoji-picker />
requestAnimationFrame is not defined
ReferenceError: requestAnimationFrame is not defined
    at file:///Users/me/dev/project/node_modules/.pnpm/emoji-picker-element@1.18.4/node_modules/emoji-picker-element/picker.js:676:13
    at ModuleJob.run (node:internal/modules/esm/module_job:192:25)
    at async DefaultModuleLoader.import (node:internal/modules/esm/loader:228:24)
    at async nodeImport (file:///Users/me/dev/project/node_modules/.pnpm/vite@4.5.0_terser@5.22.0/node_modules/vite/dist/node/chunks/dep-bb8a8339.js:56097:17)
    at async ssrImport (file:///Users/me/dev/project/node_modules/.pnpm/vite@4.5.0_terser
nolanlawson commented 9 months ago

Yep, that's because it only works in client-side rendering, not server-side rendering. I don't know what the mechanism in SvelteKit is to force something to render only client-side, but that's what you would need to do. requestAnimationFrame is only available in the browser, not in Node.js.

BigBoulard commented 9 months ago

Hi,

about prerendering: https://kit.svelte.dev/docs/page-options#prerender about SSR: https://kit.svelte.dev/docs/page-options#ssr

I disabled prerendering by adding this file /routes/+layout.js with the code below

export const prerender = false;
export const ssr = false;

From there, It would have been great if I could be able to import like this but there's probably some adaptation to do in the npm package.

import { Picker } from 'emoji-picker-element';
// or this if there was a default export: import Picker from 'emoji-picker-element';
// or something like: import { Picker } from 'emoji-picker-element/svelte';

let picker = new Picker.default({
    locale: 'en'
});
picker.addEventListener('emoji-click', onPickerClick);
emojiPanel.appendChild(picker);

So, in the end, here is what's working. Pretty ugly but it works I think it would be beneficial if you could adapt this a little bit this snippet and add it as an example to your README.md.

import { onMount } from 'svelte';

let Picker = null; // picker lib
let picker = null; // picker instance

onMount(async () => {
    Picker = await import('emoji-picker-element/svelte');

    picker = new Picker.default({
        locale: 'en'
    });
    picker.addEventListener('emoji-click', onPickerClick);
    emojiPanel.appendChild(picker);
});

function onPickerClick(e) {
    console.log(e.detail);
}
</script>

<div class="emoji-panel" class:hidden={!showEmojiPanel} bind:this={emojiPanel} />

<div class="input-wrapper">
  <input
        type="text"
        class="input-field"
        name="message"
        autocomplete="off"
  />
  <button class="emoji-button" on:click={() => (showEmojiPanel = !showEmojiPanel)}>&#128516;</button>
  <button class="send-button" on:click={send}>Send</button>
</div>

<style>
.emoji-panel {
    position: absolute;
    bottom: 50px;
    right: 80px;
}

 .input-wrapper{
    display: flex;
    align-items: center;
    padding: 10px;
}

.input-field {
    flex-grow: 1;
    border: none;
    outline: none;
    padding: 5px;
    text-align: right;
    width: 100%;
}

.emoji-button {
    background-color: transparent;
    border: none;
    cursor: pointer;
    font-size: 20px;
    margin-left: 10px;
}

.send-button {
    background-color: blue;
    padding: 10px 20px;
    cursor: pointer;
}

</style>
nolanlawson commented 9 months ago

Thanks for the explanation. This is the key point I think:

onMount(async () => {
  Picker = await import('emoji-picker-element/svelte');
  // ...
})

onMount only runs in the browser when using Svelte. So this avoids the SSR issue.

I would add something to the README, but I don't necessarily want to put something SvelteKit-specific in there. The advice will vary from container to container (Next, Remix, SolidStart, Vue+Vite, etc.).

nolanlawson commented 9 months ago

BTW here is a simpler solution (IMO):

<emoji-picker></emoji-picker>
<script>
  import { onMount } from 'svelte'
  onMount(() => import('emoji-picker-element/svelte'))
</script>

The <emoji-picker> tag is rendered server-side, but it's only upgraded to a custom element in onMount when the component is imported. I tested it in a minimal SvelteKit app, and it works:

Screenshot from 2023-11-15 07-05-49

BigBoulard commented 9 months ago

BTW here is a simpler solution (IMO):

<emoji-picker></emoji-picker>
<script>
  import { onMount } from 'svelte'
  onMount(() => import('emoji-picker-element/svelte'))
</script>

The <emoji-picker> tag is rendered server-side, but it's only upgraded to a custom element in onMount when the component is imported. I tested it in a minimal SvelteKit app, and it works:

Screenshot from 2023-11-15 07-05-49

To complete the answer, you can listen to events as you do on the JSX part of the code like below:

<emoji-picker on:emoji-click={doSomething} />
Terkwood commented 8 months ago

This was really helpful. I wish the instructions were printed in the README, because I had to spend about 30 minutes searching around for it. I'm not very skilled as a frontend dev so it was nice to find this explanation in the issue tracker.

nolanlawson commented 8 months ago

Thanks for the feedback. Added a note to the README ^