missive / emoji-mart

🏪 One component to pick them all
https://missiveapp.com/open/emoji-mart
MIT License
8.59k stars 826 forks source link

How replace emoji codes in html to Emoji Component? #79

Closed Jenan closed 6 years ago

Jenan commented 7 years ago

Hello,

I want to display the Emoji Component like bellow but I don't know how can I replace the html result with component like:

Can you help me with this please?

Thank you.

I get html like this

Hello, how are you? :thumbsup:

I would like to get something like this:

Hello, how are you? <Emoji emoji=':thumbsup:'  size={64} />
EtienneLem commented 7 years ago

👋

You will need a regex to detect the colons-syntax emojis, then you will be able to reconstruct your children with a mix of strings and Emoji components (given you already are in a React app)

Here’s a quick snippet of how we parse them in Missive:

let string = 'Hello, how are you? :thumbsup:'
let regex = new RegExp('(^|\\s)(\:[a-zA-Z0-9-_+]+\:(\:skin-tone-[2-6]\:)?)', 'g')

let match
while (match = colonsRegex.exec(string)) {
  let colons = match[2]
  let offset = match.index + match[1].length
  let length = colons.length

  console.log(colons, offset, length)
}

The colons variable can be used as-is w/ the Emoji component: <Emoji emoji={colons}>, it supports skin color too.

The rest (instead of the console.log) can vary depending on many factors, so I’ll let you figure out how that can be implemented in your app 😄

Jenan commented 7 years ago

Thank you for your suggestion.

I would like to ask you about the implementation this awesome component with some input or textarea. Can you share some code or suggest some example how implement this component with input/textbox? :)

BWT: second question - how display in component <Emoji emoji=':)' /> smile like this :)

kiranbabu189 commented 7 years ago

how to set emoji into a textarea box or input box

janbuerling commented 7 years ago

Hello @kiranbabu189 , this is a bit complicated, but I have found a good solution.

I will explain it step by step.

1. Handler for the emoji-mart

Access the onClick method from the emoji-mart and act the chosen emoji.

onClick={this.handleClickEmojiIcon}

handleClickEmojiIcon(emoji) { ... }

2. Get the curser positon in textarea

Add these two lines to both componentDidMount and componentWillUnmount.

document.addEventListener('input', this.handleCursorPosition.bind(this), true); document.addEventListener('click', this.handleCursorPosition.bind(this), true);

And create the method

handleCursorPosition(e) {
    if (e.target === this.textarea) {
      this.setState({ 
         curserPositonStart: e.target.selectionStart,
         curserPositonEnd: e.target.selectionEnd });
      }
  }

Be aware that you must place a reference on the textarea. I have called this in my case textarea. ref={ref => (this.textarea = ref)}

3. The content of handleClickEmojiIcon method

const textareaStrParts = [
        `${this.textarea.value.substring(0, this.state.curserPositonStart)}`,
        `${emoji.native}`,
        `${this.textarea.value.substring(this.state.curserPositonEnd, this.length)}`,
      ];

      textareaValue = textareaStrParts.join('');

Then you can transfer the result back to the textarea.

I hope I could help you

kiranbabu189 commented 7 years ago

thanks man for your effort. i will certainly give a try... :)

On Mon, Jul 31, 2017 at 2:05 PM, Jan Bürling notifications@github.com wrote:

Hello @kiranbabu189 https://github.com/kiranbabu189 , this is a bit complicated, but I have found a good solution.

I will explain it step by step.

  1. Handler for the emoji-mart

Access the onClick method from the emoji-mart and act the chosen emoji.

onClick={this.handleClickEmojiIcon}

handleClickEmojiIcon(emoji) { ... }

  1. Get the curser positon in textarea

Add these two lines to both componentDidMount and componentWillUnmount.

document.addEventListener('input', this.handleCursorPosition.bind(this), true); document.addEventListener('click', this.handleCursorPosition.bind(this), true);

And create the method

handleCursorPosition(e) { if (e.target === this.textarea) { this.setState({ curserPositonStart: e.target.selectionStart, curserPositonEnd: e.target.selectionEnd }); } }

Be aware that you must place a reference on the textarea. I have called this in my case textarea. ref={ref => (this.textarea = ref)}

  1. The content of handleClickEmojiIcon method

const textareaStrParts = [ ${this.textarea.value.substring(0, this.state.curserPositonStart)}, ${emoji.native}, ${this.textarea.value.substring(this.state.curserPositonEnd, this.length)}, ];

  textareaValue = textareaStrParts.join('');

Then you can transfer the result back to the textarea.

I hope I could help you

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/missive/emoji-mart/issues/79#issuecomment-319003881, or mute the thread https://github.com/notifications/unsubscribe-auth/ATR8Sdcc54q29MYC3Xi3zs4sDJwthMEDks5sTZHtgaJpZM4NdA-s .

Jenan commented 7 years ago

@kiranbabu189 Please, if you create a detailed working sample, feel free to share it. :)

jmcrthrs commented 7 years ago

@janbuerling Why not just use the following this.textarea.selectionStart directly like so:

const textareaStrParts = [
        `${this.textarea.value.substring(0, this.textarea.selectionStart)}`,
        `${emoji.native}`,
        `${this.textarea.value.substring(this.textarea.selectionEnd, this.length)}`,
      ];

      textareaValue = textareaStrParts.join('');

I tried this and it works, however I feel I may be missing an edge case that you aren't...

ravikantmishra commented 6 years ago

Hi @EtienneLem Could you please help me figure this out. How I can render var body = "Hello, how are you? <Emoji emoji=':thumbsup:' size={64} />" with

<div dangerouslySetInnerHTML={{__html: body}}></div>

EtienneLem commented 6 years ago

Only way I can think of, but only works with native emojis:

var EmojiMart = require('emoji-mart')
var body = "Hello, how are you? ${EmojiMart.emojiIndex.emojis['+1'].native}"

Because dangerouslySetInnerHTML doesn’t support components, only pure HTML. We could eventually add a toHTML function to the component that would return an HTML string.

ajbeaven commented 6 years ago

I'd be keen for a toHTML function. I'm working with a markdown component which requires its input to be supplied in a parameter (it too would be using dangerouslySetInnerHTML).

ivawzh commented 6 years ago

+1, I really need toHTML to support dangerouslySetInnerHTML too.

veob commented 6 years ago

Here is my solution to render native emoji as <Emoji /> components. Hope it'll be useful to somebody. Feel free to suggest improvements in the comments to the gist.

Whole code: https://gist.github.com/veob/1dfe6cfb39a4a5768ec66c5e2cec9574

The gist of the gist (sorry):

    let matchArr;
    let lastOffset = 0;
    const parts = [];
        // *children* is a string containing native emoji 

    while ((matchArr = regexp.exec(children)) !== null) {
        parts.push(children.substring(lastOffset, matchArr.index));

        // skip 2 chars, because an emoji is 2-chars wide
        lastOffset = matchArr.index + 2;

        const emoji = getByNative(matchArr[0]);

        if (emoji) {
            parts.push(<Emoji emoji={emoji.id} size={16} />);
        } else {
            parts.push(matchArr[0]);
        }
    }

    parts.push(children.substring(lastOffset, children.length));
EtienneLem commented 6 years ago

Starting v2.4.1, you can now use the Emoji component with dangerouslySetInnerHTML

Using with dangerouslySetInnerHTML

The Emoji component being a functional component, you can call it as you would call any function instead of using JSX. Make sure you pass html: true for it to return an HTML string.

<span dangerouslySetInnerHTML={{
__html: Emoji({
html: true
set: 'apple'
emoji: '+1'
size: 24
})
}}></span>
NikolasStelmah commented 5 years ago

here is my variant how to take a string like "something righft here and some smile here :shrug: with another :santa::skin-tone-3:" and replace colons in string to emojiis and futher parsing into html:

let matchArr;
let lastOffset = 0;
const regex = new RegExp('(\:[a-zA-Z0-9-_+]+\:(\:skin-tone-[2-6]\:)?)', 'g');
const partsOfTheMessageText = [];
while ((matchArr = regex.exec(currentMessage.text)) !== null) {
  const previousText = currentMessage.text.substring(lastOffset, matchArr.index);
  if (previousText.length) partsOfTheMessageText.push(previousText);

  lastOffset = matchArr.index + matchArr[0].length;

 const emoji = (
    <Emoji
      emoji={matchArr[0]}
      set="emojione"
      size={22}
      fallback={(em, props) => {
        return em ? `:${em.short_names[0]}:` : props.emoji;
      }}
    />
  );

  if (emoji) {
    partsOfTheMessageText.push(emoji);
  } else {
    partsOfTheMessageText.push(matchArr[0]);
  }
}
const finalPartOfTheText = currentMessage.text.substring(lastOffset, currentMessage.text.length);
if (finalPartOfTheText.length) partsOfTheMessageText.push(finalPartOfTheText);`

and somewhere in return method of message bubble: `return ( ...

{partsOfTheMessageText.map(p => {p})}
   ...`
JcFavour commented 4 years ago

adasdaasasdasda 24e48938e916a714d704222827379a20 ``

Pong420 commented 3 years ago

Just tested on latest chrome and safari.

const colons = `:[a-zA-Z0-9-_+]+:`;
const skin = `:skin-tone-[2-6]:`;
const colonsRegex = new RegExp(`(${colons}${skin}|${colons})`, 'g');

interface Props {
  content: string;
}

 function Component({content}: Props) {
    return (
      <div>
        {content
          .split(colonsRegex)
          .map(
            (emoji, idx) =>
              !!emoji && (
                <Emoji
                  size={24}
                  key={idx}
                  emoji={emoji}
                  fallback={() => emoji as any}
                />
              )
          )}
      </div>
    );
  }